/* ---- tokens.css ---- */
/* GENERATED by scripts/build-tokens.mjs from design/tokens.json — do not edit by hand. */
/* Modes: Desktop = default; 768 = @media (max-width: 1023.98px); 375 = @media (max-width: 639.98px) (cascade order) */

:root {
  --font-sans: "Fira Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;

  --color-bg-page: #060b18;
  --color-bg-page-top: #000713;
  --color-bg-page-bottom: #002057;
  --color-bg-footer: #001233;
  --color-bg-card: #0d1626;
  --color-bg-surface: #20263a;
  --color-bg-input: #1d212e;
  --color-bg-disabled: #2a3145;
  --color-bg-scrim: #070e1cd1;
  --color-bg-veil: #060b18ed;
  --color-accent: #e8b02d;
  --color-accent-hover: #f2c55e;
  --color-accent-pressed: #c8961f;
  --color-accent-on: #000000;
  --color-text-primary: #ffffff;
  --color-text-secondary: #d7dee4;
  --color-text-body: #c6d0dc;
  --color-text-muted: #a8b0be;
  --color-text-label: #8c97a8;
  --color-text-field-label: #7e8b9c;
  --color-text-disabled: #7c8698;
  --color-text-separator: #6e7a8f;
  --color-icon-default: #c6d0dc;
  --color-border-subtle: #6e7a8f;
  --color-border-card: #ffffff14;
  --color-rule-hairline: #ffffff1f;
  --color-rule-mark: #ffffff38;
  --color-link: #64a4e2;
  --color-link-hover: #8fbdea;
  --color-danger: #ff6b5c;
  --color-focus-ring: #ffffff;
  --color-focus-gap: #000000;
  --space-container-margin: 80px;
  --space-header-margin: 40px;
  --space-gutter: 24px;
  --space-section-top: 48px;
  --space-section-bottom: 72px;
  --space-heading-gap: 32px;
  --space-card-padding: 28px;
  --space-hero-top: 254px;
  --space-metric-padding-x: 32px;
  --space-metric-padding-y: 34px;
  --size-value-title-min: 52px;
  --size-step-heading-min: 71px;
  --radius-card: 12px;
  --radius-card-lg: 20px;
  --radius-control: 10px;
  --radius-panel: 10px;
  --radius-check: 4px;
  --stroke-hairline: 1px;
  --stroke-control: 1.5px;
  --stroke-icon: 1.75px;
  --focus-width: 2px;
  --focus-offset-tight: 2px;
  --focus-offset-loose: 4px;

  --gradient-card-scrim: linear-gradient(180deg, #0a0f2400 0%, #0a0f249e 30%, #0a0f24e6 60%, #0a0f24f7 100%);
  --gradient-wordmark: linear-gradient(180deg, #d9d9d9cc 0.05em, #a3a7b0b8 0.174em, #6d7686a3 0.299em, #36445d8f 0.423em, #0012337a 0.547em);
  --gradient-hero-veil-x: linear-gradient(90deg, #060b18f2 0%, #060b18c7 45%, #060b1873 100%);
  --gradient-hero-veil-y: linear-gradient(180deg, #060b182e 0%, #060b184d 34%, #060b18e0 58%, #060b18 78%, var(--color-bg-page-top) 100%);
  --gradient-page: linear-gradient(180deg, var(--color-bg-page-top) 47.383%, var(--color-bg-page-bottom) 100%);

  --decor-contact-plate-1: #01308696;
  --decor-contact-plate-2: #3552873d;
  --decor-contact-plate-3: #16356ecc;

  --text-display-h1: 700 64px/77px var(--font-sans);
  --text-display-wordmark: 700 250px/325px var(--font-mono);
  --tracking-display-wordmark: 0.1em;
  --text-heading-section: 700 40px/52px var(--font-mono);
  --text-heading-panel: 700 40px/48px var(--font-sans);
  --text-heading-page: 500 48px/56px var(--font-sans);
  --text-heading-overlay: 500 25px/30px var(--font-sans);
  --text-body-lead: 400 25px/34px var(--font-sans);
  --text-body-paragraph: 400 20px/30px var(--font-sans);
  --text-body-highlight: 500 20px/24px var(--font-sans);
  --text-body-base: 400 18px/26px var(--font-sans);
  --text-body-form: 400 16px/22px var(--font-sans);
  --text-body-card: 400 15px/22px var(--font-sans);
  --text-body-card-strong: 500 15px/22px var(--font-sans);
  --text-body-small: 400 14px/20px var(--font-sans);
  --text-title-service: 700 20px/26px var(--font-mono);
  --text-title-value: 500 20px/26px var(--font-sans);
  --text-title-step: 500 18px/24px var(--font-sans);
  --text-title-form: 400 25px/30px var(--font-sans);
  --text-metric-value: 700 48px/62px var(--font-mono);
  --text-label-mono: 500 11px/14px var(--font-mono);
  --tracking-label-mono: 0.09em;
  --case-label-mono: uppercase;
  --text-label-mono-md: 500 13px/17px var(--font-mono);
  --text-nav-item: 400 20px/24px var(--font-sans);
  --text-action-text: 500 14px/18px var(--font-sans);
  --text-control-s: 500 16px/19px var(--font-sans);
  --text-control-m: 500 18px/22px var(--font-sans);
  --text-control-l: 500 20px/24px var(--font-sans);
  --text-control-xl: 400 25px/30px var(--font-sans);
  --text-field-upload: 500 16px/24px var(--font-sans);
  --text-field-error: 400 13px/18px var(--font-sans);
}

@media (max-width: 1023.98px) {
  :root {
    --space-container-margin: 40px;
    --space-gutter: 16px;
    --space-section-top: 32px;
    --space-section-bottom: 48px;
    --space-heading-gap: 24px;
    --space-card-padding: 20px;
    --size-step-heading-min: 0px;
  }
}

@media (max-width: 639.98px) {
  :root {
    --space-container-margin: 20px;
    --space-header-margin: 20px;
    --space-gutter: 12px;
    --space-section-top: 24px;
    --space-section-bottom: 36px;
    --space-heading-gap: 16px;
    --space-card-padding: 16px;
    --space-hero-top: 160px;
    --space-metric-padding-x: 16px;
    --space-metric-padding-y: 20px;
    --size-value-title-min: 0px;
    --text-display-h1: 700 40px/48px var(--font-sans);
    --text-display-wordmark: 700 65px/85px var(--font-mono);
    --text-heading-section: 700 25px/32px var(--font-mono);
    --text-heading-panel: 700 25px/32px var(--font-sans);
    --text-body-lead: 400 20px/30px var(--font-sans);
    --text-metric-value: 700 40px/52px var(--font-mono);
  }
}

/* ---- fonts.css ---- */
/* Self-hosted fonts (SIL OFL 1.1, files in /assets/fonts, licences alongside). No request ever goes to Google Fonts. */
@font-face { font-family: "Fira Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/fira-sans-latin-400-normal.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Fira Sans"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/fonts/fira-sans-latin-500-normal.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Fira Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/fira-sans-latin-700-normal.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/ibm-plex-mono-latin-700-normal.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

/* ---- base.css ---- */
/* Reset + page ground + global a11y helpers. Every colour comes from tokens.css (enforced by scripts/check-css.mjs). */
*, *::before, *::after { box-sizing: border-box; }
/* scrollbar-gutter keeps the layout still when the open menu locks the page scroll (body overflow hidden) on classic scrollbars. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scrollbar-gutter: stable; }
body {
  margin: 0;
  min-height: 100vh;
  /* The frames' canvas: color/bg/page-top flat down to 47.383 % of the page, then to color/bg/page-bottom — so the hero background,
     which ends in page-top at y 1420, meets it without a step. Wider than 1440 this is also the ground beside the canvas (D25). */
  background: var(--gradient-page);
  color: var(--color-text-body);
  font: var(--text-body-base);
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
h1, h2, h3, p, ul, address { margin: 0; }
ul { padding: 0; list-style: none; }
address { font-style: normal; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

/* Focus rings per «Блок N» 1352:591 (focus/width 2, offsets tight 2 / loose 4). Components refine the offset, radius and gap band. */
:focus-visible { outline: var(--focus-width) solid var(--color-focus-ring); outline-offset: var(--focus-offset-loose); }
/* Programmatic focus targets (the skip link's <main tabindex="-1">) are not tab stops and draw no ring. */
[tabindex="-1"]:focus-visible { outline: none; }

/* SkipLink 1326:61 — first element of <body> on every width, visible only on focus (Блок O 1508:640): color/bg/surface, radius/control,
   control/m in color/text/primary, 11 / 20 padding (44 high); ring at −4 (r14) without a gap band. Parked at the header margins. */
.skip-link {
  position: absolute; z-index: 100;
  inset-block-start: var(--space-header-margin); inset-inline-start: var(--space-header-margin);
  padding: 11px 20px; border-radius: var(--radius-control);
  background: var(--color-bg-surface); color: var(--color-text-primary); font: var(--text-control-m);
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }
.skip-link:focus-visible { outline-offset: calc(var(--focus-offset-loose) - var(--focus-width)); }

/* Icons from the sprite (partials/icons.njk): the stroke width comes from the using <svg>, the colour (currentColor) from a non-SVG
   ancestor, so forced-colors themes can recolour it (an SVG keeps a colour set on itself). Line icons 16 / 14 px use stroke/control,
   the 44 × 44 ValueCard icons stroke/icon (.icon--value; color/accent is set on .value-card__head). The sprite itself stays rendered
   at 0 × 0 so the flags' clipPaths resolve. */
.icon { flex: none; overflow: visible; stroke-width: var(--stroke-control); }
.icon--value { stroke-width: var(--stroke-icon); }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

@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; }
}

/* ---- layout.css ---- */
/* Layout contract per «Блок N» 1352:577: content 1280 + margins 80 on 1440; wider than 1440 the 1440 canvas stays centred and nothing runs
   the full window width (D25). Breakpoints per design/decisions.md D1 (768 mode ≤ 1023.98px, 375 mode ≤ 639.98px).
   Header rules live in sections/header.css (S3). */
.container {
  width: 100%;
  max-width: calc(1280px + 2 * var(--space-container-margin));
  margin-inline: auto;
  padding-inline: var(--space-container-margin);
}
.section { padding-top: var(--space-section-top); }
main { display: block; }
/* The sheet («поле листа», Блок N 1400:642) under the sections About … Contact (954:768 «Основная часть»): a 1 px margin line
   (color/border/card, as bound in 564:31 / 1127:1434 / 1099:909 / 1108:1169) running from the top of About to 66 px above the footer,
   at canvas x 40 · 20 · 8 (container margin − 40 / − 20 / − 12), and a 13 × 1 tick (color/rule/mark) at the top of every section at
   canvas x 34, only from 640 px. Both move with the 1440 canvas, not the window (D25), and paint above the sections' content
   (in Figma the line is the top sibling over the sections, e.g. over the About photo). Every section also opens with a 1 px divider
   (color/rule/hairline, «разделитель секции» 564:32 … 564:42 and their twins on 1200 / 768 / 375) across the container — 1280 on 1440,
   window − 2 × space/container-margin below that; below 640 the tick is hidden because x 34 falls inside the divider (20 … 355).
   The ::before and ::after of a section inside .sheet are reserved for its tick and divider; sections decorate with inner elements.
   The divider is a border, so forced-colors themes keep it (as the rules between the About parameters); the margin line and the ticks
   are ornament and drop out there. */
.sheet { position: relative; }
.sheet::before,
.sheet > section::before,
.sheet > section::after {
  content: "";
  position: absolute;
  pointer-events: none;
}
.sheet::before {
  z-index: 1;
  inset-block: 0 66px;
  inset-inline-start: calc(max(0px, (100% - 1440px) / 2) + 40px);
  width: var(--stroke-hairline);
  background-color: var(--color-border-card);
}
.sheet > section { position: relative; }
.sheet > section::before {
  z-index: 1;
  inset-block-start: 0;
  inset-inline-start: calc(max(0px, (100% - 1440px) / 2) + 34px);
  width: 13px;
  height: var(--stroke-hairline);
  background-color: var(--color-rule-mark);
}
.sheet > section::after {
  inset-block-start: 0;
  inset-inline: max(var(--space-container-margin), (100% - 1280px) / 2);
  border-block-start: var(--stroke-hairline) solid var(--color-rule-hairline);
}
@media (max-width: 1023.98px) {
  .sheet::before { inset-inline-start: 20px; }
}
@media (max-width: 639.98px) {
  .sheet::before { inset-inline-start: 8px; }
  .sheet > section::before { display: none; }
}

/* ---- components/button.css ---- */
/* Button 610:55 — <button> or <a class="btn">. Variant primary / secondary × Size S 39 / M 46 / L 48 / XL 54 × State.
   Paddings are the master's (unbound in Figma). The secondary stroke is drawn INSIDE (inset box-shadow), so it never changes the size.
   Focus (Блок N 1352:591): ring focus/ring at −4 (r14) over a focus/gap band at −2 (r12) — outline + box-shadow around radius/control.
   Arrow: the master's Arrow property is an icon/arrow-right instance, 16 × 16 in currentColor (D30): <svg class="icon"><use href="#icon-arrow-right"></use></svg>.
   Loading (primary L only, 174 → 243 px, the form's send button): .is-loading + aria-disabled, set by js/form.js while sending — the
   label changes, the fill stays color/accent with no hover / pressed reaction, and the button stays focusable (a second send is ignored). */
/* The transparent inner outline is invisible here; forced-colors themes, which drop box-shadows and backgrounds, draw it as the
   button's frame (focus-visible replaces it with the ring). */
.btn {
  --btn-stroke: 0 0 0 0 transparent;
  outline: var(--stroke-control) solid transparent;
  outline-offset: calc(-1 * var(--stroke-control));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border-radius: var(--radius-control);
  box-shadow: var(--btn-stroke);
  white-space: nowrap;
  text-align: center;
  cursor: pointer;
}
.btn--s { padding: 10px 20px; font: var(--text-control-s); }
.btn--m { padding: 12px 24px; font: var(--text-control-m); }
.btn--l { padding: 12px 24px; font: var(--text-control-l); }
.btn--xl { padding: 12px 20px; font: var(--text-control-xl); }
.btn--block { display: flex; width: 100%; }

.btn--primary { background-color: var(--color-accent); color: var(--color-accent-on); }
.btn--primary:hover { background-color: var(--color-accent-hover); }
.btn--primary:active { background-color: var(--color-accent-pressed); }

.btn--secondary { --btn-stroke: inset 0 0 0 var(--stroke-control) var(--color-accent); background-color: transparent; color: var(--color-accent); }
/* Hover / pressed stroke is 2 in the master and bound to no variable; stroke/control is kept until a token exists (PR open question). */
.btn--secondary:hover { --btn-stroke: inset 0 0 0 var(--stroke-control) var(--color-accent-hover); color: var(--color-accent-hover); }
.btn--secondary:active { --btn-stroke: inset 0 0 0 var(--stroke-control) var(--color-accent-pressed); color: var(--color-accent-pressed); }

.btn:disabled,
.btn[aria-disabled="true"] {
  --btn-stroke: inset 0 0 0 var(--stroke-control) var(--color-border-subtle);
  background-color: var(--color-bg-disabled);
  color: var(--color-text-disabled);
  cursor: not-allowed;
}
.btn--secondary:disabled,
.btn--secondary[aria-disabled="true"] { background-color: transparent; }
.btn--primary.is-loading,
.btn--primary.is-loading:hover,
.btn--primary.is-loading:active { --btn-stroke: 0 0 0 0 transparent; background-color: var(--color-accent); color: var(--color-accent-on); cursor: default; }

.btn:focus-visible {
  outline: var(--focus-width) solid var(--color-focus-ring);
  outline-offset: calc(var(--focus-offset-loose) - var(--focus-width));
  box-shadow: var(--btn-stroke), 0 0 0 calc(var(--focus-offset-loose) - var(--focus-width)) var(--color-focus-gap);
}

/* ---- components/link.css ---- */
/* Link 878:719 — <a>. Kind inline (in running text: color/link, hover color/link-hover) / standalone (.link--standalone: footer columns and
   contact lines, body/card, color/text/body → color/text/primary on hover, as wide as the word). The hover underline is a 1 px layer along the
   bottom of the text box (y 21 of 22), as in the master — a border, so forced-colors themes keep it (there it is always drawn). Focus ring per «Блок N» 1352:591: −6 horizontally, −5 vertically, r8, no gap band —
   drawn by ::before because an outline cannot have two offsets. */
.link {
  position: relative;
  display: inline-block;
  color: var(--color-link);
}
.link::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  border-block-end: var(--stroke-hairline) solid transparent;
}
.link:hover { color: var(--color-link-hover); }
.link:hover::after { border-block-end-color: currentColor; }

.link--standalone { font: var(--text-body-card); color: var(--color-text-body); }
.link--standalone:hover { color: var(--color-text-primary); }

.link:focus-visible { outline: none; }
.link:focus-visible::before {
  content: "";
  position: absolute;
  inset: -5px -6px;
  border: var(--focus-width) solid var(--color-focus-ring);
  border-radius: 8px;
}

/* ---- components/legal-links.css ---- */
/* Legal links (partials/legal-links.njk) — the footer's legal row (954:789 …) and the 404 strip (885:773 / 1219:1206 / 1219:4575): Link inline
   items 16 apart with «·» (color/text/muted) between them; below 640 a wrapping row, gap 16 / 8, without the dots (1508:638). The font
   (body/card) comes from the row. «Cookie settings» stays invisible, keeping its place, until js/cookie.js has wired it. */
.legal-links { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }
.legal-links li + li::before { content: "·"; content: "·" / ""; margin-inline-end: 16px; color: var(--color-text-muted); }
html:not(.cookie-ready) .legal-links__cookie { visibility: hidden; }

@media (max-width: 639.98px) {
  .legal-links { gap: 8px 16px; }
  .legal-links li + li::before { content: none; }
}

/* ---- components/nav.css ---- */
/* NavItem 850:539 — <a class="nav__link"><span class="nav__label">…</span></a> inside <nav>. 43 high: a 2 px track above the text
   ("you are here", color/accent when aria-current) and a 1 px track below it ("pointer here", color/text/primary on hover); nav/item 20/24
   in color/text/primary, 8 between. :active = hover. The tracks are real borders of the label that exist only in those states (padding
   compensates), so their ends stay square and forced-colors themes keep them. Touch target 44 high (::before, 0.5 px over each edge);
   in the mobile menu the whole 56 px row is the link (sections/header.css). Focus ring at −6 without a gap band, r10 (Блок N 1352:591):
   outline-offset focus/offset-loose on a box whose radius makes the outline's outer radius radius/control. */
.nav__link {
  position: relative;
  display: block;
  border-radius: calc(var(--radius-control) - var(--focus-offset-loose) - var(--focus-width));
  font: var(--text-nav-item);
  color: var(--color-text-primary);
  white-space: nowrap;
}
.nav__link::before { content: ""; position: absolute; inset: -0.5px 0; }
.nav__label { display: block; padding-block: 10px 9px; }
.nav__link[aria-current="page"] .nav__label,
.nav__link[aria-current="location"] .nav__label { padding-block-start: 8px; border-block-start: 2px solid var(--color-accent); }
.nav__link:hover .nav__label { padding-block-end: 8px; border-block-end: var(--stroke-hairline) solid var(--color-text-primary); }
.nav__link:focus-visible { outline: var(--focus-width) solid var(--color-focus-ring); outline-offset: var(--focus-offset-loose); }

/* ---- components/lang-switch.css ---- */
/* LangSwitch 851:555 / LangItem 850:572 — <nav class="lang"> with two <a hreflang lang aria-label="English|Deutsch"><span class="lang__label">EN</span></a>;
   the current one has aria-current="true": color/accent text over a 2 px color/accent track; others color/text/muted; hover draws the 1 px
   color/text/primary track under the text. Tracks are borders of the label that exist only in those states (padding compensates, 39 high),
   so forced-colors themes keep the current marker. 70 wide for two locales: chip · 6 · «|» 9 · 6 · chip; the «|» is a decorative
   pseudo-element hidden from assistive technology (Блок O 1508:640), centred like 851:544 (y 7.5). Touch target 44 × 44 around each
   25 × 39 chip (::before). Focus ring at −6 without a gap band, r10 (Блок N 1352:591). Locale names are locale data from locales.json
   (endonyms, the same on every page), not t/*.json strings. Rendered only when two locales are built (D3). */
.lang { display: flex; align-items: center; gap: 21px; }
.lang__item {
  position: relative;
  display: block;
  border-radius: calc(var(--radius-control) - var(--focus-offset-loose) - var(--focus-width));
  font: var(--text-nav-item);
  color: var(--color-text-muted);
}
.lang__label { display: block; padding-block: 8px 7px; }
.lang__item::before {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
.lang__item:not(:last-child)::after {
  content: "|";
  content: "|" / "";
  position: absolute;
  inset-block-start: 7.5px;
  inset-inline-start: calc(100% + 6px);
  width: 9px;
  text-align: center;
  color: var(--color-text-separator);
  pointer-events: none;
}
.lang__item[aria-current="true"] { color: var(--color-accent); }
.lang__item[aria-current="true"] .lang__label { padding-block-start: 6px; border-block-start: 2px solid var(--color-accent); }
.lang__item:hover .lang__label { padding-block-end: 6px; border-block-end: var(--stroke-hairline) solid var(--color-text-primary); }
.lang__item:focus-visible { outline: var(--focus-width) solid var(--color-focus-ring); outline-offset: var(--focus-offset-loose); }

/* ---- components/burger.css ---- */
/* Burger 1225:89 — <button class="burger" aria-expanded aria-controls="site-nav">, 44 × 44 touch target, three 24 × 2 bars at x 10,
   y 13 / 21 / 29. Open: the same bars form the cross (top and bottom turned ±45°, middle hidden) so the change animates layer by layer.
   Bars: color/text/primary, hover color/accent, pressed color/accent-pressed; drawn as borders in currentColor, so forced-colors themes keep
   them. Focus like Button: ring at −4 (r14) over a gap band at −2 (r12). Shown only in the 768 and 375 modes (sections/header.css) and only
   once js/menu.js has wired it (html.menu-ready) — before that, or without JS, it would be a dead control. */
.burger {
  position: relative;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-control);
  color: var(--color-text-primary);
}
.burger:hover { color: var(--color-accent); }
.burger:active { color: var(--color-accent-pressed); }
.burger:focus-visible {
  outline: var(--focus-width) solid var(--color-focus-ring);
  outline-offset: calc(var(--focus-offset-loose) - var(--focus-width));
  box-shadow: 0 0 0 calc(var(--focus-offset-loose) - var(--focus-width)) var(--color-focus-gap);
}
.burger__bar {
  position: absolute;
  inset-inline-start: 10px;
  width: 24px;
  height: 0;
  border-block-start: 2px solid;
  transition: transform 200ms ease, opacity 200ms ease;
}
.burger__bar:nth-child(1) { inset-block-start: 13px; }
.burger__bar:nth-child(2) { inset-block-start: 21px; }
.burger__bar:nth-child(3) { inset-block-start: 29px; }
.burger[aria-expanded="true"] .burger__bar:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__bar:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] .burger__bar:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }
html:not(.menu-ready) .burger { visibility: hidden; }

/* Touch screens keep :hover after a tap; the burger must return to its resting colour. */
@media (hover: none) {
  .burger:hover { color: var(--color-text-primary); }
}

/* ---- components/section-heading.css ---- */
/* SectionHeading 658:219 — <h2 class="section-heading">, heading/section (IBM Plex Mono 40/52; 25/32 below 640 through the 375 token mode),
   color/text/primary. The master has no width of its own: each section limits it on the instance (e.g. services HUG with max 420). */
.section-heading {
  font: var(--text-heading-section);
  color: var(--color-text-primary);
  overflow-wrap: break-word;
}

/* ---- components/metric.css ---- */
/* MetricCell 642:89 in the trust bar 539:30 — <dl class="metrics"> of <div class="metric"><dt>label</dt><dd>value</dd></div>; the value
   (metric/value, color/accent; 40/52 below 640 through the 375 token mode) is shown above the label (body/base, color/text/primary), 12 apart.
   Padding space/metric-padding-y / -x (34 / 32, below 640 20 / 16). The bar: color/bg/scrim with a color/rule/hairline line at the top and
   bottom drawn inside (as Figma's INSIDE strokes — they add no height). From 1024: four equal cells with 1 px color/rule/hairline dividers
   (1440 → 319.25, 1200 → 259.25); 640–1023 and below 640: a 2 × 2 grid, space/gutter apart, no dividers, cells without a minimum width,
   each row as tall as its tallest cell (Блок N 1508:638). */
.metrics {
  display: flex;
  margin: 0;
  background-color: var(--color-bg-scrim);
  box-shadow:
    inset 0 var(--stroke-hairline) 0 var(--color-rule-hairline),
    inset 0 calc(-1 * var(--stroke-hairline)) 0 var(--color-rule-hairline);
}
.metric {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding: var(--space-metric-padding-y) var(--space-metric-padding-x);
}
.metric + .metric { border-inline-start: var(--stroke-hairline) solid var(--color-rule-hairline); }
.metric__value { order: -1; margin: 0; font: var(--text-metric-value); color: var(--color-accent); }
.metric__label { font: var(--text-body-base); color: var(--color-text-primary); overflow-wrap: break-word; }

@media (max-width: 1023.98px) {
  .metrics { flex-wrap: wrap; gap: var(--space-gutter); }
  .metric { flex: 0 0 calc((100% - var(--space-gutter)) / 2); }
  .metric + .metric { border-inline-start: 0; }
}

/* ---- components/service-card.css ---- */
/* ServiceCard 529:29 — <article class="service-card"> with a stretched link in its <h3>: the link's ::after covers the card, so the whole
   card is one tab stop and one click target (the card clips the overlay); radius/card-lg, clips its content.
   Layers: the photo; the veil color/bg/veil (::after, z 1); the body with the text (z 2) and the scrim under the text (body ::before);
   the focus ring (::before, z 3, never takes the pointer).
   Default 529:27 (from 1024 with a hover-capable pointer): 446 high, content at the bottom — 24 at the sides, 21 below; title (title/service,
   color/text/primary) · 16 · «Client objective» (label/mono, color/accent) · 7 · task (body/small, color/text/body) · 37 · «Read more»
   (action/text, color/accent) + icon/arrow-right, 6 apart, decorative (D30). Scrim 529:14: --gradient-card-scrim, stops over the band's
   height. In the master the band is the bottom 232 and starts 53 above the title (1440); here it always starts 53 above the title and is
   never shorter than 232, so where the task wraps to three lines (about 1024–1365) the band grows with the text and the title and label
   stay on the scrim (WCAG 1.4.3; on 1440 the band is exactly 232 — open question in the S7 PR).
   Hover 529:28 = Focus 1306:57 (:hover, and keyboard focus — :focus-visible on the link): the veil over the whole card, 32 / 24 / 41,
   title · 16 · accent rule 44 × 2 · 18 · detail (body/card, color/text/secondary) at the top, the task at the bottom (space between, at
   least 18); «Read more» and the scrim go. The veil fades in and the scrim out in 250 ms, the detail fades in (1400:646);
   prefers-reduced-motion stops the motion (base.css). Focus ring inside the card (Блок N 1352:591): focus/gap on the edge (r20),
   focus/ring 2 further in (r18); its transparent border keeps the card's outline in forced-colors themes.
   Expanded 1183:1174 — always below 1024 and with (hover: none): the same veil and content, packed from the top (the task 18 below the
   detail), as tall as the content; from 1024 the card keeps at least the row's 446. The Hover / Focus triggers sit in :where(), so the
   Expanded rules (same specificity, later) win over a sticky touch hover or a mouse below 1024.
   The detail is always in the DOM: visually hidden in Default (not display: none). */
.service-card {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 446px;
  overflow: hidden;
  border-radius: var(--radius-card-lg);
}
.service-card__photo { position: absolute; inset: 0; }
.service-card__photo img { width: 100%; height: 100%; object-fit: cover; }
.service-card::after,
.service-card__body::before {
  content: "";
  position: absolute;
  pointer-events: none;
  transition: opacity 250ms ease-out;
}
.service-card::after {
  z-index: 1;
  inset: 0;
  background-color: var(--color-bg-veil);
  opacity: 0;
}
.service-card::before {
  content: "";
  position: absolute;
  z-index: 3;
  inset: 0;
  border: var(--focus-width) solid transparent;
  border-radius: var(--radius-card-lg);
  pointer-events: none;
}
.service-card:has(.service-card__link:focus-visible)::before {
  border-color: var(--color-focus-gap);
  outline: var(--focus-width) solid var(--color-focus-ring);
  outline-offset: calc(-2 * var(--focus-width));
}
.service-card__body {
  position: relative;
  z-index: 2;
  display: flex;
  flex: none;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 232px;
  padding: 53px 24px 21px;
}
.service-card__body::before {
  z-index: -1;
  inset: 0;
  background-image: var(--gradient-card-scrim);
}
.service-card__title { margin-bottom: 16px; font: var(--text-title-service); color: var(--color-text-primary); overflow-wrap: break-word; }
.service-card__link { color: inherit; text-decoration: none; }
.service-card__link:focus-visible { outline: none; }
/* The body holds the link's overlay; in Default it covers only the lower part of the card, so the overlay reaches far up and the card clips it */
.service-card__link::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: -1000px 0 0;
}
.service-card__detail {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  font: var(--text-body-card);
  color: var(--color-text-secondary);
}
.service-card__label {
  margin-bottom: 7px;
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-accent);
}
.service-card__text { font: var(--text-body-small); color: var(--color-text-body); }
.service-card__detail,
.service-card__text { overflow-wrap: break-word; }
.service-card__more {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 37px;
  font: var(--text-action-text);
  color: var(--color-accent);
}

