/* ═══════════════════════════════════════════════════════════════════════════
   muteq.co — «ПЕЛЕНГ»: the landing as night operations seen through a sensor.

   Surfaces, text and signal colours come from the vendored @muteq/ui tokens
   (assets/muteq-ui/*). This file adds the page grammar: a flat ground, what the
   page holds back in one classified frame with sensor corner ticks, thin rules,
   and a signal triad that keeps its
   product meaning — cyan detects, amber jams, green verifies, red is the loss.
   The light theme is daylight, not a second night: a clean neutral page, white
   panels, near-black ink, black sight ticks, and the same signals darkened for sun. Literal
   colours appear only where a value must not follow the theme: text and scrims
   over photographs, which stay in the night palette in both themes, and the
   video letterbox.

   The hero is its own block, the «СТІНА» wall: the model under a wall of type
   (see its section below).

   Three faces:
     Roboto Flex   every display line — the hero's wall, every h2 and h3, the big
                   figures — in caps, weight 900, its width axis (wdth 25–151,
                   reached through font-stretch) fitted per line where a heading
                   is a statement (site.js), fixed elsewhere.
     Martian Mono  the instrument layer only — the section index on its rule.
     Inter         every sentence, navigation, the buttons and every other label.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* One grid for the page: the hero wall's margins (40 px at 1440, a 1600 px column). */
  --max: 1600px;
  --gutter: clamp(16px, 2.8vw, 40px);
  --section: clamp(56px, 5.5vw, 80px);
  /* The page's second column: two equal halves of the column with this gap, so
     it starts at x = 756 at 1440. Every paired block below the hero uses it. */
  --col-gap: clamp(24px, 5vw, 72px);
  --header: 64px;
  --sticky: 72px;

  /* The classified treatment (see its block below): the frame's margin, where its
     ticks stand, and the blur, about 0.55 % of the image's width — 3.5 px over a
     640 px screenshot. The poster's file is baked at the same ratio, the device
     photographs' at three times it (σ 14 px at 840 px). */
  --cls-pad: clamp(14px, 2vw, 28px);
  --cls-blur: clamp(2px, .24vw, 3.5px);

  --f-display: 'Roboto Flex', var(--font-sans);
  --f-mono: 'Martian Mono', var(--font-mono);
  --f-text: var(--font-sans);

  /* One h2 scale: 104 px at 1440. */
  --t-h2: clamp(40px, 7.2vw, 112px);
  --t-lead: clamp(18px, 1.45vw, 21px);
  --t-body: 17px;
  --t-small: 14px;
  --t-label: 12px;

  --ease: cubic-bezier(.2, .7, .2, 1);
  --snap: cubic-bezier(.3, 1.5, .5, 1);

  /* One accent in both themes, #058151: the OKLCH midpoint of the design system's
     success green and the brand mark's green, set just dark enough for 4.5:1 on the
     daylight page (3.9:1 on the night page — fills, marks, rules and large figures
     only; small text beside it stays ink). It sets the primary action and the
     verified state — never the model, which is drawn in --hero-wire. */
  --lp-accent: #058151;

  --ground: var(--color-surface-page);
  --ink: var(--color-text-primary);
  --ink-2: var(--color-text-secondary);
  --ink-3: var(--color-text-muted);
  --rule: rgba(var(--color-text-muted-rgb), .3);
  --rule-strong: rgba(var(--color-text-muted-rgb), .6);
  --panel: var(--color-surface-panel);

  --detect: var(--color-status-active-ink);
  --jam: var(--color-status-warning-ink);
  --verify: var(--lp-accent);
  --loss: var(--color-status-error-ink);
  --fill: var(--lp-accent);
  --on-fill: #ffffff;        /* 4.9:1 on the accent */
  /* The hero's model: hero-3d.js draws its wire and dust in --hero-wire (read as a
     plain colour). The words over it are drawn in difference, in colours that come
     out over the ground as the ink, the running-text ink and the rule. */
  --hero-wire: #f1f1ee;
  --wall-ink: #f4fbff;
  --wall-ink-2: #a2afbe;
  --wall-rule: #37404b;

  /* Over photographs: the night palette in both themes. */
  --scrim: 6, 8, 11;
  --on-photo: #e9edf2;
  --on-photo-2: #c3cad2;
  --on-photo-detect: #38bdf8;
  --on-photo-jam: #f3b24a;
  --photo-grade: saturate(.9) brightness(.92) contrast(1.04);
}
/* Daylight: a clean, bright, neutral page — the design system's light surface, white
   panels, near-black ink, neutral greys for rules and secondary text, sight ticks in
   ink, the signals in their day inks (each ≥ 4.5:1 on the page). */
[data-theme="light"] {
  --ground: #f5f6f8;
  --ink: #0b0c0e;
  --ink-2: #2e343c;
  --ink-3: #5f6874;
  --rule: rgba(11, 12, 14, .14);
  --rule-strong: rgba(11, 12, 14, .5);
  --panel: #ffffff;
  --detect: #015a88;
  --jam: #b45309;            /* 4.6:1 on the page, 5.0:1 on white; apart from the loss red */
  --loss: #ad2229;
  --hero-wire: #0b0c0e;
  /* ground − ink, ground − ink-2, ground − the rule: difference over the ground gives them back */
  --wall-ink: #eaeaea;
  --wall-ink-2: #c7c2bc;
  --wall-rule: #d4d5d7;
  --photo-grade: none;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--header);
  scroll-padding-bottom: calc(var(--sticky) + 16px);
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;
}
html[data-theme="light"] { color-scheme: light; }

body {
  font-family: var(--f-text);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--ink);
  background: var(--ground);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, video, svg { display: block; max-width: 100%; }
img { height: auto; }
picture { display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul, ol { list-style: none; }

:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.wrap {
  width: 100%;
  max-width: calc(var(--max) + 2 * var(--gutter));
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.icon {
  width: 20px;
  height: 20px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: square;
  stroke-linejoin: miter;
}
.icon-fill { fill: currentColor; stroke: none; }

.skip-link {
  position: absolute;
  left: 16px;
  top: -64px;
  z-index: 100;
  padding: 12px 16px;
  background: var(--fill);
  color: var(--on-fill);
  font: 600 15px / 1 var(--f-text);
}
.skip-link:focus { top: 12px; }

/* ── Type ─────────────────────────────────────────────────────────────────
   One display system with the hero: Roboto Flex 900 in caps. Headings keep one
   width; a statement ([data-wall]) is fitted line by line in site.js. */
h1, h2, h3 {
  font-family: var(--f-display);
  font-stretch: 88%;
  font-weight: 900;
  text-transform: uppercase;
  color: var(--ink);
  overflow-wrap: normal;
  hyphens: none;
}
h1 { line-height: .9; }
/* One law for h2: every section heading is a wall ([data-wall]), its ink exactly
   on both margins of the column on every line (site.js). Before the fit — and
   without script — it is set at the h2 size with a full line of leading, so Ї
   and Й clear the line above. */
h2 { font-size: var(--t-h2); line-height: 1; letter-spacing: -.01em; }
h3 { font-size: clamp(22px, 2.1vw, 30px); line-height: 1; letter-spacing: -.005em; }
/* A statement wall: each line on its own, sized, widened and shifted onto the
   margin by site.js. */
.wall-l { display: block; white-space: nowrap; letter-spacing: .01em; line-height: 1; }
/* A line with Ї or Й takes extra room above, so the marks clear the line over it. */
.wall-l.has-mark { padding-top: .1em; }
/* The measuring probe is emptied after every fit, so it never takes room. */
.statement-probe {
  position: absolute;
  left: 0;
  top: 0;
  visibility: hidden;
  white-space: nowrap;
  font-family: var(--f-display);
  font-weight: 900;
  font-size: 100px;
  letter-spacing: .01em;
  text-transform: uppercase;
}

.lead, .section-lead {
  font-size: var(--t-lead);
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 34em;
  text-wrap: pretty;
}

/* The instrument layer: the only mono. The section index — a readout, never a
   sentence or navigation. */
.kicker {
  font-family: var(--f-mono);
  font-stretch: 87.5%;
  font-weight: 500;
  font-size: var(--t-label);
  line-height: 1.45;
  letter-spacing: .06em;
  text-transform: uppercase;
}

/* Every other label is a plain Inter line. */
.metric dt, .timing-label, .device-specs dt, .chart figcaption, .field label {
  font-family: var(--f-text);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
}

/* Section index: «02 / Рішення» set ON the section's rule, which runs on from it to
   the column edge; the h2 follows one step below. */
main { counter-reset: sec; }
.kicker {
  counter-increment: sec;
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 13px;
  color: var(--ink-2);
}
.kicker::before { content: counter(sec, decimal-leading-zero) "\00a0/"; color: var(--ink); }
.kicker::after { content: ""; flex: 1 1 0; min-width: 32px; height: 1px; background: var(--rule-strong); }

/* ── Buttons ──────────────────────────────────────────────────────────────
   One primary action, one look, the hero's: a solid accent slab with a bold
   Inter label. Hover inverts it hard to ink. The slab is drawn by ::before so
   the focus ring is never clipped. */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  height: 52px;
  max-width: 100%;
  padding: 0 26px;
  color: var(--on-fill);
  font-family: var(--f-text);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.2;
  letter-spacing: -.005em;
  white-space: nowrap;
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--fill);
}
.btn:hover { color: var(--ground); }
.btn:hover::before { background: var(--ink); }
.btn .icon { width: 18px; height: 18px; transition: transform .2s var(--snap); }
.btn:hover .icon { transform: translateX(4px); }
.btn:disabled, .btn:disabled:hover { color: var(--on-fill); cursor: progress; opacity: .6; }
.btn:disabled::before, .btn:disabled:hover::before { background: var(--fill); }
.btn-sm { height: 38px; padding: 0 16px; font-size: 15px; }
.btn-lg { height: 56px; padding: 0 24px; }

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 1px solid var(--rule);
  color: var(--ink-2);
}
.icon-btn .icon { width: 18px; height: 18px; }
.icon-btn:hover { color: var(--ink); border-color: var(--ink); }

