/* ==========================================================================
   LONSTERN. Own it. Don't rent it.

   The identity already carried two accents, and they map straight onto the
   argument, so nothing had to be invented over the top of it:

       #FF4343 red   = rent. Money leaving. Never used decoratively.
       #0130F9 blue  = owned. Yours. The thing that stops the meter.

   Type is the brand's own: Asgard for display, London for text. PlexMono is
   the one addition, and only for figures, because a meter has to be tabular.

   Fluid from 320px to 1600px. No dead zones, no fixed pixel type.
   ========================================================================== */

@font-face {
  font-family: "Asgard";
  src: url("/assets/fonts/Asgard-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Asgard";
  src: url("/assets/fonts/Asgard-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "London";
  src: url("/assets/fonts/London-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "London";
  src: url("/assets/fonts/London-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "London";
  src: url("/assets/fonts/London-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "London";
  src: url("/assets/fonts/London-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "PlexMono";
  src: url("/assets/fonts/PlexMono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "PlexMono";
  src: url("/assets/fonts/PlexMono-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens -------------------------------------------------------------- */

:root {
  --ink: #000108;
  --ink-2: #06080e;
  --ink-3: #0e1119;
  --white: #fcfcfc;
  --off: #f2f3f6;

  --blue: #0130f9;
  /* The blue that survives a dark background. Panels reached for a lighter
     invented one in seven places before this was noticed; there is one blue
     for light surfaces and one for dark, and no third. */
  --blue-lo: #4a6cff;
  /* Свет тёмной комнаты, снятый в фоновый слой. Те же два источника, что у
     `.mig-pin::before` (синий справа-выше, белый слева-ниже) и те же альфы, но
     размеры в единицах ЭКРАНА, а не в rem: на телефоне блоб в 48rem шире окна
     вдвое и ложится ровной заливкой вместо пятна. Нужен фоном, а не
     псевдоэлементом, там, где пин перестаёт быть системой координат и
     `inset: -20%` уезжает за край документа. */
  --night-glow:
    radial-gradient(150vw 78svh at 68% 46svh, rgb(74 108 255 / 0.15), transparent 70%),
    radial-gradient(100vw 54svh at 2% 88svh, rgb(252 252 252 / 0.045), transparent 72%);
  --red: #ff4343;
  /* Свет первого экрана на большом экране, числа `.mig-pin::before` с
     `/migration/`: синий 0.11 справа-выше, белый 0.03 слева-ниже. Позиция в
     svh, а не в процентах контейнера: контейнер бывает и в 100svh (пин), и в
     5600px (секция с дорожкой), а свет обязан стоять на ПЕРВОМ экране. */
  --first-glow:
    radial-gradient(48rem 44rem at 66% 44svh, rgb(74 108 255 / 0.11), transparent 68%),
    radial-gradient(34rem 34rem at 8% 70svh, rgb(252 252 252 / 0.03), transparent 72%);

  /* Work arriving that nobody is measuring. Introduced for the paths screen
     on /money-trail/, where the threads that stop short are not a loss but
     the opposite: money coming in from sources no account can name. Red would
     have said the money was leaving, which on that screen is false, and the
     owner rejected it on sight before the semantics were argued.
     11.68:1 against --ink, and a different hue from both blues rather than a
     third value on the same axis. */
  --green: #3ddc84;
  --red-lo: #ff8a8a;

  /* The near-black every solid panel is cut from: the interface rail, the
     sorting board, the doors. Body text is the same ink, which is why one
     token serves both. */
  --panel-dark: #0b0d14;

  --grey: #626579;
  --grey-2: #8c8f9a;

  --text: var(--panel-dark);
  --text-muted: #626579;
  --on-dark: #fcfcfc;
  --on-dark-muted: #8c8f9a;

  --line: #dfe1e8;
  --line-dark: #191d28;

  /* Below the scale, and deliberately fixed: the uppercase mono label that
     marks a section, a door or a form field. Was typed by hand as 0.6rem,
     9.6px, 10.5px and 11px in different places. */
  --step-label: 0.6rem;
  --step--1: clamp(0.8rem, 0.77rem + 0.13vw, 0.88rem);
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.13rem);
  --step-1: clamp(1.18rem, 1.1rem + 0.4vw, 1.5rem);
  --step-2: clamp(1.45rem, 1.26rem + 0.9vw, 2.1rem);
  --step-3: clamp(1.8rem, 1.42rem + 1.9vw, 3.1rem);
  --step-4: clamp(2.2rem, 1.5rem + 3.4vw, 4.4rem);
  --step-5: clamp(2.7rem, 1.4rem + 6.4vw, 7rem);
  /* Заголовок первого экрана (h1). 80px на 1900, 40 на телефоне.
     18.09.2026: был --step-5 (112px), PO трижды за день: «заголовок наехал
     на контент» (/site/), «слишком большой» (/money-trail/), «слишком
     большой, не вмещается» (/ai-seo/). Замер по 17 первым экранам: при 112
     обрезка и наезд на /ai-seo/, 4-5 строк на /money-trail/ и
     /brand-platform/; при 90 /ai-seo/ всё ещё режется, /digital-inventory/
     уходит за верх экрана. 70 чист везде, но это --step-4, кегль заголовка
     раздела, и тогда герой страницы равен разделу; PO на /services/: «заголовок
     не может быть по иерархии меньше, чем всё остальное». 80 держит ступень
     над разделом, а два оставшихся конфликта это раскладка, и они чинятся
     раскладкой (колонка /ai-seo/, ширина заголовка /site/). */
  --hero: clamp(2.4rem, 1.4rem + 4.4vw, 5rem);
  /* Ступень ВЫШЕ героя, и роль у неё ровно одна: единственная цифра-итог,
     на которую приземляется целый экран. Заведена 25.08 по требованию PO
     сделать итог пролёта на `/money-trail/` крупнее: `--step-5` там уже
     стоял, то есть упирался в потолок шкалы.

     Не для заголовков. Заголовок секции это `--step-4`, герой страницы
     `--step-5`. Если этой ступенью набрано что-то кроме итоговой цифры,
     это ошибка, а не решение. */
  --step-6: clamp(3.6rem, 1.6rem + 9.6vw, 10.5rem);

  /* Two roles above the body scale, and nothing is allowed its own size.
     --title is every section heading on the site, whatever surface it sits
     on. --display is a statement standing alone on a screen: the panels
     between sections and the line over the arrival. */
  --title: var(--step-4);
  --display: clamp(2.2rem, 1.1rem + 4.4vw, 5rem);

  --sp-xs: clamp(0.35rem, 0.3rem + 0.25vw, 0.5rem);
  --sp-s: clamp(0.6rem, 0.5rem + 0.5vw, 0.9rem);
  --sp-m: clamp(1rem, 0.85rem + 0.75vw, 1.6rem);
  --sp-l: clamp(1.6rem, 1.3rem + 1.5vw, 2.8rem);
  --sp-xl: clamp(2.5rem, 1.9rem + 3vw, 5rem);
  --sp-xxl: clamp(4rem, 2.8rem + 6vw, 9rem);

  --wrap: 84rem;
  --wrap-narrow: 46rem;
  --header-h: 4.25rem;
  --meter-h: 2.35rem;
  --radius: 4px;
  /* ⚠️ ОДИН радиус на все карточки. Аудит 05.09 по 21 странице нашёл три
     радиуса в одной роли: 18 (двери на главной, `clamp(12px,1.2vw,18px)`),
     20 (эталон `.doors`, `clamp(14px,1.4vw,20px)`), 22 (плита работ,
     `clamp(14px,1.6vw,22px)`), и у формы заявки 4px в герое против 20 в
     закрытии. Радиус карточки теперь токен, и своего clamp у карточки нет. */
  --radius-card: clamp(14px, 1.4vw, 20px);

  --card-bg: #fff;
  --card-line: var(--line);
  --card-shadow: 0 1px 2px rgb(0 1 8 / 0.06);

  /* The lift, in three steps. Before this there were eight different values
     across two stylesheets: the same panel geometry at three alphas, a second
     geometry at two more, and the interface object had a bespoke three layer
     one, which is why it read as sitting in a different room from the price
     panel above it. Everything that lifts now names a step. */
  --lift-1: 0 0.8rem 2rem -0.8rem rgb(0 2 24 / 0.3);
  --lift-2: 0 1.2rem 2.6rem -1rem rgb(0 2 24 / 0.32);
  --lift-3: 0 1.6rem 3.4rem -1.2rem rgb(0 2 24 / 0.5);
  /* The lit top edge a white panel needs on a dark ground, or it reads as
     printed on rather than laid over. Was 0.6 on one panel and 0.9 on the
     other, for the same job. */
  --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.9);
  --heading-color: var(--text);
  --lede-color: var(--text-muted);
  --tag-color: var(--text-muted);
  --panel-bg: rgb(1 48 249 / 0.04);
  --panel-line: var(--line);
}

/* Dark surfaces reset the same tokens, so a light island inside a dark
   section only has to reset them back. Without this a card inherits a
   dark-surface shadow and a paper-on-paper heading at 1:1 contrast. */
.sect--ink,
.sect--ink-2,
.hero,
.footer,
.on-dark {
  --card-bg: var(--ink-3);
  --card-line: var(--line-dark);
  --card-shadow: 0 24px 60px rgb(0 0 0 / 0.5);
  --heading-color: var(--white);
  --lede-color: var(--on-dark-muted);
  --tag-color: var(--on-dark-muted);
  --panel-bg: rgb(252 252 252 / 0.035);
  --panel-line: var(--line-dark);
}

/* --- Reset --------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--white);
  color: var(--text);
  font-family: London, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 400;
  font-size: var(--step-0);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.005em;
  margin: 0;
  color: var(--heading-color);
  text-wrap: balance;
}

/* ⚠️ `h1` это ГЕРОЙ СТРАНИЦЫ, и его ступень `--hero` (80px, с 18.09; до того
   `--step-5`, 112px, почему сменилась - у самого токена).
   Аудит 05.09 по 21 странице нашёл h1 тремя кеглями: 112 на восьми
   страницах, 70.4 на двенадцати, 80 на одной. Одна роль, три размера.
   Теперь размер задан здесь один раз; частные правила `h1 { --step-4 }` на
   /ai-seo/, /brand-platform/ и /money-trail/ сняты. У каждой страницы ровно
   один h1 (гейт check это держит), поэтому правило не может задеть ничего,
   кроме героя. Телефонные потолки `min(..., 7.6vw)` остаются: там 7.6vw
   выигрывает у любой ступени. */
h1 { font-size: var(--hero); }
h2 { font-size: var(--title); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

p { margin: 0; }
a { color: inherit; text-underline-offset: 0.18em; }
ul, ol { margin: 0; padding: 0; }

:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
::selection { background: var(--blue); color: var(--white); }

/* --- Layout -------------------------------------------------------------- */

/* Full bleed. The measure is the screen, with the same five per cent inset
   the header and the canvas use, so every edge on the site lines up.
   Line length is held by the prose elements themselves rather than by the
   container, because a 1900px paragraph is unreadable however good it looks. */
.wrap {
  width: 100%;
  padding-inline: max(1.25rem, 5vw);
}
.wrap--narrow { max-width: var(--wrap-narrow); margin-inline: auto; }

.sect { padding-block: var(--sp-xxl); }
.sect--tight { padding-block: var(--sp-xl); }
.sect--ink { background: var(--ink); color: var(--on-dark); }
.sect--ink-2 { background: var(--ink-2); color: var(--on-dark); }
.sect--off { background: var(--off); }

.stack > * + * { margin-top: var(--sp-m); }

.split { display: grid; gap: var(--sp-xl); align-items: start; }

@media (min-width: 56rem) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--wide-left { grid-template-columns: 1.2fr 1fr; }
  .split--wide-right { grid-template-columns: 1fr 1.2fr; }
}

.sect__head { max-width: 48rem; margin-bottom: var(--sp-xl); }
.sect__head > * + * { margin-top: var(--sp-s); }

/* --- Type helpers -------------------------------------------------------- */

.tag {
  display: block;
  font-family: PlexMono, ui-monospace, monospace;
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tag-color);
}

.tag--rent { color: var(--red); }
.tag--own { color: var(--blue-lo); }

/* The uppercase mono label. One definition, from .door__mark, which was the
   first place it appeared. */
.label {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: var(--step-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.on-dark .label, .sect--ink .label { color: var(--on-dark-muted); }

.lede {
  font-family: London, sans-serif;
  font-weight: 300;
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--lede-color);
  max-width: 44ch;
  text-wrap: pretty;
}
.lede--wide { max-width: 60ch; }

.mono { font-family: PlexMono, ui-monospace, monospace; font-size: var(--step--1); }

.figure {
  font-family: PlexMono, monospace;
  font-weight: 600;
  font-size: var(--step-5);
  line-height: 0.95;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

/* --- Buttons ------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  /* Свой интерлиньяж. Без него кнопка брала его у родителя: в тексте
     рельсы `/services/` выходила 57px, в форме 49. Одна роль, одна высота. */
  line-height: 1.2;
  min-height: 2.95rem;
  padding: 0.7em 1.5em;
  border: 1px solid var(--blue);
  border-radius: var(--radius);
  background: var(--blue);
  color: var(--white);
  font-family: London, sans-serif;
  font-weight: 500;
  font-size: var(--step-0);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.14s ease, background 0.14s ease, color 0.14s ease;
}

.btn:hover { transform: translateY(-1px); background: #0027cc; border-color: #0027cc; }
.btn:active { transform: translateY(0); }

.btn--ghost { background: transparent; color: var(--text); border-color: var(--line); }
.btn--ghost:hover { background: var(--text); border-color: var(--text); color: var(--white); }

.on-dark .btn--ghost,
.sect--ink .btn--ghost,
.sect--ink-2 .btn--ghost,
.hero .btn--ghost,
.footer .btn--ghost { color: var(--white); border-color: var(--line-dark); background: transparent; }

.on-dark .btn--ghost:hover,
.sect--ink .btn--ghost:hover,
.sect--ink-2 .btn--ghost:hover,
.hero .btn--ghost:hover,
.footer .btn--ghost:hover { background: var(--white); color: var(--ink); border-color: var(--white); }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-s); }

/* --- Meter (the concept) ------------------------------------------------- */

/* Sits under the header on every page. Once a visitor has set their own
   figures on the home page, this follows them and keeps counting. */
.meter {
  position: sticky;
  top: var(--header-h);
  z-index: 45;
  background: var(--ink-2);
  border-bottom: 1px solid var(--line-dark);
  color: var(--on-dark-muted);
}

/* On the page that carries the calculator the strip is revealed after the
   reader has scrolled past it, so it has to be out of flow: revealing a
   sticky element mid-scroll inserts its height at the top of the document
   and shoves everything below it down by 39px. Fixed, and it slides in. */
.meter--float {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--header-h);
}

.js .meter--float { transform: translate3d(0, -100%, 0); }

.js .meter--float[data-in="true"] {
  transform: none;
  transition: transform 0.55s cubic-bezier(0.23, 1, 0.32, 1);
}

@media (prefers-reduced-motion: reduce) {
  .js .meter--float, .js .meter--float[data-in="true"] { transform: none; transition: none; }
}

.meter__inner {
  min-height: var(--meter-h);
  display: flex;
  align-items: center;
  gap: var(--sp-s) var(--sp-m);
  flex-wrap: wrap;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  letter-spacing: 0.01em;
  padding-block: 0.4rem;
}

.meter__dot {
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: var(--red);
  flex: none;
  animation: pulse 2.4s ease-in-out infinite;
}

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

.meter b {
  color: var(--red);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.meter a { color: var(--on-dark-muted); margin-left: auto; }
.meter a:hover { color: var(--white); }

.meter[hidden] { display: none; }

/* --- Header -------------------------------------------------------------- */

/* The drawer lives OUTSIDE this element: a backdrop-filter here makes the
   header the containing block for position:fixed children, which silently
   collapses a full-screen drawer to the header's own box. */
/* Over the flight the header has nothing to sit on, so it sits on nothing:
   a bar with a blurred backdrop reads as a panel from another site pasted
   across the top of the shot. It takes its surface back once the page below
   is ordinary content again. */
.header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: transparent;
  border-bottom: 1px solid transparent;
  min-height: var(--header-h);
  color: var(--on-dark);
  transition: background 0.3s ease, border-color 0.3s ease, opacity 0.35s ease;
}

.header[data-solid="true"],
.header[data-plate="true"] {
  background: rgb(0 1 8 / 0.92);
  backdrop-filter: blur(10px);
  border-bottom-color: var(--line-dark);
}

/* Второй вход в то же состояние, для страниц, где тёмное и светлое
   чередуются по всей длине, а не только на первом экране. `data-solid` знает
   ровно один вопрос: проехал ли читатель полноэкранный первый экран. На
   странице, где после него идут ещё три смены грунта, этого мало, а две руки
   на одном атрибуте дерутся за него на каждой прокрутке.

   Логотип и кнопка при этом остаются белыми ВСЕГДА. Перекрашивать их в
   чёрный над светлым это лечение симптома: под белым логотипом должна быть
   тёмная плита, и вопрос только в том, чтобы она там была. */

/* Full bleed, not the 84rem measure. The canvas insets its type by five per
   cent of the viewport, so the header uses the same figure and the mark lines
   up with the first letter of the sentence underneath it. */
.header__inner {
  min-height: calc(var(--header-h) - 1px);
  padding-inline: max(1.25rem, 5vw);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-m);
}

.header__logo { display: inline-flex; align-items: center; min-height: 2.75rem; }
.header__logo img { width: clamp(7.5rem, 12vw, 9.5rem); height: auto; }

.nav { display: none; align-items: center; gap: clamp(0.9rem, 1.9vw, 2rem); }

.nav a {
  font-size: var(--step--1);
  color: var(--on-dark-muted);
  text-decoration: none;
  padding-block: 0.5rem;
  transition: color 0.14s ease;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--white); }

/* Quiet on purpose. On a black corridor a solid blue slab is the loudest
   thing on the screen and it is pointing at the least interesting place. */
.header__cta {
  display: none;
  font-size: var(--step--1);
  color: var(--white);
  text-decoration: none;
  padding: 0.5rem 0.95rem;
  border: 1px solid rgb(252 252 252 / 0.28);
  border-radius: var(--radius);
  transition: border-color 0.14s ease, background 0.14s ease;
}
.header__cta:hover { border-color: var(--white); background: rgb(252 252 252 / 0.08); }

.burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-right: -0.5rem;
  border: 0;
  background: none;
  color: var(--white);
  cursor: pointer;
}
.burger svg { width: 1.5rem; height: 1.5rem; }

@media (min-width: 64rem) {
  .nav { display: flex; }
  .header__cta { display: inline-flex; align-items: center; }
  .burger { display: none; }
}

/* --- Mobile drawer ------------------------------------------------------- */

.mobile-nav {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  flex-direction: column;
  gap: var(--sp-m);
  padding: calc(var(--header-h) + var(--sp-l)) var(--sp-l) var(--sp-xl);
  background: var(--ink);
  color: var(--on-dark);
  overflow-y: auto;
}

.mobile-nav[data-open="true"] { display: flex; }

/* Scoped away from .btn: an unscoped `.mobile-nav a` rule wins on
   specificity and blows the button up to heading size. */
.mobile-nav a:not(.btn) {
  font-family: Asgard, sans-serif;
  font-size: var(--step-2);
  color: var(--white);
  text-decoration: none;
}
.mobile-nav a:not(.btn):hover,
.mobile-nav a:not(.btn)[aria-current="page"] { color: var(--blue-lo); }

.mobile-nav .btn { margin-top: var(--sp-m); white-space: nowrap; }

.mobile-nav__close {
  position: absolute;
  top: calc((var(--header-h) - 2.75rem) / 2);
  right: max(var(--sp-l), calc((100vw - var(--wrap)) / 2));
  width: 2.75rem;
  height: 2.75rem;
  border: 0;
  background: none;
  color: var(--white);
  cursor: pointer;
}

body[data-nav-open="true"] { overflow: hidden; }

@media (min-width: 64rem) { .mobile-nav { display: none !important; } }

/* --- Hero ---------------------------------------------------------------- */

.hero {
  position: relative;
  background-color: var(--ink); /* не шортхенд: фон-картинку кладёт `data-glow` */
  color: var(--on-dark);
  padding-block: clamp(2.5rem, 1.5rem + 6vw, 6rem) var(--sp-xxl);
  overflow: hidden;
}

/* A faint ledger the meter is written on. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0 calc(2.75rem - 1px),
    rgb(252 252 252 / 0.035) calc(2.75rem - 1px) 2.75rem
  );
  pointer-events: none;
}

.hero__inner { position: relative; display: grid; gap: var(--sp-xl); align-items: start; }

@media (min-width: 64rem) {
  .hero__inner { grid-template-columns: 1fr 1fr; gap: clamp(2rem, 4vw, 4.5rem); }
}

.hero__title { font-size: var(--hero); }
.hero__title em { font-style: normal; color: var(--red); }
.hero__title b { font-weight: 500; color: var(--blue-lo); }

/* --- The raster hero ----------------------------------------------------- */

/* Full height and no padding: the sentence is the screen, and anything the
   section adds around it eats into the type. The ledger lines are dropped
   here because the raster is already the texture. */
.hero--dither {
  min-height: 100svh;
  padding: 0;
  display: grid;
  grid-template-rows: 1fr auto;
  align-items: end;
}

.hero--dither::before,
.hero--dither::after { display: none; }

.dither {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* The pool of clarity has to follow the pointer over the whole screen, but
     the buttons underneath still have to be clickable, so only the canvas
     itself listens and the footer sits above it. */
  z-index: 0;
}

.hero__foot {
  position: relative;
  z-index: 2;
  grid-row: 2;
  display: grid;
  gap: var(--sp-m);
  padding-block: var(--sp-l) var(--sp-xl);
  pointer-events: none;
}

.hero__foot > * { pointer-events: auto; }

.hero__sub {
  max-width: 46ch;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  line-height: 1.7;
  color: var(--on-dark-muted);
}

.hero__hint {
  position: absolute;
  right: clamp(1.25rem, 5vw, 5rem);
  bottom: var(--sp-l);
  z-index: 2;
  font-family: PlexMono, monospace;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #6b6f7d;
  pointer-events: none;
}

@media (max-width: 63.99rem) {
  .hero__hint { display: none; }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Rent calculator ----------------------------------------------------- */

.calc {
  background: var(--ink-3);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  padding: var(--sp-l);
}

.calc__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-s);
  flex-wrap: wrap;
  margin-bottom: var(--sp-m);
}

.calc__list {
  list-style: none;
  display: grid;
  gap: 1px;
  background: var(--line-dark);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  max-height: 20rem;
  overflow-y: auto;
}

.calc__item {
  display: flex;
  align-items: center;
  gap: var(--sp-s);
  padding: 0.5rem var(--sp-s);
  background: var(--ink-3);
}

.calc__item label {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.25rem;
  font-size: var(--step--1);
  color: var(--on-dark-muted);
  cursor: pointer;
}

.calc__item input[type="checkbox"] {
  flex: none;
  width: 1.1rem;
  height: 1.1rem;
  accent-color: var(--red);
  cursor: pointer;
}

.calc__item input[type="checkbox"]:checked + span { color: var(--white); }

/* 16px minimum, or iOS zooms the viewport when the field takes focus. */
.calc__item input[type="number"] {
  flex: none;
  width: 4.75rem;
  min-height: 2.25rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--white);
  font-family: PlexMono, monospace;
  font-size: 1rem;
  text-align: right;
}