/* Hover / Focus. The accent rule is a background of the detail (a block ::before inside the <p> would drop its paragraph role). */
.service-card:where(:hover, :has(.service-card__link:focus-visible))::after { opacity: 1; }
.service-card:where(:hover, :has(.service-card__link:focus-visible)) .service-card__body { flex: 1 1 auto; justify-content: flex-start; padding: 32px 24px 41px; }
.service-card:where(:hover, :has(.service-card__link:focus-visible)) .service-card__body::before { opacity: 0; }
.service-card:where(:hover, :has(.service-card__link:focus-visible)) .service-card__detail {
  position: static;
  width: auto;
  height: auto;
  margin: 0 0 18px;
  padding-top: 20px;
  overflow: visible;
  clip-path: none;
  white-space: normal;
  background: linear-gradient(var(--color-accent), var(--color-accent)) no-repeat 0 0 / 44px 2px;
  animation: service-card-reveal 250ms ease-out;
}
.service-card:where(:hover, :has(.service-card__link:focus-visible)) .service-card__task { margin-top: auto; }
.service-card:where(:hover, :has(.service-card__link:focus-visible)) .service-card__more { display: none; }
@keyframes service-card-reveal {
  from { opacity: 0; }
}

/* Expanded on touch screens (no hover): the same content, packed from the top */
@media (hover: none) {
  .service-card::after { opacity: 1; }
  .service-card .service-card__body { flex: 1 1 auto; justify-content: flex-start; padding: 32px 24px 41px; }
  .service-card .service-card__body::before { opacity: 0; }
  .service-card .service-card__detail { position: static; width: auto; height: auto; margin: 0 0 18px; padding-top: 20px; overflow: visible; clip-path: none; white-space: normal; background: linear-gradient(var(--color-accent), var(--color-accent)) no-repeat 0 0 / 44px 2px; animation: none; }
  .service-card .service-card__task { margin-top: 0; }
  .service-card .service-card__more { display: none; }
}