/* ── Header ───────────────────────────────────────────────────────────────*/
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--header);
  background: var(--ground);
  border-bottom: 1px solid transparent;
}
.site-header.is-scrolled { border-bottom-color: var(--rule); }
.header-inner { height: 100%; display: flex; align-items: center; gap: 40px; }

.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--ink); }
.brand-mark { width: 30px; height: 30px; }
.brand .wordmark { height: 14px; width: auto; }
.brand .wordmark g { fill: currentColor; }

.site-nav { display: flex; gap: 4px; margin-right: auto; }
.site-nav a {
  --tick: var(--ink);
  position: relative;
  padding: 10px 12px;
  color: var(--ink-2);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
}
.site-nav a:hover, .site-nav a[aria-current="true"] { color: var(--ink); }
.site-nav a[aria-current="true"] { --tick: var(--ink); }

.header-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.segmented { display: inline-flex; height: 38px; border: 1px solid var(--rule); }
.seg-btn { min-width: 40px; padding: 0 10px; color: var(--ink-3); font-size: 14px; font-weight: 600; }
.seg-btn:hover { color: var(--ink); }
.seg-btn[aria-pressed="true"] { background: var(--ink); color: var(--ground); }

#theme-toggle .icon-moon { display: none; }
[data-theme="light"] #theme-toggle .icon-sun { display: none; }
[data-theme="light"] #theme-toggle .icon-moon { display: block; }
.header-cta { margin-left: 8px; }

.nav-toggle { display: none; }
.nav-toggle .icon-close { display: none; }
.nav-open .nav-toggle .icon-open { display: none; }
.nav-open .nav-toggle .icon-close { display: block; }

/* ── Sensor ticks and target brackets ─────────────────────────────────────
   Four L-shaped corners drawn as eight gradient strokes on one overlay. Only the
   classified frames (the devices, the field video, the Kamerton screenshots)
   carry them as a viewfinder; the scenario and capability rows take them as
   brackets while they cross mid-window. */
.cls::before, .case::before, .cap::after, .site-nav a::before {
  --tl: 18px;
  --tw: 1.5px;
  content: "";
  position: absolute;
  pointer-events: none;
  z-index: 3;
  background:
    linear-gradient(var(--tick) 0 0) left top / var(--tl) var(--tw),
    linear-gradient(var(--tick) 0 0) left top / var(--tw) var(--tl),
    linear-gradient(var(--tick) 0 0) right top / var(--tl) var(--tw),
    linear-gradient(var(--tick) 0 0) right top / var(--tw) var(--tl),
    linear-gradient(var(--tick) 0 0) left bottom / var(--tl) var(--tw),
    linear-gradient(var(--tick) 0 0) left bottom / var(--tw) var(--tl),
    linear-gradient(var(--tick) 0 0) right bottom / var(--tl) var(--tw),
    linear-gradient(var(--tick) 0 0) right bottom / var(--tw) var(--tl);
  background-repeat: no-repeat;
}
.site-nav a::before {
  --tl: 6px;
  inset: 2px 0;
  opacity: 0;
  transform: scale(1.25);
  transition: opacity .12s linear, transform .18s var(--snap);
}
.site-nav a:hover::before, .site-nav a[aria-current="true"]::before { opacity: 1; transform: none; }

/* ── Classified: one treatment for everything the page holds back ────────
   The device photographs, the field video's poster and the Kamerton OS
   screenshots are shown the same way. Each stands in a frame with a margin of
   --cls-pad, its four ticks in that margin in the running-text ink, on the
   night field; the image is blurred (the device photographs and the poster as
   files, so nothing sharp ships — the poster at the screenshots' ratio, the
   devices at three times it; the screenshots by the stylesheet) and seen
   through the same scanlines and tint;
   a stamp sits on the frame's top arm past the corner tick; the one action that
   opens it is the page's accent slab at the reveal's size. Opening a frame
   (.is-open) lifts the scanlines. */
.cls { position: relative; isolation: isolate; padding: var(--cls-pad); }
.cls::before { --tick: var(--ink-2); inset: 0; }
.cls-media { position: relative; display: block; background: #0b0c0e; }
.cls::after {
  content: "";
  position: absolute;
  inset: var(--cls-pad);
  z-index: 3;
  pointer-events: none;
  background:
    repeating-linear-gradient(to bottom, rgba(var(--scrim), .5) 0 1px, transparent 1px 3px),
    rgba(var(--scrim), .08);
  transition: opacity .3s linear;
}
.cls.is-open::after { opacity: 0; }
/* Without script only a video can be opened (by its own controls), so it plays clear. */
html:not(.js) .cls:has(> video)::after { opacity: 0; }
.cls-tag {
  position: absolute;
  z-index: 5;
  top: 0;
  left: 26px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  background: var(--ground);
  color: var(--ink-2);
  font-family: var(--f-text);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.cls-action {
  height: auto;
  min-height: 64px;
  padding: 12px 26px 14px;
  font-size: clamp(17px, 1.4vw, 20px);
}
/* Straight on the frame the action stands at the image's centre. */
.cls > .cls-action { position: absolute; z-index: 6; left: 50%; top: 50%; transform: translate(-50%, -50%); }
.cls-action[hidden] { display: none; }
/* Over the image the slab inverts to the night palette's light, not to the
   theme's ink, which in daylight would sink into a dark image. */
.cls-action:hover, .classified.is-holding .declassify { color: rgb(var(--scrim)); }
.cls-action:hover::before, .classified.is-holding .declassify::before { background: var(--on-photo); }
.cls-action:focus-visible { outline-color: var(--on-photo); }
@media (prefers-reduced-motion: reduce) {
  .cls::after { transition: none; }
}

/* A single scan sweep, in white, crosses each photograph as it enters. */
.device-media picture::after, .screen picture::after, .field-card picture::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(90deg, transparent 62%, rgba(255, 255, 255, .12) 97%, rgba(255, 255, 255, .85) 99.6%, transparent);
  transform: translateX(-100%);
}
.device-media picture, .screen picture, .field-card picture { position: relative; overflow: hidden; }
.device.is-in .device-media picture::after,
.screen.is-in picture::after,
.field-card.is-in picture::after { animation: sweep 1.5s cubic-bezier(.45, 0, .55, 1) .15s 1 both; }
@keyframes sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); visibility: hidden; }
}

/* ── Hero: the model under a wall of type ─────────────────────────────────
   The hero keeps its own grammar (the «СТІНА» block): a 12-column grid on a wider
   column, Roboto Flex 900 for the wall and its figures, Inter for the rest, the
   model's wire in the page's ink. Where the model runs (script on, 768 px and
   wider) its canvas covers the whole fold and the headline lies over it, three
   lines edge to edge. The headline, the descriptor, the lead, the video link and
   the metrics are drawn in mix-blend-mode: difference, in colours that come out
   as the ink, the running-text ink and the rule over the page, so the wire
   inverts every letter it crosses; the descriptor and the headline take no
   pointer, so a drag on the words turns the model. Below 768 px, and wherever
   the model cannot run, the photo stands under the headline, darkened under the
   type, with no blend. */