.calc__total { margin-top: var(--sp-m); padding-top: var(--sp-m); border-top: 1px solid var(--line-dark); }
.calc__total .figure { color: var(--red); }
.calc__note { margin-top: var(--sp-s); font-size: var(--step--1); color: var(--on-dark-muted); }
.calc__note b { color: var(--white); }

/* --- Price table --------------------------------------------------------- */

.prices {
  display: grid;
  gap: 1px;
  background: var(--panel-line);
  border: 1px solid var(--panel-line);
  /* The card radius, not --radius. This is a block object of the same family
     as .doors and .panel, and at 4px it read as a square cornered table while
     every other block on the site is rounded. */
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
}

.price-row {
  display: grid;
  gap: var(--sp-xs) var(--sp-m);
  grid-template-columns: 1fr;
  padding: var(--sp-m);
  background: var(--card-bg);
  text-decoration: none;
  transition: background 0.14s ease;
}

/* Composited over the row ground. --panel-bg is 4% blue and .prices is painted
   --panel-line to make the 1px seams, so a translucent hover let that grey
   through and the row went lilac on the way past. Same fix as .versus--ours. */
a.price-row:hover {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}

.price-row__name {
  font-family: Asgard, sans-serif;
  font-size: var(--step-1);
  color: var(--heading-color);
}

.price-row__price {
  font-family: PlexMono, monospace;
  font-weight: 600;
  font-size: var(--step-1);
  color: var(--blue);
  font-variant-numeric: tabular-nums;
}

.sect--ink .price-row__price,
.sect--ink-2 .price-row__price,
.on-dark .price-row__price { color: var(--blue-lo); }

.price-row__what { color: var(--lede-color); max-width: 62ch; }
.price-row__when { font-family: PlexMono, monospace; font-size: var(--step--1); color: var(--tag-color); }

@media (min-width: 48rem) {
  .price-row { grid-template-columns: minmax(9rem, 13rem) 1fr minmax(6.5rem, auto); align-items: baseline; }
  .price-row__what { grid-column: 2; }
  .price-row__price { text-align: right; }
  .price-row__when { grid-column: 2; }
  .price-row__bar { grid-column: 1 / -1; }
}

/* --- Rent Check ----------------------------------------------------------- */

/* The page is the tool, so the tool gets the room a hero would have taken.
   The column that makes this page different from the calculator on the home
   page is the verdict chip: whether a system you own can take a line over at
   all. That column is why the answer at the bottom is worth reading. */

.check { padding-block: calc(var(--header-h) + var(--sp-l)) var(--sp-l); }
.check__head { display: grid; gap: var(--sp-s); max-width: 46rem; }

.check__terms {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  margin-top: var(--sp-xs);
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue);
}

.check__tool {
  margin-top: var(--sp-l);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  overflow: hidden;
}

.check__bar {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-s);
  padding: 0.7rem var(--sp-s);
  border-bottom: 1px solid var(--panel-line);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.check__list { list-style: none; }

.check__row {
  display: grid;
  gap: 0.4rem var(--sp-m);
  align-items: center;
  padding: 0.7rem var(--sp-s);
  border-top: 1px solid var(--panel-line);
  transition: opacity 0.16s ease;
}

.check__row:first-child { border-top: 0; }
.check__row[data-off="true"] { opacity: 0.42; }

@media (min-width: 60rem) {
  .check__row { grid-template-columns: minmax(12rem, 1fr) auto auto 9.5rem; }
}

.check__pick { display: flex; align-items: center; gap: 0.6rem; cursor: pointer; }
.check__pick input { width: 1.05rem; height: 1.05rem; accent-color: var(--blue); flex: none; }
.check__label { font-size: 0.95rem; }

/* The column the home page does not have. */
.verdict {
  justify-self: start;
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.2rem 0.5rem;
  border-radius: 3px;
  white-space: nowrap;
}

.verdict--own { color: var(--blue); background: rgb(1 48 249 / 0.09); }
.verdict--part { color: var(--text-muted); background: rgb(0 0 0 / 0.05); }
.verdict--rent { color: var(--red); background: rgb(255 67 67 / 0.1); }

.check__money { display: flex; align-items: baseline; gap: 0.2rem; justify-self: end; }
.check__money em { font-family: PlexMono, ui-monospace, monospace; font-size: 0.7rem; font-style: normal; color: var(--text-muted); }

.check__money input {
  width: 5rem;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 1rem;
  text-align: right;
  color: var(--text);
  padding: 0.15rem 0;
}

.check__money input:focus { outline: 0; border-bottom-color: var(--blue); }

.check__years {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem var(--sp-m);
  padding: var(--sp-s);
  border-top: 1px solid var(--panel-line);
}

.check__years select {
  font-family: Asgard, Impact, sans-serif;
  font-size: var(--step-1);
  border: 0;
  background: none;
  color: var(--text);
  padding: 0.15rem 0;
}

/* --- Un-SaaS OS ----------------------------------------------------------- */

/* Three versions are buried under this one: a checklist reporting our own
   reuse ratio, a risk memo that opened on the failure rate of projects like
   this, and real screens from Gotlift where every row said Cela DT 80 and an
   HVAC owner saw somebody else's business.

   This one shows the promise instead of describing it. The page says the
   business runs when he is not there, so a whole job runs itself while he
   watches: website form to money in the ledger, nine steps, and he is in none
   of them. The interface is drawn rather than borrowed, and deliberately
   generic to the trade. */

/* His figure against ours. The comparison is the reason to buy, so it is one
   object cut down the middle like the sorting board, and the side he is on
   now is the red one. */
.versus {
  display: grid;
  gap: 1px;
  background: var(--panel-line);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  box-shadow: var(--lift-2);
}

/* Not halves. The rented side is carrying thirty four marks and the owned
   side five, and giving them equal room either crushes the wall or strands
   the other card in space. The asymmetry is also the argument. */
@media (min-width: 52rem) { .versus { grid-template-columns: 1.24fr 1fr; } }

.versus__side {
  display: grid;
  gap: 0.5rem;
  align-content: start;
  grid-template-rows: auto 1fr auto; /* the wall takes the slack, so both notes sit on the same line */
  padding: clamp(1.3rem, 3vw, 2.4rem);
}
.versus__side--now { background: var(--card-bg); }
.versus__side--own { background: var(--panel-dark); color: #fff; }

.versus__key { font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase; }
.versus__side--now .versus__key { color: var(--red); }
.versus__side--own .versus__key { color: var(--blue-lo); }

.versus__fig {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(2.6rem, 1.3rem + 4.4vw, 5rem);
  line-height: 0.88;
  letter-spacing: -0.04em;
}

.versus__side--now .versus__fig { color: var(--red); }
.versus__side--own .versus__fig { color: #fff; }

.versus__top { display: grid; gap: 0.5rem; }

/* The wall of companies.

   It is read as a quantity first and as names second, so every mark is set to
   one optical height and none of them carries a label. Wordmarks run wide and
   square icons run square, which is why this wraps as a line of type rather
   than sitting in a grid: a grid would leave a ragged column of air beside
   every wide mark.

   The marks are held well back. At full strength thirty four logos in four
   brand colours is a bag of sweets, and the figure above them stops being the
   loudest thing on the card. */
.wall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.6rem, 1fr));
  gap: 1.15rem 0.9rem;
  align-items: center;
  justify-items: center;
  margin: var(--sp-s) 0;
  align-content: start;
}
/* Equal cells rather than a wrapped line. Wordmarks run three times the width
   of a square icon, so packing them as type broke the wall into rows of
   sixteen, fourteen and four, and the short last row read as an accident. */
.wall__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  max-width: 100%;
  color: inherit;
}
.wall__mark svg { display: block; width: auto; max-width: 100%; height: 1.15rem; }
.wall__mark--word svg { height: 0.82rem; }
.wall__mark--text {
  font-family: PlexMono, monospace;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

.versus__side--now .wall { color: rgb(11 13 20 / 0.42); }
.versus__side--own .wall { color: rgb(255 255 255 / 0.62); }

/* Five accounts against thirty four. They get room rather than being lined up
   tight, so the side reads as sparse on purpose and not as a short list. */
.wall--few {
  display: flex;
  flex-wrap: wrap;
  justify-items: start;
  gap: 0.9rem 1.6rem;
}
.wall--few .wall__mark svg { height: 1.5rem; }
.wall--few .wall__mark--word svg { height: 1.05rem; }

.versus__note { font-size: 0.9rem; max-width: 40ch; }
.versus__side--now .versus__note { color: var(--lede-color); }
.versus__side--own .versus__note { color: rgb(255 255 255 / 0.62); }

/* The line that keeps the comparison honest, and it is not hidden in small
   print: some of what he rents stays rented whatever he does. */
.versus__caveat { margin-top: var(--sp-m); color: var(--text-muted); font-size: 0.9rem; max-width: 56ch; }

/* The object. Same near-black the doors and the accounts are cut from, so it
   belongs to this site rather than to a stock dashboard. */
.panel {
  background: var(--panel-dark);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  color: #fff;
  box-shadow: var(--lift-3);
}

.panel__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-s);
  padding: clamp(0.8rem, 1.4vw, 1.1rem) clamp(1rem, 2vw, 1.5rem);
  border-bottom: 1px solid rgb(255 255 255 / 0.1);
}

.panel__mark { font-family: Asgard, Impact, sans-serif; font-size: 0.85rem; }
.panel__clock { font-size: 0.72rem; color: var(--blue-lo); font-variant-numeric: tabular-nums; }

.panel__feed { list-style: none; padding: 0.4rem 0; }

.ev {
  display: grid;
  gap: 0.1rem 1rem;
  padding: 0.55rem clamp(1rem, 2vw, 1.5rem);
  align-items: baseline;
}

@media (min-width: 34rem) {
  .ev { grid-template-columns: 3.4rem minmax(0, 1fr) minmax(0, 1.2fr); }
}

.ev__at { font-size: 0.66rem; color: rgb(255 255 255 / 0.4); font-variant-numeric: tabular-nums; }
.ev__what { font-size: 0.95rem; color: #fff; }
.ev__detail { font-size: 0.85rem; color: rgb(255 255 255 / 0.5); }

/* The two rows that are money get the blue. Colour used three times in nine
   rows rather than on every one of them. */
.ev--money .ev__what { color: var(--blue-lo); }

/* Lit by default. The dim state is gated on .js so a blocked script leaves a
   finished list rather than an empty box, which is the rule this site learned
   the hard way on the guarantee block. */
.js .panel[data-done="false"] .ev[data-on="false"] { opacity: 0.14; }

.js .ev {
  transition: opacity 0.45s ease, transform 0.45s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.js .panel[data-done="false"] .ev[data-on="false"] { transform: translateY(6px); }

.panel__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: clamp(0.8rem, 1.4vw, 1.1rem) clamp(1rem, 2vw, 1.5rem);
  border-top: 1px solid rgb(255 255 255 / 0.1);
  font-size: 0.85rem;
  color: rgb(255 255 255 / 0.55);
}

.js .panel[data-done="false"] .panel__foot span { opacity: 0.3; }
.panel__foot span { transition: opacity 0.45s ease; }

.panel__again {
  appearance: none;
  border: 1px solid rgb(255 255 255 / 0.25);
  border-radius: 999px;
  background: none;
  color: #fff;
  font: inherit;
  font-size: 0.78rem;
  padding: 0.3rem 0.8rem;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease;
}

.panel__again:hover { background: var(--blue); border-color: var(--blue); }

.pitch__line {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: var(--display);
  line-height: 1.02;
  letter-spacing: -0.03em;
}

/* Eight weeks on one line. An earlier version gave this four columns and six
   paragraphs, which nobody would have read. */
.track { list-style: none; display: grid; gap: 1px; background: var(--panel-line); border: 1px solid var(--panel-line); border-radius: var(--radius); overflow: hidden; }

@media (min-width: 60rem) { .track { grid-auto-flow: column; grid-auto-columns: 1fr; } }

.track__step { display: grid; gap: 0.3rem; align-content: start; padding: var(--sp-s) var(--sp-m); background: var(--card-bg); }
.track__when { font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--blue); }
.track__what { font-size: 0.95rem; color: var(--heading-color); }

@media (prefers-reduced-motion: reduce) {
  .js .ev, .panel__foot span { transition: none; }
}

/* The screens, two up, so "already built" is a claim with pictures under it. */
.os__shots { display: grid; gap: var(--sp-m); }
@media (min-width: 52rem) { .os__shots { grid-template-columns: 1fr 1fr; } }
.os__shots .shot { margin-top: 0; }

/* --- The three totals ----------------------------------------------------- */

.totals { display: flex; flex-wrap: wrap; gap: clamp(1.6rem, 5vw, 4.5rem); }
.totals__item { display: grid; gap: 0.25rem; }
.totals__item span { font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--tag-color); }

.totals__item b {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(2.2rem, 1.2rem + 3.4vw, 4.2rem);
  line-height: 0.9;
  letter-spacing: -0.03em;
  color: var(--heading-color);
}

.totals__item--paid b { color: var(--red); }

/* --- The verdict ---------------------------------------------------------- */

/* This is the page. It changes colour with its own answer, and the answer is
   allowed to be "keep renting", in the same type it would have said the
   opposite in. An answer that can only say buy is not an answer. */
.ruling {
  padding: clamp(1.4rem, 3vw, 2.6rem);
  border-radius: clamp(14px, 1.4vw, 20px);
  background: var(--panel-dark);
  color: #fff;
  box-shadow: var(--lift-2);
}

.ruling__line {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(1.5rem, 0.9rem + 2.2vw, 2.8rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  max-width: 26ch;
}

.ruling[data-verdict="own"] .ruling__line { color: #fff; }
.ruling[data-verdict="rent"] { background: var(--red); }
.ruling[data-verdict="none"] { background: var(--panel-dark); }
.ruling[data-verdict="none"] .ruling__line { color: rgb(255 255 255 / 0.72); }

.ruling__sums {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem clamp(1.6rem, 5vw, 3.6rem);
  margin-top: var(--sp-l);
  padding-top: var(--sp-m);
  border-top: 1px solid rgb(255 255 255 / 0.2);
}

.ruling__sums div { display: grid; gap: 0.2rem; }

.ruling__sums dt {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.55);
}

.ruling__sums dd {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: var(--step-1);
  font-variant-numeric: tabular-nums;
  color: #fff;
}

.ruling__how { margin-top: var(--sp-m); font-size: 0.85rem; color: rgb(255 255 255 / 0.62); max-width: 60ch; }
.ruling__how a { color: #fff; text-decoration: underline; text-underline-offset: 0.2em; }

/* --- The close ------------------------------------------------------------ */

/* The last block on every page, and it was the weakest: a short column with
   half a screen of nothing under it, a white slab of default controls beside
   it, and three calls to action competing for one decision.

   One object now, in the same near-black the doors and the accounts are cut
   from, with the reader's own figure closing the loop the first screen
   opened. The form is the only action left. */

.close__inner {
  display: grid;
  gap: var(--sp-l);
  align-items: start;
}

@media (min-width: 60rem) {
  .close__inner { grid-template-columns: 1fr 1fr; gap: var(--sp-xl); align-items: center; }
}

.close__say { display: grid; gap: var(--sp-s); align-content: start; }

/* Their number, brought down from the calculator. This is what used to be
   half a screen of empty dark. */
.close__figure {
  display: grid;
  gap: 0.3rem;
  margin-top: var(--sp-s);
  padding-top: var(--sp-m);
  border-top: 1px solid rgb(255 255 255 / 0.14);
}
/* ⚠️ `display: grid` выше ПЕРЕБИВАЕТ атрибут `hidden`.

   Блок отдаётся с `hidden`, потому что цифра принадлежит калькулятору и
   заполняется скриптом только у того, кто им пользовался. Правило `display`
   специфичнее правила `[hidden] { display: none }` из таблицы браузера,
   поэтому атрибут не работал ни на одной странице.

   Замерено в браузере на трёх страницах сразу: `hidden` стоит, а
   `getBoundingClientRect` даёт ненулевой размер, и в кадре светится красный
   `$0` со словами «is what the next five years cost, on your own figures».
   Новому посетителю показывали чужой ноль как его собственный.

   Дефект живой и не новый: он есть на `/un-saas/`, `/content/`,
   `/digital-inventory/`, `/work/` и на этой странице. Поймал его КАДР, а не
   гейт: в разметке атрибут стоял на месте. */
.close__figure[hidden] { display: none; }

.close__figure b {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(2.6rem, 1.4rem + 4.4vw, 5rem);
  line-height: 0.88;
  letter-spacing: -0.04em;
  color: var(--red);
}

.close__figure span { color: var(--on-dark-muted); max-width: 34ch; }

.close__figure a {
  justify-self: start;
  margin-top: 0.3rem;
  font-size: 0.88rem;
  color: var(--blue-lo);
}

/* The form, as part of the object rather than a card dropped on it. */
.close .form-card {
  --heading-color: #fff;
  --lede-color: var(--on-dark-muted);
  background: var(--panel-dark);
  border: 0;
  border-radius: var(--radius-card);
  color: var(--on-dark);
  box-shadow: var(--lift-2);
}

/* Hairline underlines rather than four boxes. A box per field is what a form
   looks like when nobody has decided anything about it. */
.close .field input,
.close .field textarea {
  background: none;
  border: 0;
  border-bottom: 1px solid rgb(255 255 255 / 0.22);
  border-radius: 0;
  padding-inline: 0;
  color: #fff;
}

.close .field input::placeholder,
.close .field textarea::placeholder { color: rgb(255 255 255 / 0.32); }

.close .field input:focus,
.close .field textarea:focus {
  border-bottom-color: var(--blue-lo);
  outline: 0;
  box-shadow: 0 1px 0 0 var(--blue-lo);
}

.close .field > span { color: rgb(255 255 255 / 0.45); }
.close .field textarea { min-height: 5.5rem; }

.field-row { display: grid; gap: var(--sp-m); }
@media (min-width: 30rem) { .field-row { grid-template-columns: 1fr 1fr; } }

/* --- The handover --------------------------------------------------------- */

/* The claim that nothing switches off if we vanish was a clause in a
   paragraph. It is a demonstration now: six accounts that belong to the
   client, one tile that is us, and a control that takes us out. Everything
   else holding perfectly still is the whole argument, so nothing else on this
   block is allowed to move. */

/* This block was 544px wide, lavender and shouting in uppercase, and none of
   that came from here: the class name was already taken by the killed lease
   concept, whose rules set a max-width, a translucent blue and a monospace
   caps treatment. Two components answering to one name, and the dead one won
   every property the live one did not set. Its CSS is deleted, not overridden,
   because a name collision that is patched instead of removed comes back. */
.handover { margin-top: var(--sp-l); }

/* Built in the doors' language, because it is the same kind of thing: one
   object cut into panels by hairlines, near-black, with the brand's blue
   doing the only talking. Two blocks that both present a row of choices
   should not be two different components. */
.handover__grid {
  list-style: none;
  display: grid;
  gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  background: rgb(255 255 255 / 0.1);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  box-shadow: var(--lift-2);
}

.acct {
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 0.7rem;
  min-height: 9.5rem;
  padding: var(--sp-m);
  background: var(--panel-dark);
}

.acct__mark { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; min-height: 1.3rem; }

/* The shared mark chips are drawn for a white surface, so on this one they
   take the panel's own hairline and its muted white instead. */
.acct .calc__mark,
.acct .calc__logo { border-color: rgb(255 255 255 / 0.22); color: rgb(255 255 255 / 0.75); }

.acct__none {
  font-family: PlexMono, ui-monospace, monospace;
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  padding: 0.16rem 0.45rem;
  border: 1px solid rgb(255 255 255 / 0.22);
  border-radius: 3px;
  color: rgb(255 255 255 / 0.75);
}

/* The account name is the line that carries the panel, set like a door's. */
.acct__label {
  font-family: Asgard, Impact, sans-serif;
  font-size: var(--step-1);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #fff;
  align-self: start;
}

.acct__whose {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue-lo);
  padding-top: var(--sp-xs);
  border-top: 1px solid rgb(255 255 255 / 0.14);
}

/* The odd panel out, and the only one that can be taken away, so it is the
   one that is filled. Held back to a muted dark at first, which made the
   demonstration hard to see: dimming a panel that is already dark says
   nothing, and a solid colour leaving a row of black says it at a glance. */
.acct--us { background: var(--blue); }
.acct--us .acct__label { color: #fff; }
.acct--us .acct__whose { color: rgb(255 255 255 / 0.72); border-top-color: rgb(255 255 255 / 0.3); }
.acct--us .acct__none { border-color: rgb(255 255 255 / 0.45); color: #fff; }

.js .acct--us { transition: opacity 0.45s ease, filter 0.45s ease; }
[data-handover][data-gone="true"] .acct--us { opacity: 0.16; filter: grayscale(1); }

.handover__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1rem;
  margin-top: var(--sp-m);
}

.handover__btn { padding-block: 0.5rem; font-size: 0.85rem; }
.handover__say { color: var(--lede-color); font-size: 0.92rem; }

@media (prefers-reduced-motion: reduce) {
  .js .acct--us { transition: none; }
}

/* --- Work ----------------------------------------------------------------- */

/* Where a case study puts its result, this puts its status, because there is
   no result yet and a made up one is a phone call away from being caught. The
   line is styled to be read rather than skipped: the honest version of this
   page is only worth anything if the honesty is the visible part. */


.work__state {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.7rem;
  margin-top: var(--sp-m);
  padding: 0.65rem 1rem;
  border: 1px solid var(--panel-line);
  border-left: 3px solid var(--red);
  border-radius: var(--radius);
  background: var(--card-bg);
  font-size: 0.88rem;
  color: var(--lede-color);
}

.work__state span {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
}

.work__soon { font-size: 0.6rem; letter-spacing: 0.1em; color: var(--tag-color); }

/* Four counted things, set large. Every one of them came out of the
   repository rather than out of a memory of it. */
.figures { display: flex; flex-wrap: wrap; gap: clamp(1.6rem, 5vw, 4.5rem); }
.figures__item { display: grid; gap: 0.2rem; }

.figures__item b {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(2.4rem, 1.2rem + 3.6vw, 4.4rem);
  line-height: 0.92;
  letter-spacing: -0.03em;
  color: var(--heading-color);
}

.figures__item span {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--tag-color);
}

/* The bill, at the size of the argument. Everything else on this site builds
   towards a number this small sitting next to a number that large. */
.bill {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1rem;
  margin-top: var(--sp-m);
}

.bill b {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(3.6rem, 1.6rem + 7vw, 8rem);
  line-height: 0.86;
  letter-spacing: -0.04em;
  color: var(--blue);
}

.bill__unit { font-family: PlexMono, ui-monospace, monospace; font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--tag-color); }
.bill__five { flex-basis: 100%; color: var(--lede-color); }