/* Below 1024 every card is Expanded, whatever the pointer, and as tall as its content */
@media (max-width: 1023.98px) {
  .service-card { min-height: 0; }
  .service-card::after { opacity: 1; }
  .service-card .service-card__body { flex: 1 1 auto; justify-content: flex-start; padding: 32px 24px 41px; }
  .service-card .service-card__body::before { opacity: 0; }
  .service-card .service-card__detail { position: static; width: auto; height: auto; margin: 0 0 18px; padding-top: 20px; overflow: visible; clip-path: none; white-space: normal; background: linear-gradient(var(--color-accent), var(--color-accent)) no-repeat 0 0 / 44px 2px; animation: none; }
  .service-card .service-card__task { margin-top: 0; }
  .service-card .service-card__more { display: none; }
}

/* ---- components/value-card.css ---- */
/* ValueCard 644:105 — <article class="value-card">: color/bg/card, radius/card, a stroke/hairline color/border/card stroke drawn INSIDE in
   Figma (so the padding space/card-padding 28 · 20 · 16 is counted from the outer edge: the border takes 1 of it here); head · 20 ·
   hairline color/rule/hairline across the content · 20 · body.
   Head: icon 44 × 44 (sprite, stroke/icon — .icon--value, aria-hidden; color/accent comes from the head, so forced-colors themes can
   recolour it) · 14 · title (title/value, color/text/primary), centred; min-height size/value-title-min (52 · 52 · 0, D18): in a row a
   one-line title keeps the height of a two-line one, so the hairlines and bodies of a row line up (client decision 24.09, «Блок N»
   1445:638). Below 640 the head is as tall as its content (44 for one line, 52 for two, 78 at 320–329 where a title takes three).
   Body: body/card in color/text/body. The hairline is the body's top border (forced-colors themes keep it).
   The card is a two-row grid (head, body) with one column that may shrink below the longest word (the words wrap instead of pushing the
   head and body out of the card); in a row of cards the section makes it a subgrid of the row, so the head track is shared by the row even
   when a title wraps to three lines between the frames (about 1024–1147 and 640–690) — the head box stays at the top, the hairlines line up. */
.value-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  min-width: 0;
  padding: calc(var(--space-card-padding) - var(--stroke-hairline));
  border: var(--stroke-hairline) solid var(--color-border-card);
  border-radius: var(--radius-card);
  background-color: var(--color-bg-card);
}
.value-card__head {
  display: flex;
  align-self: start;
  align-items: center;
  gap: 14px;
  min-height: var(--size-value-title-min);
  color: var(--color-accent);
}
.value-card__icon { width: 44px; height: 44px; }
.value-card__title {
  flex: 1 1 0;
  min-width: 0;
  font: var(--text-title-value);
  color: var(--color-text-primary);
  overflow-wrap: break-word;
}
.value-card__body {
  margin-top: 20px;
  padding-top: 20px;
  border-top: var(--stroke-hairline) solid var(--color-rule-hairline);
  font: var(--text-body-card);
  color: var(--color-text-body);
  overflow-wrap: break-word;
}

/* ---- components/timeline.css ---- */
/* TimelineStep 1506:61 in the process timeline 569:30 — <ol class="timeline"> of <li class="timeline__step">.
   Step: dot 12 × 12 color/accent (the li's ::before) · text: heading (number label/mono-md in color/accent · 6 · title title/step in
   color/text/primary; min-height size/step-heading-min 71 · 0 · 0, D18 — the five descriptions start on one line on the desktop) · 18 ·
   description (body/small, color/text/body).
   Direction=horizontal 641:67 from 1024: five equal steps 25 apart («Блок N» 1352:585), dot above the text, 18 between them.
   Direction=vertical 1506:62 below 1024: the steps in a column 28 apart, dot on the left, text 20 to its right.
   Connector 569:61 / 1099:868 / 1509:1640 (color/rule/mark, 1 px): every step but the last draws the piece from its dot's centre to the
   next dot's centre (the li's ::after: 100 % of the step plus the gap), so the line runs from the first centre to the fifth on any width —
   from the grid, not a number (1352:593); horizontal at y 6 like the frame, vertical at x 5.5 like 1099:868 / 1509:1640. The dots sit
   above the line.
   Horizontal: the timeline is a grid of three rows (dot, heading, description) and every step and its text are subgrids of it, so the
   headings of the five steps share one row: with the min-height token (71) and even when a title wraps to three lines (longer copy,
   user text spacing) the descriptions start on one line. */
.timeline {
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  grid-template-rows: repeat(3, auto);
  gap: 18px 25px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.timeline__step {
  position: relative;
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
  grid-template-columns: minmax(0, 1fr);
}
.timeline__step::before {
  content: "";
  position: relative;
  z-index: 1;
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background-color: var(--color-accent);
}
.timeline__step:not(:last-child)::after {
  content: "";
  position: absolute;
  inset-block-start: 6px;
  inset-inline-start: 6px;
  width: calc(100% + 25px);
  height: var(--stroke-hairline);
  background-color: var(--color-rule-mark);
}
.timeline__text {
  display: grid;
  grid-row: 2 / span 2;
  grid-template-rows: subgrid;
  grid-template-columns: minmax(0, 1fr);
}
.timeline__heading { display: flex; flex-direction: column; gap: 6px; min-height: var(--size-step-heading-min); }
.timeline__number { font: var(--text-label-mono-md); color: var(--color-accent); }
.timeline__title { font: var(--text-title-step); color: var(--color-text-primary); }
.timeline__body { font: var(--text-body-small); color: var(--color-text-body); }
.timeline__title,
.timeline__body { overflow-wrap: break-word; }

@media (max-width: 1023.98px) {
  .timeline { display: flex; flex-direction: column; gap: 28px; }
  .timeline__step { display: flex; flex-direction: row; gap: 20px; }
  .timeline__text { display: flex; flex: 1 1 0; flex-direction: column; gap: 18px; min-width: 0; }
  .timeline__step:not(:last-child)::after {
    inset-inline-start: 5.5px;
    width: var(--stroke-hairline);
    height: calc(100% + 28px);
  }
}

/* ---- components/project-card.css ---- */
/* ProjectCard 649:185 — <article class="project-card">: 272 wide (the carousel item sets it), color/bg/card, radius/card, no stroke,
   clipped. Photo slot 130 high on every width (FILL × FIXED 130 in the master); until licensed project photos exist it is a flat
   color/bg/surface placeholder (the grey of the master has no token, D12 / D40).
   Body: padding 20, fields 14 apart (numbers without tokens, the same on every width); each field is a label (label/mono in
   color/text/field-label, written in normal case in t/*.json and upper-cased here, «Блок N» 1400:644) · 4 · value. The facility field is
   at least 62 high (two lines of its value): the labels of neighbouring cards stay on one line when a facility fits on one line. Country:
   the flag 21 × 14 · 8 · the name, centred on the 22 px line. Values: facility body/card-strong in color/text/primary, country and role
   body/card, result body/small, all three in color/text/secondary. A color/rule/hairline line sits 14 above the result label (a border,
   so forced-colors themes keep it). In a row the cards are as tall as the tallest one; extra height stays at the bottom.
   A transparent inner outline draws the card's edge in forced-colors themes, which drop its background. */
.project-card {
  display: flex;
  flex-direction: column;
  inline-size: 100%;
  border-radius: var(--radius-card);
  background-color: var(--color-bg-card);
  outline: var(--stroke-hairline) solid transparent;
  outline-offset: calc(-1 * var(--stroke-hairline));
  overflow: hidden;
}
.project-card__photo { flex: none; height: 130px; background-color: var(--color-bg-surface); }
.project-card__body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
  padding: 20px;
}
.project-card__field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.project-card__field--facility { min-height: 62px; }
.project-card__field--result { padding-top: 14px; border-top: var(--stroke-hairline) solid var(--color-rule-hairline); }
.project-card__label {
  overflow-wrap: break-word;
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-field-label);
}
.project-card__facility,
.project-card__value { margin: 0; overflow-wrap: break-word; }
.project-card__facility { font: var(--text-body-card-strong); color: var(--color-text-primary); }
.project-card__value { font: var(--text-body-card); color: var(--color-text-secondary); }
.project-card__country { display: flex; align-items: center; gap: 8px; }
.project-card__country > span { min-width: 0; }
.project-card__flag { flex: none; width: 21px; height: 14px; }
.project-card__result { font: var(--text-body-small); }

