/* ==========================================================================
   site-theme.css  —  IBM Plex Sans typography + dark theme + hero video
   Loaded after webflow.shared.css and odyn-bundle.css.

   Nothing here touches animation logic: only font, colour and the hero
   video layer. Scramble/SplitText spans inherit type from their parents,
   so every animated text element picks up IBM Plex Sans automatically.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. IBM Plex Sans — self-hosted
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-300-normal.woff2") format("woff2");
  font-weight: 300; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-400-italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-500-normal.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-600-normal.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-700-normal.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* --------------------------------------------------------------------------
   2. Typography tokens
   The design system routes every heading/paragraph through these three
   variables, so redefining them re-fonts the entire site in one place.
   -------------------------------------------------------------------------- */
:root {
  --pm-font: "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif;

  --_fonts---fonts--heading:   var(--pm-font);
  --_fonts---fonts--paragraph: var(--pm-font);
  --_fonts---fonts--eyebrow:   var(--pm-font);
}

/* Elements that hardcoded a family instead of using the tokens.
   webflow-icons is deliberately left alone — it is an icon font. */
html,
body,
.text-align-center,
input, select, textarea, button {
  font-family: var(--pm-font);
}

/* Plex renders slightly denser than PPFrama; nudge optical settings so the
   page keeps its original colour and rhythm. */
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "kern" 1, "liga" 1;
}

/* --------------------------------------------------------------------------
   3. Type re-tuning
   PPFrama is wider and more geometric than Plex. Display sizes need a little
   negative tracking and slightly tighter leading to hold the same silhouette;
   small text needs a touch of positive tracking to stay legible on dark.
   -------------------------------------------------------------------------- */
:root {
  --_headings---heading-xxl--letter-spacing: -.032em;
  --_headings---heading-xxl--line-height: 1.02em;

  --_headings---heading-xl--letter-spacing: -.028em;
  --_headings---heading-xl--line-height: 1.14em;

  --_headings---heading-l--letter-spacing: -.026em;
  --_headings---heading-l--line-height: 1.15em;

  --_headings---heading-m--letter-spacing: -.02em;
  --_headings---heading-m--line-height: 1.26em;

  --_headings---heading-s--letter-spacing: -.017em;
  --_headings---heading-s--line-height: 1.28em;

  --_headings---heading-xs--letter-spacing: -.008em;

  --_paragraphs---paragraph-xl--letter-spacing: -.012em;
  --_paragraphs---paragraph-l--letter-spacing: -.008em;
  --_paragraphs---paragraph-m--letter-spacing: .004em;
  --_paragraphs---paragraph-s--letter-spacing: .006em;
  --_paragraphs---paragraph-xs--letter-spacing: .01em;
  --_paragraphs---paragraph-xxs--letter-spacing: .014em;
}

/* --------------------------------------------------------------------------
   4. Dark palette
   -------------------------------------------------------------------------- */
:root {
  --pm-bg:             #0b0b0d;  /* page */
  --pm-bg-elev:        #131318;  /* nav, cards, modal */
  --pm-bg-elev-2:      #1b1b22;  /* hover / raised */

  --pm-heading:        #f6f6f9;
  --pm-text:           #e4e4ea;
  --pm-muted:          #9b9baa;

  --pm-border:         #2a2a33;
  --pm-border-strong:  #3b3b47;

  --pm-accent:         #ff2d6e;  /* fire pink - 5.4:1 on the page ground */
  --pm-accent-light:   #ff7ba6;  /* hover / secondary emphasis */
  --pm-accent-deep:    #33091e;  /* deep hot-pink section surface */
  --pm-accent-mid:     #7a2246;  /* outlines inside the deep-pink section */
  --pm-on-deep:        #ffd6e4;  /* foreground on the deep-pink section */
  --pm-accent-soft:    #6b1a3c;  /* hairlines inside the deep-pink section */

  --pm-olive:          #c7beaa;
  --pm-olive-surface:  #221f18;
  --pm-olive-border:   #4a442f;

  --pm-on-accent:      #1a0410;  /* dark ink reads on fire pink; white does not */
}

/* Swatches kept in sync so the four direct consumers follow the theme. */
:root {
  --_colors---swatch--off-white:    var(--pm-bg);
  --_colors---swatch--black:        var(--pm-text);
  --_colors---swatch--grey:         var(--pm-border);
  --_colors---swatch--grey-40:      #ffffff1f;
  --_colors---swatch--indigo:       var(--pm-accent);
  --_colors---swatch--indigo-light: var(--pm-accent-light);
  --_colors---swatch--olive:        var(--pm-olive-surface);
  --_colors---swatch--olive-dark:   var(--pm-olive-border);
}

/* The four theme blocks the site switches between while scrolling.
   theme-base has no rule of its own and falls through to :root. */
:root,
.theme-dark, .theme-light {
  --_theme---background:          var(--pm-bg);
  --_theme---body-text:           var(--pm-text);
  --_theme---heading:             var(--pm-heading);
  --_theme---border:              var(--pm-border);
  --_theme---button-outline:      var(--pm-border-strong);
  --_theme---button-text:         var(--pm-text);
  --_theme---cursor-select-bg:    var(--pm-accent);
  --_theme---cursor-select-text:  var(--pm-on-accent);
  --_theme---background-olive:    var(--pm-olive-surface);
  --_theme---background-indigo:   var(--pm-accent-deep);
  --_theme---nav--nav-bg:         #0b0b0dcc;
  --_theme---nav--nav-body:       var(--pm-text);
  --_theme---nav--nav-outline:    var(--pm-border);
  --_theme---nav--menu-cta-bg:    var(--pm-accent);
  --_theme---transition--shutter: var(--pm-accent);
}

.theme-olive {
  --_theme---background:          var(--pm-olive-surface);
  --_theme---body-text:           var(--pm-olive);
  --_theme---heading:             #efe9dc;
  --_theme---border:              var(--pm-olive-border);
  --_theme---button-outline:      var(--pm-olive-border);
  --_theme---button-text:         var(--pm-olive);
  --_theme---nav--nav-bg:         #221f18e6;
  --_theme---nav--nav-body:       var(--pm-olive);
  --_theme---nav--nav-outline:    var(--pm-olive-border);
  --_theme---nav--menu-cta-bg:    var(--pm-olive);
  --_theme---transition--shutter: var(--pm-olive-surface);
  --_theme---background-olive:    var(--pm-olive-surface);
  --_theme---background-indigo:   var(--pm-accent-deep);
}