.hero {
  --gutter: clamp(16px, 2.8vw, 40px);
  --max: 1600px;
  --hero-gap: clamp(12px, 1.7vw, 24px);
  --hero-pad: clamp(20px, 2vw, 28px);
  --hero-foot: clamp(32px, 3vw, 40px);
  --hero-track: 0.01em;
  --f-wall: 'Roboto Flex', var(--font-sans);
  --hero-edge: max(var(--gutter), (100% - var(--max)) / 2);
  position: relative;
  display: flex;
  flex-direction: column;
  color: var(--ink);
}
.hero-inner {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-template-rows: auto auto 1fr;
  column-gap: var(--hero-gap);
  padding-top: var(--hero-pad);
  padding-bottom: var(--hero-foot);
}
.hero .eyebrow {
  grid-column: 1 / -1;
  grid-row: 1;
  margin-bottom: clamp(12px, 1.3vw, 20px);
  font-family: var(--f-text);
  font-size: 14px;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink);
}
/* Every word and control of the hero lies over the stage, each on its own layer: a
   word blends with the stage only while no wrapper between them makes a layer of
   its own, so the lead's block stays a plain box. */
.hero .eyebrow, .hero h1, .hero .lead, .hero-actions > *, .hero-metrics { position: relative; z-index: 1; }
.hero .eyebrow, .hero h1 { pointer-events: none; }

.hero h1 {
  grid-column: 1 / -1;
  grid-row: 2;
  /* Without script «Переконатись.» at 60 % width fills the column; site.js sets the
     size and each line's width. */
  font-family: var(--f-wall);
  font-size: min(212px, (100vw - 2 * var(--gutter)) / 6.45);
  font-weight: 900;
  line-height: .92;
  font-stretch: 62%;
  letter-spacing: var(--hero-track);
  text-transform: uppercase;
  color: var(--ink);
}
/* Typed headline: the ghost copy holds the finished lines so the page never jumps
   while the live copy types over it; without script the ghost shows, one sentence
   per line. */
.typed-title { display: grid; justify-items: start; }
.typed-ghost, .typed-live { grid-area: 1 / 1; }
.typed-ghost { width: min-content; }
.typed-live, #hero-typed { display: block; }
.js .typed-ghost { visibility: hidden; }
.wall-line { display: block; width: max-content; white-space: nowrap; letter-spacing: var(--hero-track); }
.wall-probe { position: absolute; left: 0; top: 0; visibility: hidden; }
/* The caret rides in the line being typed and takes no room on it (its margins
   cancel its width), so a line never grows past the column while it is typed. */
.typed-caret {
  display: inline-block;
  width: .07em;
  height: .71em;
  margin: 0 -.11em 0 .04em;
  background: currentColor;
  animation: caret-blink .9s steps(1) infinite;
}
.typed-caret.is-done { animation: caret-blink .9s steps(1) 3, caret-hide 0s linear 2.7s forwards; }
html:not(.js) .typed-caret { display: none; }
@keyframes caret-blink { 50% { opacity: 0; } }
@keyframes caret-hide { to { visibility: hidden; } }

.hero-body {
  grid-column: 1 / 6;
  grid-row: 3;
  align-self: end;
  margin-top: clamp(24px, 2vw, 28px);
}
.hero .lead { max-width: 30em; font-size: var(--t-lead); line-height: 1.5; color: var(--ink-2); }
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 32px;
  margin-top: 32px;
}
/* The primary action is the page's accent slab in the hero's own cut: square, an
   Inter label. The video link is underlined, the arrow pointing down the page. */
.hero .btn { gap: 12px; font-family: var(--f-text); font-stretch: 100%; font-size: 16px; line-height: 1.2; font-weight: 700; letter-spacing: -.005em; text-transform: none; }
.hero .btn::before { clip-path: none; }
.hero .btn-lg { height: 56px; padding: 0 24px; }
.hero .icon { stroke-width: 2.25; }
.hero .btn .icon { width: 20px; height: 20px; transition: none; }
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  font-weight: 700;
  font-size: 16px;
  line-height: 1.3;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
.link-arrow .icon { width: 18px; height: 18px; }
.link-arrow:hover { text-decoration-thickness: 4px; }
.icon-down { transform: rotate(90deg); }

/* Metrics as a ruled table: figure, then what it measures. */
.hero-metrics {
  grid-column: 7 / -1;
  grid-row: 3;
  align-self: end;
  margin-top: clamp(24px, 2vw, 28px);
  border-top: 1px solid var(--ink);
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: clamp(20px, 4vw, 64px);
}
/* Each row shares the table's two columns, so the labels line up after the widest figure. */
.metric {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 10.5rem minmax(0, 1fr);
  grid-template-columns: subgrid;
  column-gap: inherit;
  align-items: baseline;
  padding: 10px 0;
  border-bottom: 1px solid var(--rule);
}
.metric dd {
  grid-column: 1;
  grid-row: 1;
  font-family: var(--f-wall);
  font-size: clamp(30px, 2.5vw, 36px);
  font-stretch: 88%;
  line-height: 1;
  font-weight: 900;
  letter-spacing: var(--hero-track);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hero .metric dt { grid-column: 2; font-size: 16px; font-weight: 400; line-height: 1.35; color: var(--ink-2); }

/* The stage. Wherever the model runs the stage is set from the first paint, the
   canvas over its whole area, and the model appears once loaded; the photo stands in
   only where the model cannot run — narrow screens, no WebGL, a failed load
   (hero-3d.js marks those with .no-model). */
.hero-visual {
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  right: 0;
  height: 100%;
  background: var(--ground);
  overflow: hidden;
}
.hero-photo, .hero-photo img { display: block; width: 100%; height: 100%; }
.hero-visual img { object-fit: cover; object-position: 50% 40%; }
#hero-canvas {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  display: block;
  cursor: grab;
  touch-action: pan-y;
}
#hero-canvas:active { cursor: grabbing; }
.hero-visual:not(.has-model) #hero-canvas { pointer-events: none; }
/* The photo is darkened under the type (the shade is set per layout below). */
.hero-visual::after { content: ""; position: absolute; inset: 0; pointer-events: none; }

@media (min-width: 768px) {
  .hero { min-height: calc(100svh - var(--header)); }
  /* One fold tall whatever the hero holds, so the canvas keeps its size. */
  .hero-visual { height: calc(100svh - var(--header)); }
  .js .hero-visual:not(.no-model) .hero-photo { display: none; }
  /* Over the model every word of the hero is drawn in difference; only the accent
     button stays a solid block. */
  .hero:has(.model-live:not(.no-model)) :is(.eyebrow, h1, .lead, .link-arrow, .hero-metrics) {
    --ink: var(--wall-ink);
    --ink-2: var(--wall-ink-2);
    --rule: var(--wall-rule);
    color: var(--ink);
    mix-blend-mode: difference;
  }
  /* Without the model the wall stands on the plain page in the page's own ink —
     monochrome, no photo (owner, 29.10.2026). */
  .hero-visual.no-model .hero-photo, html:not(.js) .hero-visual .hero-photo { display: none; }
}
@media (max-width: 767px) {
  #hero-canvas { display: none; }
  /* Phones set the hero as the wide screens do without the model: the wall in the
     page's own ink on the plain page, no photo (owner, 31.10.2026). */
  .hero-visual { display: none; }
  .hero h1 { padding-bottom: 24px; }
}
/* Below 1100 px the metrics take their own row under the lead. */
@media (max-width: 1099px) {
  .hero-body { grid-column: 1 / 8; }
  .hero-metrics { grid-column: 1 / -1; grid-row: 4; }
}
@media (max-width: 720px) {
  .hero-body, .hero-metrics { grid-column: 1 / -1; }
  .hero-actions { gap: 12px 24px; }
  .hero-scroll { display: none; }
}
@media (max-width: 420px) {
  .hero-actions .btn { width: 100%; }
}
@media (max-width: 359px) {
  .hero .btn-lg { padding: 0 12px; }
  .hero .btn { gap: 8px; font-size: 15px; }
}

.hero-scroll {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: 0;
  opacity: 0;
  animation: scroll-cue-in .6s linear 2.5s forwards;
}
.hero-scroll-line {
  width: 3px;
  height: 40px;
  background: var(--ink);
  transform-origin: top;
  animation: scroll-pulse 2s steps(4) 3;
}
@keyframes scroll-cue-in { to { opacity: 1; } }
@keyframes scroll-pulse { from { transform: scaleY(.25); } to { transform: scaleY(1); } }

/* ── The model's acquisition ─────────────────────────────────────────────
   Shown only where the model can run, from the first paint until the model is on
   screen (hero-3d.js), in the model's own ink, and gone without a trace after. It
   has no words: a bearing line crosses the fold once, as far as the model's bytes
   have come, past a sight cross at the aim point, which holds while the model waits;
   the reticle waits on the fold's corners and, once the model is built, snaps onto
   its outline, holds, and goes. They lie above the words, so the blend never
   recolours them. Nothing appears for the first .3 s. Reduced motion shows none of it. */