/* ---- components/carousel.css ---- */
/* Carousel of the Projects section: track 954:783 · space/gutter · arrows 954:784 (CarouselArrow 659:236), «Блок N» 1470:638 / 1508:638.
   Track: the cards in a row, space/gutter apart, clipped to the column and scrolled sideways (a native scroll region, so wheel, trackpad
   and swipe work with or without the script); every card is 272 wide (step 296 · 296 · 288 · 284), below 640 min(272, track − 40) so at
   least 28 px of the next card shows at 320. Below 1024 the track snaps to the cards (1508:638); the desktop scrolls freely and the
   arrows land on card edges (js/carousel.js). The scrollbar is hidden while the script is expected (html.js) and comes back when it
   fails to load (html.carousel-failed), so the row can always be scrolled by mouse; ::-webkit-scrollbar is the fallback for engines
   without scrollbar-width (Safari before 18.2; newer Chrome ignores it once scrollbar-width is set).
   Arrows: right-aligned, 16 apart (a number without a token, «Блок N» 1352:585). Each is a 44 × 44 button whose stroke/hairline border is
   the ring; the chevron is the sprite's 44 × 44 icon laid over the border box (so it keeps its size and centre: no max-width from base.css,
   which would squeeze it into the 42 px inside the border), mirrored for «next».
   Colours: color/icon/default → color/text/primary on hover and press → color/text/disabled when there is nothing to scroll that way;
   hover moves the chevron 3 px towards its direction in 140 ms, press puts it back in 90 ms (1400:646; ease-out as the Button); with
   reduced motion the colour still changes, the chevron does not move. Focus ring at −6 without a gap band (1352:591). The arrows stay
   hidden (their place kept) until the script has wired them (html.carousel-ready). */
.carousel { display: flex; flex-direction: column; gap: var(--space-gutter); }
.carousel__track {
  display: flex;
  gap: var(--space-gutter);
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
html.js:not(.carousel-failed) .carousel__track { scrollbar-width: none; }
html.js:not(.carousel-failed) .carousel__track::-webkit-scrollbar { display: none; }
.carousel__item { display: flex; flex: 0 0 272px; min-width: 0; }

.carousel__arrows { display: flex; justify-content: flex-end; gap: 16px; }
.carousel__arrow {
  position: relative;
  flex: none;
  width: 44px;
  height: 44px;
  border: var(--stroke-hairline) solid currentColor;
  border-radius: 50%;
  color: var(--color-icon-default);
  transition: color 140ms ease-out;
}
.carousel__arrow:hover,
.carousel__arrow:active { color: var(--color-text-primary); }
.carousel__arrow:disabled { color: var(--color-text-disabled); cursor: default; }
.carousel__chevron {
  position: absolute;
  inset: calc(-1 * var(--stroke-hairline));
  width: 44px;
  height: 44px;
  max-width: none;
  transition: translate 140ms ease-out;
}
.carousel__arrow--next .carousel__chevron { scale: -1 1; }
.carousel__arrow--prev:hover .carousel__chevron { translate: -3px 0; }
.carousel__arrow--next:hover .carousel__chevron { translate: 3px 0; }
.carousel__arrow:is(:active, :disabled) .carousel__chevron { translate: none; }
.carousel__arrow:active .carousel__chevron { transition-duration: 90ms; }
html:not(.carousel-ready) .carousel__arrow { visibility: hidden; }

@media (max-width: 1023.98px) {
  .carousel__track { scroll-snap-type: x mandatory; }
  .carousel__item { scroll-snap-align: start; }
}

@media (max-width: 639.98px) {
  .carousel__item { flex-basis: min(272px, 100% - 40px); }
}

/* Touch screens keep :hover after a tap; the arrow returns to its resting colour and place. */
@media (hover: none) {
  .carousel__arrow:hover:not(:active, :disabled) { color: var(--color-icon-default); }
  .carousel__arrow:hover .carousel__chevron { translate: none; }
}

@media (prefers-reduced-motion: reduce) {
  .carousel__arrow:hover .carousel__chevron { translate: none; }
}

/* ---- components/field.css ---- */
/* Field 633:59 — <div class="field"> with <label class="field__label"> (label/mono in color/text/label; the asterisk color/accent,
   aria-hidden, 5 after the label), 8, the input zone .field__control (the value body/form in color/text/primary, the placeholder in
   color/text/muted; 8 below the value a 1 px color/border/subtle underline) and the error line. Text: 53 high; textarea rows=3: 97.
   The gap and the underline are the <input>'s own padding and border (so the gap stays clickable and forced colours still recolour the
   focused control's border); for the textarea they move to the zone (.field--textarea), so its scroll box ends with its three lines and
   a fourth line never shows through the gap.
   States (726:262 …): focus — label color/text/primary, the underline 2 px color/accent and the ring focus/ring 2 px around the zone
   (−6 on every side, r10: 1352:591 «263×43, −6 / +16»); filled — the value in color/text/primary; error — label and a 2 px underline in
   color/danger (the error wins over the focus colour), aria-invalid + aria-describedby on the error line; disabled (every control while
   sending) — a color/bg/disabled plate around the zone (−8 / −4, r10) under the underline (726:280), label, asterisk and text in
   color/text/disabled.
   Error line 728:265: icon/error 14 · 6 · field/error in color/danger, 8 below the field. It takes the first 8 + 18 of the 38 gap below
   the field, so the field keeps its height (Figma draws it absolute); a second line — below 640 by design (paired fields are one line
   from 1024: ≤ 41 characters, full width ≤ 84), or with user text spacing — grows the field by 18 instead of covering the next control
   («Ошибки до 639» 1508:638). */
.field { position: relative; isolation: isolate; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.field__label {
  display: flex;
  align-items: center;
  gap: 5px;
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-label);
}
.field__required { color: var(--color-accent); }
.field__control { position: relative; }
.field__input {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0 0 8px;
  border: 0;
  border-block-end: var(--stroke-hairline) solid var(--color-border-subtle);
  border-radius: 0;
  background: transparent;
  font: var(--text-body-form);
  color: var(--color-text-primary);
  appearance: none;
}
.field__input::placeholder { color: var(--color-text-muted); opacity: 1; }
.field__input:focus { outline: none; }
textarea.field__input { block-size: 3lh; padding: 0; border: 0; resize: none; overflow: auto; }
.field--textarea .field__control { padding-block-end: 8px; border-block-end: var(--stroke-hairline) solid var(--color-border-subtle); }

/* focus */
.field:has(.field__input:focus) .field__label { color: var(--color-text-primary); }
input.field__input:focus,
.field--textarea .field__control:has(.field__input:focus) { border-block-end-color: var(--color-accent); box-shadow: inset 0 calc(-1 * var(--stroke-hairline)) 0 var(--color-accent); }
.field__control::after {
  content: "";
  position: absolute;
  inset: -6px -6px calc(-6px - var(--stroke-hairline));
  border: var(--focus-width) solid transparent;
  border-radius: var(--radius-control);
  pointer-events: none;
  visibility: hidden;
}
.field__control:has(.field__input:focus-visible)::after { border-color: var(--color-focus-ring); visibility: visible; }

/* error (after focus, same weight: the error colours win) */
.field:has(.field__input[aria-invalid="true"]) .field__label { color: var(--color-danger); }
input.field__input[aria-invalid="true"],
.field--textarea .field__control:has(.field__input[aria-invalid="true"]) { border-block-end-color: var(--color-danger); box-shadow: inset 0 calc(-1 * var(--stroke-hairline)) 0 var(--color-danger); }
.field__error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 0 0 -26px;
  font: var(--text-field-error);
  color: var(--color-danger);
}
.field__error[hidden] { display: none; }
.field__error-icon { width: 14px; height: 14px; }

/* disabled: the plate sits in the field's stacking context, under the zone and its underline */
.field__control::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -4px -8px calc(-4px - var(--stroke-hairline));
  border-radius: var(--radius-control);
  background-color: var(--color-bg-disabled);
  visibility: hidden;
}
.field:has(.field__input:disabled) .field__control::before { visibility: visible; }
.field:has(.field__input:disabled) :is(.field__label, .field__required),
.field__input:disabled,
.field__input:disabled::placeholder { color: var(--color-text-disabled); }

/* ---- components/checkbox.css ---- */
/* Checkbox 772:328 — <span class="check"> with the native <input type="checkbox"> (transparent, 44 × 44 over the 20 × 20 box — the
   hit layer 1232:1315) and the check mark (the vector of 772:316, 2 px, round caps, in viewBox 20); the name comes from the separate
   <label for> (consent text). Box: color/bg/input, stroke/control color/border/subtle inside, radius/check.
   Checked — color/accent, no stroke, the mark color/accent-on; error — the stroke color/danger (aria-invalid, with the consent error
   line); disabled (while sending) — color/bg/disabled, the mark color/text/disabled; focus — ring focus/ring at −4 (r8) over a
   focus/gap band at −2 (r6) («Блок N» 1352:591). The colour sits on the span, so forced-colors themes recolour the mark too. */