/* The screens. Framed rather than floated: the same rounded panel and the
   same shadow the interface object on the home page wears, so a screenshot of
   somebody's product sits in the page instead of on it. */
.shot { margin-top: var(--sp-s); }

.shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: clamp(10px, 1vw, 14px);
  border: 1px solid var(--panel-line);
  box-shadow: var(--lift-1);
  background: #fff;
}

.shot figcaption {
  margin-top: 0.6rem;
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  color: var(--tag-color);
  max-width: 60ch;
}

/* The spine: five stages, each naming what was built and what it replaced. */
.spine { list-style: none; display: grid; gap: 1px; background: var(--panel-line); border: 1px solid var(--panel-line); border-radius: var(--radius); overflow: hidden; }

.spine__row {
  display: grid;
  gap: 0.7rem;
  padding: var(--sp-m);
  background: var(--card-bg);
}

.spine__head { display: flex; align-items: baseline; gap: 0.8rem; }
.spine__n { font-size: 0.7rem; color: var(--blue); }
.spine__row h3 { font-size: var(--step-1); }
.spine__note { color: var(--lede-color); max-width: 64ch; }

/* The function names are the evidence, so they are set as what they are:
   identifiers, in the monospace every figure on this site already uses. */
.spine__fns { list-style: none; display: flex; flex-wrap: wrap; gap: 0.3rem; }

.spine__fns li {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.66rem;
  padding: 0.16rem 0.45rem;
  border: 1px solid var(--panel-line);
  border-radius: 3px;
  color: var(--text-muted);
}

.spine__rented {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.6rem;
  font-size: 0.85rem;
  color: var(--red);
}

.spine__rented span {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--tag-color);
}

@media (min-width: 52rem) {
  .spine__row { grid-template-columns: minmax(10rem, 14rem) 1fr; align-items: start; column-gap: var(--sp-l); }
  .spine__head { grid-row: 1 / span 3; flex-direction: column; gap: 0.4rem; }
  .spine__note, .spine__fns, .spine__rented, .shot { grid-column: 2; }
  .spine__head { grid-row: 1 / span 4; }
}

/* --- The work pointer on the home page ------------------------------------ */

/* A description of a case is not a case. The screen carries this block and
   the words sit beside it, so the reader sees the thing before reading a
   word about it. */
.proof {
  display: grid;
  gap: var(--sp-l);
  padding: clamp(1.4rem, 3vw, 2.6rem);
  background: var(--panel-dark);
  border-radius: clamp(14px, 1.4vw, 20px);
  color: #fff;
  text-decoration: none;
  overflow: hidden;
  box-shadow: var(--lift-2);
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
}

@media (min-width: 60rem) {
  .proof {
    grid-template-columns: minmax(19rem, 27rem) 1fr;
    align-items: center;
    padding-right: 0;
  }
}

.proof__say { display: grid; gap: var(--sp-s); align-content: start; }

/* Cut by the frame on purpose: a screen that runs past the edge reads as a
   real thing continuing, where one centred inside the panel reads as a
   picture of a thing. */
.proof__shot { position: relative; }

.proof__shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px 0 0 10px;
  border: 1px solid rgb(255 255 255 / 0.14);
  border-right: 0;
  /* Not on the lift scale, and deliberately. This screen is cut by the right
     edge of its panel, so its shadow is thrown leftwards, back into the page.
     A centred lift would say it is floating above the section instead of
     running out of it. */
  box-shadow: -0.6rem 1rem 2.4rem -0.8rem rgb(0 0 0 / 0.7);
  transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1);
}

@media (min-width: 60rem) {
  /* Wider than its column, so the right edge leaves the panel. */
  .proof__shot img { width: 122%; max-width: none; }
}

a.proof:hover, a.proof:focus-visible { transform: translateY(-3px); }
a.proof:hover .proof__shot img, a.proof:focus-visible .proof__shot img { transform: translateX(-1.2%); }

/* The headline is the bill, not the name: twenty five dollars is the whole
   argument of the page above it, made by somebody else's real invoice. */
.proof__bill { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.8rem; margin-top: var(--sp-xs); }

.proof__bill b {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(3rem, 1.6rem + 4.4vw, 5rem);
  line-height: 0.86;
  letter-spacing: -0.04em;
  color: var(--blue-lo);
}

.proof__bill span { flex-basis: 100%; font-size: 0.92rem; color: rgb(255 255 255 / 0.72); }

.proof__name { font-family: Asgard, Impact, sans-serif; font-size: var(--step-3); line-height: 1; letter-spacing: -0.02em; }
.proof__what { color: rgb(255 255 255 / 0.72); max-width: 60ch; }


.proof__go { font-size: 0.88rem; color: var(--blue-lo); }

@media (prefers-reduced-motion: reduce) {
  a.proof:hover, a.proof:focus-visible { transform: none; }
}

/* --- The guarantee -------------------------------------------------------- */

/* The second black panel used to repeat the first one's trick. It is the
   guarantee, so it is drawn as one: the three things a subscription charges
   for, struck out one after another as the panel arrives. Red, because that
   is the colour rent wears everywhere else on this page. */

.vow {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem clamp(1.4rem, 4vw, 3.4rem);
  margin-bottom: clamp(1.4rem, 3vw, 2.4rem);
}

.vow li {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: clamp(1.6rem, 0.9rem + 2.6vw, 3.2rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: rgb(255 255 255 / 0.4);
}

.vow em { font-style: normal; color: var(--red); }

.vow s {
  position: relative;
  text-decoration: none;
  color: rgb(255 255 255 / 0.62);
}

/* Drawn, not printed: the bar sweeps across once the panel is on screen, one
   word after the next. Without the script the class never lands and the bar
   simply stays at full width, which is the same meaning held still. */
.vow s::after {
  content: "";
  position: absolute;
  left: -0.04em;
  right: -0.04em;
  top: 52%;
  height: 0.09em;
  background: var(--red);
  transform-origin: left center;
  transform: scaleX(1);
}

/* Gated on .js, not on [data-reveal] alone: if the script never runs there is
   nothing to un-collapse it, and the guarantee would read as an unfinished
   design instead of a struck-out one. */
.js [data-reveal] .vow s::after { transform: scaleX(0); }

.js [data-reveal].is-in .vow s::after {
  transform: scaleX(1);
  transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.is-in .vow li:nth-child(1) s::after { transition-delay: 0.15s; }
.is-in .vow li:nth-child(2) s::after { transition-delay: 0.35s; }
.is-in .vow li:nth-child(3) s::after { transition-delay: 0.55s; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] .vow s::after,
  [data-reveal].is-in .vow s::after { transform: scaleX(1); transition: none; }
}

/* --- Three doors ---------------------------------------------------------- */

/* These were three text cards, which is three advertisements side by side. A
   door now opens on the sentence the reader would say about themselves, set
   large enough to be the thing they scan, and closes on the published price
   and duration of whatever is behind it. Hovering fills the whole panel with
   the brand's blue rather than nudging a border, so choosing feels like
   choosing. */

/* One object cut in three, the way the sorting board is one object cut in
   two. Three separate white cards floating in a field of white was the
   weakest thing on the page: nothing to look at, and a quiz to answer. */
.doors {
  display: grid;
  gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  background: rgb(255 255 255 / 0.1);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  box-shadow: var(--lift-2);
}

.door {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: var(--sp-s);
  padding: var(--sp-m);
  background: var(--panel-dark);
  text-decoration: none;
  color: inherit;
  transition: background 0.22s ease, transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* The answer, given rather than asked for. */
.door__mark {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue-lo);
  min-height: 0.9rem;
}

.door__mark[hidden] { display: block; visibility: hidden; }

.door.is-you { background: var(--blue); }
.door.is-you .door__mark { color: rgb(255 255 255 / 0.9); }

.door__voice {
  font-family: Asgard, Impact, sans-serif;
  font-size: var(--step-2);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #fff;
}

.door__body { display: grid; gap: 0.35rem; align-content: start; }

.door h3 {
  font-family: PlexMono, ui-monospace, monospace;
  font-weight: 400;
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.45);
}

.door__text { font-size: var(--step--1); color: rgb(255 255 255 / 0.72); }

.door__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.8rem;
  padding-top: var(--sp-s);
  border-top: 1px solid rgb(255 255 255 / 0.14);
}

.door__price {
  font-family: PlexMono, ui-monospace, monospace;
  font-weight: 600;
  color: var(--blue-lo);
  font-variant-numeric: tabular-nums;
}

.door__when { font-family: PlexMono, ui-monospace, monospace; font-size: 0.7rem; color: rgb(255 255 255 / 0.45); }
.door__go { flex-basis: 100%; font-size: 0.82rem; color: rgb(255 255 255 / 0.62); }

/* The whole panel takes the colour, both for the door the page picked and
   for whichever one the reader points at. A border that changes tint is a
   detail nobody notices; a filled panel is an answer. */
.door:hover,
.door:focus-visible { background: var(--blue); transform: translateY(-3px); }

.door:hover .door__price,
.door:focus-visible .door__price,
.door.is-you .door__price { color: #fff; }

.door:hover .door__mark,
.door:focus-visible .door__mark,
.door:hover h3,
.door:focus-visible h3,
.door.is-you h3,
.door:hover .door__when,
.door:focus-visible .door__when,
.door.is-you .door__when,
.door:hover .door__go,
.door:focus-visible .door__go,
.door.is-you .door__go,
.door:hover .door__text,
.door:focus-visible .door__text,
.door.is-you .door__text { color: rgb(255 255 255 / 0.85); }

.door:hover .door__foot,
.door:focus-visible .door__foot,
.door.is-you .door__foot { border-top-color: rgb(255 255 255 / 0.32); }

@media (prefers-reduced-motion: reduce) {
  .door, .door:hover, .door:focus-visible { transform: none; }
}

/* --- The price scale ------------------------------------------------------ */

/* This block used to be the tallest thing on the page with nothing drawn in
   it: a column of numbers, and a number on its own says nothing. $4,000 is a
   lot or a rounding error depending entirely on what sits beside it.

   So the reader's own five year rent is laid down first, at full width, in
   red. Every price under it is the same bar at true scale, and a thousand
   dollars against seventy-eight thousand is a sliver. Nobody has to be told
   the ratio, and no sentence could make it as hard to argue with. */

.scale__ref {
  padding: var(--sp-m) var(--sp-m) var(--sp-s);
  background: var(--card-bg);
}

.scale__key {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem 1rem;
  margin-bottom: 0.6rem;
}

.scale__key span {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
}

.scale__key b {
  font-family: PlexMono, ui-monospace, monospace;
  font-size: var(--step-1);
  font-variant-numeric: tabular-nums;
  color: var(--red);
}

.scale__bar {
  display: block;
  height: 0.5rem;
  border-radius: 999px;
  background: var(--panel-line);
  overflow: hidden;
}

.scale__bar i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--heading-color);
  transition: width 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.scale__bar--ref { height: 0.85rem; }
.scale__bar--ref i { background: var(--red); }

.price-row__bar { margin-top: 0.55rem; }
a.price-row:hover .scale__bar i { background: var(--blue); }

/* The percentage sits with the price, not on the bar: a label floating on a
   sliver two pixels wide is unreadable at every width. */
.price-row__share {
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  color: var(--tag-color);
  font-variant-numeric: tabular-nums;
}

@media (min-width: 48rem) {
  .price-row__share { grid-column: 3; text-align: right; margin-top: -0.35rem; }
}

/* Rows with no single one-off price get no bar at all, rather than a bar of
   zero: free, a price list and a monthly fee are not small versions of a
   build price, they are different kinds of number. */
.price-row:not([data-scale-of]) .price-row__bar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .scale__bar i { transition: none; }
}

/* --- Cards --------------------------------------------------------------- */

.cards { display: grid; gap: var(--sp-m); grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }

.card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-s);
  padding: var(--sp-l);
  background: var(--card-bg);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
  box-shadow: var(--card-shadow);
}


.card p { color: var(--lede-color); }
.card__n { font-family: PlexMono, monospace; font-size: var(--step--1); letter-spacing: 0.1em; color: var(--blue); }
.sect--ink .card__n, .sect--ink-2 .card__n, .on-dark .card__n { color: var(--blue-lo); }

/* --- The sorting board (keep renting vs worth owning) --------------------- */

/* This was two lists of grey text side by side, which read as filler between
   two blocks that had been designed. It is a decision, so it is drawn as one
   object split down the middle: a pale half carrying the marks of the
   companies that keep taking the money, and a solid black half with no marks
   at all. The absence on the right is the argument, and the last line of that
   panel already explains it, so no extra copy was added to say so. */

.board {
  display: grid;
  gap: 1px;
  background: var(--panel-line);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  box-shadow: var(--lift-2);
}

@media (min-width: 52rem) { .board { grid-template-columns: 1fr 1fr; } }

/* Head at the top, list filling whatever is left. The two sides hold a
   different number of entries, so the list has to be able to stretch: it is
   what lets the closing rule sit on the floor of both panels at the same
   height instead of trailing whichever list happened to be shorter. */
.board__side {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: clamp(1.1rem, 2vw, 1.7rem);
  padding: clamp(1.3rem, 3vw, 2.4rem);
}

.board__side--rent { background: #fff; color: var(--text); }
.board__side--own { background: var(--panel-dark); color: #fff; }

.board__head { display: grid; gap: 0.4rem; }


.board__side h3 { font-size: var(--step-2); letter-spacing: -0.02em; }
.board__side--own h3 { color: #fff; }

.board__list { list-style: none; display: flex; flex-direction: column; }

/* Label left, marks right, and they drop under the label rather than
   squeezing it when the panel gets narrow. */
.board__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.45rem 1rem;
  padding: 0.8rem 0;
  border-top: 1px solid var(--panel-line);
}

.board__row:first-child { border-top: 0; padding-top: 0; }
.board__side--own .board__row { border-top-color: rgb(255 255 255 / 0.12); }
.board__label { font-size: 0.95rem; }
.board__side--rent .board__label { color: var(--lede-color); }
.board__side--own .board__label { color: rgb(255 255 255 / 0.88); }

/* The rule that produced the list, set apart from it and pushed to the floor
   of the panel. `margin-top: auto` is what holds the two sides level: three
   entries on the left against four on the right used to leave the two closing
   lines at different heights, which is the first thing the eye picks up. */
.board__row--rule {
  margin-top: auto;
  border-top-width: 2px;
  padding-top: 1rem;
  padding-bottom: 0;
}

.board__side--rent .board__row--rule { border-top-color: var(--red); }
.board__side--own .board__row--rule { border-top-color: #2f4bff; }
.board__side--rent .board__row--rule .board__label { color: var(--red); }
.board__side--own .board__row--rule .board__label { color: var(--blue-lo); }

/* Marks a shade larger here than in the calculator: this block asks the
   reader to recognise the company, not to scan past it. */
.board .calc__mark { padding-block: 0.18rem; }
.board .calc__mark svg { width: 0.92rem; height: 0.92rem; }
.board .calc__mark em { font-size: 0.66rem; }
.board .calc__logo { height: 1.3rem; }
.board .calc__logo svg { height: 0.88rem; }

/* --- Forms --------------------------------------------------------------- */

.form-card {
  /* A light island on a dark surface has to put the tokens back, or the
     heading inherits white-on-white and drops to 1:1 contrast. */
  --heading-color: var(--text);
  --lede-color: var(--text-muted);
  --card-bg: #fff;
  --card-line: var(--line);
  --card-shadow: 0 1px 2px rgb(0 1 8 / 0.06);

  background: var(--card-bg);
  border: 1px solid var(--card-line);
  /* Радиус карточный везде: в герое `/audit/`, `/contact/`, `/repairs/`
     форма стояла с 4px, в закрытии с 20px. Одна роль, один радиус.
     ⚠️ Якорь правки был селектором `.form-card` с открывающей скобкой, и он
     ловил `.close .form-card` строкой выше: подстрока совпадает. Два прогона
     отчитались «уже сделано» о чужом блоке. Скобок в этом комментарии нет
     намеренно: гейт `css` считает их и в комментариях. */
  border-radius: var(--radius-card);
  padding: var(--sp-l);
  color: var(--text);
  box-shadow: var(--card-shadow);
}

.field { display: block; margin-top: var(--sp-m); }

.field > span {
  display: block;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.35rem;
}

.field input, .field select, .field textarea {
  width: 100%;
  min-height: 2.95rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  color: var(--text);
  font-family: inherit;
  /* Never below 16px: iOS zooms the page on focus otherwise. */
  font-size: 1rem;
}

.field textarea { min-height: 7rem; resize: vertical; }

.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--blue);
  outline: 2px solid var(--blue);
  outline-offset: 1px;
}

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-note { margin-top: var(--sp-s); font-size: var(--step--1); color: var(--text-muted); }
.form-status { margin-top: var(--sp-s); font-size: var(--step--1); }
.form-status[data-state="ok"] { color: #0a7a3d; }
.form-status[data-state="err"] { color: var(--red); }

/* --- Prose (article body only) ------------------------------------------- */

/* Scoped to .article deliberately: an unscoped .prose max-width once
   collapsed an unrelated page's container and shifted its whole layout. */
.article .prose { max-width: 44rem; }

.prose > * + * { margin-top: var(--sp-m); }
.prose h2 { margin-top: var(--sp-xl); font-size: var(--step-2); }
.prose h3 { margin-top: var(--sp-l); font-size: var(--step-1); }
.prose ul, .prose ol { padding-left: 1.2em; }
.prose li + li { margin-top: 0.4em; }
.prose a { color: var(--blue); }

/* --- Свет первого экрана ------------------------------------------------- */

/* PO 07.09.2026, после листа из 17 первых экранов: «может просто экран с
   синей подсветкой везде?». Свечение было на 6 экранах из 17, и на каждом
   своей копией правила (`.mig-pin`, `.dpt-pin`, `.ai-pin`, `.say-pin`,
   `.case-pin`, `.play-pin`). Остальные получают его ОДНИМ атрибутом
   `data-glow` на контейнере первого экрана, а не седьмой копией.

   `isolation` плюс `z-index: -1`: свет ложится над фоном контейнера и под
   всем его содержимым без правки z-index у детей. `inset: 0`, а не `-20%`
   как у `.mig-pin`: минус двадцать процентов давали прокрутку вбок там, где
   контейнер не режет переполнение (замерено на телефоне, 468 при 390), а
   при позиции в svh пятну и так есть где растаять до края.

   Position у контейнера НЕ трогается: липкие пины уже позиционированы, и
   `position: relative` их ломал (см. `.arv-pin`). Единственный статичный
   контейнер, `.prc-hero`, получает relative у себя в `pages.css`. */
[data-glow] { isolation: isolate; }
[data-glow]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--first-glow);
}
/* На телефоне тот же ход, что у пинов в `pages.css`: `::before` снят, свет
   фоном коробки. Лежит ЗДЕСЬ, а не в `pages.css`: четыре страницы
   (`/contact/`, `/content/`, `/un-saas/`, `/digital-inventory/`) `pages.css`
   не грузят, и правило там до них не доходило. Замерено 07.09: фон none.
   ⚠️ Контейнеры первого экрана красят фон через `background-color`, не
   шортхендом `background`: шортхенд в файле, который грузится позже,
   стирал эту картинку. */