.acq { display: none; }
@media (min-width: 768px) {
  .js .hero-visual.is-acquiring:not(.no-model) ~ .acq { display: block; }
}
.acq {
  --p: 0;
  --aim-y: 50%;
  --field-h: calc(100svh - var(--header) - var(--hero-pad) - var(--hero-foot));
  container-type: inline-size;
  position: absolute;
  z-index: 2;
  top: var(--hero-pad);
  left: var(--hero-edge);
  right: var(--hero-edge);
  height: var(--field-h);
  pointer-events: none;
  transition: opacity .3s linear;
}
.acq > * { animation: acq-in 0s linear .3s both; }
@keyframes acq-in { from { opacity: 0; } to { opacity: 1; } }
.acq-reticle {
  --tl: 30px;
  --tw: 3px;
  position: absolute;
  inset: 0;
}
/* Each corner of the reticle is its own mark, moved by a transform (never by layout,
   so its jump onto the model is no layout shift). hero-3d.js sets --x0 --y0 --x1 --y1,
   the model's outline in the field's pixels; until then the corners stand on the
   field's own corners. */
.acq-reticle > i {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--tl);
  height: var(--tl);
  background:
    linear-gradient(var(--hero-wire) 0 0) left top / 100% var(--tw),
    linear-gradient(var(--hero-wire) 0 0) left top / var(--tw) 100%;
  background-repeat: no-repeat;
}
.acq-reticle > i:nth-child(1) { transform: translate(var(--x0, 0px), var(--y0, 0px)); }
.acq-reticle > i:nth-child(2) { transform: translate(calc(var(--x1, 100cqw) - var(--tl)), var(--y0, 0px)) scaleX(-1); }
.acq-reticle > i:nth-child(3) { transform: translate(var(--x0, 0px), calc(var(--y1, var(--field-h)) - var(--tl))) scaleY(-1); }
.acq-reticle > i:nth-child(4) { transform: translate(calc(var(--x1, 100cqw) - var(--tl)), calc(var(--y1, var(--field-h)) - var(--tl))) scale(-1); }
.is-locked ~ .acq .acq-reticle > i { transition: transform .16s steps(2, end); }
/* The bearing line runs the fold's height, a pointer at each end. It moves by a
   transform (the field is its size container), so it keeps moving on the compositor
   while the main thread builds the model. */