.check {
  position: relative;
  flex: none;
  width: 20px;
  height: 20px;
  border: var(--stroke-control) solid var(--color-border-subtle);
  border-radius: var(--radius-check);
  background-color: var(--color-bg-input);
  color: var(--color-accent-on);
}
.check__input {
  position: absolute;
  z-index: 1;
  inset: -12px;
  width: 44px;
  height: 44px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.check__mark {
  position: absolute;
  inset: calc(-1 * var(--stroke-control));
  stroke-width: 2px;
  visibility: hidden;
}
.check:has(.check__input:checked) { border-color: transparent; background-color: var(--color-accent); }
.check:has(.check__input:checked) .check__mark { visibility: visible; }
.check:has(.check__input[aria-invalid="true"]) { border-color: var(--color-danger); }
.check:has(.check__input:focus-visible) {
  outline: var(--focus-width) solid var(--color-focus-ring);
  outline-offset: var(--focus-offset-tight);
  box-shadow: 0 0 0 var(--focus-offset-tight) var(--color-focus-gap);
}
.check:has(.check__input:disabled) { border-color: var(--color-border-subtle); background-color: var(--color-bg-disabled); color: var(--color-text-disabled); }
.check__input:disabled { cursor: default; }

/* ---- components/file-upload.css ---- */
/* FileUpload 773:343 — <div class="upload" data-state>: the native <input type="file"> (visually hidden under html.js; without JS, or
   when js/form.js fails to load — html.form-failed — the native control shows under the zone), the zone .upload__zone = its <label class=
   "upload__box"> plus, outside the label, the remove <button> (aria-label «Remove attached file»), and the error line. The wrapper keeps
   12 below the zone (773:344). Box 773:307: min-height 40, 8 / 16, 10 between items, centred on the cross axis, color/bg/input,
   stroke/hairline color/border/subtle dashed, radius/control; text field/upload. The dash is the browser's (Figma 6 / 4).
   States (js/form.js sets data-state): empty — «+  Attach …» in color/text/muted; dragover 1312:60 — dashed color/accent, «Drop the file
   here» in color/accent, no remove button, the error line hidden in place (its space kept); selected 773:316 — solid stroke, the file
   name (color/text/primary, wraps) · the meta «PDF · 2.4 MB» (label/mono, color/text/muted) · the remove button 24 × 24 centred on the box (× 8 × 8, stroke/control,
   color/text/muted, 16 from the right; hit area 44, hover and focus in color/text/primary plus the focus ring); the zone's own label
   stays in the accessible name, visually hidden; tooLarge 773:329 / wrongFormat 773:342 — as selected with the stroke and the meta in
   color/danger and the error line (as Field: it takes 8 + 18 of the 38 gap below, a second line grows the zone); disabled 1390:1634
   (while sending) — color/bg/disabled, text color/text/disabled. Focus: ring focus/ring 2 at −6 around the box, r14, following its height. */
.upload { position: relative; display: flex; flex-direction: column; padding-block-end: 12px; }
.upload__input { margin: 8px 0 0; order: 1; color: var(--color-text-muted); }
html.js:not(.form-failed) .upload__input { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.upload__zone { position: relative; }
.upload__box {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  padding: calc(8px - var(--stroke-hairline)) calc(16px - var(--stroke-hairline));
  border: var(--stroke-hairline) dashed var(--color-border-subtle);
  border-radius: var(--radius-control);
  background-color: var(--color-bg-input);
  font: var(--text-field-upload);
  color: var(--color-text-muted);
  cursor: pointer;
}
.upload__zone::after {
  content: "";
  position: absolute;
  inset: -6px;
  border: var(--focus-width) solid transparent;
  border-radius: calc(var(--radius-control) + 4px);
  pointer-events: none;
  visibility: hidden;
}
html.js:not(.form-failed) .upload:has(.upload__input:focus-visible) .upload__zone::after { border-color: var(--color-focus-ring); visibility: visible; }
.upload__label { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
.upload__label::before { content: "+\a0\a0"; content: "+\a0\a0" / ""; white-space: pre; }
.upload__drop,
.upload__name,
.upload__meta { display: none; }
.upload__name { flex: 1 1 auto; min-width: 0; color: var(--color-text-primary); overflow-wrap: anywhere; }
.upload__meta {
  flex: none;
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
}
.upload__remove {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 16px;
  display: flex;
  width: 24px;
  height: 24px;
  margin-block: auto;
  padding: 0;
  border: 0;
  border-radius: var(--radius-check);
  background: none;
  color: var(--color-text-muted);
  stroke-width: var(--stroke-control);
  cursor: pointer;
}
.upload__remove::before { content: ""; position: absolute; inset: -10px; }
.upload__remove:hover,
.upload__remove:focus-visible { color: var(--color-text-primary); }
.upload__remove:focus-visible { outline: var(--focus-width) solid var(--color-focus-ring); outline-offset: var(--focus-offset-tight); }
.upload__remove[hidden] { display: none; }
.upload__error { margin-block-start: 8px; }

.upload[data-state="dragover"] .upload__box { border-color: var(--color-accent); color: var(--color-accent); }
.upload[data-state="dragover"] .upload__drop { display: block; }
.upload[data-state="dragover"] .upload__remove { display: none; }
.upload[data-state="dragover"] .upload__error { visibility: hidden; }
.upload:is([data-state="selected"], [data-state="tooLarge"], [data-state="wrongFormat"]) .upload__box { border-style: solid; padding-inline-end: calc(16px + 24px + 10px - var(--stroke-hairline)); }
.upload:is([data-state="selected"], [data-state="tooLarge"], [data-state="wrongFormat"]) :is(.upload__name, .upload__meta) { display: block; }
.upload:is([data-state="dragover"], [data-state="selected"], [data-state="tooLarge"], [data-state="wrongFormat"]) .upload__label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.upload:is([data-state="tooLarge"], [data-state="wrongFormat"]) .upload__box { border-color: var(--color-danger); }
.upload:is([data-state="tooLarge"], [data-state="wrongFormat"]) .upload__meta { color: var(--color-danger); }

.upload:has(.upload__input:disabled) .upload__box { background-color: var(--color-bg-disabled); color: var(--color-text-disabled); cursor: default; }
.upload:has(.upload__input:disabled) :is(.upload__name, .upload__meta, .upload__remove) { color: var(--color-text-disabled); }

/* ---- components/overlay.css ---- */
/* FormSubmitting 809:477 and FormOverlay 776:372 — both live inside the form card (.contact__card), absolute, hidden until js/form.js
   shows them («Блок N» 1352:593).
   Submitting strip 1329:1301 (for assistive tech an indeterminate native <progress>; the visible strip is aria-hidden): on the card's
   top edge — «Sending» label/mono in color/accent 30 from the card's edge, 8, a 1 px color/border/subtle track the card's full width with
   a 40 px color/accent pen running across it (clipped by the card) — the site's only endless motion; with prefers-reduced-motion the pen
   stands still at 150 like the frame (base.css stops the animation). The track is a border and the pen keeps its colour
   (forced-color-adjust), so both survive forced colours; so does the title rule below (a border).
   Result overlay (1216:1271 …): the card's size (inset 0), color/bg/surface, 30 inside, content at the top and the actions at the bottom
   (space between, at least 22). Content, 20 apart: eyebrow label/mono (color/accent; color/danger on error) · title heading/overlay in
   color/text/primary (focus target, tabindex -1) with a 2 px rule 20 below it (color/accent / color/danger) · success: the receipt <dl> —
   rows 9 / 0 / 9, color/border/subtle hairlines between them, the key label/mono in color/text/label 150 wide, the value body/small in
   color/text/secondary (number and response time in color/accent), wrapping under the key when narrower than 220; error: the text
   body/form in color/text/secondary and «Code 503 · 14:32 CEST» label/mono in color/text/label · the block «What happens next» /
   «Or contact us directly» (14 above, 8 apart; caption label/mono color/text/label, note body/small color/text/muted, links standalone).
   Actions: 20 apart, wrapping 12; below 640 the buttons fill the card (their labels may wrap) and «Back to the form» goes under them
   (1508:638). */
.submitting {
  position: absolute;
  z-index: 2;
  inset-block-start: 0;
  inset-inline: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.submitting[hidden] { display: none; }
.submitting__label {
  padding-inline: 30px;
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-accent);
}
.submitting__track { position: relative; display: block; border-block-start: var(--stroke-hairline) solid var(--color-border-subtle); }
.submitting__pen {
  position: absolute;
  inset-block-start: calc(-1 * var(--stroke-hairline));
  inset-inline-start: 150px;
  width: 40px;
  height: var(--stroke-hairline);
  background-color: var(--color-accent);
  forced-color-adjust: none;
  animation: submitting-pen 1.6s linear infinite;
}
@keyframes submitting-pen {
  from { inset-inline-start: -40px; }
  to { inset-inline-start: 100%; }
}

.overlay {
  position: absolute;
  z-index: 1;
  inset: 0;
  overflow: auto;
  padding: 30px;
  background-color: var(--color-bg-surface);
}
.overlay[hidden],
.overlay__panel[hidden] { display: none; }
.overlay__panel { display: flex; flex-direction: column; justify-content: space-between; gap: 22px; min-height: 100%; }
.overlay__content { display: flex; flex-direction: column; gap: 20px; }
.overlay__eyebrow,
.overlay__caption,
.overlay__code,
.receipt dt {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
}
.overlay__eyebrow { color: var(--color-accent); }
.overlay__title { font: var(--text-heading-overlay); color: var(--color-text-primary); }
.overlay__title::after { content: ""; display: block; margin-block-start: 20px; border-block-start: 2px solid var(--color-accent); }
.overlay__panel--error .overlay__eyebrow { color: var(--color-danger); }
.overlay__panel--error .overlay__title::after { border-block-start-color: var(--color-danger); }
.overlay__text { font: var(--text-body-form); color: var(--color-text-secondary); }
.overlay__code,
.overlay__caption,
.receipt dt { color: var(--color-text-label); }
.overlay__code[hidden] { display: none; }

.receipt { margin: 0; }
.receipt__row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 0; padding-block: 9px; }
.receipt__row + .receipt__row { border-block-start: var(--stroke-hairline) solid var(--color-border-subtle); }
.receipt__row[hidden] { display: none; }
.receipt dt { flex: none; width: 150px; }
.receipt__value { flex: 1 1 220px; min-width: 0; margin: 0; font: var(--text-body-small); color: var(--color-text-secondary); overflow-wrap: anywhere; }
.receipt__value--accent { color: var(--color-accent); }
.receipt__value--clamp { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }

.overlay__next { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding-block-start: 14px; }
.overlay__note { font: var(--text-body-small); color: var(--color-text-muted); }
.overlay__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; }
.overlay__back {
  padding: 0;
  border: 0;
  background: none;
  font: var(--text-body-card);
  cursor: pointer;
}

@media (max-width: 639.98px) {
  .overlay__actions { flex-direction: column; align-items: stretch; }
  .overlay__button { white-space: normal; }
  .overlay__back { align-self: flex-start; }
}

/* ---- components/cookie-banner.css ---- */
/* Cookie banner 892:758 (1280 × 120) / 1220:1185 (688 × 186) / 1220:1196 (335 × 298), rules «Блок N» 1470:638 and 1508:638: pinned to the
   bottom of the window space/container-margin up (80 · 40 · 20), as wide as the window minus the margins and never wider than the 1440
   canvas (1280, D25), over the content and above the header (the open mobile menu hides it), no shadow; height by content.
   color/bg/surface, a 1 px color/border/card stroke inside, radius/card. Text: «COOKIE» label/mono in color/text/label, 10, the text
   body/card in color/text/body with its second sentence in body/card-strong. Buttons: Button secondary M, Decline then Accept.
   ≥ 1024: a row, 26 / 32, 48 between the text (FILL) and the buttons (HUG, 16 apart). 640–1023: a column 20 apart, the buttons FILL in a
   row. ≤ 639: padding 20, the buttons FILL stacked 12 apart, Decline above Accept. Paddings are Figma's unbound numbers, measured from
   the outer edge (the stroke is inside). At most 60 % of the window high (then it scrolls inside), so at 400 % zoom the page stays
   readable around it (1.4.10). Shown and hidden by js/cookie.js ([hidden]); while it shows, the page keeps its end and the focused
   element clear of it: --cookie-banner-space (its height + the bottom offset) is the scroll padding at the window bottom, plus 8 for
   the widest focus ring (a field's, 6 + 1), and an equal space at the end of the last region — inside the footer or the 404 page, so
   their own ground fills it. */
.cookie-banner {
  position: fixed;
  z-index: 20;
  inset-inline: 0;
  inset-block-end: var(--space-container-margin);
  display: flex;
  align-items: center;
  gap: 48px;
  width: calc(100% - 2 * var(--space-container-margin));
  max-width: 1280px;
  max-height: calc(60vh - var(--space-container-margin));
  max-height: calc(60dvh - var(--space-container-margin));
  margin-inline: auto;
  overflow-y: auto;
  scroll-padding-block: 8px;
  padding: calc(26px - var(--stroke-hairline)) calc(32px - var(--stroke-hairline));
  border: var(--stroke-hairline) solid var(--color-border-card);
  border-radius: var(--radius-card);
  background-color: var(--color-bg-surface);
}
.cookie-banner[hidden],
body[data-menu-open] .cookie-banner { display: none; }
.cookie-banner__text { display: flex; flex: 1 1 0; flex-direction: column; gap: 10px; min-width: 0; }
.cookie-banner__label {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-label);
}
.cookie-banner__body { font: var(--text-body-card); color: var(--color-text-body); }
.cookie-banner__body strong { font: var(--text-body-card-strong); }
.cookie-banner__actions { display: flex; flex: none; gap: 16px; }

html { scroll-padding-block-end: calc(var(--cookie-banner-space, 0px) + 8px); }
.site-footer,
.page-404 { padding-block-end: var(--cookie-banner-space, 0px); }

@media (max-width: 1023.98px) {
  .cookie-banner { flex-direction: column; align-items: stretch; gap: 20px; }
  .cookie-banner__text { flex: none; }
  .cookie-banner__actions .btn { flex: 1 1 0; }
}

@media (max-width: 639.98px) {
  .cookie-banner { padding: calc(20px - var(--stroke-hairline)); }
  .cookie-banner__actions { flex-direction: column; gap: 12px; }
  .cookie-banner__actions .btn { flex: none; white-space: normal; }
}

/* ---- sections/header.css ---- */
/* Header 377:416 / 1127:1507 / 1099:982 / 1108:1242 — 118 high on every frame, laid over the hero: position: absolute, so it never adds
   to the page height (contract 5854 · 5918 · 8446 · 9671 is the sum of the eight sections only). Margins space/header-margin 40 · 40 · 20,
   content no wider than 1360 (the 1440 canvas, D25). Desktop: logo · menu (items 28 apart) · language switch + CTA Size S (gap space/gutter).
   640–1023: logo · switch + CTA Size M + burger; the menu becomes the overlay «Меню 768 · открыто» 1226:1282. ≤ 639: logo · switch + burger;
   the CTA lives in the overlay under a hairline («Меню 375 · открыто» 1226:1209). Overlay per «Блок O» 1508:640: under the header,
   100dvh − 118 high with its own scroll, color/bg/page, padding 40 (768) / 32 (375) × space/container-margin, items 13 apart. */