@media (width < 48rem) {
  [data-glow]::before { display: none; }
  [data-glow] { background-image: var(--night-glow); }
}

/* --- Footer -------------------------------------------------------------- */

.footer {
  background: var(--ink);
  color: var(--on-dark);
  padding-block: var(--sp-xl) var(--sp-l);
  border-top: 1px solid var(--line-dark);
}

/* One column on a phone; four unequal ones from tablet up.

   The tracks are DELIBERATELY unequal. `auto-fit` gave four equal columns, and
   with eleven services against five company links the result was a tall left
   half and a void bottom-right - the footer read as unfinished because one
   column ran twice as long as its neighbours. Services gets the width it needs
   and flows in two sub-columns, so the four blocks end at roughly the same
   line. */
.footer__grid { display: grid; gap: var(--sp-l); grid-template-columns: 1fr; }

@media (min-width: 48rem) {
  .footer__grid { grid-template-columns: 1.3fr 1.6fr 1fr 1.2fr; }
  /* Multi-column rather than a second grid child: the list stays ONE list in
     the markup, so a screen reader and the keyboard still walk eleven items in
     source order, and adding a service needs no layout decision. */
  .footer__services { columns: 2; column-gap: var(--sp-m); }
  .footer__services li { break-inside: avoid; }
}

/* The two paragraphs, under the logo and under the address. Both carried an
   inline `margin-top`, which put the same layout decision in the markup twice
   and hid it from the CSS audit. */
.footer__col > .footer__statement { margin-top: var(--sp-s); }

/* Was two links separated by an `&nbsp;`. A non-breaking space is a character
   doing a layout job: it cannot be spaced per breakpoint and it lands inside
   the link text for a screen reader. */
.footer__legal { display: inline-flex; gap: var(--sp-s); }

.footer h4 {
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-dark-muted);
  margin-bottom: var(--sp-s);
}

.footer ul { list-style: none; }
.footer li + li { margin-top: 0.1rem; }

.footer a {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  color: var(--on-dark-muted);
  text-decoration: none;
  font-size: var(--step--1);
  /* The address is the one unbreakable string here, and its track is now the
     narrowest of the four at tablet width. Without this it decides the column
     width for everybody by refusing to wrap. */
  overflow-wrap: anywhere;
}
.footer a:hover { color: var(--white); }

.footer__statement { max-width: 34ch; color: var(--on-dark-muted); font-size: var(--step--1); }

/* The name, at the width of the page. Kononenko (measured 06.09.2026, 1900x800):
   the footer ends on the name as an SVG 1841px wide, 550px tall, edge to edge
   with a 30px margin, and one 15.8px line of legal text under it - a 33x
   contrast. Here the wordmark is 198x25, so at wrap width it stands about
   230px tall against 15px links: 15x, above the 6x floor in REFERENCES.md.

   The reveal is theirs too: the line sits in a mask (overflow: clip) and
   rises into it. The generic reveal fades and lifts 12px; for a name this
   size a fade reads as a bug, so it is replaced with the mask rise. Gated
   on .motion for the same reason as the generic one: with the script blocked
   the name is simply there. */
.footer__name { margin-top: var(--sp-xl); overflow: clip; }
.footer__name img { display: block; width: 100%; height: auto; }
.motion .footer__name { opacity: 1; transform: none; }
.motion .footer__name img {
  transform: translateY(104%);
  transition: transform 0.9s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.motion .footer__name.is-in img { transform: none; }
@media (prefers-reduced-motion: reduce) { .motion .footer__name img { transform: none; } }

.footer__bottom {
  margin-top: var(--sp-m);
  padding-top: var(--sp-m);
  border-top: 1px solid var(--line-dark);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-s) var(--sp-m);
  align-items: center;
  justify-content: space-between;
  font-size: var(--step--1);
  color: var(--on-dark-muted);
}

/* --- Reveal -------------------------------------------------------------- */

/* Gated on .motion, which motion.js adds only once it has actually set up,
   and not on [data-reveal] alone.
   
   Ungated, this hid every block on the site behind a script: motion.js
   returns early if GSAP has not loaded, and the reveal that would have
   brought them back never runs, so a reader whose CDN request failed, whose
   network dropped one file, or whose browser blocked it got a page with
   nothing on it. The rule two hundred lines above states this principle and
   this one broke it. */
.motion [data-reveal] { opacity: 0; transform: translateY(12px); transition: opacity 0.5s ease, transform 0.5s ease; }
.motion [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) { .motion [data-reveal] { opacity: 1; transform: none; } }

/* --- Skip link ----------------------------------------------------------- */

.skip { position: absolute; left: -9999px; top: 0; z-index: 100; padding: 0.75rem 1rem; background: var(--white); color: var(--ink); }
.skip:focus { left: 0.5rem; top: 0.5rem; }

@media (max-width: 47.99rem) { .calc__list { max-height: none; } }

/* --- Years-paying selector ----------------------------------------------- */

.calc__years {
  display: block;
  margin-top: var(--sp-m);
}

.calc__years > span {
  display: block;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--on-dark-muted);
  margin-bottom: 0.35rem;
}

.calc__years select {
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--white);
  font-family: London, sans-serif;
  /* 16px minimum: iOS zooms the page when a smaller control takes focus. */
  font-size: 1rem;
}

.calc__years select:focus { border-color: var(--blue); }

/* --- The brand's light --------------------------------------------------- */

/* The identity's own background bloom, promoted from wallpaper to subject.
   Blue-cyan-green is what you own; the red bleed is what renting takes. */

.bloom {
  position: absolute;
  inset: auto;
  width: 130%;
  max-width: none;
  height: auto;
  aspect-ratio: 1013 / 976;
  pointer-events: none;
  z-index: 0;
  opacity: 0.85;
}

.bloom__core { transform-origin: 50% 50%; }
.bloom__leak { transform-origin: 50% 50%; }

/* Rent: the light drifts and the red bleeds off it, endlessly. */
[data-bloom="drain"] .bloom__core { animation: bloom-breathe 17s ease-in-out infinite; }
[data-bloom="drain"] .bloom__leak { animation: bloom-leak 13s ease-in-out infinite; }

/* Owned: whole, steady, going nowhere. */
[data-bloom="held"] .bloom__core { animation: bloom-hold 24s ease-in-out infinite; }

@keyframes bloom-breathe {
  0%, 100% { transform: scale(1) translate(0, 0); }
  50%      { transform: scale(0.9) translate(-2%, 2%); }
}

@keyframes bloom-leak {
  0%, 100% { transform: translate(6%, -4%) scale(1.05); opacity: 0.75; }
  50%      { transform: translate(14%, -10%) scale(1.25); opacity: 0.3; }
}