.acq-bearing {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 2px;
  margin-left: -1px;
  background: var(--hero-wire);
  transform: translateX(calc(var(--p) * 100cqw));
  transition: transform .25s linear;
}
.acq-bearing::before, .acq-bearing::after {
  content: "";
  position: absolute;
  left: -6px;
  width: 14px;
  height: 9px;
  background: var(--hero-wire);
}
.acq-bearing::before { top: 0; clip-path: polygon(0 0, 100% 0, 50% 100%); }
.acq-bearing::after { bottom: 0; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
/* The aim point: a sight cross, open at the centre. */
.acq-aim {
  position: absolute;
  left: 50%;
  top: var(--aim-y);
  width: 88px;
  height: 88px;
  margin: -44px 0 0 -44px;
  background:
    linear-gradient(var(--hero-wire) 0 0) left center / 30px 2px,
    linear-gradient(var(--hero-wire) 0 0) right center / 30px 2px,
    linear-gradient(var(--hero-wire) 0 0) center top / 2px 30px,
    linear-gradient(var(--hero-wire) 0 0) center bottom / 2px 30px;
  background-repeat: no-repeat;
}
.is-open ~ .acq .acq-bearing, .is-ready ~ .acq .acq-bearing,
.is-revealing ~ .acq .acq-aim, .is-locked ~ .acq .acq-aim { display: none; }
.is-leaving ~ .acq { opacity: 0; }

/* ── The model's radio ───────────────────────────────────────────────────
   The detector's waves are drawn with the model, under the words, and inverted by them like
   the wire. The jam's waves keep their colour: they have a canvas of their own over the
   model's, above the words, which hero-3d.js blends (screen by night, multiply by day) so a
   jam front lights the ground and passes behind the words.
   Once the model is built its radio shows on the descriptor's line at the column's
   right edge, above the words and never blended: one button, «Jam». Idle it offers the
   jam; jamming, it counts the seconds down while a ring around its glyph drains (in
   whole seconds under reduced motion), and a press stops it. The detector needs no
   control: it always listens. */
.hero-waves { display: none; }
@media (min-width: 768px) {
  .js .hero-visual.model-live:not(.no-model) ~ .hero-waves {
    display: block;
    position: absolute;
    z-index: 1;
    top: 0;
    left: 0;
    width: 100%;
    height: calc(100svh - var(--header));
    pointer-events: none;
  }
}
.hero-radio {
  position: absolute;
  z-index: 2;
  top: calc(var(--hero-pad) + 9px);
  right: var(--hero-edge);
  translate: 0 -50%;
  animation: radio-in .4s var(--ease) both;
}
.hero-radio[hidden], .hero-visual.no-model ~ .hero-radio { display: none; }
@keyframes radio-in { from { opacity: 0; } }
.jam {
  /* The state's colour paints the glyph, its ring and the frame; the words stay ink. */
  --state: #f1f1ee;
  --state-on: #f3b24a;
  position: relative;
  display: inline-grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: 10px;
  height: 40px;
  padding: 0 12px;
  border: 1px solid var(--rule-strong);
  background: var(--ground);
  color: var(--ink);
  font-family: var(--f-text);
  font-size: 14px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  transition: border-color .16s linear, background-color .16s linear;
}
[data-theme="light"] .jam { --state: #0b0c0e; --state-on: #b45309; }
.jam.is-on { --state: var(--state-on); border-color: var(--state); }
.jam:hover { border-color: var(--state); background: color-mix(in srgb, var(--state) 8%, var(--ground)); }
.jam:active { background: color-mix(in srgb, var(--state) 14%, var(--ground)); }
.jam:focus-visible { outline: 1.5px solid var(--state); outline-offset: 3px; border-radius: 0; }
/* The glyph: a source and two mirrored pairs of arcs, the two antennas; they never move.
   While jamming a ring around it drains from 12 o'clock, clockwise, over the jam's
   seconds — with the countdown; at the end or at a stop nothing of it remains. */
.jam-glyph {
  width: 1.5em;
  height: 1.5em;
  overflow: visible;
  color: var(--state);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: butt;
  transition: color .16s linear;
}
.jam-arcs { vector-effect: non-scaling-stroke; }
.jam-dot { fill: currentColor; stroke: none; }
.jam-track, .jam-ring { opacity: 0; }
.jam-track { transition: opacity .16s linear; }
.jam-ring {
  stroke-dasharray: 53.41;
  stroke-dashoffset: 0;
  transform: rotate(-90deg);
  transform-box: view-box;
  transform-origin: 10px 10px;
}
.jam.is-on .jam-track { opacity: .25; }
.jam.is-on .jam-ring { opacity: 1; animation: jam-ring var(--jam-s) linear forwards; }
@keyframes jam-ring { to { stroke-dashoffset: -53.41; } }
/* The label keeps one width whatever it says: the word in use stands in one cell with an
   unseen specimen of the page language's longest countdown (hero.jam.spec), centred. */
.jam-label { display: grid; justify-items: center; }
.jam-label > * { grid-area: 1 / 1; }
.jam-spec { visibility: hidden; }
.jam-on, .jam.is-on .jam-idle { display: none; }
.jam.is-on .jam-on { display: inline; }
.jam-said {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
@media (max-width: 767px) {
  .hero-radio { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-radio { animation: none; }
  .jam.is-on .jam-ring { animation-timing-function: steps(var(--jam-n), end); }
  .jam, .jam-glyph, .jam-track { transition: none; }
}

/* ── Sections ─────────────────────────────────────────────────────────────
   Sections by weight: the strikes (problem, traction, contact) run full width
   with their statement as a wall; the quiet ones stay compact. No section
   carries an empty tail: its last block has no margin below it. */
.section {
  position: relative;
  padding: 0 0 var(--section);
}
.section .kicker { margin-bottom: clamp(24px, 2.4vw, 36px); }
.section-head { margin-bottom: clamp(32px, 4vw, 56px); }
.section-head .section-lead { margin-top: clamp(20px, 2.2vw, 32px); }
.section > :last-child, .section > .wrap:last-child > :last-child { margin-bottom: 0; }

.source-note {
  margin-top: 28px;
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--ink-3);
  max-width: 64em;
}

/* ── Problem: the blindness scale ─────────────────────────────────────────
   One time axis across the column: 00:30 falls at 12.5 % of 04:00. The short
   solid stub is the time the crew still had the drone; the hatched red gap is
   the time it is blind. The proportion reads before a word does. Wide screens
   run it horizontally, narrow ones as a 70svh column. The stub is solid ink,
   the gap a dense red hatching with no frame — only the two ticks; the hatching
   is the page's only one, so the gap stays the loudest thing on the axis. The
   solution's time bars stand on the same scale and hatching. */
.blind {
  --bar: clamp(64px, 8.4vw, 120px);
  --tick-w: 3px;
  --stub: var(--ink);
  /* The figures are sized from the axis's own width (cqi). The container is the
     axis, not the section's column, so the section index counter is not scoped. */
  container-type: inline-size;
  display: grid;
  margin: clamp(24px, 3vw, 48px) 0 clamp(40px, 5vw, 72px);
}
.axis-stub { background: var(--stub); }
.axis-gap {
  position: relative;
  background: repeating-linear-gradient(135deg, var(--loss) 0 4px, transparent 4px 12px);
}
.axis-gap::before, .axis-gap::after { content: ""; position: absolute; }
.axis-gap::before { background: var(--loss); }
.axis-gap::after { background: var(--ink); }
.blind-value {
  font-family: var(--f-display);
  font-stretch: 80%;
  font-weight: 900;
  line-height: .8;
  letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.blind-mark-loss .blind-value { color: var(--loss); }
.blind-label { font-size: var(--t-lead); line-height: 1.4; color: var(--ink-2); max-width: 17em; }

@media (min-width: 760px) {
  .blind {
    grid-template-columns: 12.5% 87.5%;
    grid-template-rows: auto var(--bar) auto;
  }
  .blind-mark { display: contents; }
  .axis-stub { grid-column: 1; grid-row: 2; }
  .axis-gap { grid-column: 2; grid-row: 2; }
  /* Each figure stands on its tick: 00:30 from the red one rightwards, 04:00 up to
     the end of the axis. */
  .blind-value { grid-row: 1; align-self: end; font-size: clamp(56px, 9.2cqi, 132px); padding-bottom: 14px; }
  .blind-mark-loss .blind-value { grid-column: 2; justify-self: start; padding-left: 16px; border-left: var(--tick-w) solid var(--loss); }
  .blind-mark-end .blind-value { grid-column: 2; justify-self: end; padding-right: 16px; border-right: var(--tick-w) solid var(--ink); }
  .axis-gap::before { left: 0; top: 0; bottom: 0; width: var(--tick-w); }
  .axis-gap::after { right: 0; top: 0; bottom: 0; width: var(--tick-w); }
  .blind-label { grid-row: 3; grid-column: 2; padding-top: 16px; max-width: min(21em, 46%); }
  .blind-mark-loss .blind-label { justify-self: start; padding-left: 19px; }
  .blind-mark-end .blind-label { justify-self: end; padding-right: 19px; text-align: right; }
  .js .blind .axis-gap { transform: scaleX(0); transform-origin: left; }
  .blind.is-in .axis-gap { transform: none; transition: transform 1.1s linear .35s; }
}
@media (max-width: 759px) {
  .blind {
    height: 70svh;
    min-height: 460px;
    grid-template-columns: 24px minmax(0, 1fr);
    grid-template-rows: 12.5fr 87.5fr;
    column-gap: 0;
  }
  .axis-stub { grid-column: 1; grid-row: 1; }
  .axis-gap { grid-column: 1; grid-row: 2; }
  .axis-gap::before { left: 0; right: 0; top: 0; height: var(--tick-w); }
  .axis-gap::after { left: 0; right: 0; bottom: 0; height: var(--tick-w); }
  .blind-mark { grid-column: 2; grid-row: 2; display: flex; flex-direction: column; gap: 10px; padding-left: 16px; }
  .blind-mark-loss { align-self: start; border-top: var(--tick-w) solid var(--loss); padding-top: 12px; }
  .blind-mark-end { align-self: end; border-bottom: var(--tick-w) solid var(--ink); padding-bottom: 12px; }
  .blind-value { font-size: clamp(64px, 26cqi, 120px); }
  .js .blind .axis-gap { transform: scaleY(0); transform-origin: top; }
  .blind.is-in .axis-gap { transform: none; transition: transform 1.1s linear .35s; }
}

.problem-notes { display: grid; gap: 24px; }
.note { color: var(--ink-2); font-size: var(--t-lead); line-height: 1.55; max-width: 34em; }
@media (min-width: 760px) {
  .problem-notes { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--col-gap); }
}

/* ── Solution: the problem's axis answered, then the tracer ───────────────
   The two times stand on one scale: today's four minutes are a solid ink line
   across the whole column; PUHACH's five to ten seconds a green block 10/240 of
   it long — solid to five seconds, lighter on to ten. Each figure stands on its
   bar's end. */
.timing {
  --bar: clamp(40px, 4.4vw, 64px);
  --line: clamp(6px, .6vw, 8px);
  --tick-w: 3px;
  --us: calc(100% * 10 / 240);
  display: grid;
  row-gap: clamp(28px, 3vw, 44px);
  margin-bottom: clamp(48px, 6vw, 88px);
}
.timing-row { display: grid; grid-template-columns: var(--us) minmax(0, 1fr); grid-template-rows: auto var(--bar) auto; }
.timing-row-today { grid-template-rows: auto var(--line) auto; }
.timing-value {
  grid-row: 1;
  grid-column: 2;
  align-self: end;
  padding-bottom: 12px;
  font-family: var(--f-display);
  font-stretch: 80%;
  font-weight: 900;
  font-size: clamp(44px, 7vw, 108px);
  line-height: .8;
  letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--ink);
}
.timing-row-today .timing-value { justify-self: end; padding-right: 16px; border-right: var(--tick-w) solid var(--ink); }
.timing-row-us .timing-value { justify-self: start; padding-left: 16px; border-left: var(--tick-w) solid var(--verify); color: var(--verify); }
.timing-bar { grid-row: 2; grid-column: 1 / -1; position: relative; }
.timing-row-us .timing-bar { grid-column: 1; }
.timing-bar span { display: block; height: 100%; }
.timing-bar-today span { background: var(--ink); }
.timing-bar-us span { min-width: 6px; background: linear-gradient(90deg, var(--fill) 0 50%, color-mix(in srgb, var(--fill) 45%, transparent) 50%); }
.timing-label { grid-row: 3; grid-column: 1 / -1; padding-top: 12px; color: var(--ink-2); }
.timing-row-us .timing-label { color: var(--ink); }
.js .timing .timing-bar span { transform: scaleX(0); transform-origin: left; }
.timing.is-in .timing-bar-today span { animation: bar-grow 2.4s linear .3s forwards; }
.timing.is-in .timing-bar-us span { animation: bar-grow .3s var(--snap) .3s forwards; }
@keyframes bar-grow { to { transform: scaleX(1); } }
.timing .source-note { margin-top: 0; }

/* The tracer: detect → jam → verify down one rail. Each step is a row of equal
   padding: on the left a segment of the rail in its signal colour, the row's
   full height, then the step's digit — the one large thing in the row, 120 px,
   single digits, so they never echo the section index — and beside it the
   step's name with its sentence directly under it, the name's caps on the
   digit's top. On reveal the rail fills once from the top, each digit lighting
   as the fill reaches its row; then a pulse keeps travelling down it. */
.pipeline { position: relative; display: grid; grid-auto-rows: 1fr; }
.stage {
  --seg: 6px;
  --num-pad: clamp(14px, 1.8vw, 28px);
  position: relative;
  display: grid;
  grid-template-columns: var(--seg) auto minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  align-items: start;
  padding: clamp(18px, 2vw, 28px) 0;
}
.stage-detect { --stop: var(--detect); }
.stage-jam { --stop: var(--jam); }
.stage-verify { --stop: var(--verify); }
.stage::before, .stage::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--seg); }
.stage::before { background: var(--rule); }
.stage::after { background: var(--stop); }
.js .steps .stage::after { transform: scaleY(0); transform-origin: top; }
/* Both trimmed to their caps, so the name's top meets the digit's. */
.stage-num, .stage h3 { text-box: trim-both cap alphabetic; }
.stage-num {
  grid-column: 2;
  grid-row: 1 / span 2;
  padding: 0 var(--num-pad);
  font-family: var(--f-display);
  font-stretch: 88%;
  font-weight: 900;
  font-size: clamp(64px, 8.4vw, 120px);
  line-height: .8;
  font-variant-numeric: tabular-nums;
  color: var(--stop);
}
.js .steps .stage-num { color: var(--ink-3); transition: color .15s linear; }
.stage h3 { grid-column: 3; grid-row: 1; font-size: clamp(22px, 2.2vw, 32px); }
.stage p { grid-column: 3; grid-row: 2; padding-top: 10px; color: var(--ink-2); max-width: 34em; }
.pipeline::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 6px;
  height: 100%;
  /* A soft band at the middle of a rail-high strip: moving the strip by half its
     own height carries the band from the rail's top to its bottom, whatever the
     rail's height. */
  background: linear-gradient(180deg, transparent 42%, rgba(255, 255, 255, .7) 50%, transparent 58%);
  opacity: 0;
  pointer-events: none;
}
.steps.is-in .stage::after { animation: rail-fill .53s linear forwards; }
.steps.is-in .stage-num { color: var(--stop); }
.steps.is-in .stage-detect::after, .steps.is-in .stage-detect .stage-num { animation-delay: .2s; transition-delay: .2s; }
.steps.is-in .stage-jam::after, .steps.is-in .stage-jam .stage-num { animation-delay: .73s; transition-delay: .73s; }
.steps.is-in .stage-verify::after, .steps.is-in .stage-verify .stage-num { animation-delay: 1.26s; transition-delay: 1.26s; }
.steps.is-in .pipeline::after { animation: rail-pulse 2.8s ease-in-out 2.1s 3; }
@keyframes rail-fill { to { transform: scaleY(1); } }
@keyframes rail-pulse {
  0% { transform: translateY(-50%); opacity: 0; }
  15%, 85% { opacity: .8; }
  100% { transform: translateY(50%); opacity: 0; }
}