.site-header {
  position: absolute;
  inset: 0 0 auto;
  z-index: 10;
  height: 118px;
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  column-gap: var(--space-gutter);
  max-width: calc(1360px + 2 * var(--space-header-margin));
  height: 100%;
  margin-inline: auto;
  padding-inline: var(--space-header-margin);
}
/* The logo's white parts are currentColor: the colour sits on the link, so forced-colors themes can recolour it (an SVG keeps its own colour). */
.site-header__logo { display: block; flex: none; border-radius: var(--radius-control); color: var(--color-text-primary); }
.site-header__logo-mark { width: 112px; height: 78px; }
.site-header__actions { display: flex; flex: none; align-items: center; gap: var(--space-gutter); }

/* D36: the design draws 1160–1440 only; from 1160 down to 1024 the 28 px between menu items shrinks linearly to 8 px, so that logo,
   menu and actions keep fitting the 944 px of content at 1024 (with the language switch too). Above 1160 the gap stays 28.
   The groups never touch (column-gap above) and, should user text spacing still not fit, the menu wraps to a second row inside the 118
   instead of pushing the row wider than the window (WCAG 1.4.12). */
.nav { min-width: 0; }
.nav__list { display: flex; flex-wrap: wrap; gap: 0 clamp(8px, calc(8px + (100vw - 1024px) * 20 / 136), 28px); }
.nav__cta { display: none; }
.burger { display: none; }

@media (max-width: 1023.98px) {
  .site-header__cta { padding: 12px 24px; font: var(--text-control-m); }
  .burger { display: block; }

  .nav {
    display: none;
    position: fixed;
    inset: 118px 0 auto;
    height: calc(100vh - 118px);
    height: calc(100dvh - 118px);
    overflow-y: auto;
    flex-direction: column;
    gap: 13px;
    padding: 40px var(--space-container-margin);
    background-color: var(--color-bg-page);
  }
  .nav__list { flex-direction: column; flex-wrap: nowrap; gap: 13px; }
  /* «Блок O» 1508:640: the whole 56 px row (43 + 13) is the link; the ring stays on the 43 px box as in 1226:1324 / 1226:1248. */
  .nav__link::before { inset: 0 0 -13px; }
  body[data-menu-open] { overflow: hidden; }
  body[data-menu-open] .site-header { position: fixed; background-color: var(--color-bg-page); }
  body[data-menu-open] .nav { display: flex; }
}

@media (max-width: 639.98px) {
  .site-header__cta { display: none; }
  .nav { padding-block: 32px; }
  .nav__cta {
    display: block;
    padding-block-start: 13px;
    border-block-start: var(--stroke-hairline) solid var(--color-rule-hairline);
  }
}

/* ---- sections/hero.css ---- */
/* Hero 954:766 / 1127:1310 / 1099:785 / 1108:1047 — 1009 · 1009 · 1304 · 1193, starting at y 0 under the absolute header (S3).
   Inner: padding-top space/hero-top (254 · 254 · 160), padding-bottom 39, space/container-margin at the sides, content ≤ 1280 (D25);
   text → trust bar 80; H1 → lead 57 · 57 · 32; lead → CTA 41 · 41 · 28 (1445:638 — numbers without tokens, the same on the three frames).
   H1 display/h1 in color/accent, at most 914 wide; lead body/lead in color/text/primary, at most 760 wide (both FILL below that).
   CTA «Request a Technical Proposal» → #contact: Button Size XL from 640; below 640 Size M across the content, label centred, wrapping to two
   lines (68 high) when the label no longer fits: below 287 of content (label 239 + 2 × 24, viewport < 327). «Блок N» 1508:638 says 314,
   which matches a button with the arrow (239 + 10 + 16 + 48) — the hero CTA has none (open question in the S5 PR).
   Background: 1440 × 1420 inside the 1440 canvas on the desktop (the 411 below the hero run under «О компании»; nothing runs the full
   window, D25), the section's own height on 640–1023 and below 640. It paints below all in-flow content (z-index −1 in the root stacking
   context: nothing above it may create a stacking context), over the page background it ends in (--gradient-page, base.css). */
.hero { position: relative; }
.hero__bg {
  position: absolute;
  z-index: -1;
  inset-block-start: 0;
  inset-inline-start: max(0px, (100% - 1440px) / 2);
  width: min(100%, 1440px);
  height: 1420px;
  overflow: hidden;
  background-color: var(--color-bg-page-top);
}
.hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--gradient-hero-veil-y), var(--gradient-hero-veil-x);
}
.hero__bg picture { display: block; height: 100%; }
.hero__photo { width: 100%; height: 100%; max-width: none; object-fit: cover; object-position: 50% 50%; }

.hero__inner {
  display: flex;
  flex-direction: column;
  gap: 80px;
  max-width: calc(1280px + 2 * var(--space-container-margin));
  margin-inline: auto;
  padding: var(--space-hero-top) var(--space-container-margin) 39px;
}
.hero__text { display: flex; flex-direction: column; gap: 57px; }
.hero__title { max-width: 914px; font: var(--text-display-h1); color: var(--color-accent); overflow-wrap: break-word; }
.hero__body { display: flex; flex-direction: column; align-items: flex-start; gap: 41px; }
.hero__lead { max-width: 760px; font: var(--text-body-lead); color: var(--color-text-primary); }

@media (max-width: 1023.98px) {
  .hero__bg { height: 100%; }
}

@media (max-width: 639.98px) {
  .hero__text { gap: 32px; }
  .hero__body { align-items: stretch; gap: 28px; }
  .hero__cta { padding: 12px 24px; font: var(--text-control-m); white-space: normal; }
}

/* ---- sections/about.css ---- */
/* About 954:769 / 1127:1326 / 1099:801 / 1108:1063 — 831 · 857 · 1287 · 1282. padding-top space/section-top, padding-bottom
   space/section-bottom; heading → columns space/heading-gap; «Колонки»: intro → photo row 52 (a number without a token, the same on the
   three frames, «Блок N» 1352:577). Heading: SectionHeading, at most 480 wide. Both text blocks body/paragraph in color/text/secondary,
   the intro at most 620 wide from 1024 (full container width below).
   Photo row from 1024 («Блок N» 1352:583): the photo is flush with the left edge of the 1440 canvas, (min(100vw, 1440) − 64 − 88) / 2 wide
   (1440 → 644, 1200 → 524, 1160 → 504, 1024 → 436) and 446 high whatever the column's height (455 · 481 — intentional), right corners
   radius/card-lg; the right column starts 88 after it and keeps at most 628 (the row ends 64 before the canvas edge, so on 1440 the column
   ends on the container line). Wider than 1440 the canvas stays centred and the photo stays 644 inside it (D25).
   640–1023: the photo runs the window width at 768 : 532 without rounding, then 32, then the column in the container; below 640 the
   photo is 1024 / 709 (375 × 260 in the frame). Column: text → parameters 43. Parameters 492:12: rows with a color/rule/hairline line
   between them (not above the first or below the last); from 640 the label (label/mono-md, color/text/muted, 224 wide) sits left of the
   value (body/base, color/text/secondary), space/gutter apart, 16 above and below; below 640 the label is above the value, 6 apart, the
   row padded space/gutter (12). */
.about { padding-bottom: var(--space-section-bottom); }
.about__head { margin-bottom: var(--space-heading-gap); }
.about__title { max-width: 480px; }
.about__columns { display: flex; flex-direction: column; gap: 52px; }
.about__intro { max-width: 620px; font: var(--text-body-paragraph); color: var(--color-text-secondary); }
.about__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 88px;
  align-items: start;
  max-width: 1440px;
  margin-inline: auto;
  padding-inline-end: 64px;
  width: 100%;
}
.about__photo { display: block; }
.about__photo img {
  width: 100%;
  height: 446px;
  object-fit: cover;
  border-start-end-radius: var(--radius-card-lg);
  border-end-end-radius: var(--radius-card-lg);
}
.about__aside { display: flex; flex-direction: column; gap: 43px; min-width: 0; max-width: 628px; }
.about__text { font: var(--text-body-paragraph); color: var(--color-text-secondary); }
.about__intro,
.about__text,
.param__label,
.param__value { overflow-wrap: break-word; }

.params { margin: 0; }
.param { display: flex; gap: var(--space-gutter); padding-block: 16px; }
.param + .param { border-block-start: var(--stroke-hairline) solid var(--color-rule-hairline); }
.param__label { flex: none; width: 224px; font: var(--text-label-mono-md); color: var(--color-text-muted); }
.param__value { flex: 1 1 0; min-width: 0; margin: 0; font: var(--text-body-base); color: var(--color-text-secondary); }

@media (max-width: 1023.98px) {
  .about__intro { max-width: none; }
  /* stretch: the photo box takes the row width before the lazy image arrives, so aspect-ratio holds its height (no layout shift) */
  .about__row { display: flex; flex-direction: column; align-items: stretch; gap: 32px; padding-inline-end: 0; }
  .about__photo img { height: auto; aspect-ratio: 768 / 532; border-radius: 0; }
  .about__aside { max-width: none; width: 100%; padding-inline: var(--space-container-margin); }
}

@media (max-width: 639.98px) {
  .about__photo img { aspect-ratio: 1024 / 709; }
  .param { flex-direction: column; gap: 6px; padding-block: var(--space-gutter); }
  .param__label { width: auto; }
}

/* ---- sections/services.css ---- */
/* Services 954:775 / 1127:1350 / 1099:825 / 1108:1087 — 650 · 650 · 972 · 1326. padding-top space/section-top, padding-bottom
   space/section-bottom, header → cards space/heading-gap (D17).
   Header 954:776: from 1024 the heading (SectionHeading, HUG, max 420) and the CTA (Button secondary M with the arrow, HUG) in one row,
   space between, centred; 640–1023 in a column, 20 apart, both HUG; below 640 the heading and the CTA run the content width
   (1508:638 «CTA до 639»), the label wraps when the content is narrower than the button (239 + 10 + 16 + 48 = 313).
   Cards 377:655: from 1024 three in a row, space/gutter apart (1440 → 410.67, 1200 → 330.67); 640–1023 two columns (content − gutter) / 2
   with the third card on a half-width cell (768 → 336, rows as tall as their tallest card); below 640 one column (1508:638). */
.services { padding-bottom: var(--space-section-bottom); }
.services__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  column-gap: var(--space-gutter);
  margin-bottom: var(--space-heading-gap);
}
.services__title { max-width: 420px; }
.services__cta { flex: none; }
.services__cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-gutter);
}

@media (max-width: 1023.98px) {
  .services__head { flex-direction: column; align-items: flex-start; row-gap: 20px; }
  .services__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 639.98px) {
  .services__title { align-self: stretch; max-width: none; }
  .services__cta { align-self: stretch; white-space: normal; }
  .services__cards { grid-template-columns: minmax(0, 1fr); }
}

/* ---- sections/advantages.css ---- */
/* Advantages («Почему заказчики») 954:777 / 1127:1360 / 1099:835 / 1108:1097 — 656 · 678 · 763 · 1242. padding-top space/section-top,
   padding-bottom space/section-bottom, heading → cards space/heading-gap (D17). Heading: SectionHeading, at most 792 wide (FIXED 792
   on 1440 / 1200, FILL below; on 375 it wraps to two lines).
   Cards 954:778: from 1024 two rows of three, space/gutter between the cards and 22 between the rows (a number without a token, as in
   954:778 / 1127:1362); 640–1023 two columns (content − gutter) / 2, space/gutter both ways (1099:837, 768 → 336); below 640 one column,
   space/gutter apart (1108:1099, 1508:638). Every row is as tall as its tallest card (the cards FILL the row in the frames).
   Each card spans two tracks (head, body) as a subgrid, so the heads of a row share one height and the hairlines line up at any width;
   the gap between the rows is the cards' top margin (the tracks have no gap of their own), the grid's negative top margin takes it back
   for the first row. */
.advantages { padding-bottom: var(--space-section-bottom); }
.advantages__title { max-width: 792px; margin-bottom: var(--space-heading-gap); }
.advantages__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--space-gutter);
  margin-top: -22px;
}
.advantages__grid > .value-card {
  grid-row: span 2;
  grid-template-rows: subgrid;
  row-gap: 0;
  margin-top: 22px;
}

@media (max-width: 1023.98px) {
  .advantages__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: calc(-1 * var(--space-gutter)); }
  .advantages__grid > .value-card { margin-top: var(--space-gutter); }
}