.theme-indigo {
  --_theme---background:          var(--pm-accent-deep);
  --_theme---body-text:           var(--pm-on-deep);
  --_theme---heading:             #ffffff;
  --_theme---border:              var(--pm-accent-soft);
  --_theme---button-outline:      var(--pm-accent-mid);
  --_theme---button-text:         var(--pm-on-deep);
  --_theme---nav--nav-bg:         #33091ee6;
  --_theme---nav--nav-body:       var(--pm-on-deep);
  --_theme---nav--nav-outline:    var(--pm-accent-mid);
  --_theme---nav--menu-cta-bg:    var(--pm-accent);
  --_theme---transition--shutter: var(--pm-accent-deep);
  --_theme---background-olive:    var(--pm-olive-surface);
  --_theme---background-indigo:   var(--pm-accent-deep);
}

/* --------------------------------------------------------------------------
   5. Rules that hardcoded light-theme colours
   -------------------------------------------------------------------------- */
html, body { background-color: var(--pm-bg); color: var(--pm-text); }
mark { color: var(--pm-bg); background-color: var(--pm-accent-light); }

/* headings sit a step brighter than body copy */
h1, h2, h3, h4, h5, h6,
.heading-xxl, .heading-xl, .heading-l, .heading-m, .heading-s, .heading-xs {
  color: var(--_theme---heading);
}

/* nav + menu */
.hamburger-nav__a:hover { border-bottom-color: var(--pm-border-strong); }
.arrow-icon.is--reverse-color { color: var(--pm-bg); }