/* ── Devices: two photographs side by side on the page's two columns, each a
   classified frame (the treatment above) — the same frame as the Kamerton
   screenshots, KRYK's on x = 756 — with its spec sheet under it. A photograph is
   never shown larger than its own pixels (840 × 528; a column holds at most
   708) and never cropped. The name is a tab of the page's ground that runs from
   the column's edge into the photograph's lower left corner; the role is the
   frame's stamp, with its signal colour as a square. ────────────────────────*/
.device-grid {
  display: grid;
  gap: clamp(40px, 5vw, 56px) var(--col-gap);
  max-width: calc(var(--max) + 2 * var(--gutter));
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.device {
  --dev: var(--detect);
  display: flex;
  flex-direction: column;
}
.device-jam { --dev: var(--jam); }
.device-media img { width: 100%; max-width: 840px; aspect-ratio: 840 / 528; }
.device-name {
  position: absolute;
  z-index: 4;
  left: 0;
  bottom: var(--cls-pad);
  padding: .12em .16em 0 0;
  background: var(--ground);
  font-size: clamp(44px, 6.4vw, 104px);
  line-height: .8;
  letter-spacing: -.01em;
  color: var(--ink);
}
/* The photographs ship blurred (the files themselves; the sharp ones never ship).
   On each, centred, the frame's action, «Unblur»; pressed, it gives way to the
   offer of a closed briefing on a plate of the ground. Names, roles, ticks and
   specs stay sharp around it. Without script a plain link to the form stands
   there. */
.dev-lock {
  position: absolute;
  z-index: 5;
  inset: var(--cls-pad);
  display: grid;
  place-items: center;
  padding: 12px var(--gutter);
  pointer-events: none;
}
/* One cell: the action and the offer that replaces it both stand at the centre. */
.dev-lock > * { grid-area: 1 / 1; pointer-events: auto; }
.dev-lock-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  font-weight: 600;
  color: var(--ink);
  background: var(--ground);
  text-decoration: underline;
  text-underline-offset: 5px;
}
.js .dev-lock-link, html:not(.js) .dev-unblur { display: none; }
.dev-lock-body {
  display: grid;
  justify-items: start;
  gap: 14px;
  max-width: 26em;
  padding: 18px 22px 20px;
  color: var(--ink);
  background: var(--ground);
  font-size: 17px;
  line-height: 1.4;
}
.dev-lock-body[hidden] { display: none; }
@media (max-width: 559px) {
  .dev-lock { padding: 8px 12px; }
  .dev-lock-body { gap: 10px; padding: 12px 14px 14px; font-size: 15px; }
  .dev-lock-cta { height: 44px; padding: 0 14px; font-size: 14px; white-space: normal; text-align: left; }
}

/* The role is the frame's stamp; its square keeps the signal colour. */
.device-role::before { content: ""; width: 9px; height: 9px; background: var(--dev); }
.device-specs { padding: 12px 0 0; }
.device-specs > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 14px;
  padding: 14px 0;
}
.device-specs dt { grid-column: 1; grid-row: 1; color: var(--ink-2); }
.device-specs dd {
  grid-column: 2;
  grid-row: 1;
  font-size: 19px;
  font-weight: 600;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap;
}
@media (min-width: 760px) {
  .device-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The jamming specs every jammer shares stand under KRYK: the band as one more
   row, the types as a block of their own under their label. */
.device-specs .device-spec-types { grid-template-columns: minmax(0, 1fr); row-gap: 10px; }
.device-spec-types dd { grid-column: 1; grid-row: 2; font-size: 17px; font-weight: 400; line-height: 1.5; white-space: normal; }
.chips { display: flex; flex-wrap: wrap; row-gap: 4px; }
.chips li { color: var(--ink); }
.chips li:not(:last-child)::after { content: "/"; margin: 0 .6em; color: var(--ink-3); font-weight: 400; }
.chips .chip-accent { color: var(--jam); font-weight: 600; }
.spec-note { margin-top: 10px; font-size: var(--t-small); line-height: 1.5; color: var(--ink-3); max-width: 60em; }

/* ── Use cases: four scenarios, two by two on the page's columns — each name a
   caps label with its sentence under it; no rules. The wall above is the
   section's one shout. ────────────────────────────────────────────────────*/
.cases { display: grid; gap: clamp(28px, 3vw, 44px) var(--col-gap); }
.case {
  --tick: var(--ink);
  position: relative;
  display: grid;
  align-content: start;
  gap: 8px;
  padding: 14px 0;
}
/* One shout per screen: the names are labels, not headings that compete with
   the wall — Inter 600 in caps. */
.case h3, .cap h3 {
  font-family: var(--f-text);
  font-stretch: 100%;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: .02em;
}
.case h3 { font-size: clamp(20px, 1.7vw, 24px); }
.case p { font-size: 16px; line-height: 1.5; color: var(--ink-2); max-width: 30em; }
.case::before, .cap::after {
  --tl: 14px;
  --tw: 2px;
  inset: -6px -18px;
  opacity: 0;
  transition: inset .2s var(--snap), opacity .1s linear;
}
/* site.js marks the row crossing the middle of the window: the brackets follow the
   reading position, work on touch and never suggest that a row is clickable. */
/* The brackets stand clear of the words: 10 px beside the column edge (inside the
   16 px phone gutter) and in the row's 14 px of padding above and below. */
.case.is-aimed::before, .cap.is-aimed::after { inset: 0 -10px; opacity: 1; }
@media (min-width: 900px) {
  .cases { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The two section CTAs are quiet: a ruled row across the column with the action in
   display caps and the accent arrow. The green slab stays for the hero, the header,
   the sticky bar and the form. */
.section-cta { margin-top: clamp(32px, 4vw, 56px); }
.cta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: clamp(18px, 2vw, 28px) 0;
  border-top: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule-strong);
  font-family: var(--f-display);
  font-weight: 900;
  font-stretch: 88%;
  font-size: clamp(26px, 3.6vw, 52px);
  line-height: .95;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--ink);
}
.cta-row .icon { width: clamp(28px, 3vw, 44px); height: clamp(28px, 3vw, 44px); color: var(--lp-accent); stroke-width: 2.5; transition: transform .2s var(--snap); }
.cta-row:hover { border-color: var(--ink); }
.cta-row:hover .icon { transform: translateX(8px); }

/* ── Video: full width under its heading, a classified frame until it plays ──
   The poster is the video's opening frame, blurred in the file; nothing of the
   video loads before it is started (preload="none"), so no sharp frame is ever
   painted over it. Until the first play the frame's action, ▶ «Відео з поля»,
   stands on the poster (site.js); once playing the scanlines lift and the video
   runs clear under its native controls, in the accent, and a pause keeps it so. */