@keyframes bloom-hold {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

@media (prefers-reduced-motion: reduce) {
  .bloom__core, .bloom__leak { animation: none !important; }
}

.bloom--hero {
  top: -22%;
  right: -34%;
  width: 105%;
}

.bloom--left {
  top: -30%;
  left: -38%;
  width: 95%;
  opacity: 0.6;
}

@media (max-width: 63.99rem) {
  .bloom--hero { top: -8%; right: -45%; width: 135%; opacity: 0.6; }
  .bloom--left { opacity: 0.4; }
}

/* Anything that must sit above the light says so. */
.above { position: relative; z-index: 1; }

/* --- Gradient text and rules --------------------------------------------- */

/* The identity's 87deg sweep, used where a word carries the idea. */
.grad {
  background: linear-gradient(88deg, var(--blue), var(--blue-lo) 62%, var(--blue));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.grad-rule {
  height: 2px;
  border: 0;
  background: linear-gradient(88deg, var(--blue), var(--blue-lo) 62%, var(--blue));
  opacity: 0.9;
}

/* A bleed behind a headline, straight from the identity's own h1 treatment. */
.bleed {
  position: relative;
  isolation: isolate;
}

.bleed::before {
  content: "";
  position: absolute;
  inset: -12% -20% -12% -8%;
  z-index: -1;
  background: linear-gradient(90deg, rgb(1 48 249 / 0.55), rgb(0 1 8 / 0) 54%);
  filter: blur(28px);
  pointer-events: none;
}

/* ==========================================================================
   Composition pass. The first build had correct components and no craft:
   symmetric splits, boxed panels, and sections that were mostly dead space.
   ========================================================================== */

/* The light has to be a presence, not a wash. */
.bloom { opacity: 1; }
.bloom--hero { top: -40%; right: -22%; width: 118%; }
.bloom--left { top: -34%; left: -30%; width: 105%; opacity: 0.9; }

@media (max-width: 63.99rem) {
  .bloom--hero { top: -14%; right: -40%; width: 150%; opacity: 0.75; }
  .bloom--left { opacity: 0.6; }
}

/* --- Hero composition ---------------------------------------------------- */

/* Asymmetric on purpose: the headline gets the room, the instrument sits
   tight to the right and slightly low, so the two are not a matched pair. */
@media (min-width: 64rem) {
  .hero__inner { grid-template-columns: 1.15fr 0.85fr; align-items: end; }
  .hero__inner > div:last-child { padding-top: clamp(2rem, 6vw, 6rem); }
}

.hero__title { letter-spacing: -0.03em; line-height: 0.88; }

/* --- The instrument ------------------------------------------------------ */

/* Not a card. Hairlines and monospace, like a readout on a machine. */
.calc {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line-dark);
  border-radius: 0;
  padding: var(--sp-m) 0 0;
  backdrop-filter: blur(2px);
}

.calc__list {
  background: transparent;
  border: 0;
  border-radius: 0;
  gap: 0;
}

.calc__item {
  background: transparent;
  border-bottom: 1px solid rgb(252 252 252 / 0.07);
  padding-inline: 0;
}

.calc__item input[type="number"] {
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgb(252 252 252 / 0.14);
  border-radius: 0;
  width: 4.25rem;
}

.calc__item input[type="number"]:focus { border-bottom-color: var(--blue-lo); }

.calc__years select {
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgb(252 252 252 / 0.14);
  border-radius: 0;
  padding-inline: 0;
}

.calc__total { border-top-color: rgb(252 252 252 / 0.14); }

.calc__total .figure {
  font-size: clamp(3rem, 2rem + 6vw, 5.5rem);
  letter-spacing: -0.05em;
}

/* --- Editorial index ----------------------------------------------------- */

.idx {
  display: flex;
  align-items: baseline;
  gap: var(--sp-m);
  padding-block: var(--sp-l);
  border-top: 1px solid var(--panel-line);
}

.idx__n {
  flex: none;
  width: 3.5rem;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  color: var(--tag-color);
  letter-spacing: 0.1em;
}

.idx__body { flex: 1; display: grid; gap: var(--sp-s); }

.idx__title {
  font-family: Asgard, sans-serif;
  font-size: var(--step-2);
  line-height: 1;
  color: var(--heading-color);
}

@media (min-width: 56rem) {
  .idx__body { grid-template-columns: minmax(12rem, 22rem) 1fr; gap: var(--sp-xl); align-items: baseline; }
}

/* --- Then / Now spread --------------------------------------------------- */

.spread { display: grid; gap: var(--sp-l); }

@media (min-width: 56rem) {
  .spread { grid-template-columns: 1fr 1fr; gap: clamp(2rem, 6vw, 7rem); }
  /* The second column drops, so the eye travels rather than compares. */
  .spread > *:last-child { margin-top: clamp(3rem, 8vw, 8rem); }
}

.spread__k {
  font-family: Asgard, sans-serif;
  font-size: clamp(3.5rem, 2rem + 8vw, 8rem);
  line-height: 0.82;
  letter-spacing: -0.04em;
  display: block;
  margin-bottom: var(--sp-s);
}

.spread__k--rent { color: var(--red); }

.spread__k--own {
  background: linear-gradient(88deg, var(--blue), var(--blue-lo) 62%, var(--blue));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Sections carry their own light, so they need less air, not more. */
.sect--ink-2 { padding-block: clamp(3.5rem, 2.5rem + 5vw, 7rem); }

/* --- Legibility over the light ------------------------------------------- */

/* The bloom went bright enough to wash out muted text sitting on top of it.
   A scrim keeps the light readable as light and the type readable as type. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    linear-gradient(to right, rgb(0 1 8 / 0.92) 0%, rgb(0 1 8 / 0.55) 45%, rgb(0 1 8 / 0.35) 100%),
    linear-gradient(to bottom, rgb(0 1 8 / 0.5), rgb(0 1 8 / 0) 30%, rgb(0 1 8 / 0.6));
  pointer-events: none;
}

/* Muted copy needs more weight when it sits over colour, not over black. */
.hero .tag,
.hero .calc__note,
.hero .calc__item label,
.hero .calc__years > span,
.hero .calc__head .mono { color: #c9ccd6; }

.hero .calc__item input[type="checkbox"]:checked + span { color: #fff; }

/* Content sat at the bottom of the fold because the grid aligned to the end.
   Top-aligned, with the instrument offset instead: same asymmetry, no drop. */
@media (min-width: 64rem) {
  .hero__inner { align-items: start; }
  .hero__inner > div:last-child { padding-top: clamp(0.5rem, 2vw, 2rem); }
}

.hero { padding-block: clamp(2rem, 1rem + 4vw, 4.5rem) var(--sp-xl); }

/* --- The light field (WebGL) --------------------------------------------- */

.field__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  animation: field-in 1.6s ease 0.15s forwards;
}

@keyframes field-in { to { opacity: 1; } }

/* The SVG bloom is the fallback. Once the shader is live it steps aside,
   so the two never stack into mud. */
.field--on .bloom { opacity: 0.25; }

@media (prefers-reduced-motion: reduce) {
  .field__canvas { animation: none; opacity: 1; }
}

/* --- Hero layer order ---------------------------------------------------- */

/* The scrim sat at the same z-index as the canvas and, being later in paint
   order, covered the shader at 0.92 alpha. Three explicit layers instead:
   light at 0, scrim at 1, content at 2. */

.hero .bloom,
.hero .field__canvas { z-index: 0; }

.hero::after { z-index: 1; }

/* ⚠️ Плёнка нужна только поверх света. Её ставили на главную, где под ней
   был шейдер или SVG-пятно, а контент там помечен `.above` и лежит слоем
   выше. На страницах с простым героем (`/contact/`, правовые, 404, `/audit/`)
   света нет и `.above` нет, и плёнка ложилась ПОВЕРХ текста и формы: слева
   затемнение 0.92, справа 0.35. Белая карточка формы читалась серой, заголовок
   тонул. Висело с 11.08, замечено PO 18.09 на `/contact/`. */
.hero:not(:has(.bloom, .field__canvas))::after { content: none; }

.hero .above { z-index: 2; }

/* The shader already falls off at its own edges and is far more controlled
   than the SVG bloom, so the scrim can be much lighter over it. */
.field--on::after {
  background:
    linear-gradient(to right, rgb(0 1 8 / 0.72) 0%, rgb(0 1 8 / 0.3) 42%, rgb(0 1 8 / 0.12) 100%),
    linear-gradient(to bottom, rgb(0 1 8 / 0.35), rgb(0 1 8 / 0) 32%, rgb(0 1 8 / 0.55)) !important;
}

/* ==========================================================================
   THE BOX. LONSTERN's own visual language: a service is a package, and
   scrolling unpacks it. Replaces the shader, which was decoration.
   ========================================================================== */

/* Reference deck: no rules, no cards, no hairlines in the hero. The object
   carries the composition and the type stays restrained. */
.hero--box { --box-w: clamp(13rem, 20vw, 21rem); }

.hero--box .hero__title {
  font-size: clamp(1.9rem, 1.1rem + 2.6vw, 3.3rem);
  text-transform: uppercase;
  letter-spacing: -0.012em;
  line-height: 1.04;
}

.hero--box .hero__title em,
.hero--box .hero__title span { font-style: normal; }

.scene {
  position: relative;
  perspective: 1400px;
  perspective-origin: 55% 45%;
  min-height: clamp(20rem, 46vw, 34rem);
  display: grid;
  place-items: center;
  transform-style: preserve-3d;
}

/* Lights live in the scene and fall on the object, rather than tinting a
   flat background. Three sources, matching the reference deck. */
.scene__light {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  pointer-events: none;
  opacity: 0.85;
}

.scene__light--a {
  top: -18%; right: -12%;
  width: 60%; aspect-ratio: 1;
  background: radial-gradient(circle, #00ffff 0%, rgb(0 255 255 / 0) 68%);
}

.scene__light--b {
  bottom: -22%; left: -18%;
  width: 66%; aspect-ratio: 1;
  background: radial-gradient(circle, #0fd9c0 0%, rgb(15 217 192 / 0) 68%);
  opacity: 0.7;
}

.scene__light--c {
  bottom: -8%; left: 34%;
  width: 46%; aspect-ratio: 1;
  background: radial-gradient(circle, #0130f9 0%, rgb(1 48 249 / 0) 70%);
  opacity: 0.8;
}

.box {
  position: relative;
  width: var(--box-w);
  height: calc(var(--box-w) * 1.34);
  transform-style: preserve-3d;
  transform: rotateX(14deg) rotateY(-26deg) rotateZ(-6deg);
  will-change: transform;
}

.box__face {
  position: absolute;
  inset: 0;
  background: linear-gradient(150deg, #10141d 0%, #05070c 60%);
  border-radius: 2px;
  backface-visibility: hidden;
}

.box__face--front {
  transform: translateZ(calc(var(--box-w) * 0.17));
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: clamp(1rem, 2vw, 1.6rem);
  overflow: hidden;
}

/* A soft sheen so the front face reads as a surface catching the light. */
.box__face--front::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(128deg, rgb(0 255 255 / 0.16) 0%, rgb(1 48 249 / 0.05) 34%, rgb(0 0 0 / 0) 62%);
  pointer-events: none;
}

.box__mark {
  font-family: Asgard, sans-serif;
  font-size: clamp(0.62rem, 1vw, 0.8rem);
  letter-spacing: 0.16em;
  color: rgb(252 252 252 / 0.85);
}
.box__mark i { font-style: normal; color: var(--blue-lo); }

.box__label {
  margin-top: auto;
  font-family: Asgard, sans-serif;
  font-size: clamp(1.05rem, 2.1vw, 1.7rem);
  line-height: 0.98;
  text-transform: uppercase;
  background: linear-gradient(88deg, var(--blue), var(--blue-lo) 62%, var(--blue));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.box__sub {
  font-size: clamp(0.6rem, 0.95vw, 0.75rem);
  color: rgb(252 252 252 / 0.5);
  letter-spacing: 0.02em;
}

.box__face--right {
  width: calc(var(--box-w) * 0.34);
  left: auto; right: 0;
  transform-origin: right center;
  transform: rotateY(90deg) translateZ(calc(var(--box-w) * 0.17));
  background: linear-gradient(160deg, #0a0e16, #04060a);
}

.box__face--left {
  width: calc(var(--box-w) * 0.34);
  right: auto; left: 0;
  transform-origin: left center;
  transform: rotateY(-90deg) translateZ(calc(var(--box-w) * 0.17));
  background: linear-gradient(160deg, #070a11, #03050a);
}

.box__face--bottom {
  height: calc(var(--box-w) * 0.34);
  top: auto; bottom: 0;
  transform-origin: bottom center;
  transform: rotateX(-90deg) translateZ(calc(var(--box-w) * 0.17));
  background: #04060b;
}

/* The lid hinges on the top edge and opens away from the reader. */
.box__lid {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: calc(var(--box-w) * 0.34);
  transform-origin: top center;
  transform: rotateX(-90deg) translateZ(calc(var(--box-w) * 0.17));
  transform-style: preserve-3d;
  will-change: transform;
}

.box__lid-out,
.box__lid-in { position: absolute; inset: 0; border-radius: 2px; backface-visibility: hidden; }

.box__lid-out {
  background: linear-gradient(94deg, #0130f9, #00ffff 52%, #74ff80);
}

.box__lid-in {
  transform: rotateX(180deg);
  background: #05070c;
  display: grid;
  place-items: center;
  padding: 0.8rem;
}

.box__lid-in p {
  font-size: clamp(0.55rem, 0.85vw, 0.72rem);
  line-height: 1.45;
  color: rgb(252 252 252 / 0.62);
  text-align: center;
  max-width: 26ch;
}

@media (prefers-reduced-motion: reduce) {
  .box { transform: rotateX(12deg) rotateY(-24deg) rotateZ(-5deg) !important; }
  .box__lid { transform: rotateX(-160deg) translateZ(calc(var(--box-w) * 0.17)) !important; }
}

/* --- Hero density -------------------------------------------------------- */

/* The first build left half a screen of dead space under the buttons because
   the object column is taller than the text column. Centring the text against
   the object closes it without shrinking either. */
@media (min-width: 64rem) {
  .hero--box .hero__inner { align-items: center; }
  .hero--box .hero__inner > div:last-child { padding-top: 0; }
}

.hero--box { padding-block: clamp(2rem, 1rem + 3vw, 3.5rem) clamp(2.5rem, 1.5rem + 4vw, 5rem); }

/* The object was running off the right edge. Pull it back inside. */
.hero--box .scene { min-height: clamp(17rem, 34vw, 27rem); }
.hero--box .box { transform-origin: center center; }

/* --- Packaging ----------------------------------------------------------- */

/* The brand's own product renders. Replaces the CSS 3D box, which never read
   as a solid: at the hero's angle the hinged lid detached into a floating
   slab. Not a geometry to be fixed, a wheel already built. */
.pack {
  margin: 0;
  position: relative;
}

.pack img {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 40px 70px rgb(0 0 0 / 0.55));
}

/* A pool of the brand light under the packs, so they sit in the scene
   instead of floating on flat black. */
.pack::before {
  content: "";
  position: absolute;
  inset: 8% -12% -14% -12%;
  z-index: -1;
  background:
    radial-gradient(60% 55% at 70% 45%, rgb(0 255 255 / 0.22), rgb(0 255 255 / 0) 70%),
    radial-gradient(55% 50% at 25% 70%, rgb(1 48 249 / 0.28), rgb(1 48 249 / 0) 72%);
  filter: blur(50px);
  pointer-events: none;
}

@media (min-width: 64rem) {
  .hero--box .hero__inner { grid-template-columns: 1fr 1.05fr; }
}

/* ==========================================================================
   THE BOX, rebuilt.

   Every face is centred in the box and pushed out along its own axis by half
   the relevant dimension. No transform-origin, no hinges, so nothing can
   detach. Dimensions live in variables so the six offsets stay in agreement.
   ========================================================================== */

.hero--box .scene { min-height: clamp(19rem, 38vw, 30rem); }

.box {
  --bw: clamp(11rem, 17vw, 17rem);   /* width  */
  --bh: clamp(15rem, 23vw, 23rem);   /* height */
  --bd: clamp(3rem, 4.6vw, 4.6rem);  /* depth  */

  position: relative;
  width: var(--bw);
  height: var(--bh);
  transform-style: preserve-3d;
  transform: rotateX(12deg) rotateY(-28deg);
  will-change: transform;
}

.box__f {
  position: absolute;
  background: linear-gradient(155deg, #12161f 0%, #05070c 65%);
}

/* Front and back: box footprint, pushed half the depth each way. */
.box__f--front,
.box__f--back { inset: 0; }
.box__f--front { transform: translateZ(calc(var(--bd) / 2)); }
.box__f--back  { transform: rotateY(180deg) translateZ(calc(var(--bd) / 2)); background: #04060b; }

/* Sides: depth wide, full height, centred then rotated out. */
.box__f--left,
.box__f--right {
  top: 0;
  height: 100%;
  width: var(--bd);
  left: calc(50% - var(--bd) / 2);
}
.box__f--right { transform: rotateY(90deg) translateZ(calc(var(--bw) / 2)); }
.box__f--left  { transform: rotateY(-90deg) translateZ(calc(var(--bw) / 2)); }

/* Top and bottom: depth tall, full width, centred then rotated out. */
.box__f--top,
.box__f--bottom {
  left: 0;
  width: 100%;
  height: var(--bd);
  top: calc(50% - var(--bd) / 2);
}
.box__f--top    { transform: rotateX(90deg) translateZ(calc(var(--bh) / 2)); }
.box__f--bottom { transform: rotateX(-90deg) translateZ(calc(var(--bh) / 2)); background: #03050a; }

/* --- Surfaces ------------------------------------------------------------ */

/* The lid carries the identity's own sweep. */
.box__f--top {
  background: linear-gradient(94deg, #0130f9 0%, #00ffff 52%, #74ff80 100%);
}

/* A band of the same gradient across the head of the front face, the way the
   brand's own packaging is printed. */
.box__f--front {
  padding: clamp(0.9rem, 1.6vw, 1.5rem);
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  overflow: hidden;
}

.box__f--front::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 0.34rem;
  background: linear-gradient(94deg, #0130f9, #00ffff 52%, #74ff80);
}

/* Light falling across the face, so it reads as a surface and not a rectangle. */
.box__f--front::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(122deg, rgb(0 255 255 / 0.14) 0%, rgb(1 48 249 / 0.06) 30%, rgb(0 0 0 / 0) 58%);
  pointer-events: none;
}

.box__mark {
  position: relative;
  font-family: Asgard, sans-serif;
  font-size: clamp(0.6rem, 0.95vw, 0.78rem);
  letter-spacing: 0.16em;
  color: rgb(252 252 252 / 0.9);
}
.box__mark i { font-style: normal; color: var(--blue-lo); }

.box__body {
  position: relative;
  margin-top: auto;
  font-size: clamp(0.55rem, 0.8vw, 0.68rem);
  line-height: 1.4;
  color: rgb(252 252 252 / 0.42);
  max-width: 18ch;
}

/* The product name, printed in the brand gradient like the reference packs. */
.box__title {
  position: relative;
  font-family: Asgard, sans-serif;
  font-size: clamp(1.05rem, 2vw, 1.75rem);
  line-height: 0.95;
  text-transform: uppercase;
  background: linear-gradient(88deg, var(--blue), var(--blue-lo) 62%, var(--blue));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Spine text, rotated to run up the side. */
.box__f--right {
  display: grid;
  place-items: center;
  background: linear-gradient(170deg, #0b0f18, #04060b);
}
.box__f--right span {
  writing-mode: vertical-rl;
  font-family: PlexMono, monospace;
  font-size: clamp(0.45rem, 0.62vw, 0.58rem);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgb(252 252 252 / 0.34);
  white-space: nowrap;
}

/* Ground shadow so the object sits in the scene. */
.scene::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 8%;
  width: 70%;
  height: 3rem;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgb(0 0 0 / 0.6), rgb(0 0 0 / 0) 70%);
  filter: blur(14px);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .box { transform: rotateX(12deg) rotateY(-28deg) !important; }
}

/* --- Box, finishing pass ------------------------------------------------- */

/* It read as a thin card: the depth was too small against the face, and the
   faces were all the same darkness, so no edge was legible. */
.box {
  --bw: clamp(12rem, 19vw, 19rem);
  --bh: clamp(16rem, 25vw, 25rem);
  --bd: clamp(4.5rem, 7vw, 7rem);
}

.hero--box .scene { min-height: clamp(21rem, 42vw, 33rem); }

/* Each plane catches a different amount of light, which is what makes the
   edges read. Flat-shaded faces are why the first version looked like paper. */
.box__f--front { background: linear-gradient(148deg, #1a212e 0%, #0a0e16 55%, #06080e 100%); }
.box__f--right { background: linear-gradient(170deg, #060910 0%, #020306 100%); }
.box__f--left  { background: linear-gradient(170deg, #0d121c 0%, #05070d 100%); }

/* A lit edge where the lid meets the face, the way a real printed box has a
   crease that catches the light. */
.box__f--front::before { height: 0.5rem; box-shadow: 0 0 22px rgb(0 255 255 / 0.45); }

/* The lid picks up a sheen so it does not read as flat colour. */
.box__f--top {
  background:
    linear-gradient(180deg, rgb(255 255 255 / 0.22), rgb(255 255 255 / 0) 60%),
    linear-gradient(94deg, #0130f9 0%, #00ffff 52%, #74ff80 100%);
}

/* Depth cue: the box is lit from the upper right, so the left edge falls off. */
.box::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateZ(calc(var(--bd) / 2 + 1px));
  background: linear-gradient(255deg, rgb(0 0 0 / 0) 55%, rgb(0 0 0 / 0.45) 100%);
  pointer-events: none;
}

.pack { display: none; }

/* --- Box sequence -------------------------------------------------------- */

.boxseq {
  position: relative;
  width: min(100%, 30rem);
  margin-inline: auto;
  aspect-ratio: 760 / 980;
}

.boxseq img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 30px 60px rgb(0 0 0 / 0.5));
}

.boxseq img[hidden] { display: none; }

/* ==========================================================================
   The raster stage
   --------------------------------------------------------------------------
   Four screens of scroll driving one sticky canvas: the sentence, the flight
   through everything the reader rents, the turn to paper. The document
   underneath is the real page, kept out of sight because the canvas repeats
   every word of it.
   ========================================================================== */

.stage {
  position: relative;
  height: 420svh;
  background: var(--ink);
  color: var(--on-dark);
  /* The header is sticky, so it takes its own height out of the flow and the
     stage began below it: a white band across the top of a black corridor,
     with a white logo sitting invisibly in it. The stage starts at the very
     top of the page instead and the header floats over it. */
  margin-top: calc(var(--header-h) * -1);
}

.stage__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
}

.stage__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* The head-up display stays crisp on purpose. Everything behind it goes
   through the threshold, so type that has not is the one thing in the frame
   that already belongs to the reader. */
.stage__hud {
  position: absolute;
  left: clamp(1.25rem, 5vw, 5rem);
  bottom: var(--sp-l);
  z-index: 2;
  display: grid;
  gap: 0.35rem;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  pointer-events: none;
}

.stage__meter { display: flex; gap: 0.6rem; align-items: baseline; }
.stage__meter-key { color: #6b6f7d; letter-spacing: 0.08em; text-transform: uppercase; font-size: 0.66rem; }
.stage__meter b { color: var(--red); font-weight: 600; font-variant-numeric: tabular-nums; font-size: var(--step-1); }
.stage__caption { color: var(--on-dark-muted); }

.stage__doc { position: absolute; left: -9999px; top: 0; width: 1px; height: 1px; overflow: hidden; }

@media (max-width: 47.99rem) {
  .stage { height: 380svh; }
}

/* --- The finale ----------------------------------------------------------- */

/* The canvas turns to paper under this, so the closing panel is set dark on
   light while everything above it was light on dark. The reversal is the
   argument: renting is the corridor, owning is the daylight at the end. */
/* --- The arrival ---------------------------------------------------------- */

/* Its own section now. It used to be painted by the flight's canvas at the end
   of the corridor, which handed the reader the answer before they had put a
   single figure in. The blue it used to get from the canvas is a gradient
   here, lit from the upper left so the object below has somewhere to sit. */
.arrival {
  /* The header is sticky and sits over whatever scrolls under it, so the top
     of this section has to clear it or the headline arrives with a dark bar
     across it. The bottom is lighter than the top for the same reason: the
     panel below is tall and the section was overflowing the screen. */
  /* The object throws a long shadow, and the bottom padding has to contain
     it or it lands on the section below. */
  /* The two figures are not the same because the top has to clear the header
     and the heading carries its own leading above the cap. Set against the
     measured result rather than the token, so the air reads equal. */
  padding-block: calc(var(--header-h) + var(--sp-s)) var(--sp-xxl);
  color: var(--white);
  background:
    radial-gradient(120% 100% at 22% -10%, #1c44dc, transparent 62%),
    linear-gradient(160deg, #0a1c84, #03071f);
}

/* The gap here is between the sentence and the object it introduces, so it
   has to be bigger than the gap inside the sentence itself. At 1.2rem the
   panel sat straight under the standfirst as if it were the next line of it. */
.arrival__inner {
  display: grid;
  gap: clamp(1.6rem, 3.2vw, 2.8rem);
}

/* The headline and the object have to share one screen, so the type gives way
   to the thing it is introducing rather than the other way round. */

.arrival .stage__end-sub { max-width: 58ch; }

.stage__end-top {
  display: grid;
  gap: var(--sp-m);
  align-items: end;
  justify-items: start;
}

@media (min-width: 56rem) {
  .stage__end-top { grid-template-columns: 1fr auto; align-items: end; }
}

.stage__end-say { display: grid; gap: 0.5rem; }

.stage__end-line {
  font-size: var(--display);
  line-height: 0.9;
  color: var(--white);
}

.stage__end-sub {
  max-width: 58ch;
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  color: rgb(252 252 252 / 0.78);
}

.btn--on-blue { background: var(--white); border-color: var(--white); color: var(--blue); }
.btn--on-blue:hover { background: var(--white); color: var(--ink); }

.btn--ghost-light {
  background: transparent;
  border-color: rgb(252 252 252 / 0.45);
  color: var(--white);
}
.btn--ghost-light:hover { background: rgb(252 252 252 / 0.12); border-color: var(--white); }

.stage:not(.stage--page)[data-done="true"] .stage__hud { opacity: 0; transition: opacity 0.35s ease; }

/* The fixed header is styled for the dark corridor and would sit as a black
   slab across the paper. It steps aside for the finale and comes straight
   back when the reader scrolls on. */
.header { transition: opacity 0.35s ease; }
body:has(.stage:not(.stage--page)[data-done="true"]) .header:not([data-solid="true"]) {
  opacity: 0;
  pointer-events: none;
}

/* ==========================================================================
   One surface after the turn
   --------------------------------------------------------------------------
   Dark is renting and light is owning. The flight ends on paper and the page
   stays there, so the reader spends the rest of the site on the other side of
   the argument. Alternating dark and light sections underneath a raster
   corridor read as a different website bolted on below it.

   Everything is drawn with hairlines and space instead of boxes and shadows,
   which is the same language as the two columns the flight lands on.
   ========================================================================== */

.sect--rule { border-top: 1px solid var(--line); }

.sect__head { max-width: 52rem; }
.sect__head h2 { line-height: 0.92; }

/* Kickers carry the colour coding the flight established. */
.tag { font-size: 0.66rem; letter-spacing: 0.14em; }

/* Cards become ruled columns: no fill, no shadow, no rounded corner. */
.card {
  background: none;
  border: 0;
  border-top: 2px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  padding: var(--sp-s) 0 0;
}
.card h3 { font-size: var(--step-2); }


/* Figures are the brand's monospace everywhere, so a price on this page and a
   price on a plate in the corridor are visibly the same kind of thing. */
.price, .price-row__price, .calc__total, .figure {
  font-family: PlexMono, ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

/* --- The calculator on paper ---------------------------------------------- */

/* It was written for a dark section and moved onto the light one when the
   page went to a single surface, which left white type on white paper: the
   labels, the fields and the two inline figures all vanished. Rewritten
   against the light tokens rather than patched with overrides. */

.calc__item label { color: var(--text-muted); }
.calc__item input[type="checkbox"]:checked + span { color: var(--text); }

.calc__item {
  border-bottom-color: var(--line);
}

.calc__item input[type="number"] {
  color: var(--text);
  border-bottom-color: var(--line);
}
.calc__item input[type="number"]:focus { border-bottom-color: var(--blue); }

.calc,
.calc__total { border-top-color: var(--line); }

.calc__years select { color: var(--text); border-bottom-color: var(--line); }
.calc__years > span { color: var(--text-muted); }

.calc__note { color: var(--text-muted); }
.calc__note b { color: var(--text); }

.calc__head .mono { color: var(--text-muted); }

/* The scrolling list inside a section that is already tall enough to hold it
   just hides half the rows behind a scrollbar. */
.calc__list { max-height: none; overflow: visible; }

/* ==========================================================================
   Punctuation and pace
   --------------------------------------------------------------------------
   A single white column of prose from the flight to the footer is a wall, and
   nobody reads a wall. Full-screen statements in the dark break it up, and
   inside them the words light as the reader passes, so the eye is led rather
   than handed a paragraph.
   ========================================================================== */

.say {
  background: var(--ink);
  color: var(--on-dark);
  min-height: 78svh;
  display: grid;
  align-items: center;
  padding-block: var(--sp-xxl);
  /* The dot grid is the corridor's own texture, brought up to the surface so
     these panels read as part of the same object rather than as a dark band. */
  background-image: radial-gradient(rgb(252 252 252 / 0.11) 1px, transparent 1px);
  background-size: 14px 14px;
}

.say__inner { width: 100%; padding-inline: max(1.25rem, 5vw); }

.say__line {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: var(--display);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.say--own { color: var(--white); }

/* Dim until read. Applied only once the words have been wrapped, so a visitor
   without the script gets the sentence at full strength. */
[data-read][data-read-on="true"] .wd {
  color: rgb(from currentColor r g b / 0.22);
  transition: color 0.28s ease;
}
[data-read][data-read-on="true"] .wd.is-lit { color: inherit; }

/* The gradient word keeps its own fill, so it only fades in and out. */
[data-read][data-read-on="true"] .grad .wd,
[data-read][data-read-on="true"] .wd .grad { color: transparent; }

/* Doors become one target each rather than a card with a button inside it. */
.card--link {
  display: flex;
  flex-direction: column;
  gap: var(--sp-s);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.18s ease, transform 0.18s ease;
}
.card--link:hover { border-top-color: var(--blue); transform: translateY(-2px); }

.card__go {
  margin-top: auto;
  padding-top: var(--sp-s);
  font-family: PlexMono, monospace;
  font-size: var(--step--1);
  color: var(--blue);
}
.card--link:hover .card__go { text-decoration: underline; text-underline-offset: 0.2em; }

/* --- Line length on a full-bleed page ------------------------------------- */

/* The container stopped holding the measure, so the text has to. These are the
   only things on the page that must not run the width of a wide screen. */
/* On a full-bleed page a 46ch head leaves two thirds of the screen empty and
   breaks the title over three or four lines. The head is a two-column band
   instead: the title on the left with room to sit on two lines, the standfirst
   beside it rather than under it. */
.sect__head { max-width: none; }
.sect__head h2,
.sect__head h1 { max-width: 26ch; }

@media (min-width: 62rem) {
  .sect__head {
    display: grid;
    grid-template-columns: 1.1fr 1fr;
    gap: var(--sp-l) clamp(2rem, 6vw, 6rem);
    align-items: end;
  }
  .sect__head > * + * { margin-top: 0; }
  .sect__head .lede { max-width: 52ch; padding-bottom: 0.35rem; }
}
.say__line { max-width: 24ch; }
.card p, .price-row__text, .board__label { max-width: 44ch; }
.footer p { max-width: 40ch; }

/* --- The calculator, as a correction -------------------------------------- */

/* The flight already showed every one of these with its price, one at a time.
   Repeating them here at full weight was a straight duplicate, so the rows are
   set compact and in two columns: the reader is correcting figures they have
   already seen, not meeting them. The years control and the accumulated total
   carry the section instead, because those are the only things on it the
   flight cannot say. */

.calc__head { font-size: var(--step--1); color: var(--text-muted); }
.calc__head .mono:first-child { max-width: 58ch; }

@media (min-width: 56rem) {
  .calc__list--tight { grid-template-columns: 1fr 1fr; column-gap: var(--sp-xl); }
}

.calc__list--tight .calc__item { padding-block: 0.3rem; }
.calc__list--tight .calc__item label { min-height: 2rem; }

.calc__years--big {
  display: grid;
  gap: 0.4rem;
  margin-top: var(--sp-l);
  padding-top: var(--sp-m);
  border-top: 2px solid var(--line);
}

.calc__years--big > span {
  font-family: PlexMono, monospace;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.calc__years--big select {
  font-family: Asgard, Impact, sans-serif;
  font-size: var(--step-2);
  padding-block: 0.2rem;
}

/* --- Where the flight lands ----------------------------------------------- */

/* The blue the home page's arrival is made of, one line, and the two
   decisions that are genuinely the owner's, drawn as white cards the way the
   interface object on the home page is drawn. Same material, so the two
   halves of the site are the same site.

   Three things are gone from the version before this. A field of blurred
   text, which read as depth on white and as dirt on blue. Red on the cards:
   on this site red means "this costs you money", and here it was being asked
   to mean "this wants your attention", which is why it came across as
   shouting. And the copy, which was the internal wording. */

.land {
  position: relative;
  height: 100svh;
  display: grid;
  place-items: center;
  padding-inline: max(1.25rem, 5vw);
  overflow: hidden;
  color: var(--white);
  background:
    radial-gradient(120% 100% at 22% -10%, #1c44dc, transparent 62%),
    linear-gradient(160deg, #0a1c84, #03071f);
}

/* The gutter lives on the section, not on this box. It used to carry 72px of
   padding inside a 704px max-width, which left 560px for content that needed
   614, so the headline overflowed its track and grid alignment pushed the
   whole block 26px to the right of centre. */
.land__inner {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--sp-s);
  width: 100%;
  max-width: 46rem;
}

.land__line {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  /* --step-4, а не свой clamp (был 80px на 1900 - кегль вне шкалы в роли
     заголовка секции; аудит 05.09). */
  font-size: var(--step-4);
  line-height: 0.94;
  letter-spacing: -0.04em;
  color: var(--white);
  text-wrap: nowrap;
}

/* The object. Two white cards on the blue, the same material as the interface
   panel the home page lands on. */
.land__keeps {
  display: grid;
  gap: 0.9rem;
  width: 100%;
  margin-top: var(--sp-m);
  text-align: left;
}

@media (min-width: 46rem) { .land__keeps { grid-template-columns: 1fr 1fr; } }

.keep {
  display: grid;
  gap: 0.35rem;
  align-content: start;
  padding: var(--sp-m);
  border-radius: 16px;
  background: #fff;
  color: var(--text);
  --heading-color: var(--text);
  --lede-color: var(--text-muted);
  /* The inset hairline is this card's own: a white panel on deep blue needs a
     lit top edge or it looks printed on. The lift underneath is the shared one. */
  box-shadow: var(--edge-light), var(--lift-3);
}

.keep__at { font-size: 0.58rem; letter-spacing: 0.16em; color: var(--text-muted); }

.keep__ask {
  font-family: Asgard, Impact, sans-serif;
  font-size: var(--step-1);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

.keep__why { font-size: 0.88rem; line-height: 1.35; color: var(--text-muted); }

.land__sub {
  margin-top: var(--sp-s);
  font-size: 0.9rem;
  color: rgb(252 252 252 / 0.55);
  white-space: nowrap;
}

@media (max-width: 45.99rem) {
  .land__line { text-wrap: balance; }
  .keep { padding: var(--sp-s) var(--sp-m); }
}

/* Every defence stated as a rule rather than a reassurance. Restored: these
   were deleted along with an earlier version of the Un-SaaS page while the
   markup that uses them stayed. */
.guards { list-style: none; display: grid; gap: 1px; background: var(--panel-line); border: 1px solid var(--panel-line); border-radius: var(--radius); overflow: hidden; }
.guards li { padding: var(--sp-s) var(--sp-m); background: var(--card-bg); color: var(--lede-color); }
.guards b { color: var(--heading-color); font-weight: 500; }

/* A flight on a page that also carries the rent meter must not pull up under
   the header: it drags the canvas under the meter too and cuts the logo. */
/* Pulls up under the whole top bar, not just the header, so the flight
   starts at the top of the document. Without this the reader scrolls the
   height of the chrome before the pin engages and the animation looks like
   it starts late. --chrome-h is measured by the script because the rent
   meter is only present on some pages and only once a figure is stored. */
.stage--page {
  margin-top: calc(var(--chrome-h, var(--header-h)) * -1);
  /* Eighteen plates rather than the rent flight's twelve. Scroll distance per
     plate is what decides whether one can be read before the next arrives, so
     the corridor grows with the cargo instead of packing it tighter. */
  height: 560svh;
}

/* The counter crosses the hand-off in place. Nothing moves: the figure the
   reader drove up the corridor is in the same corner when the corridor floods,
   which is the whole reason to keep it. Only the tense of the label changes,
   and the ink, because red on this site means "this costs you money" and here
   the number is just a count of a day. */
.stage__meter-key[data-key-landed] { display: none; }
.stage--page[data-done="true"] .stage__meter-key[data-key-flight] { display: none; }
.stage--page[data-done="true"] .stage__meter-key[data-key-landed] { display: inline; }
.stage--page[data-done="true"] .stage__meter-key { color: rgb(252 252 252 / 0.62); }
.stage--page[data-done="true"] .stage__meter b { color: var(--white); }
/* The caption narrates the flight. The screen it lands on has its own words. */
.stage--page[data-done="true"] .stage__caption { opacity: 0; transition: opacity 0.3s ease; }

/* The sheet on a page flight lands on the flooded light surface, so its type
   is ink. stage__end-* belong to the blue arrival and are white, which is how
   this ended up white on white. */
.sheet__end { display: grid; gap: 0.6rem; }

.sheet__end-line {
  font-family: Asgard, Impact, sans-serif;
  font-weight: 500;
  font-size: var(--title);
  line-height: 0.98;
  letter-spacing: -0.03em;
  color: var(--heading-color);
  max-width: 18ch;
}

.sheet__end-sub {
  max-width: 46ch;
  font-size: var(--step-0);
  color: var(--lede-color);
}

.sheet__title {
  max-width: 20ch;
  margin-bottom: var(--sp-m);
}

/* The sheet the flight hands over to: the calculator on the home page, the
   blue landing on the Un-SaaS page. It lies on top of the canvas and stays
   invisible until the corridor has flooded, which is what makes the
   dissolve read as the flight becoming the next thing rather than ending
   next to it.

   Deleted by accident in a bulk edit and restored here. Without it the sheet
   is opaque from the first frame and the flight is drawn underneath it,
   which took the animation off both pages at once. */
.stage__sheet {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  align-content: center;
  color: var(--text);
  opacity: 0;
  transform: translateY(1.1rem);
  transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;
}

.stage[data-done="true"] .stage__sheet {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.stage__sheet .calc__head { font-size: 0.72rem; }
.stage__sheet .calc__item label { font-size: 0.82rem; }
.stage__sheet .calc__list--tight .calc__item { padding-block: 0.18rem; }
.stage__sheet .calc__list--tight .calc__item label { min-height: 1.8rem; }

.stage__sheet .calc__years--big {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem var(--sp-m);
  margin-top: var(--sp-m);
  padding-top: var(--sp-s);
}

/* Sized to the longest option, not to the row. This used to be `flex: 1`,
   which was harmless while the sheet was a narrow column and absurd once the
   layout went full width: the control stretched the whole screen and parked
   its arrow a metre from the word it belongs to. A control should be as wide
   as the thing it holds. */
.stage__sheet .calc__years--big select {
  font-size: var(--step-1);
  width: auto;
  flex: none;
  max-width: 100%;
}

/* The figure, and nothing competing with it. */
.calc__total {
  margin-top: var(--sp-m);
  padding-top: var(--sp-s);
  border-top: 2px solid var(--red);
}

.calc__total-key {
  font-family: PlexMono, monospace;
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
}

.stage__sheet .calc__total .figure {
  font-size: clamp(3.4rem, 1.5rem + 8.5vw, 9rem);
  line-height: 0.86;
  letter-spacing: -0.05em;
  color: var(--red);
}

/* The second half of the arithmetic: what is still to come. Set as a sentence
   rather than a footnote, because it is the part that should land. */
.calc__ahead {
  margin-top: 0.4rem;
  font-family: London, sans-serif;
  font-size: var(--step-1);
  color: var(--text-muted);
}
.calc__ahead b { color: var(--text); font-weight: 500; }

/* --- The names on the bill ------------------------------------------------ */

/* Set in type rather than as logo files. Half of these companies have no mark
   in any open set, so a row of real logos would be half pictures and half
   words; and reproducing the marks of products we are arguing against reads
   as a claim of some relationship with them. The name is the recognition. */
.calc__vendor {
  font-family: PlexMono, ui-monospace, monospace;
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  padding: 0.12rem 0.42rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--text-muted);
  white-space: nowrap;
  flex: none;
}

.calc__item input[type="checkbox"]:checked ~ .calc__vendor,
.calc__item label:hover .calc__vendor { border-color: var(--text-muted); color: var(--text); }

/* Where the company has a mark in the open set it is drawn; where it does not,
   the name stands in the same slot at the same height so the column reads as
   one thing. Monochrome on purpose: twelve brand colours in a row is a mess,
   and colour marks read as endorsements in a way grey ones do not. */
.calc__mark {
  display: inline-flex;
  align-items: center;
  gap: 0.34rem;
  padding: 0.12rem 0.42rem 0.12rem 0.3rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--text-muted);
  flex: none;
}

.calc__mark svg { width: 0.8rem; height: 0.8rem; flex: none; }

.calc__mark em {
  font-family: PlexMono, ui-monospace, monospace;
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.calc__item label:hover .calc__mark { border-color: var(--text-muted); color: var(--text); }

/* A wordmark already says the name, so it is shown on its own and sized to the
   same optical height as the icon-and-name slot beside it. */
.calc__logo {
  display: inline-flex;
  align-items: center;
  height: 1.15rem;
  padding-inline: 0.42rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--text-muted);
  flex: none;
}

/* Sized by height so every mark shares one optical weight, and capped in
   width so a 6:1 wordmark cannot take the row. */
.calc__logo svg { height: 0.78rem; width: auto; max-width: 5rem; display: block; }
.calc__item label:hover .calc__logo { border-color: var(--text-muted); color: var(--text); }

/* Several marks per row: they wrap as a group and never push the price out of
   its column. */
.marks { display: inline-flex; flex-wrap: wrap; gap: 0.28rem; align-items: center; }


/* The one line the merge block leaves behind: what he keeps paying after the
   switch, said next to the object rather than in small print under it. */
.fold__kept { font-size: 0.9rem; color: var(--text-muted); max-width: 62ch; margin-top: var(--sp-s); }

/* Each screen names the subscription it took over. Quiet: it is a footnote
   inside the object, not a second headline. */
.pane__was { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; padding-top: 0.5rem; }
.pane__was .mono { font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); }
.pane__was .wall__mark { color: rgb(11 13 20 / 0.45); }
.pane__was .wall__mark svg { display: block; width: auto; height: 0.85rem; max-width: 4.2rem; }

/* The run of weeks is the doors component, so it inherits every door rule and
   cannot drift from them. Two differences: six cards want three columns
   rather than auto-fit, which would leave a row of five and a widow, and
   these are not links, so they must not light up under the cursor as if they
   were. */
.doors--run { grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }
@media (min-width: 62rem) { .doors--run { grid-template-columns: repeat(3, 1fr); } }

.door--static { cursor: default; }
.door--static:hover,
.door--static:focus-visible { background: var(--panel-dark); transform: none; }
.door--static.is-you:hover { background: var(--blue); }
.door--static:hover .door__voice { color: #fff; }


/* --- The four fears ------------------------------------------------------
   His worry at display scale and in grey, because it is a thought and not a
   fact, and the answer landing on it as a stamp rather than sitting under it
   as another sentence. The stamp is the visual event of the block: something
   arrives and closes the worry.

   It is set on the worry's last line, not across its middle, so nothing is
   read through anything. */
/* Four objections, four cards, two by two.

   What was here put each quote at 3.1rem and threw its stamp to the opposite
   edge of the row, alternating sides down the list. Four of them ran four
   screens tall, the mark sat nowhere near the thing it was marking, and the
   alternation read as a layout fault rather than as rhythm.

   A card each instead: who says it, what they say, the mark, and what closes
   it, in that order and inside one box. The stamp keeps its rotation, which
   is the one thing the old version had right. */
.fears {
  list-style: none;
  display: grid;
  gap: var(--sp-s);
  margin-top: var(--sp-l);
  counter-reset: fear;
}
@media (min-width: 54rem) {
  .fears { grid-template-columns: 1fr 1fr; }
}

.fear {
  counter-increment: fear;
  display: grid;
  gap: 0.7rem;
  align-content: start;
  padding: clamp(1.2rem, 0.8rem + 1.2vw, 1.9rem);
  background: var(--card, #fff);
  border: 1px solid var(--panel-line);
  border-radius: 14px;
  box-shadow: var(--lift-1);
  transition: box-shadow 0.25s ease, transform 0.25s ease;
}
.fear:hover { box-shadow: var(--lift-2); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
  .fear, .fear__stamp { transition: none; }
  .fear:hover { transform: none; }
}

/* The count comes off a counter rather than the template, so the markup stays
   three paragraphs and the numbering cannot drift from the order on screen. */
.fear::before {
  content: counter(fear, decimal-leading-zero) " · THEY SAY";
  font-family: PlexMono, monospace;
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  color: var(--text-muted);
}

.fear__said {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.2rem, 0.92rem + 0.95vw, 1.66rem);
  line-height: 1.12;
  letter-spacing: -0.015em;
  color: var(--text-muted);
  text-wrap: balance;
}
.fear__said::before { content: "\201C"; }
.fear__said::after { content: "\201D"; }

/* The mark. Rotated a degree and a half, which is enough to read as stamped
   and not enough to read as broken. It fills on hover, so the card has one
   thing that answers the pointer. */
.fear__stamp {
  justify-self: start;
  padding: 0.34rem 0.8rem;
  font-family: PlexMono, monospace;
  font-size: clamp(0.68rem, 0.6rem + 0.25vw, 0.8rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue);
  border: 2px solid var(--blue);
  border-radius: 4px;
  rotate: -1.5deg;
  transition: background-color 0.25s ease, color 0.25s ease;
  /* Stamped across the rule, not parked above it: the mark is pulled down by
     half its own height so the hairline runs behind it, and its background
     knocks the line out where it crosses. That is the whole reason the thing
     is called a stamp. */
  position: relative;
  z-index: 1;
  margin-bottom: -1.65rem;
  background: var(--card, #fff);
}
.fear:hover .fear__stamp { background: var(--blue); color: #fff; }

.fear__so {
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text);
  border-top: 1px solid var(--panel-line);
  padding-top: 1.55rem;
}

/* Do not rename these to .close. That is this site's CTA section, set by the
   cta() macro on every page, and a bare .close rule here turned every call to
   action on the site into a squeezed white card. */

.keys__out { margin-top: var(--sp-l); font-size: 0.95rem; }

/* Composited over the card ground, not laid on the container's. --panel-bg
   is 4% blue and the .versus container is painted --line to make the 1px seam:
   a translucent panel let that grey through and the side came out lilac, a
   colour that exists nowhere else on the site. Same two tokens, no third. */
.versus--ours .versus__side--now {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}

/* Three across, on the page whose price has three parts: what it costs once,
   what it costs every month, and what is not ours to charge for at all. The
   two-column rule above is 1.24fr 1fr, which is the rent-against-own board
   elsewhere and would strand the third panel, so this overrides it by
   specificity rather than by order. */
@media (min-width: 52rem) {
  .versus.versus--three { grid-template-columns: 1fr 1fr 1fr; }
}
.versus--three .versus__side--rent { background: var(--card-bg); }
.versus--three .versus__side--rent .versus__key { color: var(--text-muted); }
.versus--three .versus__side--rent .versus__fig { color: var(--heading-color); }
.versus--three .versus__side--rent .versus__note { color: var(--lede-color); }
/* The third figure is a monthly, like the second, and saying so on the panel
   costs less than a reader having to work it out. */
.versus--three .versus__side--rent .versus__fig::after,
.versus--three .versus__side--own .versus__fig::after {
  content: "/mo";
  font-size: 0.42em;
  letter-spacing: 0.02em;
  opacity: 0.55;
  margin-left: 0.15em;
}
.versus--ours .versus__side--now .versus__key,
.versus--ours .versus__side--now .versus__fig { color: var(--blue); }

/* --- The question --------------------------------------------------------
   The first screen asks instead of talking. He completes a sentence about
   himself and the rest of the page is the answer, which is this page's own
   experience rather than a second run of the home page's flight. */
.ask { margin-top: var(--sp-l); display: grid; gap: var(--sp-s); justify-items: start; }

.ask__line {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.8rem, 1rem + 2.8vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

/* The blank, drawn as a blank until he fills it. */
.ask__blank {
  color: var(--blue);
  border-bottom: 3px solid var(--blue);
  padding-bottom: 0.05em;
  transition: color 0.25s ease;
}
[data-open]:not([data-picked="true"]) .ask__blank { color: rgb(1 48 249 / 0.35); }

.ask__picks { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ask__pick {
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease,
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ask__pick:hover { border-color: var(--blue); color: var(--blue); transform: translateY(-2px); }
.ask__pick[aria-selected="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.ask__hint { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--text-muted); }

/* Before he picks, the half that depends on his trade says what it is waiting
   for. An empty panel reads as broken; a panel showing one trade's list reads
   as everybody's. */
/* Same trap as the panes: display on the class beats the browser's [hidden],
   so this stayed on screen after he had answered it. */
.scope__wait[hidden] { display: none; }
.scope__wait { display: grid; gap: 0.4rem; font-size: 0.9rem; color: var(--text-muted); max-width: 44ch; }
.scope__extras[hidden] { display: none; }
.scope__list li[hidden] { display: none; }

/* --- The four fears ------------------------------------------------------
   His worry at display scale and in grey, because it is a thought and not a
   fact, and the answer landing on it as a stamp rather than sitting under it
   as another sentence. The stamp is the visual event of the block: something
   arrives and closes the worry.

   It is set on the worry's last line, not across its middle, so nothing is
   read through anything. */
.keys__out { margin-top: var(--sp-l); font-size: 0.95rem; }

/* Composited over the card ground, not laid on the container's. --panel-bg
   is 4% blue and the .versus container is painted --line to make the 1px seam:
   a translucent panel let that grey through and the side came out lilac, a
   colour that exists nowhere else on the site. Same two tokens, no third. */
.versus--ours .versus__side--now {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}
.versus--ours .versus__side--now .versus__key,
.versus--ours .versus__side--now .versus__fig { color: var(--blue); }

/* --- The question --------------------------------------------------------
   The first screen asks instead of talking. He completes a sentence about
   himself and the rest of the page is the answer, which is this page's own
   experience rather than a second run of the home page's flight. */
.ask { margin-top: var(--sp-l); display: grid; gap: var(--sp-s); justify-items: start; }

.ask__line {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.8rem, 1rem + 2.8vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

/* The blank, drawn as a blank until he fills it. */
.ask__blank {
  color: var(--blue);
  border-bottom: 3px solid var(--blue);
  padding-bottom: 0.05em;
  transition: color 0.25s ease;
}
[data-open]:not([data-picked="true"]) .ask__blank { color: rgb(1 48 249 / 0.35); }

.ask__picks { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ask__pick {
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease,
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ask__pick:hover { border-color: var(--blue); color: var(--blue); transform: translateY(-2px); }
.ask__pick[aria-selected="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.ask__hint { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--text-muted); }

/* Before he picks, the half that depends on his trade says what it is waiting
   for. An empty panel reads as broken; a panel showing one trade's list reads
   as everybody's. */
/* Same trap as the panes: display on the class beats the browser's [hidden],
   so this stayed on screen after he had answered it. */
.scope__wait[hidden] { display: none; }
.scope__wait { display: grid; gap: 0.4rem; font-size: 0.9rem; color: var(--text-muted); max-width: 44ch; }
.scope__extras[hidden] { display: none; }
.scope__list li[hidden] { display: none; }

/* --- The product, shown ---------------------------------------------------
   Three drawn screens, set in space rather than pasted flat. Perspective on
   the group and a rotation on each, so they read as objects under a light and
   not as a row of pictures.

   The screens are vector and inlined, so this stays sharp at any size and the
   type inside them is the site's own. */
.rig {
  position: relative;
  margin-top: var(--sp-l);
  /* The phone hangs below the group on purpose, so the group needs room under
     it or it lands on the figures. */
  margin-bottom: 6rem;
  perspective: 2400px;
  perspective-origin: 62% 38%;
  isolation: isolate;
}

/* The light the screens sit in. Without it they float on the page ground and
   the depth reads as a mistake. */
.rig__glow {
  position: absolute;
  /* No horizontal bleed: at six percent it ran five pixels past the viewport
     and gave the whole page a sideways scroll. The blur softens the edges
     without needing to reach outside. */
  inset: -12% 0 -18%;
  z-index: 0;
  background:
    radial-gradient(58% 62% at 62% 34%, rgb(1 48 249 / 0.16), transparent 70%),
    radial-gradient(46% 50% at 22% 76%, rgb(255 67 67 / 0.07), transparent 72%);
  filter: blur(10px);
  pointer-events: none;
}

.rig__screen {
  position: absolute;
  transform-style: preserve-3d;
  border-radius: clamp(10px, 1.2vw, 18px);
  overflow: hidden;
  box-shadow: var(--lift-3);
}
.rig__screen svg { display: block; width: 100%; height: auto; }

/* The board is the object. Everything else is arranged around it. */
.rig__screen--main {
  position: relative;
  z-index: 2;
  width: 78%;
  margin-left: 6%;
  transform: rotateX(6deg) rotateY(-13deg) rotateZ(0.6deg);
}

/* Behind and further off, so the group has depth rather than just overlap. */
.rig__screen--back {
  z-index: 1;
  top: -4%;
  right: 0;
  width: 56%;
  transform: rotateX(8deg) rotateY(-17deg) translateZ(-180px);
  /* At 0.72 it read as a shadow of a screen rather than a screen further
     away. Depth is carried by the translate; the opacity was doing the job
     twice and losing the picture. */
  opacity: 0.9;
}

/* In front, and the only thing that breaks the group's plane, because it is
   the one screen that is not on a desk. */
/* In front, and the only thing that breaks the group's plane, because it is
   the one screen that is not on a desk. It used to hang fourteen percent
   below the group and the section clipped it, which reads as a mistake rather
   than as depth: a screen half out of frame at the bottom looks like a bug in
   the layout. It sits inside the frame now and still overlaps the board. */
.rig__screen--phone {
  z-index: 3;
  left: 1%;
  bottom: 2%;
  width: 15.5%;
  transform: rotateX(4deg) rotateY(-9deg) translateZ(120px);
}

@media (max-width: 60rem) {
  .rig { perspective: none; }
  .rig__screen { position: relative; transform: none; opacity: 1; width: 100%; margin: 0 0 var(--sp-s); }
  .rig__screen--back, .rig__screen--phone { display: none; }
}

/* --- The rail ------------------------------------------------------------
   The section pins and its content runs sideways, tied straight to the
   scrollbar. Continuous, so it holds wherever the reader stops.

   Every height is declared. The first build let the drawings size themselves
   off the width of their column, which put a nine hundred pixel board into
   five hundred pixels of room and cut it off at the frame; the fix has to
   start from a height that resolves, which is --face. */
/* One screen of scroll per slide, so adding a module lengthens the
   corridor instead of speeding every slide up. */
.rail-sect { position: relative; height: calc(var(--n, 4) * 105svh); }
.rail-sect__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* From the top, not centred in the whole screen: centred, the head floated
     with a screenful of nothing above it. */
  align-content: start;
  gap: var(--sp-m);
  overflow: hidden;
  /* --chrome-h is the header plus the rent strip, which arrives under it once
     the reader is past the flight. Counting the header alone put the heading
     where the strip lands. */
  /* Capped against the screen, not just declared: a pinned section has one
     screenful to spend, and on a short laptop a fixed --sp-xl here pushed the
     drawings past the bottom edge, where the clip ate them. */
  padding-top: calc(var(--chrome-cover, var(--header-h)) + min(var(--sp-xl), 7svh));
  padding-bottom: var(--sp-m);
}
.rail-sect__head { display: grid; gap: 0.5rem; }
.rail-sect__head h2 { max-width: 26ch; }
@media (min-width: 62rem) {
  .rail-sect__head {
    grid-template-columns: 1.1fr 1fr;
    gap: var(--sp-l) clamp(2rem, 6vw, 6rem);
    align-items: end;
  }
  .rail-sect__head .lede { max-width: 52ch; padding-bottom: 0.35rem; }
}

/* The clip has to hold the drawings' shadows as well as the drawings, so the
   rail is taller than the face by the room a shadow needs and pads it back.
   Clipped tight to the face, both mockups lost their shadow at the bottom
   edge and read as cut out. */
.rail {
  /* The face shrank from 50svh when the head was pushed clear of the rent
     strip. A pinned section has exactly one screen to spend and the head now
     spends more of it; the alternative was drawings running off the bottom. */
  --face: 44svh;
  /* Room for the shadow inside the clip: the blur reaches 2.6rem in every
     direction and the offset carries it 1.4rem further down, so this cannot go
     below about 3.5rem without cutting it top and bottom again. */
  --shade: 3.6rem;
  height: calc(var(--face) + var(--shade) * 2);
  padding-block: var(--shade);
  overflow: hidden;
  align-self: center;
}

/* n slides wide, travelling (n-1)/n of itself, which lands the last exactly.
   In percentages of the track, so it needs no measurement. */
.rail__track {
  display: flex;
  height: 100%;
  width: calc(var(--n) * 100%);
  transform: translate3d(calc(var(--p, 0) * (var(--n) - 1) / var(--n) * -100%), 0, 0);
  will-change: transform;
}

.rail__slide {
  flex: 0 0 calc(100% / var(--n));
  height: 100%;
  display: grid;
  grid-template-rows: 1fr auto;
  gap: var(--sp-s);
  padding-right: clamp(1.5rem, 4vw, 4rem);
  min-height: 0;
}
/* The drawings and their caption are one group, and the group is centred.
   Pinning the pair to the right of a 1fr column looked like it closed the gap
   and in fact shoved the whole slide at the right edge, where the ninth
   caption ran off the screen. The pair column is sized by its own drawing now
   (auto), and the two columns are centred together. */
@media (min-width: 66rem) {
  .rail__slide {
    grid-template-columns: auto minmax(11rem, 15rem);
    grid-template-rows: 1fr;
    justify-content: center;
    align-items: center;
    gap: var(--sp-s);
    padding-right: 0;
  }
}

/* Both mockups stand on the same line. The phone was centred against the
   board, so their bottoms did not agree and the pair read as two objects at
   different distances. */
.rail__pair { position: relative; height: 100%; display: flex; align-items: flex-end; justify-content: center; min-height: 0; }

.rail__desk { height: 100%; display: grid; place-items: center; min-width: 0; }
/* drop-shadow, not box-shadow, and no radius of our own.
   
   Both drawings clip themselves to their own rounded corners, so a CSS radius
   is a second, different shape laid over the first. On the phone it was worse
   than redundant: a percentage radius on a 390 by 800 box is elliptical, 42
   pixels across and 86 down, which stretched every corner. A drop-shadow
   follows what the drawing actually paints. */
.rail__desk svg {
  display: block;
  height: 100%;
  width: auto;
  filter: drop-shadow(0 1.4rem 2.6rem rgb(0 2 24 / 0.22));
  pointer-events: none;
}

/* Standing against the board's right edge, and travelling a touch slower,
   which is the depth now that nothing is blurred to fake it. */
/* No overhang. The rail clips horizontally, so anything hanging below the
   face was cut off at the frame rather than reading as depth. */
.rail__phone {
  height: 88%;
  margin-left: -2.5%;
  align-self: flex-end;
  transform: translate3d(calc(var(--p, 0) * 1.4rem), 0, 0);
}
.rail__phone svg {
  display: block;
  height: 100%;
  width: auto;
  filter: drop-shadow(0 1.1rem 2rem rgb(0 2 24 / 0.26));
  pointer-events: none;
}

.rail__cap { display: grid; gap: 0.3rem; align-content: center; }
.rail__n { font-size: 0.62rem; letter-spacing: 0.18em; color: var(--blue); }
.rail__cap b { font-family: Asgard, Impact, sans-serif; font-size: var(--step-1); line-height: 1.12; }
.rail__note { font-size: 0.9rem; color: var(--lede-color); }

.rail__bar { height: 2px; background: var(--panel-line); }
.rail__bar i {
  display: block;
  height: 100%;
  width: calc((var(--p, 0) * (var(--n, 4) - 1) + 1) / var(--n, 4) * 100%);
  background: var(--blue);
}

/* The beat: the rows in the slide he is on. */
.motion .rail__slide .scr-row {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.45s ease, transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
  transition-delay: calc(var(--r, 0) * 65ms);
}
.motion .rail__slide[data-state="live"] .scr-row { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .motion .rail__slide .scr-row { transition: none; }
}

@media (max-width: 60rem) {
  .rail-sect { height: auto; }
  .rail-sect__pin { position: static; height: auto; padding-block: var(--sp-xxl); overflow: visible; }
  .rail { height: auto; overflow: visible; }
  .rail__track { width: 100%; display: grid; gap: var(--sp-xl); transform: none; height: auto; }
  .rail__slide { flex: none; height: auto; padding-right: 0; }
  .rail__pair { height: auto; display: grid; }
  .rail__desk { height: auto; }
  .rail__desk svg { height: auto; width: 100%; }
  .rail__phone { height: auto; width: 38%; margin: var(--sp-s) auto 0; transform: none; }
  .rail__phone svg { height: auto; width: 100%; }
  .rail__bar { display: none; }
  .motion .rail__slide .scr-row { opacity: 1; transform: none; }

  /* ⚠️ ПЕРВЫЕ ЭКРАНЫ ИЗ ЭТОГО ИСКЛЮЧЕНЫ.
   *
   * Правило выше глушит рельсу целиком, и на телефоне каждый первый экран
   * сайта распадался в столбик: сцена, под ней лист, между ними дыра в
   * несколько экранов. PO: «все первые экраны иммерсивный опыт при скролле, а
   * не просто скролл». Опыт этих страниц и ЕСТЬ смена тактов по прокрутке;
   * без пина от страницы остаётся оглавление.
   *
   * Признак `data-rail-phone` ставится на секцию ПОИМЁННО, страница за
   * страницей. Не по `[data-dark-top]` скопом: у каждой сцены своя мобильная
   * раскладка, и включить липкость всем разом означает выкатить шесть
   * непроверенных экранов ради двух проверенных. Замерено на `/money-trail/`:
   * липкость пришла, а старая мобильная раскладка продолжала показывать все
   * двенадцать тактов сразу, и они легли друг на друга кучей.
   *
   * Готовы: главная (`#case`), `/services/` (своё правило рядом с `.say-pin`).
   *
   * Такт на телефоне короче десктопного (92svh против 105): экран выше и
   * уже, и на длинном такте палец успевает устать между двумя кадрами. */
  .rail-sect[data-rail-phone] { height: calc(var(--n, 4) * 92svh); }
  /* ⚠️ `padding-block` ЗДЕСЬ НЕ ЗАДАЁТСЯ. Стояло `0`, и оно перебивало
     собственный верхний отступ страницы (два класса против одного): заголовок
     главной вылезал под шапку и ложился на логотип. Отступ - дело страницы,
     общее правило отвечает только за липкость. */
  [data-rail-phone] .rail-sect__pin {
    position: sticky;
    top: 0;
    height: 100svh;
    overflow: clip;
  }
}

/* --- The four fears ------------------------------------------------------
   His worry at display scale and in grey, because it is a thought and not a
   fact, and the answer landing on it as a stamp rather than sitting under it
   as another sentence. The stamp is the visual event of the block: something
   arrives and closes the worry.

   It is set on the worry's last line, not across its middle, so nothing is
   read through anything. */
.keys__out { margin-top: var(--sp-l); font-size: 0.95rem; }

/* Composited over the card ground, not laid on the container's. --panel-bg
   is 4% blue and the .versus container is painted --line to make the 1px seam:
   a translucent panel let that grey through and the side came out lilac, a
   colour that exists nowhere else on the site. Same two tokens, no third. */
.versus--ours .versus__side--now {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}
.versus--ours .versus__side--now .versus__key,
.versus--ours .versus__side--now .versus__fig { color: var(--blue); }

/* --- The question --------------------------------------------------------
   The first screen asks instead of talking. He completes a sentence about
   himself and the rest of the page is the answer, which is this page's own
   experience rather than a second run of the home page's flight. */
.ask { margin-top: var(--sp-l); display: grid; gap: var(--sp-s); justify-items: start; }

.ask__line {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.8rem, 1rem + 2.8vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

/* The blank, drawn as a blank until he fills it. */
.ask__blank {
  color: var(--blue);
  border-bottom: 3px solid var(--blue);
  padding-bottom: 0.05em;
  transition: color 0.25s ease;
}
[data-open]:not([data-picked="true"]) .ask__blank { color: rgb(1 48 249 / 0.35); }

.ask__picks { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ask__pick {
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease,
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ask__pick:hover { border-color: var(--blue); color: var(--blue); transform: translateY(-2px); }
.ask__pick[aria-selected="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.ask__hint { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--text-muted); }

/* Before he picks, the half that depends on his trade says what it is waiting
   for. An empty panel reads as broken; a panel showing one trade's list reads
   as everybody's. */
/* Same trap as the panes: display on the class beats the browser's [hidden],
   so this stayed on screen after he had answered it. */
.scope__wait[hidden] { display: none; }
.scope__wait { display: grid; gap: 0.4rem; font-size: 0.9rem; color: var(--text-muted); max-width: 44ch; }
.scope__extras[hidden] { display: none; }
.scope__list li[hidden] { display: none; }

/* --- The four fears ------------------------------------------------------
   His worry at display scale and in grey, because it is a thought and not a
   fact, and the answer landing on it as a stamp rather than sitting under it
   as another sentence. The stamp is the visual event of the block: something
   arrives and closes the worry.

   It is set on the worry's last line, not across its middle, so nothing is
   read through anything. */
.keys__out { margin-top: var(--sp-l); font-size: 0.95rem; }

/* Composited over the card ground, not laid on the container's. --panel-bg
   is 4% blue and the .versus container is painted --line to make the 1px seam:
   a translucent panel let that grey through and the side came out lilac, a
   colour that exists nowhere else on the site. Same two tokens, no third. */
.versus--ours .versus__side--now {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}
.versus--ours .versus__side--now .versus__key,
.versus--ours .versus__side--now .versus__fig { color: var(--blue); }

/* --- The question --------------------------------------------------------
   The first screen asks instead of talking. He completes a sentence about
   himself and the rest of the page is the answer, which is this page's own
   experience rather than a second run of the home page's flight. */
.ask { margin-top: var(--sp-l); display: grid; gap: var(--sp-s); justify-items: start; }

.ask__line {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.8rem, 1rem + 2.8vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

/* The blank, drawn as a blank until he fills it. */
.ask__blank {
  color: var(--blue);
  border-bottom: 3px solid var(--blue);
  padding-bottom: 0.05em;
  transition: color 0.25s ease;
}
[data-open]:not([data-picked="true"]) .ask__blank { color: rgb(1 48 249 / 0.35); }

.ask__picks { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ask__pick {
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease,
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ask__pick:hover { border-color: var(--blue); color: var(--blue); transform: translateY(-2px); }
.ask__pick[aria-selected="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.ask__hint { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--text-muted); }

/* Before he picks, the half that depends on his trade says what it is waiting
   for. An empty panel reads as broken; a panel showing one trade's list reads
   as everybody's. */
/* Same trap as the panes: display on the class beats the browser's [hidden],
   so this stayed on screen after he had answered it. */
.scope__wait[hidden] { display: none; }
.scope__wait { display: grid; gap: 0.4rem; font-size: 0.9rem; color: var(--text-muted); max-width: 44ch; }
.scope__extras[hidden] { display: none; }
.scope__list li[hidden] { display: none; }

/* --- The product, shown ---------------------------------------------------
   Three drawn screens, set in space rather than pasted flat. Perspective on
   the group and a rotation on each, so they read as objects under a light and
   not as a row of pictures.

   The screens are vector and inlined, so this stays sharp at any size and the
   type inside them is the site's own. */
.rig {
  position: relative;
  margin-top: var(--sp-l);
  /* The phone hangs below the group on purpose, so the group needs room under
     it or it lands on the figures. */
  margin-bottom: 6rem;
  perspective: 2400px;
  perspective-origin: 62% 38%;
  isolation: isolate;
}

/* The light the screens sit in. Without it they float on the page ground and
   the depth reads as a mistake. */
.rig__glow {
  position: absolute;
  /* No horizontal bleed: at six percent it ran five pixels past the viewport
     and gave the whole page a sideways scroll. The blur softens the edges
     without needing to reach outside. */
  inset: -12% 0 -18%;
  z-index: 0;
  background:
    radial-gradient(58% 62% at 62% 34%, rgb(1 48 249 / 0.16), transparent 70%),
    radial-gradient(46% 50% at 22% 76%, rgb(255 67 67 / 0.07), transparent 72%);
  filter: blur(10px);
  pointer-events: none;
}

.rig__screen {
  position: absolute;
  transform-style: preserve-3d;
  border-radius: clamp(10px, 1.2vw, 18px);
  overflow: hidden;
  box-shadow: var(--lift-3);
}
.rig__screen svg { display: block; width: 100%; height: auto; }

/* The board is the object. Everything else is arranged around it. */
.rig__screen--main {
  position: relative;
  z-index: 2;
  width: 78%;
  margin-left: 6%;
  transform: rotateX(6deg) rotateY(-13deg) rotateZ(0.6deg);
}

/* Behind and further off, so the group has depth rather than just overlap. */
.rig__screen--back {
  z-index: 1;
  top: -4%;
  right: 0;
  width: 56%;
  transform: rotateX(8deg) rotateY(-17deg) translateZ(-180px);
  /* At 0.72 it read as a shadow of a screen rather than a screen further
     away. Depth is carried by the translate; the opacity was doing the job
     twice and losing the picture. */
  opacity: 0.9;
}

/* In front, and the only thing that breaks the group's plane, because it is
   the one screen that is not on a desk. */
/* In front, and the only thing that breaks the group's plane, because it is
   the one screen that is not on a desk. It used to hang fourteen percent
   below the group and the section clipped it, which reads as a mistake rather
   than as depth: a screen half out of frame at the bottom looks like a bug in
   the layout. It sits inside the frame now and still overlaps the board. */
.rig__screen--phone {
  z-index: 3;
  left: 1%;
  bottom: 2%;
  width: 15.5%;
  transform: rotateX(4deg) rotateY(-9deg) translateZ(120px);
}

@media (max-width: 60rem) {
  .rig { perspective: none; }
  .rig__screen { position: relative; transform: none; opacity: 1; width: 100%; margin: 0 0 var(--sp-s); }
  .rig__screen--back, .rig__screen--phone { display: none; }
}

/* --- The drum ------------------------------------------------------------
   Four slides on a cylinder, turned by his scroll. A crossfade stood here
   first and read as a slideshow: two pictures arguing about which is on top.
   A drum is one object moving, and it says the four moments are one job
   rather than four claims.

   Each slide carries both screens of that minute: the desk and the phone.

   Geometry. The slides are the faces of a prism with --n sides, so each is
   turned by 360/n and pushed out by half its height over tan(180/n). Written
   as custom properties so the maths is in one place and adding a fifth moment
   is a data change. */
.drum-sect { position: relative; height: 420svh; }
.drum-sect__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  align-content: center;
  overflow: hidden;
  /* The header is fixed over this, so centring in the whole screen put the
     heading under it. */
  padding-top: calc(var(--header-h) + var(--sp-s));
}
.drum-sect__inner { display: grid; gap: var(--sp-m); }
.drum-sect__say { display: grid; gap: 0.4rem; }
.drum-sect__say .lede { max-width: 54ch; }

.drum {
  --face: 54svh;
  display: grid;
  gap: var(--sp-m);
}
@media (min-width: 72rem) { .drum { grid-template-columns: 1fr minmax(15rem, 20rem); align-items: center; } }

/* The slides run down the same axis the corridor at the top of this page
   flies: a moment comes toward the reader, fills the frame, and carries on
   past. A drum was here before and it was a fairground ride; this is the
   page's own language, and it is continuous rather than stepped, so it holds
   at any point he stops. */
.drum__cage {
  position: relative;
  height: var(--face);
  perspective: 1400px;
  perspective-origin: 50% 50%;
  transform-style: preserve-3d;
}

.drum__slide {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  transform-style: preserve-3d;
  /* Its own distance from the camera: --i is its place in the order, --at is
     where the camera is, and the difference is how far away it is. */
  --d: calc(var(--i) - var(--at, 0));
  transform:
    translateZ(calc(var(--d) * -420px))
    translateY(calc(var(--d) * 7%))
    scale(calc(1 - var(--d) * 0.02));
  /* Gone, not dimmed. At 0.15 opacity behind a three pixel blur a slide is
     not depth, it is a grey smear around the one being read, and four of them
     framed the live slide in fog. */
  opacity: 0;
  visibility: hidden;
  transition:
    transform 0.9s cubic-bezier(0.22, 1, 0.3, 1),
    opacity 0.45s ease,
    visibility 0s linear 0.45s;
  pointer-events: none;
}
.drum__slide[data-state="live"] {
  pointer-events: auto;
  opacity: 1;
  visibility: visible;
  transition: transform 0.9s cubic-bezier(0.22, 1, 0.3, 1), opacity 0.45s ease, visibility 0s;
}

/* The pair: the desk screen, and the phone standing in front of its lower
   right, which is where a phone actually is. Both sized off the face's height
   so the pair keeps its proportion whatever the section is given. */
.drum__pair { position: relative; width: 100%; height: 100%; display: grid; place-items: center; }

/* Held clear of the phone's column, so the two screens sit beside each other
   rather than one reading through the other. */
.drum__desk { height: 100%; display: grid; place-items: center; padding-right: 9%; }
.drum__desk svg {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
  border-radius: clamp(8px, 0.9vw, 14px);
  box-shadow: var(--edge-light), var(--lift-3);
}

.drum__phone { position: absolute; right: -3%; bottom: -5%; height: 66%; }
.drum__phone svg {
  display: block;
  height: 100%;
  width: auto;
  border-radius: clamp(12px, 1.4vw, 20px);
  box-shadow: var(--lift-3);
}

.drum__legend { display: grid; gap: var(--sp-s); align-content: center; }
.drum__cap { display: grid; gap: 0.3rem; }
.drum__cap[hidden] { display: none; }
.drum__cap b { font-family: Asgard, Impact, sans-serif; font-size: var(--step-1); line-height: 1.1; }
.drum__cap span { font-size: 0.92rem; color: var(--lede-color); }

.drum__dots { list-style: none; display: flex; gap: 0.4rem; }
.drum__dots li { width: 1.6rem; height: 2px; background: var(--panel-line); transition: background 0.3s ease; }
.drum__dots li[data-on="true"] { background: var(--blue); }

/* The beat inside the face that is front. */
.motion .drum__slide .scr-row {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.45s ease, transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
  transition-delay: calc(var(--r, 0) * 70ms);
}
.motion .drum__slide[data-state="live"] .scr-row { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .drum__slide { transition: none; }
  .motion .drum__slide .scr-row { transition: none; }
}

/* No cylinder on a phone: the faces would each be forty pixels tall. It
   becomes what it is underneath, which is four moments in order. */
@media (max-width: 60rem) {
  .drum-sect { height: auto; }
  .drum-sect__pin { position: static; height: auto; padding-block: var(--sp-xxl); }
  .drum__cage { height: auto; perspective: none; display: grid; gap: var(--sp-xl); }
  .drum__slide { position: relative; inset: auto; transform: none; opacity: 1; }
  .drum__phone { position: relative; right: auto; bottom: auto; width: 34%; margin: var(--sp-s) auto 0; }
  .drum__cap[hidden] { display: grid; }
  .drum__dots { display: none; }
  .motion .drum__slide .scr-row { opacity: 1; transform: none; }
}

/* --- The four fears ------------------------------------------------------
   His worry at display scale and in grey, because it is a thought and not a
   fact, and the answer landing on it as a stamp rather than sitting under it
   as another sentence. The stamp is the visual event of the block: something
   arrives and closes the worry.

   It is set on the worry's last line, not across its middle, so nothing is
   read through anything. */
.keys__out { margin-top: var(--sp-l); font-size: 0.95rem; }

/* Composited over the card ground, not laid on the container's. --panel-bg
   is 4% blue and the .versus container is painted --line to make the 1px seam:
   a translucent panel let that grey through and the side came out lilac, a
   colour that exists nowhere else on the site. Same two tokens, no third. */
.versus--ours .versus__side--now {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}
.versus--ours .versus__side--now .versus__key,
.versus--ours .versus__side--now .versus__fig { color: var(--blue); }

/* --- The question --------------------------------------------------------
   The first screen asks instead of talking. He completes a sentence about
   himself and the rest of the page is the answer, which is this page's own
   experience rather than a second run of the home page's flight. */
.ask { margin-top: var(--sp-l); display: grid; gap: var(--sp-s); justify-items: start; }

.ask__line {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.8rem, 1rem + 2.8vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

/* The blank, drawn as a blank until he fills it. */
.ask__blank {
  color: var(--blue);
  border-bottom: 3px solid var(--blue);
  padding-bottom: 0.05em;
  transition: color 0.25s ease;
}
[data-open]:not([data-picked="true"]) .ask__blank { color: rgb(1 48 249 / 0.35); }

.ask__picks { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ask__pick {
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease,
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ask__pick:hover { border-color: var(--blue); color: var(--blue); transform: translateY(-2px); }
.ask__pick[aria-selected="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.ask__hint { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--text-muted); }

/* Before he picks, the half that depends on his trade says what it is waiting
   for. An empty panel reads as broken; a panel showing one trade's list reads
   as everybody's. */
/* Same trap as the panes: display on the class beats the browser's [hidden],
   so this stayed on screen after he had answered it. */
.scope__wait[hidden] { display: none; }
.scope__wait { display: grid; gap: 0.4rem; font-size: 0.9rem; color: var(--text-muted); max-width: 44ch; }
.scope__extras[hidden] { display: none; }
.scope__list li[hidden] { display: none; }

/* --- The four fears ------------------------------------------------------
   His worry at display scale and in grey, because it is a thought and not a
   fact, and the answer landing on it as a stamp rather than sitting under it
   as another sentence. The stamp is the visual event of the block: something
   arrives and closes the worry.

   It is set on the worry's last line, not across its middle, so nothing is
   read through anything. */
.keys__out { margin-top: var(--sp-l); font-size: 0.95rem; }

/* Composited over the card ground, not laid on the container's. --panel-bg
   is 4% blue and the .versus container is painted --line to make the 1px seam:
   a translucent panel let that grey through and the side came out lilac, a
   colour that exists nowhere else on the site. Same two tokens, no third. */
.versus--ours .versus__side--now {
  background: linear-gradient(var(--panel-bg), var(--panel-bg)), var(--card-bg);
}
.versus--ours .versus__side--now .versus__key,
.versus--ours .versus__side--now .versus__fig { color: var(--blue); }

/* --- The question --------------------------------------------------------
   The first screen asks instead of talking. He completes a sentence about
   himself and the rest of the page is the answer, which is this page's own
   experience rather than a second run of the home page's flight. */
.ask { margin-top: var(--sp-l); display: grid; gap: var(--sp-s); justify-items: start; }

.ask__line {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(1.8rem, 1rem + 2.8vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading-color);
}

/* The blank, drawn as a blank until he fills it. */
.ask__blank {
  color: var(--blue);
  border-bottom: 3px solid var(--blue);
  padding-bottom: 0.05em;
  transition: color 0.25s ease;
}
[data-open]:not([data-picked="true"]) .ask__blank { color: rgb(1 48 249 / 0.35); }

.ask__picks { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ask__pick {
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease,
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ask__pick:hover { border-color: var(--blue); color: var(--blue); transform: translateY(-2px); }
.ask__pick[aria-selected="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.ask__hint { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--text-muted); }

/* Before he picks, the half that depends on his trade says what it is waiting
   for. An empty panel reads as broken; a panel showing one trade's list reads
   as everybody's. */
/* Same trap as the panes: display on the class beats the browser's [hidden],
   so this stayed on screen after he had answered it. */
.scope__wait[hidden] { display: none; }
.scope__wait { display: grid; gap: 0.4rem; font-size: 0.9rem; color: var(--text-muted); max-width: 44ch; }
.scope__extras[hidden] { display: none; }
.scope__list li[hidden] { display: none; }

/* --- The product, shown ---------------------------------------------------
   Three drawn screens, set in space rather than pasted flat. Perspective on
   the group and a rotation on each, so they read as objects under a light and
   not as a row of pictures.

   The screens are vector and inlined, so this stays sharp at any size and the
   type inside them is the site's own. */
.rig {
  position: relative;
  margin-top: var(--sp-l);
  /* The phone hangs below the group on purpose, so the group needs room under
     it or it lands on the figures. */
  margin-bottom: 6rem;
  perspective: 2400px;
  perspective-origin: 62% 38%;
  isolation: isolate;
}

/* The light the screens sit in. Without it they float on the page ground and
   the depth reads as a mistake. */
.rig__glow {
  position: absolute;
  /* No horizontal bleed: at six percent it ran five pixels past the viewport
     and gave the whole page a sideways scroll. The blur softens the edges
     without needing to reach outside. */
  inset: -12% 0 -18%;
  z-index: 0;
  background:
    radial-gradient(58% 62% at 62% 34%, rgb(1 48 249 / 0.16), transparent 70%),
    radial-gradient(46% 50% at 22% 76%, rgb(255 67 67 / 0.07), transparent 72%);
  filter: blur(10px);
  pointer-events: none;
}

.rig__screen {
  position: absolute;
  transform-style: preserve-3d;
  border-radius: clamp(10px, 1.2vw, 18px);
  overflow: hidden;
  box-shadow: var(--lift-3);
}
.rig__screen svg { display: block; width: 100%; height: auto; }

/* The board is the object. Everything else is arranged around it. */
.rig__screen--main {
  position: relative;
  z-index: 2;
  width: 78%;
  margin-left: 6%;
  transform: rotateX(6deg) rotateY(-13deg) rotateZ(0.6deg);
}

/* Behind and further off, so the group has depth rather than just overlap. */
.rig__screen--back {
  z-index: 1;
  top: -4%;
  right: 0;
  width: 56%;
  transform: rotateX(8deg) rotateY(-17deg) translateZ(-180px);
  /* At 0.72 it read as a shadow of a screen rather than a screen further
     away. Depth is carried by the translate; the opacity was doing the job
     twice and losing the picture. */
  opacity: 0.9;
}

/* In front, and the only thing that breaks the group's plane, because it is
   the one screen that is not on a desk. */
/* In front, and the only thing that breaks the group's plane, because it is
   the one screen that is not on a desk. It used to hang fourteen percent
   below the group and the section clipped it, which reads as a mistake rather
   than as depth: a screen half out of frame at the bottom looks like a bug in
   the layout. It sits inside the frame now and still overlaps the board. */
.rig__screen--phone {
  z-index: 3;
  left: 1%;
  bottom: 2%;
  width: 15.5%;
  transform: rotateX(4deg) rotateY(-9deg) translateZ(120px);
}

@media (max-width: 60rem) {
  .rig { perspective: none; }
  .rig__screen { position: relative; transform: none; opacity: 1; width: 100%; margin: 0 0 var(--sp-s); }
  .rig__screen--back, .rig__screen--phone { display: none; }
}

/* --- The deck ------------------------------------------------------------
   The system, paged through rather than pictured. The section is pinned and
   one scrolled progress moves the stack: a screen comes forward, the last
   falls back, and the rows inside the front one arrive a beat at a time.

   What animates is the work appearing in the system. A slide changing would
   be a slideshow; this is the product filling up. */
.deck-sect { position: relative; height: 340svh; }
.deck-sect__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  align-content: center;
  overflow: hidden;
}
.deck-sect__inner { display: grid; gap: var(--sp-m); }
.deck-sect__say { display: grid; gap: 0.5rem; }
.deck-sect__say .lede { max-width: 52ch; }

.deck { position: relative; }

/* One frame every screen is dealt into. Fixed shape, so a phone arriving
   after a board does not resize the section under the reader. */
.deck__stage {
  position: relative;
  /* The drawings' own shape. At 16/8.2 the board overflowed the frame by a
     hundred and forty pixels and the caption, which hangs off the bottom of
     the frame, landed inside the picture. */
  aspect-ratio: 1280 / 800;
  perspective: 2000px;
}

.deck__screen {
  position: absolute;
  inset: 0;
  display: grid;
  /* place-items, not place-content: content-centring sizes the track to the
     drawing and then width:100% resolves against that track, which collapsed
     a fourteen hundred pixel board to three hundred. */
  place-items: center;
  transition: opacity 0.5s ease, transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.deck__screen svg {
  display: block;
  width: 100%;
  height: auto;
  border-radius: clamp(10px, 1.1vw, 16px);
  box-shadow: var(--edge-light), var(--lift-3);
}
/* A phone is tall, so it is sized by the frame's height and not its width. */
.deck__screen--phone svg { width: auto; height: 100%; border-radius: clamp(20px, 2.4vw, 34px); }

/* Waiting behind, gone in front, and the live one square on. */
.deck__screen[data-state="wait"] { opacity: 0; transform: translateY(6%) scale(0.94); }
.deck__screen[data-state="past"] { opacity: 0; transform: translateY(-7%) scale(1.05); }
.deck__screen[data-state="live"] { opacity: 1; transform: none; z-index: 2; }

/* Under the frame, not inside it. At bottom:-3.4rem the caption sat on the
   dark of the screen itself and could not be read. */
.deck__screen figcaption {
  position: absolute;
  left: 0;
  top: calc(100% + 1.1rem);
  display: grid;
  gap: 0.15rem;
}
.deck__screen figcaption b { font-size: 1rem; }
.deck__screen figcaption span { font-size: 0.88rem; color: var(--lede-color); max-width: 52ch; }

/* The beat. Rows are grouped in the drawings for this: the figures first
   (--r:-1 lands on a negative delay, so it is already there), then a row at a
   time down the screen. */
.motion .deck__screen .scr-row {
  opacity: 0;
  transform: translateY(9px);
  transition: opacity 0.45s ease, transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
  transition-delay: calc(var(--r, 0) * 80ms);
}
.motion .deck__screen[data-state="live"] .scr-row { opacity: 1; transform: none; }

.deck__dots { list-style: none; display: flex; gap: 0.4rem; margin-top: 5.6rem; }
.deck__dots li {
  width: 1.6rem;
  height: 2px;
  background: var(--panel-line);
  transition: background 0.3s ease;
}
.deck__dots li[data-on="true"] { background: var(--blue); }

@media (prefers-reduced-motion: reduce) {
  .motion .deck__screen .scr-row { transition: none; }
}
@media (max-width: 60rem) {
  .deck-sect { height: auto; }
  .deck-sect__pin { position: static; height: auto; padding-block: var(--sp-xxl); }
  .deck__stage { aspect-ratio: auto; display: grid; gap: var(--sp-l); }
  .deck__screen { position: relative; inset: auto; opacity: 1; transform: none; }
  .deck__screen figcaption { position: relative; bottom: auto; margin-top: var(--sp-s); }
  .motion .deck__screen .scr-row { opacity: 1; transform: none; }
  .deck__dots { display: none; }
}

/* ---------- Digital Inventory: the document, shown ---------- */

/* Named .folio, not .spread. .spread is already the Then/Now component at the
   top of this file, and above 62rem it sets grid-template-columns: 1fr 1fr,
   so a second .spread that only redeclared display and gap inherited two
   columns and put every caption beside its page instead of under it.
   Second name collision in a day. Grep the stylesheet before taking a word. */

/* Full width of the container, because the point of the page is that these
   are readable pages rather than pictures of pages. The drop-shadow is on the
   drawing rather than the wrapper: each sheet clips itself to its own rounded
   corners, and a box-shadow would draw a second, squarer shape behind it. */
.folios { display: grid; gap: var(--sp-xl); margin-top: var(--sp-l); }
.folio { display: grid; gap: var(--sp-s); margin: 0; }
.folio__sheet svg {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 1.4rem 3rem rgb(0 2 24 / 0.16));
}
.folio__cap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.1rem;
}
.folio__n { font-size: 0.62rem; letter-spacing: 0.18em; color: var(--blue); }
.folio__cap b { font-family: Asgard, Impact, sans-serif; font-size: var(--step-1); }
.folio__fact { font-size: 0.95rem; color: var(--lede-color); }

/* Declassification.
   
   The page arrives with every figure under black and the reader's scroll takes
   it off. This is not decoration bolted onto a static graphic: it is the only
   thing the product does, performed. The vendor names never come out from
   under it, because those are somebody else's and the page says so.

   Gated on .js, set synchronously at the top of main.js beside the observer
   that adds .is-in. Without the script every seal is simply already gone and
   the document reads finished, which is the state it must fail into.

   Retract to the right, like a marker being pulled back off the paper, and in
   order: line items first, the total last, because the total is the sentence
   and the rows are its evidence. */
/* Base state is OFF, not on. If the seals defaulted to covering, a reader with
   the script blocked would get a document blacked out end to end: the failure
   mode has to be the finished thing held still, never the sealed thing stuck
   shut. The script re-seals, the scroll takes it off again. */
.folio .seal { transform: scaleX(0); transform-origin: right center; }
.js .folio:not(.is-in) .seal { transform: scaleX(1); }
.js .folio.is-in .seal {
  transform: scaleX(0);
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1) calc(var(--s, 0) * 70ms);
}
@media (prefers-reduced-motion: reduce) {
  .js .folio.is-in .seal { transition: none; }
}

/* The contents list. Numbers ranged left in a fixed column so the titles line
   up, which is what makes a list of eleven read as a contents page and not as
   eleven bullet points. */
.contents { list-style: none; display: grid; margin-top: var(--sp-l); }
.contents__row {
  display: grid;
  grid-template-columns: 3rem 1fr;
  gap: 1rem;
  align-items: baseline;
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--panel-line);
}
.contents__row:first-child { border-top: 1px solid var(--panel-line); }
.contents__n { font-size: 0.68rem; letter-spacing: 0.16em; color: var(--text-muted); }
.contents__t { font-size: clamp(1rem, 0.94rem + 0.2vw, 1.12rem); color: var(--text); }
@media (min-width: 60rem) {
  .contents { grid-template-columns: 1fr 1fr; column-gap: var(--sp-xl); }
  /* Two columns means two rows can be first; the top rule belongs to both. */
  .contents__row:nth-child(2) { border-top: 1px solid var(--panel-line); }
}

/* ---------- Digital Inventory: the audit as a screen of the system ---------- */

/* The one visual language the owner accepted all day is the drawn interface on
   /un-saas/. This is that object, in HTML so it can fill with his numbers: the
   named dark rail, the company bar, two figures as a pair, a four-column table
   with one solid blue row, and the foot that says whose the server is.

   Plain markup and a plain grid. Nothing here can collapse. */
.scr {
  display: grid;
  grid-template-columns: 1fr;
  margin-top: var(--sp-l);
  border: 1px solid var(--panel-line);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  background: var(--card-bg, #fff);
  box-shadow: var(--lift-2);
}
@media (min-width: 62rem) { .scr { grid-template-columns: 15rem 1fr; } }

.scr__bar {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  gap: 1.2rem;
  padding: 1rem clamp(1.1rem, 1vw + 0.7rem, 1.7rem);
  border-bottom: 1px solid var(--panel-line);
}
.scr__bar b { font-family: Asgard, Impact, sans-serif; font-size: 1.05rem; }
.scr__bar span { font-size: 0.9rem; color: var(--text-muted); }

.scr__rail { background: #0b0d14; padding: 0.7rem 0; display: grid; align-content: start; }
.scr__nav { position: relative; }
.scr__nav input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.scr__nav span {
  display: block;
  padding: 0.6rem clamp(1.1rem, 1vw + 0.7rem, 1.7rem);
  font-size: 0.92rem;
  color: rgb(255 255 255 / 0.6);
  transition: background-color 0.15s ease, color 0.15s ease;
}
.scr__nav:hover span { color: #fff; }
.scr__nav input:checked + span { background: var(--blue); color: #fff; }
.scr__nav input:focus-visible + span { outline: 2px solid #fff; outline-offset: -2px; }

.scr__body { padding: clamp(1.2rem, 1vw + 0.8rem, 1.9rem); display: grid; gap: var(--sp-s); align-content: start; }

.scr__crew { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.9rem; }
.scr__crewkey { font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted); }
.scr__pick { position: relative; }
.scr__pick input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.scr__pick span {
  display: block;
  min-width: 1.9rem;
  padding: 0.25rem 0.5rem;
  border-radius: 6px;
  text-align: center;
  font-family: PlexMono, monospace;
  font-size: 0.85rem;
  color: var(--text-muted);
  transition: background-color 0.15s ease, color 0.15s ease;
}
.scr__pick input:checked + span { background: var(--blue); color: #fff; }

.scr__figs { display: flex; flex-wrap: wrap; gap: 0.4rem 3rem; margin-top: 0.4rem; }
.scr__figs p { display: grid; gap: 0.15rem; }
.scr__figs b {
  font-family: Asgard, Impact, sans-serif;
  font-size: clamp(2.2rem, 1.3rem + 2.4vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.025em;
}
.scr__figs p:first-child b { color: var(--red); }
.scr__figs span { font-size: 0.62rem; letter-spacing: 0.14em; color: var(--text-muted); }

.scr__tbl { width: 100%; border-collapse: collapse; margin-top: 0.8rem; }
.scr__tbl th {
  text-align: left;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--panel-line);
  font-family: PlexMono, monospace;
  font-weight: 400;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.scr__tbl td {
  padding: 0.75rem 0.7rem 0.75rem 0;
  border-bottom: 1px solid var(--panel-line);
  font-size: 0.92rem;
  vertical-align: baseline;
}
.scr__r { text-align: right; font-family: PlexMono, monospace; padding-right: 0 !important; }
.scr__v { font-size: 0.85rem; white-space: nowrap; }
.scr__v--cancel { color: var(--red); }
.scr__v--own { color: var(--blue); }
.scr__why { color: var(--text-muted); font-size: 0.86rem; }
@media (max-width: 52rem) { .scr__why { display: none; } }

/* The one loud thing, and it is the site's own device. */
.scr__tbl tr.is-live td { background: var(--blue); color: #fff; border-bottom-color: transparent; }
.scr__tbl tr.is-live .scr__v, .scr__tbl tr.is-live .scr__why { color: rgb(255 255 255 / 0.78); }
.scr__tbl tr.is-live td:first-child { border-radius: 8px 0 0 8px; padding-left: 0.7rem; }
.scr__tbl tr.is-live td:last-child { border-radius: 0 8px 8px 0; padding-right: 0.7rem; }

.scr__foot {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.6rem;
  padding: 0.9rem clamp(1.1rem, 1vw + 0.7rem, 1.7rem);
  border-top: 1px solid var(--panel-line);
  font-size: 0.88rem;
  color: var(--text-muted);
}
.scr__foot b { color: var(--heading-color); }
.scr__foot em { font-style: normal; color: var(--blue); }

/* Первый экран `/un-saas/` на телефоне: кегль заголовка течёт по ширине
   окна, потолок прежний. Правило стоит ЗДЕСЬ, потому что `.stage` объявлен
   в этом файле, а имя класса в двух таблицах гейт `collide` считает
   коллизией: замерено, пять коллизий становились шестью. */
@media (width < 48rem) {
  .stage h1 { font-size: min(var(--step-4), 7.6vw); line-height: 1.06; }
}