@media (max-width: 639.98px) {
  .advantages__grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- sections/projects.css ---- */
/* Projects («Проекты») 954:779 / 1127:1373 / 1099:848 / 1108:1108 — 632 · 632 · 801 · 815. padding-top space/section-top, padding-bottom
   space/section-bottom (D17). Columns 954:780: from 1024 side by side, 32 apart (a number without a token, «Блок N» 1352:585 «32 колонки
   секции проектов»); below 1024 stacked, space/heading-gap apart (1099:849 / 1108:1109).
   Left column 954:781: 384 wide on the desktop (FIXED; the heading takes exactly three lines of the monospaced heading/section there),
   heading · space/gutter · lead body/paragraph in color/text/secondary, 304 wide (FIXED) on the desktop, at most 620 below 1024.
   Right column 954:782: the carousel takes the rest (content − 416: 864 on 1440, 624 on 1200, «Блок N» 1470:638) and starts 17 lower
   on the desktop (a number without a token, 954:782 / 1127:1378), level with the heading below 1024. */
.projects { padding-bottom: var(--space-section-bottom); }
.projects__columns { display: flex; gap: 32px; }
.projects__head {
  display: flex;
  flex: 0 0 384px;
  flex-direction: column;
  gap: var(--space-gutter);
  min-width: 0;
}
.projects__lead {
  max-width: 304px;
  font: var(--text-body-paragraph);
  color: var(--color-text-secondary);
  overflow-wrap: break-word;
}
.projects__carousel { flex: 1 1 0; min-width: 0; padding-top: 17px; }

@media (max-width: 1023.98px) {
  .projects__columns { flex-direction: column; gap: var(--space-heading-gap); }
  .projects__head { flex: none; }
  .projects__lead { max-width: 620px; }
  .projects__carousel { flex: none; padding-top: 0; }
}

/* ---- sections/process.css ---- */
/* Process («Процесс») 954:785 / 1127:1388 / 1099:863 / 1108:1123 — 538 · 558 · 827 · 977. padding-top space/section-top, padding-bottom
   space/section-bottom (D17). Heading block 954:786: at most 760 wide; SectionHeading at most 384 (FIXED 384 on 1440 / 1200, HUG on 768,
   FILL on 375) · 20 · lead body/paragraph in color/text/primary. Heading block → timeline 97 on the desktop, 48 below 1024 (numbers
   without tokens, «Блок N» 1445:638). */
.process { padding-bottom: var(--space-section-bottom); }
.process__head {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 760px;
  margin-bottom: 97px;
}
.process__title { max-width: 384px; }
.process__lead { font: var(--text-body-paragraph); color: var(--color-text-primary); overflow-wrap: break-word; }

@media (max-width: 1023.98px) {
  .process__head { max-width: none; margin-bottom: 48px; }
  .process__title { align-self: flex-start; }
}

@media (max-width: 639.98px) {
  .process__title { align-self: stretch; max-width: none; }
}

/* ---- sections/contact.css ---- */
/* Contact 954:787 / 1127:1401 / 1099:876 / 1108:1136 — 848 · 848 · 1500 · 1642. padding-top space/section-top, padding-bottom
   space/section-bottom (D17); the row 377:665 takes the rest: from 1024 the text panel (FILL) and the form card (585) side by side, the
   row as tall as the card (728); below 1024 a column, panel first, 32 apart («Контакты» 60 + 36 — numbers without tokens, 1445:638).
   Panel 377:666: color/bg/footer, radius/panel on the outer corners (all four below 1024), 60 / 0 / 100 / 40 (60 / 0 / 60 / 40 below
   1024), 36 between: the section heading heading/panel in color/accent · the lead body/lead in color/text/secondary (at most 558; no
   limit below 640) · the turnaround body/highlight in color/accent · «Other channels» (label/mono, color/text/muted), 10, the contact
   links: from 1024 one line in body/base color/text/primary with «   ·   » between them; below 1024 Link standalone (15 / 22) in a
   column, 8 apart (D22, 1351:583); LinkedIn only with site.linkedinUrl (D28).
   Decorative plates 377:667 / 669 / 671 (aria-hidden, from 1024 only, under the text, clipped by the panel): 121 × 197.74 at right 45.74,
   top −20.38, rotated 35.004°, radius/panel; 261.36 × 85 at left −40.04, bottom 118.18, rotated −55.778°, radius/card-lg; 159 × 85 at
   left 91.35, bottom 62.86, rotated −55.778°, radius/card-lg — the angles are intended (1400:640); colours --decor-contact-plate-*.
   Form card 377:672: color/bg/surface, radius/panel on the outer corners, 30 inside, 26 between: title title/form · the fields (pairs
   NAME | ORGANIZATION and E-MAIL | PHONE space/gutter apart from 1024, one column below; 38 between the rows, 12 below them) ·
   the upload · the consent block (min-height 52; 104 below 640 — room for a two-line error) · the submit row (Button primary L, centred;
   it grows to 243 in loading around the same centre; below 640 it fills the row). */
.contact { padding-bottom: var(--space-section-bottom); }
.contact__row { display: flex; }
.contact__panel {
  position: relative;
  isolation: isolate;
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  gap: 36px;
  min-width: 0;
  padding: 60px 0 100px 40px;
  overflow: hidden;
  border-radius: var(--radius-panel) 0 0 var(--radius-panel);
  background-color: var(--color-bg-footer);
}
.contact__plate { position: absolute; z-index: -1; pointer-events: none; }
.contact__plate--1 {
  inset-block-start: -20.381px;
  inset-inline-end: 45.742px;
  width: 121px;
  height: 197.743px;
  border-radius: var(--radius-panel);
  background-color: var(--decor-contact-plate-1);
  rotate: 35.00409deg;
}
.contact__plate--2 {
  inset-block-end: 118.179px;
  inset-inline-start: -40.043px;
  width: 261.356px;
  height: 85px;
  border-radius: var(--radius-card-lg);
  background-color: var(--decor-contact-plate-2);
  rotate: -55.77807deg;
}
.contact__plate--3 {
  inset-block-end: 62.862px;
  inset-inline-start: 91.353px;
  width: 159px;
  height: 85px;
  border-radius: var(--radius-card-lg);
  background-color: var(--decor-contact-plate-3);
  rotate: -55.77807deg;
}
.contact__title { font: var(--text-heading-panel); color: var(--color-accent); overflow-wrap: break-word; }
.contact__lead { max-width: 558px; font: var(--text-body-lead); color: var(--color-text-secondary); }
.contact__turnaround { font: var(--text-body-highlight); color: var(--color-accent); }
.contact__channels { display: flex; flex-direction: column; gap: 10px; }
.contact__channels-label {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-muted);
}
/* Each link carries its «   ·   » in front (the width of three spaces, the dot, three spaces in body/base: 1.83em); the row is shifted
   back and clipped by one separator on the start side, so the first separator of every line is cut off — when the panel is too narrow
   for one line (1024–1085) the links wrap without a «·» hanging at a line end. The clip keeps 8 around the row for the focus ring (6). */
.contact__links {
  --contact-sep: 1.83em;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  margin-inline-start: calc(-1 * var(--contact-sep));
  clip-path: inset(-8px -8px -8px calc(var(--contact-sep) - 8px));
  font: var(--text-body-base);
  color: var(--color-text-primary);
}
.contact__item { display: flex; align-items: baseline; }
.contact__item::before { content: "·"; content: "·" / ""; flex: none; inline-size: var(--contact-sep); text-align: center; }
.contact__link { font: var(--text-body-base); color: var(--color-text-primary); }

.contact__card {
  position: relative;
  display: flex;
  flex: none;
  flex-direction: column;
  width: 585px;
  overflow: hidden;
  border-radius: 0 var(--radius-panel) var(--radius-panel) 0;
  background-color: var(--color-bg-surface);
}
.form { display: flex; flex-direction: column; gap: 26px; padding: 30px; }
.form[inert] { visibility: hidden; }
.form__title { font: var(--text-title-form); color: var(--color-text-primary); }
.form__fields { display: flex; flex-direction: column; gap: 26px; min-width: 0; }
.form__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 38px var(--space-gutter);
  padding-block-end: 12px;
}
.form__grid > .field:not(.field--pair) { grid-column: 1 / -1; }
.consent { display: flex; flex-direction: column; gap: 8px; min-height: 52px; }
.consent__row { display: flex; align-items: flex-start; gap: 12px; }
.consent__label { flex: 1 1 0; min-width: 0; font: var(--text-body-small); color: var(--color-text-muted); }
.consent__link { display: inline; }
.consent__link::after { border-block-end-color: currentColor; }
.consent__error { margin: 0; font: var(--text-field-error); color: var(--color-danger); }
.consent__error[hidden] { display: none; }
.form__trap { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.form__submit { display: flex; justify-content: center; }

@media (max-width: 1023.98px) {
  .contact__row { flex-direction: column; gap: 32px; }
  .contact__panel { flex: none; padding-block-end: 60px; border-radius: var(--radius-panel); }
  .contact__plate { display: none; }
  .contact__links { flex-direction: column; align-items: flex-start; gap: 8px; margin-inline-start: 0; clip-path: none; }
  .contact__item::before { display: none; }
  .contact__link { font: var(--text-body-card); color: var(--color-text-body); }
  .contact__link:hover { color: var(--color-text-primary); }
  .contact__card { width: auto; border-radius: var(--radius-panel); }
  .form__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 639.98px) {
  .contact__lead { max-width: none; }
  .consent { min-height: 104px; }
  .form__button { width: 100%; white-space: normal; }
}

/* ---- sections/footer.css ---- */
/* Footer 954:788 / 1127:1435 / 1099:910 / 1108:1170 — 690 · 686 · 992 · 1194 high. color/bg/footer inside the 1440 canvas only (D25),
   padding 80 / space/container-margin / 69, three blocks 56 apart: columns, the legal strip, the wordmark.
   Columns ≥ 1024: brand (flex, 240–420; logo 1346:112 160 × 130, 26, tagline body/base) · gap · Sections 190 · gap · Contact 260 · gap ·
   Company details 210, the three gaps flexing 24–66.67 (1440 → 420 + 66.67 × 3, 1200 → 240 + 46.67 × 3). Below 1132 there is not enough
   room for the minimums: the three text columns give way proportionally (D37). 640–1023: brand across, then the columns in two halves
   (space/gutter apart), rows 32 apart; below 640 one column, rows 32 apart. Column label label/mono in color/text/field-label, 12 above
   the items; items 8 apart (22 on 768 / 375 — a 44 step), company details always 8. Links: Link standalone (body/card).
   Legal strip: 2 · hairline color/border/subtle (Figma's centre stroke at y 3) · 21 · a 22 row — © in color/text/muted and the legal links
   (components/legal-links.css); below 640 they wrap under the ©, 12 lower. Wordmark 1400:638: see D29. */
.site-footer {
  max-width: 1440px;
  margin-inline: auto;
  background-color: var(--color-bg-footer);
}
.site-footer__inner {
  display: flex;
  flex-direction: column;
  gap: 56px;
  max-width: calc(1280px + 2 * var(--space-container-margin));
  margin-inline: auto;
  padding: 80px var(--space-container-margin) 69px;
}

.site-footer__columns { display: flex; align-items: flex-start; }
.site-footer__brand { display: flex; flex: 1 1 0; flex-direction: column; gap: 26px; min-width: 240px; max-width: 420px; }
.site-footer__logo { display: block; align-self: flex-start; border-radius: var(--radius-control); color: var(--color-text-primary); }
.site-footer__logo-mark { width: 160px; height: 130px; }
.site-footer__tagline { font: var(--text-body-base); color: var(--color-text-body); }
.site-footer__gap { flex: 1 1 0; min-width: 24px; max-width: 66.666px; }
.site-footer__col { display: flex; flex: 0 1 190px; flex-direction: column; gap: 12px; }
.site-footer__col--contact { flex-basis: 260px; }
.site-footer__col--company { flex-basis: 210px; }
.site-footer__label {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-field-label);
}
.site-footer__items { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; font: var(--text-body-card); color: var(--color-text-body); }
.site-footer__text { display: block; }

.site-footer__legal {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  position: relative;
  margin-block-start: 2px;
  padding-block-start: 21px;
  border-block-start: var(--stroke-hairline) solid var(--color-border-subtle);
  font: var(--text-body-card);
  color: var(--color-text-muted);
}

/* D29: the wordmark is trimmed to the cap height (1400:638: a 175 box at 250 px), sized min(17.36vw, 250px), in the flow, aria-hidden.
   The gradient paints through background-clip: text; the 0.05em padding (cancelled by the margin) keeps the overshoot of the round G
   inside the painted area. The trailing letter-spacing after the last glyph is taken back by a negative margin, so the layout box ends
   where Figma's 1200-wide node does (the border box still carries it). text-box-trim shortens the box, not the line box, which reaches
   0.327em above the cap line — over the legal strip on desktop widths: the wordmark takes no pointer events and the legal strip is
   positioned so it paints (also as a forced-colors backplate) above it. */