.video-frame video { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: #000; accent-color: var(--lp-accent); }
.video-play .icon { width: 20px; height: 20px; }

/* ── Kamerton OS: two screenshots at their own scale, classified ───────────
   Supporting evidence under the head, not a stage: side by side from 900 px, one
   under the other below that, each at most 860 CSS px wide — half its 1720 px, so
   a 2× screen draws it pixel for pixel — never cropped, never stretched past it.
   At rest each is blurred behind scanlines, its file tag on the frame's top arm.
   classified.js opens it: a lens of 30 % of the image area that follows the
   pointer, a dragged finger or the arrow keys (?reveal=lens, the default), or a
   «Declassify» slab held until its brackets lock (?reveal=confirm). The blur is a
   presentation effect, not protection: the whole image is downloaded. */
.screens {
  display: grid;
  gap: clamp(48px, 6vw, 72px);
  margin-bottom: clamp(40px, 5vw, 72px);
}
@media (min-width: 900px) {
  /* Side by side the two figures share their rows (subgrid), so a caption that
     wraps to two lines never pushes its sentence below the other's. */
  .screens { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto auto auto; column-gap: var(--col-gap); row-gap: 0; }
  .screen { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
  .screen figcaption { grid-row: span 2; grid-template-rows: subgrid; row-gap: 10px; }
}
.classified { max-width: calc(640px + 2 * var(--cls-pad)); }
/* The screenshots are blurred by the stylesheet, clipped at the image's own edge;
   the night field behind that edge keeps the blurred border from fading into a
   daylight ground. */
.classified > picture img {
  width: 100%;
  filter: blur(var(--cls-blur));
  clip-path: inset(0);
  transition: filter .45s var(--ease);
}

/* ?reveal=lens: a sharp copy clipped to the lens, a sight's four corners around
   it. --lx/--ly (the lens centre) and --ls (its side, as a fraction of the
   image's width and height) come from classified.js. */
.classified[data-reveal="lens"] {
  cursor: crosshair;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.lens-sharp {
  position: absolute;
  inset: var(--cls-pad);
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  clip-path: inset(
    calc((var(--ly) - var(--ls) / 2) * 100%) calc((1 - var(--lx) - var(--ls) / 2) * 100%)
    calc((1 - var(--ly) - var(--ls) / 2) * 100%) calc((var(--lx) - var(--ls) / 2) * 100%));
  transition: opacity .12s linear;
}
.lens-sharp img { width: 100%; height: 100%; }
.lens-frame, .declassify-lock {
  --arm: clamp(12px, 1.4vw, 20px);
  position: absolute;
  pointer-events: none;
  border: 2px solid var(--on-photo);
  -webkit-mask: conic-gradient(at var(--arm) var(--arm), #0000 75%, #000 0) 0 0 / calc(100% - var(--arm)) calc(100% - var(--arm));
  mask: conic-gradient(at var(--arm) var(--arm), #0000 75%, #000 0) 0 0 / calc(100% - var(--arm)) calc(100% - var(--arm));
}
.lens-frame {
  z-index: 5;
  left: var(--cls-pad);
  top: var(--cls-pad);
  width: calc((100% - 2 * var(--cls-pad)) * var(--ls));
  height: calc((100% - 2 * var(--cls-pad)) * var(--ls));
  transform: translate(calc((var(--lx) / var(--ls) - .5) * 100%), calc((var(--ly) / var(--ls) - .5) * 100%));
  opacity: 0;
  transition: opacity .12s linear, border-color .12s linear;
}
.classified.is-lens-open .lens-sharp { opacity: 1; }
.classified.is-lens-open .lens-frame { opacity: 1; }

/* ?reveal=confirm: the frame's action stands on the blurred image. Holding it (--p
   from 0 to 1) fills a rule along its foot and closes the brackets on it; when
   they lock they turn green and the screenshot sharpens. */
.declassify {
  flex-direction: column;
  gap: 6px;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.declassify-hint {
  font-family: var(--f-text);
  font-stretch: normal;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0;
  text-transform: none;
}
.declassify::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: currentColor;
  transform: scaleX(var(--p, 0));
  transform-origin: left;
}
.declassify-lock {
  --arm: 14px;
  inset: calc(-8px - (1 - var(--p, 0)) * clamp(12px, 1.6vw, 22px));
  opacity: calc(.45 + var(--p, 0) * .55);
}
.classified.is-locked .declassify-lock { border-color: var(--fill); opacity: 1; }
.classified.is-declassified > picture img { filter: none; }
.classified.is-declassified::after { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .classified > picture img, .lens-sharp, .lens-frame { transition: none; }
}

.screen figcaption { display: grid; gap: 10px; padding-top: clamp(16px, 1.8vw, 24px); }
.screen strong {
  font-family: var(--f-display);
  font-stretch: 88%;
  font-weight: 900;
  font-size: clamp(22px, 2vw, 30px);
  line-height: 1;
  letter-spacing: -.005em;
  text-transform: uppercase;
  color: var(--ink);
  text-wrap: balance;
}
.screen figcaption span { color: var(--ink-2); max-width: 34em; }

/* Capability index: the nine set dense in the page's two columns, five and four,
   reading down each; the name over its sentence, no numbers (the section index
   owns them) and no rules. */
.caps { display: grid; column-gap: var(--col-gap); }
.cap {
  --tick: var(--ink);
  position: relative;
  display: grid;
  align-content: start;
  gap: 6px;
  padding: 14px 0;
}
.cap h3 { font-size: 20px; }
.cap p { font-size: 15px; line-height: 1.5; color: var(--ink-2); max-width: 40em; }
@media (min-width: 900px) {
  .caps { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(5, auto); grid-auto-flow: column; }
}

/* ── Traction: the statement as a wall, then the evidence — the field
   photograph with its caption, and beside it on the second column the two
   curves side by side, each one line in its product colour (amber jamming, cyan
   video) with its three measured points over the year of field tests
   (09.2025–09.2026, the page's own dates) on a scale up to its target, drawn
   dashed with its note on it; then the facts under them, the contract twice as
   large as the funding and the months of tests. ────────────────────────────*/
.charts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto auto auto; column-gap: clamp(24px, 3vw, 40px); }
/* The figures share their rows, so a title that wraps never lowers one plot. */
.chart { --chart: var(--detect); display: grid; grid-row: span 3; grid-template-rows: subgrid; }
.chart-jam { --chart: var(--jam); }
.chart figcaption { align-self: end; color: var(--ink); font-weight: 600; }
/* The plot: the first point on the caption's line, the last on the column's
   edge, the target on its top edge, the baseline on its foot. The points and
   their values are laid out in percent of the plot, the line is drawn to the
   same scale (non-scaling stroke), so the plot takes any size. */
.chart-plot {
  position: relative;
  height: clamp(200px, 21vw, 300px);
  margin: 18px 0 10px;
  border-bottom: 1.5px solid var(--rule-strong);
}
/* A target the curve has passed stands lower (--target from the top); its note
   stands above the dashes, on the left, clear of the curve. */
.chart-video .chart-target { top: auto; bottom: calc(100% - var(--target, 0%)); padding: 0 45% 6px 0;
  border-top: 0; border-bottom: 2px dashed var(--chart); }
.chart-target {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  padding-top: 8px;
  border-top: 2px dashed var(--chart);
  font-size: 13px;
  line-height: 1.3;
  text-align: right;
  color: var(--ink-2);
}
.chart-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.chart-line { fill: none; stroke: var(--chart); stroke-width: 4; stroke-linejoin: miter; stroke-linecap: square; vector-effect: non-scaling-stroke; }
.chart-pt {
  position: absolute;
  left: calc(var(--x) * 1%);
  top: calc(var(--y) * 1%);
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--chart);
}
.chart-pt b {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--f-display);
  font-stretch: 88%;
  font-size: clamp(18px, 1.8vw, 26px);
  font-weight: 900;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
  /* A value knocks the curve out behind it, so a rising line never runs through it. */
  padding: 0 3px;
  background: var(--ground);
}
.chart-pt:first-of-type b { left: 5px; transform: none; }
.chart-pt:last-of-type b { left: auto; right: 5px; transform: none; }
.js .chart .chart-svg { transform: scaleX(0); transform-origin: left center; }
.js .chart .chart-pt { opacity: 0; transition: opacity .5s ease; }
.chart.is-in .chart-svg { animation: chart-draw 1.4s var(--ease) .2s forwards; }
.chart.is-in .chart-pt { opacity: 1; transition-delay: 1.2s; }
@keyframes chart-draw { to { transform: scaleX(1); } }
/* The dates are the quietest line of the figure: under its 16 px title. */
.chart-axis { display: flex; justify-content: space-between; font-size: 13px; line-height: 1.4; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* The curves and the facts come first in reading order (a phone shows them
   before the photograph); from 900 px the photograph takes the first column. */
.traction-evidence { display: grid; gap: clamp(40px, 5vw, 64px) var(--col-gap); align-items: start; }
.traction-side { display: grid; gap: clamp(32px, 4vw, 48px); }
.field-card { position: relative; max-width: 560px; }
.field-card picture { position: relative; overflow: hidden; }
.field-card img { width: 100%; aspect-ratio: 1050 / 1400; }
.field-card figcaption { padding-top: 16px; }
.field-title {
  font-family: var(--f-display);
  font-stretch: 88%;
  font-weight: 900;
  font-size: clamp(24px, 2.4vw, 34px);
  line-height: .95;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--ink);
}
.field-caption { margin-top: 8px; font-size: 16px; line-height: 1.5; color: var(--ink-2); max-width: 30em; }

.proof-grid { display: grid; }
.proof { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 16px; row-gap: 6px; min-width: 0; padding: 14px 0; }
.proof strong {
  font-family: var(--f-display);
  font-stretch: 88%;
  font-weight: 900;
  font-size: clamp(30px, 3.4vw, 52px);
  line-height: .9;
  letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}
.proof span { color: var(--ink-2); max-width: 26em; }
.proof-lead { flex-direction: column; align-items: flex-start; padding: 0 0 20px; }
.proof-lead strong { font-size: clamp(40px, 6.8vw, 104px); white-space: normal; }
.proof-lead span { font-size: var(--t-lead); color: var(--ink); }
@media (min-width: 900px) {
  .traction-evidence { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .field-card { grid-column: 1; grid-row: 1; }
  .traction-side { grid-column: 2; grid-row: 1; }
}

/* ── Contact: the statement as a second wall — the rhyme of the problem's —
   then one centred column: the ask in a sentence, the form on a panel, and under
   it the guarantees, one to a line, and the address. Every field is a box on the
   page's ground with its label over it, so where to write is plain at a glance;
   the box turns ink on focus. The action spans the form. Every briefing link on
   the page lands here with the form in view and the name field focused. */
.contact-layout {
  display: grid;
  justify-items: center;
  gap: clamp(28px, 4vw, 40px);
  max-width: 640px;
  margin-inline: auto;
}
.contact-lead { margin-top: 0; text-align: center; max-width: 34em; }
.contact-more { display: grid; justify-items: center; gap: 20px; text-align: center; }
.trust-list { display: grid; justify-items: center; gap: 6px; color: var(--ink-2); font-size: 15px; font-weight: 500; line-height: 1.4; }
.direct-mail {
  display: inline-block;
  color: var(--ink);
  font-size: clamp(15px, 4.6vw, 20px);
  font-weight: 600;
  overflow-wrap: anywhere;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 7px;
}
.direct-mail:hover { text-decoration-color: currentColor; }

.contact-form {
  --field-rule: var(--ink-3);
  position: relative;
  width: 100%;
  display: grid;
  gap: 22px;
  align-content: start;
  padding: clamp(20px, 4vw, 36px);
  background: var(--panel);
  border: 1px solid var(--rule-strong);
  /* The devices' briefing link scrolls the form to just under the header. */
  scroll-margin-top: calc(var(--header) + 24px);
}
[data-theme="light"] .contact-form { --field-rule: #868e98; }   /* 3.1:1 on the page */
.field { display: grid; gap: 8px; }
.field label { color: var(--ink); font-size: 15px; font-weight: 600; }
.field input, .field textarea {
  width: 100%;
  min-height: 52px;
  padding: 13px 16px;
  font: inherit;
  font-size: 17px;
  color: var(--ink);
  background: var(--ground);
  border: 1px solid var(--field-rule);
  border-radius: 0;
  box-shadow: 0 0 0 0 var(--ink);
  transition: box-shadow .12s linear, border-color .12s linear;
}
.field textarea { resize: vertical; min-height: 120px; }
/* A focused field scrolls clear of the header and the sticky briefing bar. */
.field input, .field textarea { scroll-margin: calc(var(--header) + 16px) 0 calc(var(--sticky) + 24px); }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-3); opacity: 1; }
/* On a phone the longest placeholder («Бригада / Підрозділ / Організація») is set
   smaller so it fits the field whole. */
@media (max-width: 479px) {
  .field input::placeholder, .field textarea::placeholder { font-size: 14px; }
}
@media (max-width: 359px) {
  .field input::placeholder, .field textarea::placeholder { font-size: 13px; }
}
.field input:hover, .field textarea:hover { border-color: var(--ink-2); }
/* Focus: the box turns ink and its edge thickens to 3 px (the shadow draws the
   extra 2 px round it). */
.field input:focus, .field textarea:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 2px var(--ink); }
.field input:disabled, .field textarea:disabled { color: var(--ink-3); border-color: var(--rule-strong); border-style: dashed; cursor: progress; }
.field input[aria-invalid="true"] { border-color: var(--loss); }
.field input[aria-invalid="true"]:focus { box-shadow: 0 0 0 2px var(--loss); }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
/* The action across the form and, right under it, how soon the reply comes. */
.form-send { display: grid; gap: 10px; margin-top: 6px; }
.form-send .btn { width: 100%; justify-content: center; }

.form-status { min-height: 1.5em; font-size: 15px; line-height: 1.5; font-weight: 500; color: var(--ink-2); }
/* Empty, the live region takes no room (it stays in the tree, so it is announced). */
.form-status:empty { min-height: 0; margin-top: -22px; }
.form-status.is-ok, .form-status.is-error { padding: 12px 16px; border-left: 3px solid; }
.form-status.is-ok { color: var(--ink); border-color: var(--verify); }
.form-status.is-error { color: var(--loss); border-color: var(--loss); }
.form-micro { font-size: var(--t-small); line-height: 1.5; color: var(--ink-3); text-align: center; }

/* ── Footer: the wordmark as a wall — its view box cropped to the letters, so it
   runs exactly from one margin of the page column to the other. ──────────*/
.site-footer { padding: 24px 0 calc(40px + var(--sticky)); border-top: 1px solid var(--rule); }
.footer-inner { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px 32px; align-items: baseline; }
.footer-tag, .footer-copy { font-size: var(--t-small); line-height: 1.5; color: var(--ink-3); }
.footer-tag { grid-row: 1; grid-column: 1; max-width: 36em; }
.footer-copy { grid-row: 1; grid-column: 2; }
.brand-footer { grid-row: 2; grid-column: 1 / -1; display: block; margin-top: clamp(32px, 5vw, 72px); }
.brand-footer .brand-mark { display: none; }
.brand-footer .wordmark { width: 100%; height: auto; }

/* ── Sticky briefing bar ──────────────────────────────────────────────────*/
.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  justify-content: flex-end;
  padding: 10px max(var(--gutter), (100% - var(--max)) / 2) calc(10px + env(safe-area-inset-bottom));
  background: var(--ground);
  border-top: 1px solid var(--rule);
  transform: translateY(100%);
  opacity: 0;
  /* Hidden also from the tab order and assistive technology, not only moved offscreen. */
  visibility: hidden;
  transition: transform .25s var(--ease), opacity .25s linear, visibility 0s linear .25s;
}
.sticky-cta .btn { height: 50px; }
.sticky-cta.visible { transform: translateY(0); opacity: 1; visibility: visible; transition-delay: 0s; }
@media (prefers-reduced-motion: reduce) {
  .sticky-cta { transform: none; transition: opacity .15s linear, visibility 0s linear .15s; }
}