/* transitions / shutters */
.transition__dark   { background-color: #000; }
.shutter-scroll__bg { background-color: var(--pm-bg); }

/* pricing + dividers */
.pricing__row-2  { border-top-color: var(--pm-border); }
.scramble-button { color: var(--pm-text); border-color: var(--pm-border-strong); }

/* modal */
.modal__backdrop      { background-color: #000000d9; }
.modal__scrollbar     { background-color: var(--pm-border); }
.modal-scrollbar__bar { background-color: var(--pm-text); }
.modal__close         { color: var(--pm-text); }
.modal-close__bg      { background-color: var(--pm-bg-elev-2); }
.modal-close__bg::before { background-color: var(--pm-text); }
.modal-close__icon    { color: var(--pm-text); }
.modal__close:is(:hover, :focus-visible) .modal-close__icon { color: var(--pm-bg); }

/* Hairlines, rules and dividers.
   The design system draws these with var(--_theme---body-text) — a light-theme
   idiom where the ink colour doubles as the rule colour. Inverted, that turns
   every divider into a glaring near-white line, so structure gets the border
   token instead and only progress fills keep a bright colour. */
.pricing__row,
.pricing__note-row,
.pricing__note-top,
.how__collection,
.how__item          { border-color: var(--pm-border); }
.divider-horizontal,
.different__counter-line,
.different__content-line,
.news-item__stroke,
.news-progress__wrapper { background-color: var(--pm-border); }

/* progress fills — the accent, used consistently */
.different__progress-bar,
.news-progress          { background-color: var(--pm-accent); }

/* news */
.news-item__stroke::before { background-color: var(--pm-border-strong); }
.news-item__arrow-wrapper  { color: var(--pm-bg); }

/* "copied to clipboard" toast — injected at runtime by odyn-bundle.js with
   hardcoded light-theme colours. */
[data-copy-alert] {
  background-color: var(--pm-bg-elev-2);
  border-color: var(--pm-accent);
  color: var(--pm-text);
}

/* Accent section (the site keeps the .theme-indigo class name; only the hue
   changed). .system paints itself with --_theme---background-indigo and takes
   its text colour from --_theme---background. On a light theme those were
   "brand block" and "page ground" — inverted for dark, the text token would
   land on the same value as the background, so the foreground is set here. */
.system__wrap[data-system-visual] { background-color: var(--pm-accent-deep); }
.system-visual__pin               { background-color: var(--pm-accent-deep); }
.system                           { color: var(--pm-on-deep); }
.system__chapter                  { border-color: var(--pm-accent-mid); }
.system-visual__tree .system__chapter[data-system-visual-collapsed="false"] {
  box-shadow: inset 0 0 0 1px var(--pm-accent-mid);
}

/* rich text tokens */
.rich-text-regular {
  --rt-card-bg:     var(--pm-bg-elev-2);
  --rt-text-full:   var(--pm-text);
  --rt-text-muted:  #ffffffa8;
  --rt-list-stroke: #ffffff5c;
  --rt-quote-bg:    var(--pm-bg-elev);
  --rt-quote-text:  var(--pm-heading);
  --rt-link-color:  var(--pm-accent);
}

/* form controls */
input, select, textarea {
  background-color: var(--pm-bg-elev);
  color: var(--pm-text);
  border-color: var(--pm-border);
}
::placeholder { color: var(--pm-muted); }
::selection { background: var(--pm-accent); color: var(--pm-on-accent); }

/* --------------------------------------------------------------------------
   6. Hero background video
   Sits at the bottom of the hero stacking context; .intro and .cta are
   z-index 2 and stay above it untouched.
   -------------------------------------------------------------------------- */
.hero { position: relative; min-height: 100svh; isolation: isolate; }

.hero-video {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background-color: var(--pm-bg);
}

/* instant placeholder: visible until the video paints its first frame */
.hero-video__poster {
  position: absolute;
  inset: 0;
  background: url("../img/hero-poster.jpg") center / cover no-repeat;
}

.hero-video__el {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* Readability scrim. One gradient layer only — stacking a flat colour under it
   compounds the alpha and crushes the footage. Darkest at the very top (nav)
   and bottom (CTA), ~.58 through the headline band, which keeps white display
   type well clear of AA contrast over the brightest frames. */
.hero-video__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(11, 11, 13, 0.86) 0%,
    rgba(11, 11, 13, 0.40) 10%,
    rgba(11, 11, 13, 0.25) 33%,
    rgba(11, 11, 13, 0.15) 60%,
    rgba(11, 11, 13, 0) 100%
  );
}

/* Slight lift so the footage reads behind the content, without spending any
   text contrast. contrast() pivots at mid-grey: everything below it comes up,
   everything above it goes down. So the crushed shadows open (+27% at the 25th
   percentile) while the highlights — the pixels that actually sit behind the
   headline — get marginally darker, and measured contrast against #e4e4ea
   improves rather than degrades (line 1 6.85 -> 6.87, line 2 2.01 -> 2.21,
   worst-5% background, averaged over three frames).

   A flat brightness() lift was measured and rejected: brightness(1.04) raised
   the highlights too and cost 0.26 of contrast ratio on the first headline
   line. The scrim gradient above is deliberately untouched.

   The poster carries the same filter so there is no brightness pop when the
   video paints its first frame, and so the reduced-motion path (poster only)
   gets the same treatment. */
.hero-video__el,
.hero-video__poster { filter: contrast(0.9) saturate(1.06); }

/* --------------------------------------------------------------------------
   6b. Radial focus — progressive edge blur
   The portrait stays sharp and the frame softens toward the edges, which reads
   as depth of field rather than a vignette (nothing is darkened; only sharpness
   changes).

   Why three masked layers instead of one blur:
   a single blurred copy at a fixed radius switches on at the mask edge and
   shows as a visible ring. Each layer here blurs the layer beneath it, so the
   radii compound outward — roughly 2px, then 2+6, then 2+6+14 — giving a
   continuous ramp with no seam. Blurring the whole video and masking a sharp
   copy back over it was the alternative; it needs a second decoded video
   element, so this costs less.

   Focus centre 46% 58% is where the subject actually sits: the luminance
   centroid of the footage measures x 43.6% / y 60.4%, nudged toward the head.

   inset: -6% pushes each layer's own edges outside the frame. backdrop-filter
   clamps its sampling at the element boundary, which smears the outermost
   pixels; .hero-video has overflow: hidden, so those edges are cropped away
   before they are ever seen.

   Layers are inert without backdrop-filter support: they paint nothing and the
   video simply stays sharp.
   -------------------------------------------------------------------------- */
.hero-video__focus {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hero-video__blur {
  position: absolute;
  inset: -6%;
  display: block;
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .hero-video__blur.is--near {
    -webkit-backdrop-filter: blur(2px);
            backdrop-filter: blur(2px);
    -webkit-mask-image: radial-gradient(ellipse 52% 56% at 46% 58%, transparent 30%, #000 62%);
            mask-image: radial-gradient(ellipse 52% 56% at 46% 58%, transparent 30%, #000 62%);
  }
  .hero-video__blur.is--mid {
    -webkit-backdrop-filter: blur(6px);
            backdrop-filter: blur(6px);
    -webkit-mask-image: radial-gradient(ellipse 52% 56% at 46% 58%, transparent 48%, #000 80%);
            mask-image: radial-gradient(ellipse 52% 56% at 46% 58%, transparent 48%, #000 80%);
  }
  .hero-video__blur.is--far {
    -webkit-backdrop-filter: blur(14px);
            backdrop-filter: blur(14px);
    -webkit-mask-image: radial-gradient(ellipse 52% 56% at 46% 58%, transparent 64%, #000 98%);
            mask-image: radial-gradient(ellipse 52% 56% at 46% 58%, transparent 64%, #000 98%);
  }
}

/* Portrait viewports crop the footage tighter, so the sharp core is widened to
   keep the whole head clear, and the stack drops to two layers — three
   full-width backdrop filters is more compositing than a phone should spend on
   decoration. */
@media (max-width: 767px) {
  .hero-video__blur.is--mid { display: none; }
  .hero-video__blur.is--near {
    -webkit-mask-image: radial-gradient(ellipse 66% 46% at 48% 56%, transparent 38%, #000 78%);
            mask-image: radial-gradient(ellipse 66% 46% at 48% 56%, transparent 38%, #000 78%);
  }
  .hero-video__blur.is--far {
    -webkit-backdrop-filter: blur(10px);
            backdrop-filter: blur(10px);
    -webkit-mask-image: radial-gradient(ellipse 66% 46% at 48% 56%, transparent 58%, #000 98%);
            mask-image: radial-gradient(ellipse 66% 46% at 48% 56%, transparent 58%, #000 98%);
  }
}

/* Keep the WebGL hero art above the video and read it as light-on-dark.
   The scene is greyscale (black lines on off-white), so inverting it keeps
   every animated pixel intact while flipping it into the dark palette;
   screen blending lets the video show through what is now the dark ground.

   Inverting alone leaves the ground at #1a1a1a, which screen would still
   add to the video as a visible rectangle; contrast(1.35) crushes that ground
   to true black (screen against black is a no-op) while the line art clamps
   to white.

   The blend has to sit on .unicorn, not on the canvas: .unicorn,
   .unicorn-visual and .unicorn-visual__item each carry a z-index and so each
   opens a stacking context. A mix-blend-mode on the canvas would be trapped
   inside the innermost one and never reach the video. .unicorn is a direct
   child of .hero, whose `isolation: isolate` makes it the blend group root. */
.unicorn-visual__item canvas { filter: invert(1) contrast(1.35) brightness(0.25);}
.hero .unicorn { mix-blend-mode: screen; }

@media (prefers-reduced-motion: reduce) {
  .hero-video__el { display: none; }   /* poster layer remains visible */
}

/* --------------------------------------------------------------------------
   7. Language selector — dropdown
   One trigger showing the active language, opening a short list of links. It
   replaces the five-option row; the trigger keeps the nav's own scramble hover
   (data-scramble-hover), so it behaves like every other nav control, and the
   mobile menu's reveal animation still finds it through that attribute.

   Labels stay lowercase. The list is an opaque surface rather than frosted
   glass: it opens over the hero video, where a solid ground reads better, and
   it keeps compositing effects out of the nav — a blended child has disabled
   the bar's own backdrop blur here before.
   -------------------------------------------------------------------------- */
.lang-dd {
  position: relative;
  align-items: center;
}

.lang-dd__trigger {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  margin: 0;
  padding: .2em .1em;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: inherit;
  letter-spacing: inherit;
  text-transform: lowercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.lang-dd__value { color: var(--pm-accent); }

/* Pixel chevron in the same 2px grid as the site's arrows. Sized in whole
   pixels so each 2-unit cell lands on 2 device pixels: at .6em it came out
   7x4px, which put every cell on 1.4px and anti-aliased the V into a smudge. */
.lang-dd__chevron {
  width: 10px;
  height: 6px;
  shape-rendering: crispEdges;
  flex: none;
  opacity: .7;
  transition: transform .25s ease, opacity .25s ease;
}
.lang-dd__trigger:hover .lang-dd__chevron,
.lang-dd[data-open="true"] .lang-dd__chevron { opacity: 1; }
.lang-dd[data-open="true"] .lang-dd__chevron { transform: rotate(180deg); }

.lang-dd__trigger:focus-visible {
  outline: 1px solid var(--pm-accent);
  outline-offset: .35em;
}

.lang-dd__list {
  position: absolute;
  top: calc(100% + .55em);
  right: 0;
  z-index: 20;
  min-width: 6.5em;
  margin: 0;
  padding: .3em;
  list-style: none;
  /* Fully opaque. At 96% the full stop closing the hero headline, which sits
     directly beneath the open list, ghosted through as a grey dot. */
  background: var(--pm-bg-elev);
  border: 1px solid var(--pm-border);
  box-shadow: 0 1.2em 2.4em -.8em rgba(0, 0, 0, .75);
  text-transform: lowercase;
}
.lang-dd__list[hidden] { display: none; }
.lang-dd__list:not([hidden]) { animation: lang-dd-in .18s ease-out; }

@keyframes lang-dd-in {
  from { opacity: 0; transform: translateY(-.3em); }
  to   { opacity: 1; transform: none; }
}

.lang-dd__list li { margin: 0; padding: 0; }

.lang-dd__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1em;
  min-height: 2.6em;              /* comfortable touch target */
  padding: 0 .75em;
  color: var(--pm-text);
  text-decoration: none;
  transition: background-color .15s ease, color .15s ease;
}
.lang-dd__option:hover {
  background: var(--pm-bg-elev-2);
  color: var(--pm-heading);
}
.lang-dd__option:focus-visible {
  outline: none;
  background: var(--pm-bg-elev-2);
  color: var(--pm-heading);
  box-shadow: inset 2px 0 0 var(--pm-accent);
}

/* active language: fire pink, marked with a single pixel */
.lang-dd__option[aria-current="true"] { color: var(--pm-accent); }
.lang-dd__option[aria-current="true"]::after {
  content: "";
  width: .32em;
  height: .32em;
  flex: none;
  background: currentColor;
}

@media (prefers-reduced-motion: reduce) {
  .lang-dd__list:not([hidden]) { animation: none; }
  .lang-dd__chevron { transition: none; }
}

/* --------------------------------------------------------------------------
   8. Nav wordmark fit
   The mark sits in column 1 of the nav's first block
   (#w-node-_03315f6e-8b44-0135-ba9b-cc0559c82b13-59c82b11); the menu button is
   pinned to column 2. It used to carry a fixed 8em width, which left it
   filling a little under half the block's height.

   Height drives it now. The block's content box is 3.3em tall at every
   breakpoint (its font-size is what changes), so an em height tracks the block
   exactly without hard-coding pixels. It has to be a length rather than
   height:100%: the nav's columns are minmax(auto, 1fr) and size themselves to
   their content, and a percentage height is indefinite while that intrinsic
   width is being measured — the column would size to a mark it cannot see yet
   and the artwork would overflow into the nav links.

   width:auto lets the viewBox ratio set the width, preserveAspectRatio
   "xMidYMid meet" means it can never clip or distort, and the max-width guard
   stops the mark crowding the nav on narrow screens — past that point it
   letterboxes inside its own box instead of pushing the layout.
   -------------------------------------------------------------------------- */
#w-node-_03315f6e-8b44-0135-ba9b-cc0559c82b13-59c82b11 {
  grid-template-columns: auto auto;
  column-gap: 1.15em;
}

#w-node-_03315f6e-8b44-0135-ba9b-cc0559c82b13-59c82b11 .logo__btn {
  display: flex;
  align-items: center;
}

/* replaces the fixed 8em / 10em widths in webflow.shared.css */
.monolayer__logo.is--nav {
  height: 2.3em;
  width: auto;
  max-width: 40vw;
  display: block;
}

/* the menu button keeps its own size and centres against the taller mark */
#w-node-_03315f6e-8b44-0135-ba9b-cc0559c82b14-59c82b11 {
  align-self: center;
}

/* --------------------------------------------------------------------------
   9. Frosted nav
   .nav__bg is the layer that paints the bar (absolute, inset 0, under
   .nav__main). Blurring that layer rather than .nav itself keeps the nav's own
   text out of the filter, so only what scrolls behind is softened.

   The tint is lightened only where the backdrop can actually be blurred. If
   backdrop-filter is unsupported the original, more opaque tint stays put, so
   contrast never depends on the effect landing. Blur is kept moderate — this
   layer repaints while the page scrolls.
   -------------------------------------------------------------------------- */
.nav__bg {
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  :root,
  .theme-dark, .theme-light { --_theme---nav--nav-bg: #0b0b0db3; }
  .theme-olive              { --_theme---nav--nav-bg: #221f18bf; }
  .theme-indigo             { --_theme---nav--nav-bg: #33091ebf; }
}

/* --------------------------------------------------------------------------
   10. Pixel-arrow stage separators
   Same <svg class="arrow-icon"> the main button uses, so the separators are
   the site's own visual language rather than a new icon. The plop animation on
   those rects is scoped to .main-btn / .news-item__stroke, so a bare
   .arrow-icon renders static here — which is what a divider wants.
   -------------------------------------------------------------------------- */
.stage-arrow {
  width: .4em;
  height: .4em;
  margin: 0 .3em;
  display: inline-block;
  vertical-align: middle;
  opacity: .6;
  flex: none;
}

/* --------------------------------------------------------------------------
   11. Sentence capitalisation
   Every heading class carried text-transform: lowercase, which is why the copy
   read as one long lowercase run — and why "iOS" and "SEO" came out as "ios"
   and "seo" no matter how they were authored. Turning the transform off lets
   the markup decide: sentences start with a capital, abbreviations keep their
   own casing, and the brand stays lowercase because it is written that way.
   -------------------------------------------------------------------------- */
.heading-xxl, .heading-xl, .heading-l, .heading-m, .heading-s, .heading-xs,
.rich-text-regular h1, .rich-text-regular h2, .rich-text-regular h3,
.rich-text-regular h4, .rich-text-regular h5, .rich-text-regular h6 {
  text-transform: none;
}

/* --------------------------------------------------------------------------
   12. Pricing section background
   A real section background, not an inline image: two pseudo-elements inside
   .pricing (already position:relative; overflow:hidden) — the photograph on
   ::before, the readability scrim on ::after. cover + center keeps the crop
   cinematic at any viewport, and the section keeps its solid colour underneath
   as the fallback while the image loads.
   -------------------------------------------------------------------------- */
.pricing::before,
.pricing::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

/* Fixed attachment sizes the photograph to the viewport rather than to the
   whole 3000px-tall section. Stretching it over the full section height would
   upscale it about 2.7x and crop to a narrow vertical slice; anchoring it to
   the viewport keeps it sharp, keeps the subject in frame, and reads as a
   cinematic backdrop the content scrolls over. Touch devices fall back to
   scroll, where fixed is unreliable and costly. */
.pricing::before {
  background-image: url("../img/pricing-bg.jpg");
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

@media (hover: none), (pointer: coarse) {
  .pricing::before { background-attachment: scroll; }
}




/* Scrim: heavier top and bottom so the headline and the plan tables read, a
   little more open through the middle where the artwork is worth seeing. */
.pricing::after {
  background: linear-gradient(to bottom,
    rgba(11, 11, 13, .80) 0%,
    rgba(11, 11, 13, .70) 24%,
    rgba(11, 11, 13, .60) 55%,
    rgba(11, 11, 13, .80) 100%)
}

/* Phones: the photograph becomes a band at the top of the section instead of a
   full-height cover.

   Why it has to change. With attachment falling back to scroll, `cover` sizes
   the image to the whole section, and the section is far taller than it is
   wide. At 500px across and 1307px tall the crop maths are: scale 1.17, so the
   2000px-wide source is upscaled to 2331px and then cropped to 500 — the
   viewer sees the middle 21% of the frame, softened by the upscale. The subject
   is a left-facing portrait, so that middle slice is the back of the head; the
   face and glasses are cropped away entirely. Stretched, soft, badly cropped
   and unrelated to the section, which is exactly the reported symptom.

   A 440px band inverts every one of those: scale drops to 0.39, so the image is
   downscaled (sharp, never upscaled) and 64% of the frame stays visible, which
   keeps the face in shot. The position is biased slightly left and up to hold
   the glasses in frame. The mask dissolves the lower edge into the section
   colour so it reads as the section opening with an image rather than a
   photograph that has been cut off.

   svh with a vh fallback: on phones the dynamic toolbar makes vh jump. */
@media (max-width: 767px) {
  .pricing::before {
    inset: 0 0 auto 0;
    height: min(56vh, 440px);
    height: min(56svh, 440px);
    background-position: 44% 42%;
    background-attachment: scroll;
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 56%, rgba(0, 0, 0, .45) 82%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 56%, rgba(0, 0, 0, .45) 82%, transparent 100%);
  }

  /* Scrim retuned to the band: lighter where the artwork is worth seeing,
     firmer again from the headline down so the plan tables keep their contrast
     against the section colour. */
  .pricing::after {
    background: linear-gradient(to bottom,
      rgba(11, 11, 13, .68) 0%,
      rgba(11, 11, 13, .53) 22%,
      rgba(11, 11, 13, .72) 46%,
      rgba(11, 11, 13, .84) 70%,
      rgba(11, 11, 13, .84) 100%)
  }
}

/* Lift the real content above both layers.
   :not(.shutter-scroll-transition) is load-bearing, not tidiness. The shutter
   is a direct child of .pricing and is positioned absolute by the base sheet
   (inset: auto 0 0). A blanket `.pricing > *` rule re-positions it to relative,
   which drops it into normal flow, where its ~10em of rows occupy real height
   at the top of the section and push every piece of content down — measured at
   626px on mobile and 709px on desktop, which was the whole of the section's
   unexplained top gap. */
.pricing > *:not(.shutter-scroll-transition) {
  position: relative;
  z-index: 1;
}

/* The "olive" scroll shutter — the wipe at the end of the quote section that
   runs into pricing. Its colour is the wipe target, so it moved off the olive
   surface onto the deep hot-pink one (#33091e) to match the section it opens
   into. :where() adds no specificity, so this plain-class rule wins on order. */
.shutter-scroll-transition:where(.w-variant-f591d1a7-6857-9b10-2dbf-f0bb86b8b329) {
  color: var(--pm-accent-deep);
}

/* the 50% positioning line */
.pricing__claim {
  margin-top: .9em;
  max-width: 34ch;
}

.pricing__claim-accent {
  color: var(--pm-accent);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   13. Hero AI tool-stack signature
   Bottom-right of the hero, above the video but below the headline and CTA in
   reading order. Sized in em off the hero so it scales with the layout, and
   the per-logo tweaks even out the optical weight — the traced Midjourney mark
   is line art and reads lighter than the solid Gemini star at the same height.
   -------------------------------------------------------------------------- */
.hero-tools {
  /* The nav sits above the hero in flow, so the hero's own bottom edge is one
     nav-height below the fold. That height is min-height: 3.5em on the nav,
     and the nav shares the hero's fluid font-size — measured identical at
     1418/1258/1002/746/512 — so offsetting by 3.5em keeps the group on screen
     at every breakpoint without hard-coding pixels or reading layout in JS. */
  --hero-fold-offset: 3.5em;
  position: absolute;
  right: var(--_layout---container--container-padding, 1.25em);
  bottom: calc(var(--hero-fold-offset) + 1.6em);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .55em;
  pointer-events: none;
}

.hero-tools__row {
  display: flex;
  align-items: center;
  gap: 2.4em;
}

.hero-tools__logo {
  display: block;
  width: auto;
  height: 2.0625em;         /* half the previous size */
  opacity: .62;
  filter: brightness(0) invert(1);   /* guarantees flat white whatever ships */
}

/* optical-weight matching between four very different marks */
.hero-tools__logo.is--claude     { height: 2.16em; }
.hero-tools__logo.is--chatgpt    { height: 1.95em; }
.hero-tools__logo.is--midjourney { height: 2.25em; opacity: .7; }
.hero-tools__logo.is--gemini     { height: 1.89em; }

@media screen and (max-width: 991px) {
  .hero-tools { bottom: calc(var(--hero-fold-offset) + 1.2em); gap: .45em; }
  .hero-tools__row { gap: 2em; }
  .hero-tools__logo { height: 1.725em; }
  .hero-tools__logo.is--claude     { height: 1.8em; }
  .hero-tools__logo.is--chatgpt    { height: 1.62em; }
  .hero-tools__logo.is--midjourney { height: 1.89em; }
  .hero-tools__logo.is--gemini     { height: 1.575em; }
}

/* On phones the CTA owns the bottom centre, so the label is dropped and the
   marks tighten into a single quiet row that still clears the button. */
@media screen and (max-width: 767px) {
  .hero-tools { bottom: calc(var(--hero-fold-offset) + .9em); }
  .hero-tools__row { gap: 1.6em; }
  .hero-tools__logo { height: 1.5em; opacity: .55; }
  .hero-tools__logo.is--claude     { height: 1.575em; }
  .hero-tools__logo.is--chatgpt    { height: 1.425em; }
  .hero-tools__logo.is--midjourney { height: 1.65em; opacity: .62; }
  .hero-tools__logo.is--gemini     { height: 1.38em; }
}

/* --------------------------------------------------------------------------
   14. Contact form
   Lives inside the modal every CTA opens. Field styling follows the rich-text
   block it sits under so the modal reads as one document.
   -------------------------------------------------------------------------- */
.contact {
  margin-top: 2.5em;
  padding-top: 2em;
  border-top: 1px solid var(--pm-border);
}

.contact__form {
  display: flex;
  flex-direction: column;
  gap: 1.15em;
  margin-top: 1.5em;
  max-width: 34em;
}

/* Honeypot. Off-screen rather than display:none — some bots skip hidden
   inputs, far fewer skip ones that are merely positioned away. Kept out of the
   tab order and off the accessibility tree so no real visitor can reach it. */
.contact__gotcha {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact__field {
  display: flex;
  flex-direction: column;
  gap: .4em;
}

.contact__label {
  font-size: .8em;
  letter-spacing: .04em;
  color: var(--pm-muted);
}

.contact__input {
  width: 100%;
  box-sizing: border-box;
  padding: .65em .8em;
  color: var(--pm-text);
  background-color: #ffffff08;
  border: 1px solid var(--pm-border);
  border-radius: 0;
  font: inherit;
  font-size: .95em;
  transition: border-color .2s ease, background-color .2s ease;
}

.contact__input:hover { border-color: var(--pm-border-strong); }

.contact__input:focus {
  outline: none;
  border-color: var(--pm-accent);
  background-color: #ffffff0f;
}

.contact__input[aria-invalid="true"] { border-color: var(--pm-accent-light); }

.contact__textarea { resize: vertical; min-height: 6em; }

.contact__error {
  min-height: 1em;
  font-size: .78em;
  color: var(--pm-accent-light);
}

.contact__submit {
  align-self: flex-start;
  margin-top: .4em;
  padding: .75em 1.9em;
  color: var(--pm-on-accent);
  background-color: var(--pm-accent);
  border: 1px solid var(--pm-accent);
  border-radius: 0;
  font: inherit;
  font-size: .95em;
  letter-spacing: .02em;
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease, opacity .2s ease;
}

.contact__submit:hover {
  background-color: transparent;
  color: var(--pm-accent);
}

.contact__submit:disabled,
.contact__submit[data-busy="true"] { opacity: .55; cursor: default; }

.contact__status {
  min-height: 1.2em;
  margin: 0;
  font-size: .9em;
}

.contact__status[data-state="ok"]      { color: #6ee7a8; }
.contact__status[data-state="error"]   { color: var(--pm-accent-light); }
.contact__status[data-state="pending"] { color: var(--pm-muted); }

.contact__note {
  margin: 0;
  font-size: .78em;
  color: var(--pm-muted);
  opacity: .8;
}

@media screen and (max-width: 767px) {
  .contact__form { max-width: none; }
  .contact__submit { align-self: stretch; text-align: center; }
}

/* --------------------------------------------------------------------------
   15. Contact lightbox
   A native <dialog>: focus trapping, inert background, Escape-to-close and
   focus restoration all come from the platform rather than from script.
   ::backdrop is what blurs the page behind it.
   -------------------------------------------------------------------------- */
.lightbox {
  width: min(38em, calc(100vw - 2.5em));
  max-width: none;
  max-height: min(86vh, 52em);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pm-text);
  overflow: visible;
}

.lightbox::backdrop {
  background: rgba(8, 8, 10, .62);
  -webkit-backdrop-filter: blur(18px) saturate(120%);
  backdrop-filter: blur(18px) saturate(120%);
}

/* the glass itself */
.lightbox__panel {
  position: relative;
  box-sizing: border-box;
  max-height: inherit;
  padding: 2.4em 2.4em 2.2em;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: rgba(20, 20, 26, .72);
  -webkit-backdrop-filter: blur(30px) saturate(150%);
  backdrop-filter: blur(30px) saturate(150%);
  border: 1px solid #ffffff1f;
  box-shadow: 0 1px 0 #ffffff14 inset, 0 30px 90px rgba(0, 0, 0, .6);
}

/* Without backdrop-filter the panel has to carry its own contrast, or the
   form would sit on a half-transparent ground with the page showing through. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .lightbox__panel { background: #14141a; }
  .lightbox::backdrop { background: rgba(8, 8, 10, .9); }
}

.lightbox__close {
  position: absolute;
  top: .9em;
  right: .9em;
  width: 2.2em;
  height: 2.2em;
  padding: .55em;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--pm-muted);
  background: transparent;
  border: 1px solid transparent;
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease;
}

.lightbox__close:hover { color: var(--pm-accent); border-color: var(--pm-border); }
.lightbox__close:focus-visible { outline: 1px solid var(--pm-accent); outline-offset: 2px; }
.lightbox__close svg { width: 100%; height: auto; display: block; }

/* the form block loses its divider here — the panel already frames it */
.lightbox .contact {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.lightbox .contact__form { max-width: none; }

.lightbox .contact h4 {
  margin: 0 0 .5em;
  padding-right: 2em;          /* clear of the close button */
  font-size: 1.55em;
  line-height: 1.18;
  letter-spacing: -.02em;
  color: var(--pm-heading);
}

.lightbox .contact > p {
  margin: 0 0 .4em;
  font-size: .92em;
  color: var(--pm-muted);
}

/* Stop the page scrolling behind an open dialog. Driven off the dialog's own
   [open] state first, so the lock can never desync from the dialog; the class
   is the fallback for engines without :has(). */
html:has(dialog.lightbox[open]),
html:has(dialog.lightbox[open]) body,
html.has-lightbox,
html.has-lightbox body { overflow: hidden; }

@media screen and (max-width: 767px) {
  .lightbox {
    width: 100%;
    max-width: 100%;
    max-height: 100svh;
    height: 100svh;
    margin: 0;
    top: 0;
    left: 0;
    inset: 0;
  }
  .lightbox__panel {
    min-height: 100svh;
    padding: 3.4em 1.25em calc(1.5em + env(safe-area-inset-bottom));
    border: 0;
  }
  .lightbox__close { top: .6em; right: .6em; width: 2.6em; height: 2.6em; }
}

/* the dialog is opened with showModal(); this is only for engines without it */
.lightbox.is--fallback {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 8, 10, .9);
}

/* --------------------------------------------------------------------------
   16. Cookie notice
   The three actions are deliberately identical in size, weight and contrast —
   rejecting has to be exactly as easy as accepting, so none of them is styled
   as the "preferred" answer.
   -------------------------------------------------------------------------- */
.cookie {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9998;
  padding: 1.15em 1.25em calc(1.15em + env(safe-area-inset-bottom));
  color: var(--pm-text);
  background: rgba(16, 16, 21, .82);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  backdrop-filter: blur(22px) saturate(140%);
  border-top: 1px solid #ffffff1a;
  box-shadow: 0 -18px 50px rgba(0, 0, 0, .45);
  transform: translateY(105%);
  transition: transform .28s ease;
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .cookie { background: #101015; }
}

.cookie[data-open="true"] { transform: translateY(0); }
.cookie[hidden] { display: none; }

.cookie__inner {
  display: flex;
  gap: 1.5em;
  align-items: center;
  justify-content: space-between;
  max-width: var(--size-container, 90em);
  margin: 0 auto;
  flex-wrap: wrap;
}

.cookie__copy { flex: 1 1 26em; min-width: 0; }

.cookie__title {
  margin: 0 0 .3em;
  font-size: .95em;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--pm-heading);
}

.cookie__text {
  margin: 0;
  font-size: .82em;
  line-height: 1.5;
  color: var(--pm-muted);
  max-width: 68ch;
}

.cookie__actions {
  display: flex;
  gap: .6em;
  flex-wrap: wrap;
  flex: 0 0 auto;
}

/* one shared button style: no visual hierarchy between the choices */
.cookie__btn {
  min-width: 11em;
  padding: .7em 1.2em;
  color: var(--pm-text);
  background: transparent;
  border: 1px solid var(--pm-border-strong);
  border-radius: 0;
  font: inherit;
  font-size: .82em;
  letter-spacing: .01em;
  text-align: center;
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease, background-color .2s ease;
}

.cookie__btn:hover {
  color: var(--pm-accent);
  border-color: var(--pm-accent);
  background: #ff2d6e14;
}

.cookie__btn:focus-visible {
  outline: 1px solid var(--pm-accent);
  outline-offset: 2px;
}

@media screen and (max-width: 767px) {
  .cookie__inner { gap: 1em; }
  .cookie__actions { width: 100%; }
  .cookie__btn { flex: 1 1 100%; min-width: 0; }
}

/* ---------------------------------------------------------- preferences ---
   Reuses the lightbox glass; only the inner layout is specific here. */
.cookie-prefs .lightbox__panel { padding-top: 2.6em; }

.cookie-prefs__title {
  margin: 0 0 .5em;
  padding-right: 2em;
  font-size: 1.45em;
  line-height: 1.2;
  letter-spacing: -.02em;
  color: var(--pm-heading);
}

.cookie-prefs__intro {
  margin: 0 0 1.6em;
  font-size: .9em;
  color: var(--pm-muted);
}

.cookie-prefs__group {
  padding: 1.1em 0;
  border-top: 1px solid var(--pm-border);
}

.cookie-prefs__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1em;
}

.cookie-prefs__name {
  font-size: .98em;
  color: var(--pm-heading);
}

.cookie-prefs__always {
  font-size: .78em;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--pm-muted);
}

.cookie-prefs__desc {
  margin: .45em 0 0;
  font-size: .84em;
  line-height: 1.5;
  color: var(--pm-muted);
}

.cookie-prefs__toggle {
  flex: none;
  width: 2.6em;
  height: 1.45em;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  background: #ffffff1a;
  border: 1px solid var(--pm-border-strong);
  cursor: pointer;
  position: relative;
  transition: background-color .2s ease, border-color .2s ease;
}

.cookie-prefs__toggle::after {
  content: "";
  position: absolute;
  top: 50%;
  left: .18em;
  width: .95em;
  height: .95em;
  transform: translateY(-50%);
  background: var(--pm-text);
  transition: left .2s ease, background-color .2s ease;
}

.cookie-prefs__toggle:checked {
  background: #ff2d6e2e;
  border-color: var(--pm-accent);
}

.cookie-prefs__toggle:checked::after {
  left: calc(100% - 1.13em);
  background: var(--pm-accent);
}

.cookie-prefs__toggle:focus-visible { outline: 1px solid var(--pm-accent); outline-offset: 2px; }

.cookie-prefs__save {
  margin-top: 1.6em;
  width: 100%;
  min-width: 0;
}

/* --------------------------------------------------------------------------
   17. Footer accent image
   Bottom-left of the footer, behind the content. `contain` keeps the image's
   own proportions, and it is masked away toward the text so nothing competes
   with the links. Purely decorative: it sits on a pseudo-element rather than
   in the markup, so it is skipped by assistive tech.
   -------------------------------------------------------------------------- */
.footer { position: relative; overflow: hidden; }

.footer::before {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    z-index: 0;
    width: 86%;
    height: 86%;
    background: url(../img/footer-bg.jpg) left bottom / contain no-repeat;
    pointer-events: none;
}

/* Readability scrim. Sits between the artwork and the content — the image
   keeps its full size and strength, the text keeps its contrast. Measured
   without it: 3.18:1 behind the social links and 2.40:1 behind the wordmark,
   both under the 4.5:1 AA threshold. */
.footer::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 86%;
  height: 86%;
  z-index: 0;
  pointer-events: none;
}

/* keep every footer child above the artwork */
.footer > * { position: relative; z-index: 1; }

@media screen and (max-width: 767px) {
  /* on phones the footer columns stack over the whole width, so the image
     steps back further rather than sitting under the text */
  .footer::before {
    width: 78%;
    height: 32%;
    opacity: .18;
  }
}

/* --------------------------------------------------------------------------
   18. Starting-price prefix
   "от €250/проект" is four separate <p>s inside .pricing__cost so each keeps
   its own scramble target. Only the prefix needs separating from the currency
   glyph; the rest already read as one unit.
   -------------------------------------------------------------------------- */
.pricing__from { margin-right: .28em; }

/* --------------------------------------------------------------------------
   19. Nav logo texture
   footer-bg.jpg reused as a left-side branding wash under the wordmark. The
   file is a pink/orange glow that is bright in its lower-left and already
   black toward the top and right, so `left bottom / cover` on a short, wide
   box crops straight to the glow and the artwork's own falloff does most of
   the blending.

   Applied to .nav rather than .nav.theme-base: the site swaps the nav's theme
   class while scrolling (removeThemeClasses strips every theme-* class), so
   scoping to .theme-base would make the texture vanish at the first section
   boundary. Restrict it to that selector if the intent is hero-only.

   z-index 998 matches .nav__bg — ::after is generated after it in tree order,
   so it paints above the bar and below .nav__main (999), which keeps every nav
   control and the wordmark itself in front.
   -------------------------------------------------------------------------- */
.nav::after {
  content: "";
  position: absolute;
  z-index: 998;
  left: var(--_layout---container--container-padding);
  bottom: 0;
  width: min(240px, 32vw);
  height: min(1.6em, 62%);
  pointer-events: none;
  background: url("../img/footer-bg.jpg") left bottom / cover no-repeat;
  /* No mix-blend-mode here. A blended child forces the parent to isolate, which
     silently disables the backdrop-filter on .nav__bg and takes the frosted
     glass with it — reproduced minimally: stripe ripple through the bar goes
     from 0.00 to 18.78 the moment a screen-blended ::after is added. Normal
     compositing over a near-black bar looks all but identical here, because the
     artwork's own dark falloff plus the radial mask already keep it additive in
     appearance. Opacity is lifted slightly to make up the difference. */
  opacity: .74;
  /* Width stops at 240px so the glow covers the wordmark and dies out before
     the hamburger and the nav links — measured, those sit at 12.5:1 and are
     completely untouched by it.

     The glow does sit behind the wordmark's lower half, because there is
     nowhere else for it to go: on the rendered bar the glyphs occupy y 14-37
     of roughly 43px, so there is neither an empty band below them nor empty
     horizontal space beside them. Brightness is therefore budgeted against
     contrast rather than avoided. The wordmark reads L 0.72, so 4.5:1 permits a
     background up to L 0.121; opacity .74 measures 0.106 at the 95th
     percentile, giving 4.91:1. Raising it further is what took earlier attempts
     to 2.79:1.

     Two masks intersected: the radial shapes the spill outward from the
     bottom-left corner, the linear fades its top edge so it dissolves into the
     bar rather than ending on a hard line across the letterforms. */
  -webkit-mask-image:
    radial-gradient(118% 200% at 0% 100%, #000 0%, rgba(0, 0, 0, .8) 44%, transparent 86%),
    linear-gradient(to top, #000 0%, rgba(0, 0, 0, .72) 52%, transparent 100%);
  mask-image:
    radial-gradient(118% 200% at 0% 100%, #000 0%, rgba(0, 0, 0, .8) 44%, transparent 86%),
    linear-gradient(to top, #000 0%, rgba(0, 0, 0, .72) 52%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}


/* The olive section inverts the bar to a warm light-on-dark scheme; a pink
   wash fights it, so it is dialled back rather than removed. */
.nav.theme-olive::after { opacity: .5; }

@media (max-width: 767px) {
  .nav::after {
    width: min(200px, 54vw);
    height: min(1.5em, 60%);
    opacity: .7;
  }
}

/* --------------------------------------------------------------------------
   20. «Обо мне» couplet
   Two uppercase lines set as a typographic moment, not body copy and not a
   quote card: no box, no quote marks, no rounded panel. Hairlines frame it,
   the site's own pixel arrow marks each line, the second line steps in so the
   pair reads as a composed couplet, and «ВАЙБКОДЕР» carries the fire pink with
   a low ember glow.

   The words are uppercase in the markup, exactly as supplied; text-transform is
   pinned as well so no heading or capitalisation rule can lowercase them.
   Hovering a line scrambles it through the site's own data-scramble-hover.
   -------------------------------------------------------------------------- */
/* fit-content so the hairlines span exactly the couplet and the pair centres
   as a unit. The cap is in rem and viewport units, not em: an em max-width
   resolves against this block's own inherited ~12px font rather than the 18px
   lines, which made the block ~408px wide and wrapped «НАРАВНЕ» onto a line of
   its own on desktop. Measured at true 390px and 360px viewports, both lines
   hold a single line with room to spare. */
.vibe {
  width: fit-content;
  max-width: min(46rem, calc(100vw - 2rem));
  margin: .5em auto 1.4em;
  padding: 1em 0;
  border-top: 1px solid var(--pm-border);
  border-bottom: 1px solid var(--pm-border);
  display: flex;
  flex-direction: column;
  gap: .55em;
}

.vibe__line {
  display: flex;
  align-items: center;
  gap: .75em;
  margin: 0;
  font-family: var(--pm-font);
  font-weight: 600;
  font-size: clamp(.9rem, 1.25vw, 1.25rem);
  line-height: 1.2;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--pm-heading);
  cursor: default;
}
.vibe__line + .vibe__line { padding-left: 1.75em; }

.vibe__mark {
  width: .72em;
  height: auto;
  flex: none;
  color: var(--pm-accent);
}

.vibe__accent {
  color: var(--pm-accent);
  text-shadow: 0 0 1.1em rgba(255, 45, 110, .38);
}

@media (max-width: 479px) {
  .vibe { padding: .85em 0; }
  .vibe__line {
    align-items: flex-start;
    font-size: .78rem;
    letter-spacing: .04em;
  }
  .vibe__mark { margin-top: .22em; }
  .vibe__line + .vibe__line { padding-left: 1.2em; }
}

/* --------------------------------------------------------------------------
   21. Contact form consent
   A plain checkbox and a real <label>, so the whole sentence is a hit target
   and the browser's own `required` still blocks submission with scripting off.
   accent-color tints the native control rather than replacing it: a custom box
   would have to re-implement focus, indeterminate and forced-colours states for
   no gain. Deliberately quiet — it sits under the fields as a condition of
   sending, not as a promoted call to action.
   -------------------------------------------------------------------------- */
.contact__consent {
  display: flex;
  align-items: flex-start;
  gap: .6em;
  margin: .2em 0 .85em;
}

.contact__checkbox {
  flex: none;
  width: 1.05em;
  height: 1.05em;
  margin: .2em 0 0;
  accent-color: var(--pm-accent);
  cursor: pointer;
}
.contact__checkbox:focus-visible {
  outline: 1px solid var(--pm-accent);
  outline-offset: .25em;
}
.contact__checkbox[aria-invalid="true"] {
  outline: 1px solid var(--pm-accent);
  outline-offset: .2em;
}

.contact__consent-label {
  font-size: .9em;
  line-height: 1.5;
  color: var(--pm-muted);
  cursor: pointer;
}
.contact__consent-label a {
  color: var(--pm-accent);
  text-decoration: underline;
  text-underline-offset: .15em;
}
.contact__consent-label a:hover { color: var(--pm-accent-light); }