.site-footer__wordmark {
  width: fit-content;
  margin-block: -0.05em;
  margin-inline-end: calc(-1 * var(--tracking-display-wordmark));
  padding-block: 0.05em;
  font: var(--text-display-wordmark);
  font-size: min(17.36vw, 250px);
  line-height: 1.3;
  letter-spacing: var(--tracking-display-wordmark);
  white-space: nowrap;
  color: transparent;
  background-image: var(--gradient-wordmark);
  -webkit-background-clip: text;
  background-clip: text;
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
  pointer-events: none;
  user-select: none;
}
/* Without text-box-trim: cut the 1.3 line box to the cap height with margins (IBM Plex Mono Bold: ascent 1.025, cap 0.698, descent 0.275)
   and move the gradient down so its 0.05em start lands on the cap line. */
@supports not (text-box-trim: trim-both) {
  .site-footer__wordmark { margin-block: -0.327em -0.275em; padding-block: 0; background-position: 0 0.277em; }
}

@media (max-width: 1023.98px) {
  .site-footer__columns { flex-wrap: wrap; justify-content: space-between; row-gap: 32px; }
  .site-footer__brand { flex: 0 0 100%; min-width: 0; max-width: none; }
  .site-footer__gap { display: none; }
  .site-footer__col,
  .site-footer__col--contact,
  .site-footer__col--company { flex: 0 0 calc((100% - var(--space-gutter)) / 2); }
  .site-footer__items { gap: 22px; }
  .site-footer__items--details { gap: 8px; }
}

@media (max-width: 639.98px) {
  .site-footer__columns { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 32px; }
  .site-footer__brand { flex: none; }
  .site-footer__col,
  .site-footer__col--contact,
  .site-footer__col--company { flex: none; width: 100%; }
  .site-footer__legal { flex-direction: column; align-items: flex-start; gap: 12px; }
}

/* ---- sections/form-result.css ---- */
/* No-JS result page of the contact form (public/api/_lib/bootstrap.php): the FormOverlay 776:372 panel on a page of its own — there is
   no frame for it, so the panel sits in a card like the form card 377:672 (color/bg/surface, radius/panel, 30 inside, at most 585 wide)
   centred on the page ground with the container margins. Field errors (422 / 413 without JS) are listed under the form-level message
   in field/error, color/danger. */
.form-result { display: grid; place-items: center; padding: var(--space-container-margin); }
.form-result__card {
  width: 100%;
  max-width: 585px;
  padding: 30px;
  border-radius: var(--radius-panel);
  background-color: var(--color-bg-surface);
}
.form-result__errors { display: flex; flex-direction: column; gap: 8px; font: var(--text-field-error); color: var(--color-danger); }

/* ---- sections/error-404.css ---- */
/* 404 — 883:715 (1440 × 900) / 1219:1149 (768 × 1024) / 1219:4518 (375 × 812), «Блок N» 1470:638, 1508:638, 1536:638. The page fills the
   window: the header (absolute, 118) over a flat color/bg/page ground (all three frames; also beside the 1440 canvas — no frame is wider),
   the body, then the footer strip pushed to the bottom (partials/footer-compact.njk). The sheet margin line (color/border/card) runs from
   the header down to the page bottom at canvas x 40 · 20 · 8, without ticks or dividers. While the cookie banner shows, its space is
   added below the window-high page, so the strip does not move (cookie-banner.css).
   Body: ≥ 1024 the text column 560, 60, the schematic in the rest (FILL, at most 600, scaled when narrower: 1200 → 420 × 140); the
   reference has the text 184 below the header and the schematic 24 lower than the text. 640–1023: one column, 96 / 48, 56 between the
   text and the schematic (FILL, at most 600). ≤ 639: 48 / 32, 40 between. Text column, 28 apart: eyebrow label/mono in color/accent ·
   h1 heading/page in color/text/primary (one size on every width; two lines are allowed below 640) with a 2 px color/accent rule 28
   below it (520; the column below 640) · text body/base in color/text/body · Button primary L. Schematic 887:758 «CIRCUIT OPEN»: a 3 : 1
   drawing — wires and terminals color/border/subtle 2, the open switch color/accent 3.5 with round caps, the gap dashed 3 / 9 — the
   strokes keep their width when the drawing scales (as in 1219:4563); Figma fits the dash pattern to the 164-unit gap, symmetric, with
   a half dash at each end: 14 gaps of 8.714 on the 164 px gap, 8 gaps of 8.446 on the 91.57 px gap at 375; on the widths between,
   the pattern is centred on the gap (its length is 164 / 600 of the drawing: 27.3333cqw, .circuit being as wide as the drawing);
   26 below it the caption label/mono in color/text/label.
   The stroke widths (2, 3.5) and the rule (2) are unbound in Figma (887:760, 887:762, 883:750).
   Footer strip 885:769: a 1 px color/rule/hairline, 20, a 22 row — the caption label/mono in color/text/label and the legal links
   (body/card) at the far end; below 640 the links go under the caption, 8 lower. Space under it: 80 · 24 · 20 (the frames). */
.page-404 {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: calc(100vh + var(--cookie-banner-space, 0px));
  min-height: calc(100dvh + var(--cookie-banner-space, 0px));
  background: var(--color-bg-page);
}
.page-404::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset-block: 118px 0;
  inset-inline-start: calc(max(0px, (100% - 1440px) / 2) + 40px);
  width: var(--stroke-hairline);
  background-color: var(--color-border-card);
  pointer-events: none;
}
.page-404 main { flex: 1 0 auto; padding-block-start: 118px; }

.error-404__inner { display: flex; align-items: flex-start; gap: 60px; padding-block: 184px 48px; }
.error-404__content { display: flex; flex: 0 0 560px; flex-direction: column; align-items: flex-start; gap: 28px; min-width: 0; }
.error-404__eyebrow {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-accent);
}
.error-404__title { align-self: stretch; font: var(--text-heading-page); color: var(--color-text-primary); overflow-wrap: break-word; }
.error-404__title::after { content: ""; display: block; width: 520px; margin-block-start: 28px; border-block-start: 2px solid var(--color-accent); }
.error-404__text { font: var(--text-body-base); color: var(--color-text-body); }

.circuit { display: flex; flex: 1 1 0; flex-direction: column; align-items: flex-start; gap: 26px; min-width: 0; max-width: 600px; margin-block-start: 24px; container-type: inline-size; }
.circuit__drawing { width: 100%; height: auto; fill: none; stroke: var(--color-border-subtle); stroke-width: 2px; }
.circuit__drawing > * { vector-effect: non-scaling-stroke; }
.circuit__line--open {
  stroke-dasharray: 3 8.714;
  stroke-dashoffset: 1.5;
  stroke-dashoffset: calc(1.5px - (mod(27.3333cqw + 0.1px, 11.714px) - 0.1px) / 2);
}
.circuit__switch { stroke: var(--color-accent); stroke-width: 3.5px; stroke-linecap: round; }
.circuit__label {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-label);
}

.footer-strip {
  width: 100%;
  max-width: calc(1280px + 2 * var(--space-container-margin));
  margin-inline: auto;
  padding: 0 var(--space-container-margin) var(--space-container-margin);
}
.footer-strip__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: calc(20px + 22px + var(--stroke-hairline));
  padding-block-start: 20px;
  border-block-start: var(--stroke-hairline) solid var(--color-rule-hairline);
  font: var(--text-body-card);
}
.footer-strip__caption {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-text-label);
}

@media (max-width: 1023.98px) {
  .page-404::before { inset-inline-start: 20px; }
  .error-404__inner { flex-direction: column; align-items: stretch; gap: 56px; padding-block: 96px 48px; }
  .error-404__content { flex: none; }
  .circuit { flex: none; margin-block-start: 0; }
  .footer-strip { padding-block-end: 24px; }
}

@media (max-width: 639.98px) {
  .page-404::before { inset-inline-start: 8px; }
  .error-404__inner { gap: 40px; padding-block: 48px 32px; }
  .error-404__title::after { width: auto; }
  .circuit__line--open { stroke-dasharray: 3 8.446; stroke-dashoffset: calc(1.5px - (mod(27.3333cqw + 0.1px, 11.446px) - 0.1px) / 2); }
  .footer-strip { padding-block-end: var(--space-container-margin); }
  .footer-strip__row { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ---- sections/legal.css ---- */
/* Legal pages (partials/legal-page.njk, D41). Figma has no frame for them («Блок M» 1351:587), so the page is derived from the 404
   (883:715 / 1219:1149 / 1219:4518) and the page shell: the header (absolute, 118) as on every page, the sheet margin line (color/border/card)
   from the header down to the footer at canvas x 40 · 20 · 8 without ticks, the full footer (the legal links live there) and the page
   gradient of the home page.
   Column: at most 560 on every width (the 404's text column 883:747; about 62 characters of body/base — the 404 lets its short text fill
   the container below 1024, a long legal text keeps the measure). 96 below the header (the 404 body's top padding on 768),
   48 below 640; space/section-bottom above the footer. Head, 28 apart as in the 404: eyebrow label/mono in color/accent · h1 heading/page
   in color/text/primary with the 2 px color/accent rule 28 below it (520, the column when narrower) · «Last updated» body/card in
   color/text/muted · the draft note (only while the text is a draft; never shipped, check-dist). Head → text 56 (40 below 640).
   Text: sections 40 apart (32 below 640), inside a section 16 between the h2 (title/value in color/text/primary) and the paragraphs,
   lists and address blocks (body/base in color/text/body); list markers in color/accent. Links in the text: Link inline colours
   (color/link → color/link-hover) with an always-visible underline, as the consent's «Privacy Policy» (391:16), and they may break
   anywhere, so nothing ever widens the page — except the e-mail address, the phone number and the site address, which never break
   (.legal__link--whole: short fixed strings from site.json, at most 135 px, 182 px with WCAG 1.4.12 spacing, against a 280 px column at
   320). Focus: the global outline (it follows a link that wraps, where the Link component's ::before ring cannot), shaped like the Link
   ring of 1352:591 — 6 around the glyph box on every side, r8 (on the 26 px line box that is −4 vertically, 1 inside the spec's −5).
   German text (from S16) hyphenates; links never do. */
.page-legal main { position: relative; padding-block-start: 118px; }
.page-legal main::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset-block: 118px 0;
  inset-inline-start: calc(max(0px, (100% - 1440px) / 2) + 40px);
  width: var(--stroke-hairline);
  background-color: var(--color-border-card);
  pointer-events: none;
}

.legal { display: flex; flex-direction: column; gap: 56px; padding-block: 96px var(--space-section-bottom); }
.legal__head,
.legal__body { display: flex; flex-direction: column; max-width: 560px; min-width: 0; }
.legal__head { align-items: flex-start; gap: 28px; }
.legal__eyebrow {
  font: var(--text-label-mono);
  letter-spacing: var(--tracking-label-mono);
  text-transform: var(--case-label-mono);
  color: var(--color-accent);
}
.legal__title { align-self: stretch; font: var(--text-heading-page); color: var(--color-text-primary); overflow-wrap: break-word; hyphens: auto; }
.legal__title::after { content: ""; display: block; width: min(520px, 100%); margin-block-start: 28px; border-block-start: 2px solid var(--color-accent); }
.legal__updated { font: var(--text-body-card); color: var(--color-text-muted); }
.legal__draft {
  padding: 12px 16px;
  border: var(--stroke-hairline) solid var(--color-accent);
  border-radius: var(--radius-control);
  font: var(--text-body-card-strong);
  color: var(--color-accent);
}

.legal__body { gap: 40px; }
.legal__section {
  display: flex;
  flex-direction: column;
  gap: 16px;
  font: var(--text-body-base);
  color: var(--color-text-body);
  overflow-wrap: break-word;
}
.legal__heading { font: var(--text-title-value); color: var(--color-text-primary); }
.legal__list { display: flex; flex-direction: column; gap: 8px; padding-inline-start: 1.25em; list-style: disc; }
.legal__list li::marker { color: var(--color-accent); }
.legal__link {
  color: var(--color-link);
  text-decoration: underline;
  text-decoration-thickness: var(--stroke-hairline);
  text-underline-offset: 0.2em;
  overflow-wrap: anywhere;
}
.legal__link:hover { color: var(--color-link-hover); }
.legal__link { border-radius: calc(8px - var(--focus-offset-loose) - var(--focus-width)); hyphens: manual; }
.legal__link--whole { white-space: nowrap; }
.legal__section:lang(de) { hyphens: auto; }

@media (max-width: 1023.98px) {
  .page-legal main::before { inset-inline-start: 20px; }
}

@media (max-width: 639.98px) {
  .page-legal main::before { inset-inline-start: 8px; }
  .legal { gap: 40px; padding-block-start: 48px; }
  .legal__body { gap: 32px; }
}