/* ── Motion ───────────────────────────────────────────────────────────────*/
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.js .reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .hero-scroll, .hero-scroll-line { animation: none; opacity: 1; transform: none; }
  .timing-bar span { animation: none !important; transform: none !important; }
  .js .steps .stage::after { animation: none; transform: none; }
  .pipeline::after { display: none; }
  .js .chart .chart-svg { transform: none; animation: none; }
  .js .chart .chart-pt { opacity: 1; transition: none; }
  .js .blind .axis-gap { transform: none; transition: none; }
  .js .steps .stage-num { transition: none; }
  .device-media picture::after, .screen picture::after, .field-card picture::after { animation: none !important; display: none; }
  .case::before, .cap::after, .site-nav a::before, .btn .icon { transition: none; }
  .typed-caret, .typed-caret.is-done { animation: none; visibility: hidden; }
  /* No acquisition: the model appears once built. */
  .js .hero-visual.is-acquiring:not(.no-model) ~ .acq { display: none; }
}

/* ── Responsive ───────────────────────────────────────────────────────────*/
@media (max-width: 1180px) {
  .header-inner { gap: 20px; }
  .site-nav a { padding: 10px 8px; }
}

@media (max-width: 1079px) {
  /* Touch-sized header controls: 44 px tall hit areas, the glyphs unchanged. */
  .brand { min-width: 44px; min-height: 44px; }
  .segmented { height: 46px; }
  .icon-btn { width: 44px; height: 44px; }
  .js .nav-toggle { display: inline-grid; }
  .header-cta { display: none; }
  /* Without script the menu cannot open, so it stays in the header as a scrollable row. */
  html:not(.js) .site-header { height: auto; }
  html:not(.js) .header-inner { flex-wrap: wrap; padding-block: 8px; }
  html:not(.js) .site-nav { order: 3; width: 100%; overflow-x: auto; }
  .js .site-nav {
    position: fixed;
    top: var(--header);
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    padding: 8px var(--gutter) 20px;
    background: var(--ground);
    border-bottom: 1px solid var(--rule);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity .15s linear, transform .15s var(--ease), visibility 0s linear .15s;
  }
  .js .site-nav a { padding: 16px 4px; font-size: 17px; border-bottom: 1px solid var(--rule); }
  .js .site-nav a:last-child { border-bottom: 0; }
  .js .site-nav a::before { inset: 6px -2px; }
  .nav-open .site-nav { opacity: 1; visibility: visible; transform: none; transition: opacity .15s linear, transform .15s var(--ease); }
}

@media (max-width: 767px) {
  .sticky-cta .btn { width: 100%; height: 48px; }
  .footer-inner { grid-template-columns: minmax(0, 1fr); }
  .footer-copy { grid-row: 2; grid-column: 1; }
  .brand-footer { grid-row: 3; }
}

@media (max-width: 359px) {
  .brand .wordmark { display: none; }
  .brand-footer .wordmark { display: block; }
  .btn { padding: 0 14px; font-size: 15px; gap: 10px; }
  .btn-sm { padding: 0 12px; }
  .header-tools { gap: 6px; }
}
