/* Peak Solutions — CSS.
   1:1 statická rekonstrukce originálu (Webflow), zdůvodnění jednotlivých
   rozhodnutí je v DECISIONS.md. */

/* --- Self-hosted fonty (Montserrat, Lexend Deca) ---
   Licence: SIL Open Font License 1.1, viz assets/fonts/OFL-*.txt.
   Oba soubory jsou variabilní fonty (osa wght 100–900) — Google je servíruje
   stejné pro všechny statické váhy, takže stačí 2 soubory na rodinu
   (latin + latin-ext kvůli české diakritice), ne jeden soubor na váhu. */

@font-face {
  font-family: "Montserrat";
  src: url("../fonts/montserrat-latin.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Montserrat";
  src: url("../fonts/montserrat-latin-ext.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Lexend Deca";
  src: url("../fonts/lexend-deca-latin.woff2") format("woff2");
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Lexend Deca";
  src: url("../fonts/lexend-deca-latin-ext.woff2") format("woff2");
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Barvy — ověřeno v reference/ (CSS + computed styles), jen skutečně
     použité na této stránce. */
  --bg: #eaeaee;
  --font-color: #231f20;
  --bg-underline-hover: #9a282a;
  --bg-text-white: #eeede9;
  --bg-2: #cfcfd6;
  --lines: #bbbbb6;
  --text-grey: #7f7f7f;
  --blue-light: #6491ca;
  --white: #ffffff;
  --blue-dark: #2e368f;

  /* Typografie — font-family ověřeno computed style (ne z CSS souboru,
     kde je u body zavádějící nepoužitý Generalsans). Velikosti ověřeny
     jako báze na 1280px viewportu. */
  --font-body: "Montserrat", sans-serif;
  --font-heading: "Lexend Deca", sans-serif;

  --fs-h1: 2.625rem;    /* 42px */
  --fs-h3: 1.8125rem;   /* 29px */
  --fs-text: 0.9375rem; /* 15px — subtitle, paragraph-3, paragraph-medium, testimonial (báze) */
  --fs-label: 1rem;     /* 16px — field-label, semi-bold (jména) */
  --fs-meta: 0.875rem;  /* 14px — paragraph-2, paragraph-4, semi-bold (≤479px) */
  --fs-button: 0.75rem; /* 12px — measured on the reference (hero CTA and
                           every section CTA alike) */
  --line-height-base: 1.5;

  /* Layout — ověřeno computed style na 1280px */
  --navbar-max-width: 85vw;

  /* Radius — jádro, používané napříč více komponentami */
  --radius-button: 19px;
  --radius-card: 10px;
  --radius-input: 7px;
  --radius-navbar: 76px;
  --radius-section: 45px;

  /* Pomocné tokeny stávající kostry (nejsou z ověřeného design systému,
     použité zatím v .container/.section níže) */
  --container-max: 1200px;
  --section-padding-y: 4rem;
  --section-padding-x: 1.5rem;

  /* IMPROVE, vědomá odchylka od originálu na wide desktopu (viz DECISIONS.md
     "Wide desktop: sjednocený content max-width"): originál na ≥1440px ruší
     .container max-width úplně (roste skoro na 100 % viewportu), což na
     širokém desktopu dělá obsahové sloupce nepřirozeně široké/prázdné a
     necentrovaný obsah je nekonzistentně zarovnaný vůči headeru. Sdílený
     strop pro .container i .hero — jedno pravidlo pro obě místo dvou
     nezávislých hodnot. */
  --container-wide: 1320px;    /* ≥1440px */
  --container-wide-xl: 1600px; /* ≥1920px */
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* FIX (2026-09-08, animation audit): reference smooth-scrolls EVERY
     #anchor jump (nav links, go-up button) — confirmed live, ~1.2-1.4s
     eased scroll, not an instant jump. One CSS-only rule covers all of
     them at once (no JS scroll animation needed). */
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background-color: var(--bg);
  color: var(--font-color);
  font-family: var(--font-body);
  line-height: var(--line-height-base);
}

/* weight 500 everywhere — same fix as h3 below: headings were falling back
   to the browser's default bold (700). Verified on the reference at 1280
   and 1920 (h1 42px/500, h3 29px/500). */
h1, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 500;
}

/* Ověřeno na originále: všech 5 použitých <h3> nadpisů (sluzby, problems,
   tech, reference, contact) má shodně class="h3" a 29px na base šířce,
   žádný nemá vlastní odlišnou velikost — bezpečné jako jedno globální
   pravidlo. Předtím tu žádné nebylo, takže se všechny nadpisy vykreslovaly
   prohlížečovou výchozí velikostí (18.72px) místo --fs-h3. */
/* GLOBAL FIX (audit vs reference/, confirmed across every h3 on the page,
   not just today's retext): weight was never set, so it silently
   defaulted to the browser's UA-stylesheet bold (700) — original is 500
   everywhere (sluzby, problems, tech, reference, contact headings alike). */
h3 {
  font-size: var(--fs-h3);
  font-weight: 500;
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

h1, h2, h3, h4, p {
  margin: 0 0 1rem;
}

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--section-padding-x);
}

.section {
  padding: var(--section-padding-y) 0;
}

/* The 3 full-bleed retext sections (problems/tech/qi grids — see their
   own comments above) fill the ENTIRE section height on the agency
   original, confirmed live (section height == image height exactly, no
   padding at all) — not just the grid row within a padded section. */
.section:has(.problems-grid),
.section:has(.tech-grid),
.section:has(.qi-grid) {
  padding: 0;
}

.section--dark {
  background-color: var(--bg-2);
  color: var(--font-color);
}

/* --- Header / nav ---
   Plovoucí "pilulka" nad hero, ověřeno computed stylem na originále:
   position:fixed, radius 76px, poloprůhledné pozadí + blur na desktopu,
   plné neprůhledné pozadí na ≤991px, kde se zároveň mění menu -> hamburger. */

.site-header {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%) translateY(0);
  /* FIX (2026-09-08, animation audit): reference hides the navbar on
     scroll-down (translateY -140%, own height) and brings it back on
     scroll-up — confirmed live, no threshold/delay. Own listener in
     main.js toggles .is-hidden; 500ms ease matches the reference's
     "Nav Enter"/"Nav Leave" (a-109/a-110) exactly. */
  transition: transform 500ms ease;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: var(--navbar-max-width);
  padding: 0 25px;
  height: 72px;
  background-color: rgba(253, 252, 247, 0.8);
  -webkit-backdrop-filter: blur(5px);
  backdrop-filter: blur(5px);
  border-radius: var(--radius-navbar);
}

.site-header .logo img {
  height: 36px; /* reference .brand img, verified computed height */
  width: auto;
}

/* FIX (2026-09-08, full CSS :hover sweep): reference .brand:hover
   scales to 1.04 — a plain CSS hover, not IX2, so it wasn't caught by
   the earlier interaction-only audit. transition matches 1:1
   (transform + opacity, even though nothing here toggles opacity —
   reference declares both, kept for parity). */
.site-header .logo {
  display: inline-block;
  transition: transform 0.2s, opacity 0.2s;
}

.site-header .logo:hover {
  transform: scale(1.04);
}

.site-header.is-hidden {
  transform: translateX(-50%) translateY(-140%);
}

@media (prefers-reduced-motion: reduce) {
  .site-header {
    transition: none;
  }
}

/* Reference (.navlink, downloaded CSS): font-size 17px, weight 500,
   letter-spacing -0.3px, margin-right 18px between links — distinct from
   the shared body-text size --fs-text (15px), verified as its own value. */
.nav-menu {
  display: flex;
  gap: 18px;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.3px;
}

/* Ověřeno na originále: .navlink.reference { display: none } bezpodmínečně —
   #reference je trvale skrytá, takže na ni originál nenechává vést ani
   viditelný "mrtvý" odkaz. */
.nav-link--reference {
  display: none;
}

/* Nav underline hover — jen ≥992px (originál: .underline-hover má
   display:none na ≤991/767px, na mobilu je jiný, nesouvisející efekt
   mimo scope tohoto kroku). Originál řeší reálným child divem animovaným
   přes Webflow IX2 (width 0→100%); tady čistě CSS ::after + transition,
   žádný JS, žádná markup změna. Barva/tloušťka/duration/easing (300ms
   hover-in, 500ms hover-out) jsou přesně převzaté z Webflow IX2 definic
   "Navlink Underline Hover"/"Navlink Underline Hover Out" v reference/js.
   Vertikální pozice (bottom:0) je odvozená, ne 1:1 přenesená — na
   originále je mezera mezi textem a underline matematicky 0px
   (padding-top 23px + line-height 25px = přesně naměřených 48px k horní
   hraně underline, beze zbytku), takže ekvivalent u naší bezpaddingové
   `.nav-menu a` je underline hned na hraně line-boxu textu. */
@media screen and (min-width: 992px) {
  .nav-menu a {
    position: relative;
    transition: color 0.2s;
  }

  .nav-menu a::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 0;
    height: 1.5px;
    background-color: var(--bg-underline-hover);
    transition: width 0.5s ease;
  }

  .nav-menu a:hover,
  .nav-menu a:focus-visible {
    color: var(--text-grey);
  }

  .nav-menu a:hover::after,
  .nav-menu a:focus-visible::after {
    width: 100%;
    transition: width 0.3s ease;
  }
}

/* Language switcher — added for the CZ/EN/DE retext (peaksolutions.cz +
   /en/ + /de/), same pattern as sites/starkys.software, colors adapted to
   this site's light-header tokens instead of Starkys's dark ones. */
.lang-switcher {
  position: relative;
  display: flex;
  align-items: center;
}

/* FIX (2026-09-08, header visual parity audit): reference's language
   switcher is flag + language code, sized like its sibling navlinks —
   no chevron at all (it's a plain link to the other language version,
   not a dropdown). Ours stays a real 3-language dropdown (funkčnost
   beze změny), only the trigger is restyled to match: font-size is no
   longer forced to --fs-text (15px), so it inherits .nav-menu's own
   17px/500/-0.3px exactly like the nav items next to it, and the gap
   matches the reference's flag margin-right (5px). */
.lang-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  background: transparent;
  border: 0;
  color: var(--font-color);
  /* <button> nedědí typografii z předka automaticky (UA stylesheet), takže
     bez explicitního inherit spadne na 13.3px/400 — tohle ji srovná
     s .nav-menu (17px/500/-0.3px), stejně jako reference sází svůj
     .link-text na velikost sousedních navlinků. */
  font: inherit;
  letter-spacing: inherit;
  padding: 0;
  cursor: pointer;
}

/* The flag PNGs are square (64×64 round-flag icons, same style as the
   reference's 150×150 SVGs) — the old width/height 20×14 markup squashed
   them into an ellipse. 20×20 renders them at their natural ratio,
   matching the reference's own 20×20. */
.lang-toggle img {
  width: 20px;
  height: 20px;
}

/* Reference has no chevron; kept in the DOM (harmless, reversible) but
   hidden, since the dropdown affordance is carried by the button itself
   (aria-expanded stays wired up for assistive tech). */
.lang-toggle-icon {
  display: none;
}

.lang-list {
  display: none;
  list-style: none;
  margin: 8px 0 0;
  padding: 6px;
  position: absolute;
  top: 100%;
  right: 0;
  min-width: 150px;
  background-color: var(--bg);
  border: 1px solid var(--bg-2);
  border-radius: var(--radius-input);
  box-shadow: 0 8px 24px rgba(35, 31, 32, 0.12);
  z-index: 10;
}

.lang-list.is-open {
  display: block;
}

.lang-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 40px;
  padding: 0 10px;
  color: var(--font-color);
  text-decoration: none;
  border-radius: var(--radius-input);
  transition: background-color 0.15s;
}

.lang-link:hover,
.lang-link:focus-visible {
  background-color: var(--bg-2);
}

.lang-link.is-current {
  font-weight: 600;
}

@media screen and (max-width: 991px) {
  .lang-switcher {
    width: 100%;
  }

  .lang-list {
    position: static;
    background: transparent;
    padding: 0 0 0 1rem;
  }
}

.menu-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
}

.menu-toggle .menu-icon,
.menu-toggle .menu-icon::before,
.menu-toggle .menu-icon::after {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--font-color);
}

.menu-toggle .menu-icon {
  position: relative;
}

.menu-toggle .menu-icon::before,
.menu-toggle .menu-icon::after {
  content: "";
  position: absolute;
  left: 0;
}

.menu-toggle .menu-icon::before { top: -7px; }
.menu-toggle .menu-icon::after { top: 7px; }

@media screen and (max-width: 991px) {
  .site-header {
    width: 100vw;
    background-color: #fdfcf7;
  }

  .nav-menu {
    display: none;
  }

  .menu-toggle {
    display: block;
  }

  /* Otevřený stav (JS přidává/odebírá .is-open, viz assets/js/main.js).
     Vlastní řešení, ne kopie originálu — jeho přesnou pozici/animaci
     nešlo spolehlivě změřit (position:fixed uvnitř transformovaného
     Webflow IX2 předka, stejný artefakt jako u #Top, viz DECISIONS.md).
     FIX (2026-09-08, full audit): `.site-header` má vlastní `transform`
     (translateX pro centrování, teď i translateY pro scroll-hide) — a
     transform na předkovi vytváří NOVÝ containing block pro
     `position:fixed` potomky (containing block = paddingbox předka, ne
     viewport). `#nav-menu` je uvnitř `.site-header`, takže jeho `top`
     se ve skutečnosti počítá od HORNÍHO okraje headeru (viewport y=20),
     ne od viewportu samotného — původních 92px tak vycházelo jako
     20+92=112px od viewportu, 20px mezera pod headerem navíc (živě
     potvrzeno getBoundingClientRect). Správná hodnota v headerově
     vlastní souřadné soustavě je jen jeho výška (72px), ne
     top+výška. */
  .nav-menu.is-open {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 72px;
    left: 0;
    right: 0;
    max-height: calc(100vh - 92px);
    overflow-y: auto;
    gap: 0;
    padding: 0.5rem 1.5rem;
    background-color: #fdfcf7;
  }

  .nav-menu.is-open li {
    border-bottom: 1px solid var(--lines);
  }

  .nav-menu.is-open li:last-child {
    border-bottom: none;
  }

  .nav-menu.is-open a {
    display: block;
    padding: 1rem 0;
  }
}

/* --- Hero + logo pás ---
   #Top nese pozadí (gradient + fotka) pro OBĚ podsekce najednou — v originále
   tohle zajišťovalo position:fixed na hero (ověřeno, že to není jen animační
   detail, ale reálně tvoří statické rozvržení: i v klidu je pod hero
   rezervovaný prostor přesně jeho výšky). Bez parallax/scroll animace
   dosáhneme stejného STATICKÉHO výsledku jednodušeji — obě podsekce sedí
   v normálním flow na jednom sdíleném pozadí, žádný position:fixed. */

#Top {
  position: relative;
  overflow: hidden; /*ořízne .hero-bg při scroll zoomu (scale až 1.25) */
  /* FIX (2026-09-07, Hero vertical parity audit): reference .hero-section.home
     positions its H1 via padding-top (60/130/148px) + .hero-inner-padding's
     own margin-top(52/52/10px) — replicated here as a single padding-top per
     tier, back-solved from the live-measured reference headline top (137/
     200/213px) minus .hero's own existing padding-top:3rem (48px, unrelated
     pre-existing rule). Verified 0-1px delta at 992-3840 for headline top /
     CTA bottom / marquee top (see DECISIONS.md). */
  padding-top: 89px;
  padding-bottom: 0; /* was 2.5rem — the gap to .logo-marquee is now an
    explicit margin-top on .logo-marquee itself (see below), not section
    padding; keeps the two concerns independent. */
  /* FIX (2026-09-07, Hero height audit): reference .hero-section.home is
     an explicit height (not min-height) — 695px flat 992-1439, confirmed
     source + live measurement to the pixel. Safety-checked first: natural
     content (694px at every tested breakpoint) never exceeds this, so
     overflow:hidden above never clips anything. */
  height: 695px;
}

/* Pozadí oddělené do vlastní vrstvy kvůli scroll-zoom efektu (viz DECISIONS.md
   "Hero scroll zoom") — jen tahle vrstva se škáluje, text/CTA/marquee ne.
   position:absolute + inset:0, žádný z-index potřeba (je první potomek
   #Top, zbytek obsahu přirozeně vykresluje nad ní podle pořadí v DOM,
   stejně jako originální .hero-image-scale). */
.hero-bg {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgb(26, 26, 26), rgba(16, 16, 16, 0.62) 38%, rgba(0, 0, 0, 0)),
    url("../img/backgrounds/hero-photo.jpg") center / cover no-repeat;
}

.hero {
  /* FIX (2026-09-07, Hero vertical parity audit): bottom padding (was 4rem)
     removed — the gap to .logo-marquee is now an explicit margin-top on
     .logo-marquee itself, not section padding (see #Top/.hero-cta-wrap). */
  padding: 3rem var(--section-padding-x) 0;
}

/* --- Hero content box: shared width + horizontal centering (≥992px) ---
   FIX (2026-09-08, hero horizontal parity audit): the reference's
   three-level chain (.content-wrapper padding × .hero-column auto margins
   × .hero-inner-padding auto margins) reduces to ONE observable rule —
   the hero content box is horizontally CENTERED in the viewport, with the
   text left-aligned inside it. Live-verified at 1280/1440/1920/2056: left
   gap == right gap to the pixel. Our earlier width work reproduced that
   box's WIDTH exactly but kept it flush-left, which is why the text and
   marquee sat ~120-340px too far left despite matching widths.

   --hero-content-width is that box, defined once per tier and consumed by
   BOTH .hero (as its width) and .logo-marquee (as its max-width cap, on
   top of the intrinsic max-content plateau) — the same coupling the
   reference gets from both living inside .hero-inner-padding. The `100%`
   inside resolves at each use site against the same containing block
   (#Top), so both boxes always agree.

   Safe against the verified vertical parity: .hero__headline/__title/
   __subtitle each carry their own max-width (680/520/520px), so widening
   .hero changes no text wrapping, no heights, no vertical geometry —
   simulated and confirmed unchanged before implementing. Mobile/tablet
   (≤991px) deliberately untouched: it keeps the padding-based layout and
   the separate 92vw/94vw marquee widths. */
@media screen and (min-width: 992px) {
  #Top {
    --hero-content-width: calc((min(100%, 1225px) - 48px) * 0.75);
  }

  .hero {
    width: var(--hero-content-width);
    max-width: none;
    margin-left: auto;
    margin-right: auto;
    padding-left: 0;
    padding-right: 0;
  }
}

@media screen and (min-width: 1280px) and (max-width: 1439px) {
  #Top {
    --hero-content-width: calc((100% * 0.95 - 48px) * 0.85);
  }
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  #Top {
    --hero-content-width: calc((100% * 0.90 - 48px) * 0.80);
  }
}

@media screen and (min-width: 1920px) {
  #Top {
    --hero-content-width: calc((100% - 48px) * 0.60);
  }
}

/* CTA reveal wrapper (4th .hero-reveal) — explicit reserved gap above the
   button, back-solved against the live-measured reference CTA bottom
   (535/602/626px) once headline-top (above) and marquee margin-top (below)
   are fixed. Verified 0-1px delta 992-3840. */
.hero-cta-wrap {
  margin-top: 44px;
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .hero-cta-wrap {
    margin-top: 48px;
  }
}

@media screen and (min-width: 1920px) {
  .hero-cta-wrap {
    margin-top: 47px;
  }
}

/* Hero on-load reveal — PAGE_START, jednorázově, ne scroll-vázané.
   Originál (Webflow IX2 "Hero On Load", a-106) animuje H1(.bottom-word)/
   H3(.h3.white)/podnadpis(.hero-subtitle)/CTA(.button.light-blue): shodně
   translateY(100%)→0 (1400ms) + opacity 0→1 (1000ms), oboje "ease", uvnitř
   overflow:hidden;position:relative wrapperu (proto "hard clip" vjezd
   zespodu, ne jen posun). Delaye — ŽIVĚ přeověřeno 2026-09-08 přímo v
   a-106 zdroji (předchozí zápis měl u subtitle chybně 3800, ve
   skutečnosti 3000): H1 2800ms, subtitle 3000ms, CTA 3200ms, H3 3300ms.
   TEĎ (na rozdíl od dřívějška) dává smysl použít je 1:1 — stránka má od
   téhle session preloader (viz "Preloader" blok níž), který stejně kryje
   celou hero do ~2600ms, takže žádná z těchhle delayí nevytváří prázdnou
   hero navíc. `.logo-marquee` v a-106 vůbec NENÍ cílem (marquee řídí
   samostatná "Infinity Loop" interakce bez reveal fáze) — proto tu už
   není, běží/scrolluje hned, přesně jako na originále. */

.hero-reveal {
  overflow: hidden;
  position: relative;
}

.hero__headline,
.hero__title,
.hero__subtitle,
.hero .button {
  animation: hero-reveal-up 1400ms ease both;
}

.hero__headline {
  animation-delay: 2800ms;
}

.hero__title {
  animation-delay: 3300ms;
}

.hero .button {
  animation-delay: 3200ms;
}

.hero__subtitle {
  animation-delay: 3000ms;
}

@keyframes hero-reveal-up {
  from {
    opacity: 0;
    transform: translateY(100%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Hero-bg fade-in (a-106: .hero-image-scale.home, opacity 0→1, delay
   2400ms/duration 1000ms/ease) — first thing to appear, before the text
   cascade starts at 2800ms. Separate keyframe (only opacity — the
   element's `transform` is owned by the scroll-zoom JS, see main.js,
   an animation touching the same property would fight it every frame). */
.hero-bg {
  animation: hero-bg-fade-in 1000ms ease both;
  animation-delay: 2400ms;
}

@keyframes hero-bg-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero__headline,
  .hero__title,
  .hero__subtitle,
  .hero .button,
  .hero-bg {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

/* Reference's real hero headline hierarchy (H1 "Excellent people form
   excellent companies." > H3 "We have the right people..." > P subtitle),
   downloaded and verified directly in its CSS — was missing here entirely;
   our one existing title line was standing in at the H1 tier, one size
   too big. .bottom-word (the actual H1 class) is 42px/500/-1px tracking;
   the hero's H3 uses ITS OWN modifier .h3.white at 400 weight, distinct
   from the 500 every other h3 on the page uses (verified side by side). */
.hero__headline {
  font-family: var(--font-heading);
  font-size: var(--fs-h1);
  font-weight: 500;
  letter-spacing: -1px;
  color: var(--bg-text-white);
  max-width: 680px;
  line-height: var(--line-height-base);
}

/* FIX 1 (2026-09-06, approved delta audit): .hero__title/.hero__subtitle
   were sharing .hero__headline's 680px max-width. Reference's H3 AND
   subtitle both live inside .hero-box-subtitle, own max-width, verified
   directly in its CSS and live at 991/992/1280/1439/1440/1920:
     992–1439px: max-width 490px
     ≥1440px:    max-width 520px
   No other Hero spacing/position/font-size touched. */
.hero__title {
  font-size: var(--fs-h3);
  font-weight: 400;
  color: var(--bg-text-white);
  max-width: 490px;
  line-height: var(--line-height-base);
}

.hero__subtitle {
  font-family: var(--font-heading);
  font-size: 1.125rem; /* 18px — reference .hero-subtitle, stable across breakpoints */
  font-weight: 400;
  color: var(--bg);
  max-width: 490px; /* reference .hero-box-subtitle */
  line-height: var(--line-height-base);
}

/* FIX 1 continued: reference's .hero-box-subtitle bumps to 520px at
   ≥1440px (verified live, no further change at 1920px). Only these two
   properties — nothing else in Hero touched. */
@media screen and (min-width: 1440px) {
  .hero__title,
  .hero__subtitle {
    max-width: 520px;
  }
}

.button {
  display: inline-block;
  height: 38.5px;
  padding: 10px 30px;
  background-color: var(--blue-light);
  border: 1px solid var(--blue-light);
  color: var(--white);
  border-radius: var(--radius-button);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: var(--fs-button);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  line-height: 18px;
  transition: color 0.2s, background-color 0.2s, border-color 0.2s;
}

.button:hover {
  background-color: var(--white);
  border-color: var(--font-color);
  color: var(--font-color);
}

/* Stav "odesíláme" u kontaktního formuláře (assets/js/main.js) — label
   zůstává v DOMu pro čtečky obrazovky (jen vizuálně schovaný přes
   color: transparent), point-events vypnuté navíc k disabled, protože
   ::after překrývá celé tlačítko. */
.button.is-loading {
  position: relative;
  color: transparent;
  pointer-events: none;
}

.button.is-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1rem;
  height: 1rem;
  margin: -0.5rem 0 0 -0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: var(--white);
  border-radius: 50%;
  animation: button-spin 0.6s linear infinite;
}

@keyframes button-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .button.is-loading::after {
    animation: none;
  }
}

/* Outline varianta — ověřeno na originále u tlačítka "Kontaktujte nás"
   (sekce Technologie): průhledné pozadí, jen okraj, opačný hover než
   .button (tmavne, ne zesvětlá). */

.button--outline {
  background-color: transparent;
  border-color: var(--font-color);
  color: var(--font-color);
}

.button--outline:hover {
  background-color: var(--font-color);
  border-color: var(--font-color);
  color: var(--bg-text-white);
}

/* --- Logo pás (partnerská loga) ---
   Wrapper ořízne (overflow:hidden) druhou sadu log, obě sady animujeme
   společně přes translateX. Na originále je pohyb čistě JS (Webflow IX2
   interakce "a-114"/"Infinity Loop", nalezená přímo v JS bundlu, ne
   uhodnutá): cíl .logo-container, 0% → -99.9%, 10000ms, lineární easing,
   trigger PAGE_START, nekonečná smyčka. U nás čisté CSS (žádný JS
   potřeba) — stejná rychlost/směr/easing, jen -100% místo -99.9%
   (matematicky čistý bezešvý loop, ověřeno na našem layoutu níž). */

/* 96vw (wider than --navbar-max-width/85vw) — needed since the retext
   unified all 3 languages on the same 6 agency client logos; 85vw
   crowded them together (see DECISIONS.md). */
.logo-marquee {
  display: flex;
  overflow: hidden;
  width: 100vw;
  margin: 0 auto;
  /* FIX (2026-09-07, Hero vertical parity audit): explicit, viewport-flat
     gap above the marquee (replaces the old section.hero padding-bottom:
     4rem removed above) — matches reference's own gap between its CTA and
     carousel-container, live-verified identical (25px) at every desktop
     tier tested (992-3840px). Unconditional (not tier-gated) since .hero's
     own padding-bottom was also unconditional — keeps mobile/tablet from
     losing their gap entirely now that padding-bottom is 0 everywhere. */
  margin-top: 25px;
  border-radius: 100px;
}

/* FIX (2026-09-07, marquee width parity audit): reference's carousel-container
   width is never its own vw-based max-width (78/85/72/70vw never actually
   bind, live-verified) — it's driven by the same ancestor chain as the hero
   text column (content-wrapper's own cap × hero-column % × hero-inner-padding
   %), capped by the logos' own natural (max-content) width once the
   viewport is wide enough that the formula would exceed it. Replicated here
   directly against #Top (the real containing block, avoiding 100vw's
   scrollbar-width error) using local's own .hero side padding (48px) and
   reference's own sourced content-wrapper cap (1225px, real value from its
   CSS — not a per-viewport guess). `width:max-content` + `max-width:<calc>`
   stands in for `min(<calc>, max-content)`, which this engine rejects when
   mixing max-content into min(). Verified 0-2px delta at 992/1279/1280/
   1439/1440/1919/1920/2056/2560/3200/3840 (see DECISIONS.md). Scope: mobile
   (≤991px) untouched. */
/* FIX (2026-09-08, hero horizontal parity audit): the per-tier width
   formulas moved to --hero-content-width (defined next to .hero above,
   shared by both boxes), and the hardcoded margin-left ladder
   (24px / var(--section-padding-x) / calc((100% − container-wide)/2 + …))
   is replaced by plain auto margins — the reference centers this box
   rather than left-aligning it. width:max-content stays, so the natural
   max-content plateau at very wide viewports is unaffected. */
@media screen and (min-width: 992px) {
  .logo-marquee {
    width: max-content;
    max-width: var(--hero-content-width);
    /* IMPROVE (2026-09-08, marquee overlap fix — approved deviation, see
       DECISIONS.md): --hero-content-width shrinks below ~780px in the
       992-~1100px band (697-778px measured), but one full logo set + its
       trailing 20px margin needs 800px flat (logo heights are already
       clamped to their 50px ceiling for this whole band, so the natural
       content width doesn't shrink with it). With no room left for
       justify-content:space-around to distribute, the track's content
       overflows into the next (duplicate) track and Citrix visually
       overlaps Oddin.gg. Confirmed this isn't a local-only bug — the
       reference itself (.carousel-container, 719px @1024px) shows the
       IDENTICAL overlap at the identical spot — but Petra asked to fix it
       here as a deliberate deviation rather than reproduce it. min-width
       floors the track at the logos' real minimum, with ~20px of slack
       left for a small space-around gap; harmless above ~1100px, where
       the formula above already exceeds it. */
    min-width: 820px;
    /* NOT margin-inline:auto — that centers the marquee itself, which is
       only the same thing while it fills the box. Above ~2560px the track
       hits its intrinsic max-content plateau (1601px) and becomes NARROWER
       than the box; the reference keeps it at the box's LEFT edge there
       (live-verified at 3200px: carousel left == headline left == 652),
       whereas auto margins would re-center the shrunken track and drift it
       ~140px right of the headline. Centering the BOX and left-aligning
       inside it reproduces both regimes with one expression. */
    margin-left: calc((100% - var(--hero-content-width)) / 2);
    margin-right: auto;
  }
}

.logo-track {
  display: flex;
  align-items: center;
  /* FIX (2026-09-08, marquee loop-continuity audit): back to space-around.
     The 2026-09-07 switch to flex-start was based on a wrong reading of the
     reference — it does NOT space its logos by the 20px margins alone. Its
     .logo-container is full-track-width with justify-content:space-around
     AND margin-right:20px per logo; the leftover space is distributed on
     top of those margins (live-verified @1920: 6 logos = 680px + 6×20px
     margins = 800px content in a 1113px track → 313px free → 72px gaps).
     With flex-start the logos bunched into the first 780px and left a
     334px dead tail at the end of every copy — which is exactly the huge
     hole visible at the loop seam, since the seam gap is that tail plus
     the next copy's leading space. space-around removes the tail, so the
     seam gap equals the normal inter-logo gap automatically (reference:
     inner 72px == seam 72px). margin-right:20px on the images stays — it's
     the reference's own value and part of that sum. */
  justify-content: space-around;
  flex-shrink: 0;
  width: 100%;
  /* FIX (2026-09-08, marquee visual parity audit): the dark translucent
     pill behind the logos comes from the TRACK, not the wrapper — on the
     reference it's .logo-container{background-color:rgba(0,0,0,.3);
     padding:15px 0} inside .carousel-container's border-radius:100px +
     overflow:hidden, which clips it into the pill shape. Our
     .logo-marquee already had the radius and the clipping, but nothing
     to clip — no background anywhere, so the rounded panel was simply
     invisible and the CTA→marquee area read as empty space. */
  background-color: rgba(0, 0, 0, 0.3);
  padding: 15px 0;
  animation: logo-marquee 18s linear infinite;
  will-change: transform;
}

.logo-track img {
  /* CLEAN: plynulé škálování místo pevné výšky — 4 loga v přirozené šířce
     přesahují dostupný prostor pod ~630px (flex položky typu img se
     nezmenší pod svou přirozenou šířku, viz DECISIONS.md), takže by pevná
     výška pod touhle šířkou logům způsobila vzájemné překrývání/nalepení.
     Hodnoty kalibrované na konstantní ~20px mezeru mezi logy v pásmu
     ~375–479px (marquee šířka 94vw). FIX (2026-09-07, marquee parity
     audit): strop zvýšen 32→50px — přesná výška referenčního Oddin loga
     (nejvyšší ze 6), dosažena od ~973px šířky viewportu. */
  height: clamp(14px, calc(5.63vw - 4.79px), 50px);
  width: auto;
  margin-right: 20px; /* FIX (2026-09-07): reference dává 20px mezi KAŽDÝM
    logem vč. posledního v track — sjednocuje mezeru s duplicitní druhou
    sadou (aria-hidden), ne jen mezi first-of-type. */
}

/* Per-logo height scaling — the 6 peaksolutions.agency client logos vary
   a lot in natural proportions (Škoda's wordmark is much wider per pixel
   of height than e.g. Oddin's mark). Uniform height made Škoda render
   ~227px wide next to Direct's ~55px, reading as visually unbalanced/
   "crowded". Ratios matched from the actual heights the agency site sets
   per logo (50/45/40/32/19/26px — Oddin is their tallest, treated as
   the 100% baseline here). */
.logo-track img.logo--direct {
  height: calc(clamp(14px, calc(5.63vw - 4.79px), 50px) * 0.9);
}

.logo-track img.logo--codasip {
  height: calc(clamp(14px, calc(5.63vw - 4.79px), 50px) * 0.8);
}

.logo-track img.logo--ententee {
  height: calc(clamp(14px, calc(5.63vw - 4.79px), 50px) * 0.64);
}

.logo-track img.logo--skoda {
  height: calc(clamp(14px, calc(5.63vw - 4.79px), 50px) * 0.38);
}

.logo-track img.logo--citrix {
  height: calc(clamp(14px, calc(5.63vw - 4.79px), 50px) * 0.52);
}

@keyframes logo-marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-100%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .logo-track {
    animation: none;
  }
}

/* --- Header/nav + hero: responsive (desktop / tablet / mobil) ---
   Škála H1/podnadpisu níže je zjednodušená (monotónně klesající) —
   originál má na 767px paradoxně větší písmo než na 991px, což jsme se
   rozhodli nereplikovat 1:1 bez dalšího zadání. ≥1440px/≥1920px doplněno
   později, viz blok na konci souboru a DECISIONS.md. */

@media screen and (max-width: 991px) {
  .logo-marquee {
    width: 92vw;
  }

  /* .bottom-word at this tier is BIGGER than desktop on the reference
     (verified directly in its CSS, unusual but real) — .h3.white has no
     override here, stays 29px. */
  .hero__headline {
    font-size: 3.125rem; /* 50px */
  }
}

@media screen and (max-width: 767px) {
  .hero__headline {
    font-size: 2.25rem; /* 36px */
  }

  .hero__title {
    font-size: 1.75rem; /* 28px */
  }

  .hero__subtitle {
    font-size: 1.125rem; /* 18px, unchanged from base — reference re-affirms it here */
  }
}

@media screen and (max-width: 479px) {
  #Top {
    padding-top: 112px;
  }

  .hero__headline {
    font-size: 1.875rem; /* 30px */
  }

  .hero__title {
    font-size: 1.625rem; /* 26px */
  }

  .hero__subtitle {
    font-size: 1.0625rem; /* 17px */
  }

  .logo-marquee {
    width: 94vw;
  }
}

/* --- #sluzby: overlap/rounded-corner sekce ---
   Statický CSS trik (ověřeno, že to NENÍ animace): sekce se posune nahoru
   a horní rohy zaoblí, takže vizuálně "přejíždí" přes okraj sekce nad sebou
   (tady hero/logo pás). Třída je samostatná (ne úprava sdílené .section),
   aby zatím needotčené sekce níž na stránce zůstaly beze změny. */

.section--card {
  position: relative;
  top: -40px;
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  background-color: var(--bg);
}

/* Layered rounded step above #reference. The reference stacks two strips
   in front of the testimonials section; both are measured live at 1280px
   and expressed here relative to the bottom edge of the photo section
   above (= #reference's own top, since the two meet exactly):

     .content-section.narrow  top -75px, height 60px, inset 4.98%, --bg-2
     .content-section.offset  top -43px, height 50px, full width, --bg

   The narrow strip is the same colour as the section above, so over that
   section's flat right half it is invisible — but its LEFT half is the
   photo, and there the dark strip does show: a ~32px band across the
   bottom of the photo, inset ~5% with rounded top corners. That band is
   the second step. Verified with elementsFromPoint on the reference:
   at x=100 the topmost element runs photo -> .narrow -> .offset, while
   at x=3 (outside the inset) the photo stays visible throughout.

   Reproduced as ::before/::after so no extra markup is needed. */
#reference::before {
  content: "";
  position: absolute;
  left: 4.98%;
  right: 4.98%;
  top: -75px;
  height: 60px;
  background-color: var(--bg-2);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
}

#reference::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -43px;
  height: 50px;
  background-color: var(--bg);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
}

/* Both strips shift 6px down against the section edge below 768px on the
   reference (its sections change their own overlap there); measured at
   767px: narrow -69, offset -37, visible band unchanged at 32px. */
@media screen and (max-width: 767px) {
  #reference::before {
    top: -69px;
  }

  #reference::after {
    top: -37px;
  }
}

/* Below 480px the reference drops the inset entirely — the dark strip
   goes full width and the visible band shrinks to 13px (measured at
   479px: narrow top -50 / height 69 / inset 0, offset top -37 / 46). */
@media screen and (max-width: 479px) {
  #reference::before {
    left: 0;
    right: 0;
    top: -50px;
    height: 69px;
  }

  #reference::after {
    top: -37px;
    height: 46px;
  }
}

/* #sluzby: po anchor-navigaci (klik na "Naše služby" v nav) potřebuje
   rezervu nad fixed headerem (20px top + 72px výška = 92px). Na originále
   tuhle rezervu mimoděk zajišťuje vertical-centering nadpisu uvnitř
   vyššího sloupce (accordion tam má panely ve výchozím stavu zavřené).
   U nás jsou panely záměrně otevřené i bez JS (viz DECISIONS.md), takže
   sloupce mají skoro stejnou výšku a ta rezerva přirozeně nevznikne —
   řešeno cíleně jen tady, ne obecně pro všechny sekce (u #contact
   necháváme stejný překryv jako na originále, viz DECISIONS.md). */
#sluzby {
  scroll-margin-top: 112px;
}

/* FIX (2026-09-07, Services total-height audit): the shared .section
   padding (4rem/4rem, see .section rule) doesn't match reference's own
   per-section top/bottom spacing here (0/40px below 1920, 40/60px at
   ≥1920) — scoped to #sluzby only, .section itself stays untouched
   (shared with #contact/#reference). Grid/image row height already
   matches exactly (588px/650px, confirmed live) — this fixes ONLY the
   section-level padding wrapping that row. ≥1920 override lives in the
   big ≥1920 media block further down, next to the existing
   .sluzby-grid{min-height:650px} rule. */
@media screen and (min-width: 992px) and (max-width: 1279px) {
  #sluzby {
    padding-top: 0;
    padding-bottom: 40px;
    /* Reference itself clips 10px off the top of its services photo at
       this tier (.flex{margin-top:-10px} inside an overflow:hidden
       section — confirmed live, not a collapse artifact: its own image
       renders 10px above its section's own clip boundary). Reproduced
       here rather than approximated, since the task asked for the
       visible result, not just the padding numbers. */
    overflow: hidden;
  }

  /* FIX (2026-09-08, 1279 regression re-check): reference's row NENÍ
     uncapped tady — je to fluidní .content-wrapper s max-width:1225px a
     paddingem 25px (živě ověřeno na 992 i 1279: width=927/1175 přesně,
     capped teprve když by přesáhl 1225). Sdílené .container (1200px/24px)
     tomu je blízko, ale ne přesně — přepsáno vlastními hodnotami .sluzby-grid,
     scoped jen na tenhle tier. Bez tohohle byl řádek buď moc úzký
     (.container 1200 strop), nebo (po chybném "max-width:none" fixu níž,
     který byl původně unconditional) moc široký (1264 místo 1175 @1279). */
  .sluzby-grid {
    margin-top: -10px;
    max-width: 1225px;
    padding-left: 25px;
    padding-right: 25px;
  }
}

@media screen and (min-width: 1280px) and (max-width: 1919px) {
  #sluzby {
    padding-top: 0;
    padding-bottom: 40px;
  }
}

/* FIX (2026-09-08, image-position canonical audit): gap byl 2rem/32px —
   reference má mezi sloupci 25px flat od 992 do 1919px (živě ověřeno na
   1280 i 1440, shodné do 1px), 5px teprve od ≥1920px (viz override níž). */
.sluzby-grid {
  display: flex;
  align-items: center;
  gap: 25px;
  position: relative;
}

/* FIX (2026-09-08, image-position canonical audit, doplněk — RESCOPED
   2026-09-08 po 1279 regresi): .sluzby-grid sdílí element s .container
   (max-width 1200px do 1439px / 1320px 1440-1919px) — na referenci ale
   tenhle řádek od 1280px žádný strop nemá, sleduje šířku viewportu
   stejně jako #Top (živě ověřeno: flex.w 1215 @1280, 1375 @1440, shodné s
   "viewport minus scrollbar"). Pod 1280px má naopak SVŮJ VLASTNÍ strop
   (1225px/25px padding, viz 992-1279 blok výš) — ne #container 1200px a
   ne "none". Původně bylo tohle pravidlo unconditional (bez media query),
   což nad 1280px fungovalo správně, ale pod 1280px řádek chybně roztáhlo
   na plnou šířku (1264px místo referenčních 1175px @1279) — proto teď
   scoped jen na 1280-1439px. Nad 1439px řeší stejný problém samostatný
   .sluzby-grid{max-width:none} v ≥1440 bloku (musí být tam, ne tady — jinak
   by ho pozdější .container{max-width:1320px} ve stejném bloku přebilo).
   ≥1920 má .container{max-width:none} už dřív, uncap tam řeší samo. */
@media screen and (min-width: 1280px) and (max-width: 1439px) {
  .sluzby-grid {
    max-width: none;
  }
}

/* FIX (2026-09-08, wave re-audit po 1279 regresi): kotví přímo proti
   .sluzby-grid (má position:relative i vlastní max-width/padding cascade
   výš — capped 1225px/25px pod 1280px, uncapped 1280-1919px, .container
   uncapped ≥1920px). Na referenci je .wave taky přímým sourozencem .flex
   uvnitř STEJNÉHO .content-wrapper — žádný samostatný "anchor" element
   navíc (ten byl založený na chybném předpokladu, že content-wrapper je
   vždy neomezený; live re-audit na 1279px ukázal, že pod 1280px capped
   je, přesně jako .sluzby-grid teď). Velikost 105×35 a right ladder
   0/6/8/14 % jsou živě ověřené referenční hodnoty (right vůči
   content-wrapper šířce, bottom vůči jeho výšce — obojí konstantní 10 %
   na všech testovaných breakpointech). */
.sluzby-decor {
  position: absolute;
  right: 0%;
  bottom: 10%;
  width: 105px;
  height: 35px;
  pointer-events: none;
}

@media screen and (min-width: 1280px) and (max-width: 1439px) {
  .sluzby-decor {
    right: 6%;
  }
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .sluzby-decor {
    right: 8%;
  }
}

@media screen and (min-width: 1920px) {
  .sluzby-decor {
    right: 14%;
  }
}

.sluzby-col {
  width: 50%;
}

/* FIX (2026-09-08, Services canonical audit): the reference's text column
   (.column-justify-right) is flex with justify-content/align-items:center
   and the text block (.faq-container) is CENTERED inside it — measured
   canonically (with the lazy layout spacer force-loaded) at every tier:
   faq top = (row − block) / 2 exactly (67 @1280, 53 @1440, 116 @1920).
   Ours top-aligned wherever the column is stretched (≥1920). flex column
   + center reproduces it; it's a no-op at tiers where the column height
   already equals its content. */
.sluzby-col:not(.sluzby-col--image) {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* align-self dělá tu práci na tierech, kde .sluzby-grid NEROZTAHUJE
     sloupce (base align-items:center, ≥1440 flex-start) — tam má sloupec
     výšku obsahu a justify-content uvnitř nemá co centrovat.
     justify-content:center pak pokrývá ≥1920, kde je grid stretch. */
  align-self: center;
}

/* FIX (2026-09-08, image-position canonical audit): max-width přesunut z
   .sluzby-col na jeho DĚTI (h3/p/.accordion). Na referenci je to taky
   takhle — .faq-container (vnitřní blok) má max-width, .column-justify-
   right (celý sloupec) žádný — sloupec musí zůstat plnohodnotných 50 %,
   jinak se nedodrží 50/50 poměr s obrázkovým sloupcem a foto se posune
   (živě potvrzený root cause dřívější −66px/+23px delty). Ladder hodnot
   beze změny: 449px (≤1279) / 500px (1280-1439) / 580px (1440-1919) /
   47,6 % sloupce (≥1920, = 75 % referenčního content-boxu, viz níž). */
.sluzby-col:not(.sluzby-col--image) > * {
  width: 100%;
  max-width: 449px;
}

/* Odstupy uvnitř textového bloku — referenční mechanismus (computed):
   h3 margin-bottom 10px, .subtitle-box margin-top 20px (dohromady 30px),
   .faq-box margin-top 30px. Náš sloupec je flex column, takže se marginy
   nesčítají přes collapse, ale prostě sečtou — stejně jako na referenci,
   kde jsou to taky flow-item marginy bez collapse. */
.sluzby-col:not(.sluzby-col--image) > h3 {
  margin-bottom: 10px;
}

.sluzby-col:not(.sluzby-col--image) > p {
  margin-top: 20px;
  margin-bottom: 0;
}

.sluzby-col:not(.sluzby-col--image) > .accordion {
  margin-top: 30px;
}

@media screen and (min-width: 1280px) and (max-width: 1439px) {
  .sluzby-col:not(.sluzby-col--image) > * {
    max-width: 500px;
  }
}

.sluzby-col--image {
  display: flex;
  justify-content: center;
}

/* FIX S1 (2026-09-07, approved width-mechanism audit): base was 420px,
   reference's actual value (from the <img width="392"> HTML attribute
   driving its unsized flex-child .relative wrapper) is 392px — flat
   from 992px up to 1439px, no change at 1440px.
   FIX S2a (2026-09-07, approved height-mechanism audit, part a): base
   aspect-ratio was 4/5 — reference's actual image at 992–1919px isn't
   ratio-driven CSS at all, it's the raw file dimensions of an unsized
   <img> (392×588, confirmed via img.naturalWidth/Height), which is a
   2:3 ratio, not 4:5. Scoped to ≤1919px only — the ≥1920px override
   below pins the old 4/5 back explicitly so S2b (row/stretch mechanism,
   still unresolved) isn't touched by this change. */
.sluzby-image {
  position: relative;
  width: 100%;
  max-width: 392px;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: var(--radius-card);
}


.sluzby-image__photo {
  position: absolute;
  inset: 0;
  background-image: url("../img/backgrounds/67cacca080996182890240ff_Depositphotos_331303138_XL.jpg");
  background-position: center;
  background-size: cover;
}

/* --- Accordion (Naše služby) ---
   Panely výchozím stavem zavřené (height:0), stejně jako originál.
   Otevírání/zavírání řeší assets/js/main.js — nezávisle pro každou
   položku (ověřeno na originále: může být otevřeno víc položek
   najednou, není to exkluzivní "jen jedna otevřená" accordion). Ikona
   +/- se řídí čistě přes `[aria-expanded="true"]` na tlačítku — jeden
   zdroj pravdy, žádná extra CSS třída pro otevřený stav. */

/* FIX (2026-09-08, Services canonical audit): řádek accordionu je na
   referenci 43px vysoký s 17px mezerou mezi řádky — na KAŽDÉM breakpointu
   (naměřeno 992→2056). Není to dopočet paddingem: reference to skládá z
   trigger 33px (label 23px = line-height, ikona 28px, padding-bottom 5px)
   + 9px odsazení + 1px divider = 43. Naše dřívější hodnoty (řádek 71,
   mezera 0) plynuly z `padding:1.25rem 0` na triggeru a z line-height 1.3
   na .question. Divider je teď samostatný element (.accordion-divider),
   protože reference ho animuje wipe efektem — viz .scroll-reveal--wipe. */
.accordion-item {
  padding-bottom: 9px;
}

.accordion-item + .accordion-item {
  margin-top: 17px;
}

.accordion-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: 0 0 5px;
  cursor: pointer;
  color: inherit;
}

/* Reference .question: Lexend Deca 27px / line-height 23px / 500 /
   letter-spacing -0.3px (ověřeno computed stylem). Ta těsná line-height je
   to, co drží label na 23px a řádek na 43px — ne menší písmo. */
.question {
  font-family: var(--font-heading);
  font-size: 1.6875rem; /* 27px */
  font-weight: 500;
  line-height: 23px;
  letter-spacing: -0.3px;
  color: var(--font-color);
}

.accordion-divider {
  display: block;
  height: 1px;
  background-color: var(--lines);
}

/* Ikona +/- — dva pruhy, výchozí (zavřený) stav = kříž. Otevřený stav:
   .accordion-icon__bar--v se otočí o 90° a zprůhlední se
   .accordion-icon__bar--h, takže zbyde jen jeden vodorovný pruh ("−").
   Ověřeno klikem na originále (tam -270deg, vizuálně identické s 90deg,
   používáme kratší zápis). */

/* Reference .open-close-box je 20×28 a jeho čárky 2×20 / 20×2. */
.accordion-icon {
  position: relative;
  width: 20px;
  height: 28px;
  flex-shrink: 0;
}

/* Zavírací timing reference (a-42): rotace i opacity 800ms ease. */
.accordion-icon__bar {
  position: absolute;
  top: 50%;
  left: 50%;
  background-color: var(--blue-dark);
  transform: translate(-50%, -50%);
  transition: transform 800ms ease, opacity 800ms ease;
}

.accordion-icon__bar--v {
  width: 2px;
  height: 20px;
}

.accordion-icon__bar--h {
  width: 20px;
  height: 2px;
}

/* Otevírací timing reference (a-41): 500ms ease, a rotace je -270deg
   (ne kratší -90deg) — koncový tvar je stejný, ale směr a délka otáčení
   se liší, což je při 500ms viditelné. */
.accordion-trigger[aria-expanded="true"] .accordion-icon__bar {
  transition: transform 500ms ease, opacity 500ms ease;
}

.accordion-trigger[aria-expanded="true"] .accordion-icon__bar--v {
  transform: translate(-50%, -50%) rotate(-270deg);
}

.accordion-trigger[aria-expanded="true"] .accordion-icon__bar--h {
  opacity: 0;
}

/* height:0 + overflow:hidden = zavřeno (výchozí stav, stejně jako
   originál). padding-bottom je na <p> uvnitř, ne na .accordion-panel
   samotném — jinak by i při height:0 zůstal viditelný spodní odstup
   (box-sizing:border-box height:0 nedokáže "sníst" vlastní padding). */
/* Reference: otevření 300ms ease (a-41), zavření 200ms ease (a-42) —
   asymetrické. Uplatní se vždy transition z cílového stavu, takže base
   pravidlo nese zavírací dobu a otevřený stav tu otevírací. */
.accordion-panel {
  height: 0;
  overflow: hidden;
  transition: height 200ms ease;
}

.accordion-trigger[aria-expanded="true"] + .accordion-panel {
  transition: height 300ms ease;
}

/* BUG FIX: main.js po otevření (transitionend) dělá panel.style.height=""
   (viz komentář v assets/js/main.js), aby výška zůstala responzivní
   ("auto"). Bez tohohle pravidla se ale odstraněný inline styl vrátí
   na base `.accordion-panel { height: 0 }` výš — panel se tedy hned po
   doběhnutí otevírací animace sám znovu zavřel (viditelně: klik → krátce
   otevřeno → hned zpátky zavřeno). Tohle pravidlo dá panelu při
   aria-expanded="true" skutečnou `auto` výšku, na kterou má co spadnout. */
.accordion-trigger[aria-expanded="true"] + .accordion-panel {
  height: auto;
}

.accordion-panel p {
  padding-bottom: 1.25rem;
  color: var(--text-grey);
  font-size: var(--fs-text);
}

@media screen and (max-width: 991px) {
  .sluzby-decor {
    display: none;
  }
}

@media screen and (max-width: 767px) {
  .sluzby-grid {
    flex-direction: column;
  }

  .sluzby-col,
  .sluzby-col--image {
    width: 100%;
  }
}

/* --- Řešíme vaše problémy ---
   Overlap bez zaoblení (ověřeno na originále: .full-section.dark nemá
   border-radius, jen top:-40px) — samostatná třída od .section--dark,
   která je sdílená i se sekcí QI níž na stránce. */

.section--overlap {
  position: relative;
  top: -40px;
}

/* Full-bleed layout (retext, matches peaksolutions.agency "Are you seeking
   individuals or complete IT teams?"): image touches the actual viewport
   edge, NOT inset in .container like the rest of the page — confirmed on
   the live agency page (image column left:0, section itself full-width,
   no border-radius on the image). Text column gets its own right padding,
   calculated to line up with .container's right edge at any width (same
   max()-based formula as .container: 1.5rem up to 1200px content width,
   then keeps pace with the centered container beyond that) — everything
   else on the page still reads as aligned to one consistent margin. */
.problems-grid {
  display: flex;
  align-items: stretch;
  /* FIX (2026-09-08, Problems parity audit): was 2rem/32px — reference's
     `.flex.second-section` gap is a flat 25px from 992 clear through
     1919px (live-verified 992/1279/1280/1439/1440/1919, all exactly
     25px), dropping to 5px only at ≥1920px (see override there). Same
     ladder as .sluzby-grid in Services. */
  gap: 25px;
  position: relative;
  width: 100%;
  /* FIX (2026-09-07, live-verified reference @992/1024/1200/1280/1366/
     1439/1440, source-confirmed in ref.css): the shared clamp(320px,
     30vw, 600px) here was a guess. Reference has NO row min-height at
     all below 1280px — height is purely content-driven (row is flex
     with default align-items:stretch, so the image just stretches to
     match the text column's natural height). A real min-height:450px
     only starts at 1280px (.flex.second-section) — see below. */
}

/* FIX (2026-09-08, Problems parity audit): reference clips 10px off the
   top of this row at this tier — same mechanism already established for
   #sluzby (.flex{margin-top:-10px} inside an overflow:hidden section,
   confirmed on the ORIGINAL, not a collapse artifact). Live-verified at
   both 992 and 1279 (flat -10px offset at both ends of the band). Scoped
   via :has() to .problems-grid specifically — NOT the shared tech-grid/
   qi-grid rule above, since only Problems was verified to have this. */
@media screen and (min-width: 992px) and (max-width: 1279px) {
  .section:has(.problems-grid) {
    overflow: hidden;
  }

  .problems-grid {
    margin-top: -10px;
  }
}

@media screen and (min-width: 1280px) and (max-width: 1439px) {
  .problems-grid {
    min-height: 450px;
  }
}

/* Gap belongs on the IMAGE-adjacent side (left, since the image sits at
   left:0), not the viewport side — verified against the reference's own
   CSS (.half-column-text.left, downloaded directly): its gap-to-image is
   padding-left: 8% of ITS OWN column width, uncapped at any viewport.
   Our column here IS the 50%-wide flex item itself (one level shallower
   than the reference's nested half-column > half-column-text), so the
   percentage's containing block is the full grid, not the half column —
   4% of the grid equals 8% of a 50%-wide column, verified to land within
   ~4px of the reference at 991/1280/1920/2560. */
.problems-col {
  /* FIX (2026-09-08, Problems parity audit): was 50%/52% (summing 102% —
     a real overflow that silently triggered flex-shrink and distorted
     both columns away from their specified widths). Reference's real
     split, live-verified flat at every tested tier (992-2056): the image
     column is 51% and the text column 49% of the row's OWN content box
     (i.e. of row-width-minus-gap — confirmed via flexbox's own gap-aware
     percentage resolution, not something extra to reproduce by hand).
     51%+49% = 100% exactly, no shrink distortion. */
  width: 49%;
  position: relative;
  /* FIX S3 (2026-09-07, live-verified reference 992/1439/1440/1919, all
     flat): vertical padding was a wrong shared 48/48 guess — reference
     uses 70px top/bottom here, unchanged through 1919px (verified
     internal heading/body/CTA y-offsets, not just total column height —
     the min-height stretch below was masking this at ≥1440). ≥1920px
     switches to a separate 20/20 mechanism, handled in that block. */
  padding: 70px 0 70px 4%;
}

.problems-col--image {
  width: 51%;
  padding: 0;
}

.problems-image {
  width: 100%;
  height: 100%;
  min-height: 320px;
  background-image: url("../img/backgrounds/67cacca08099618289024101_Depositphotos_318769736_XL.jpg");
  background-position: center;
  background-size: cover;
}

/* FIX (2026-09-08, Problems parity audit): same class of bug as the
   Services wave — containing block was wrong. Reference's decorative
   circle (`.absolute` wrapping the polkruh img) is NOT nested inside the
   text column; it's a direct child of the ROW itself (`.flex.
   second-section`), straddling the seam between the two columns. Moved
   in HTML to be a direct child of .problems-grid (already position:
   relative) instead of a child of .problems-col — left:-60px (relative
   to the TEXT column) is replaced by left:43% (relative to the ROW,
   live-verified flat 43.00% through 992-1279px, see ladder below).
   width corrected 110→133px (reference's real natural image width,
   confirmed via naturalWidth) — auto height keeps its ~2:1 ratio
   (66.5px). top+bottom:0 with margin:auto 0 centers it vertically
   against the FULL row height, matching reference's flex-centered
   wrapper (reference: height 450/500px stretched, image vertically
   centered inside via align-items:center — margin:auto reproduces the
   same resting position without needing an extra flex wrapper here,
   since this is a single absolutely-positioned img, not a wrap+img
   pair). */
.problems-decor {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 43%;
  width: 133px;
  height: auto;
  margin: auto 0;
  pointer-events: none;
}

@media screen and (min-width: 1280px) and (max-width: 1439px) {
  .problems-decor {
    left: 44%;
  }
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .problems-decor {
    left: 46%;
  }
}

@media screen and (min-width: 1920px) {
  .problems-decor {
    left: 47%;
  }
}

@media screen and (max-width: 767px) {
  .problems-decor {
    display: none;
  }

  .problems-grid {
    flex-direction: column-reverse;
  }

  .problems-col,
  .problems-col--image {
    width: 100%;
  }

  /* Stacked layout: text gets normal padding on both sides again (the
     desktop full-bleed formula only made sense as one-sided spacing next
     to an edge-to-edge image in a row). Image stays edge-to-edge. */
  .problems-col {
    padding: 1.5rem;
  }

  .problems-col--image {
    padding: 0;
  }
}

/* --- Technologie, se kterými pracujeme ---
   Overlap bez zaoblení, stejný mechanismus jako "Řešíme vaše problémy"
   (.section--overlap je znovu použitá — ověřeno, že .full-section bez
   .dark má identické top:-40px/žádný radius).
   Text vlevo, obrázek vpravo (ověřeno, NE zrcadleno podle "problémů").
   Žádná dekorace v této sekci — ověřeno, originál žádnou nemá.
   Obrázek (.image-column.rounded) nemá navzdory názvu třídy žádný
   border-radius (ověřeno computed stylem 0px) — neaplikuji radius.

   FIX (2026-09-08, Tech parity audit — RULE ABOVE CORRECTED): předchozí
   verze tvrdila "světlé pozadí (--bg), sedí s výchozím pozadím stránky,
   není co přebarvovat" — nikdy to ale nebylo živě změřené, jen odhad
   podle oka. Čerstvé měření computed background-color na originále:
   `.full-section` (bez .dark) = rgb(207,207,214) = přesně #cfcfd6 =
   naše --bg-2 proměnná — VIDITELNĚ tmavší než stránkové pozadí
   rgb(234,234,238)/--bg. Text zůstává tmavý (--font-color, #231f20 —
   žádná bílá jako na .problems-col, který .section--dark taky jen mění
   pozadí, ne barvu textu). Tech sekce v HTML nemá třídu .section--dark
   (a přidávat ji by bylo riskantnější než nutné), takže pozadí řešeno
   samostatným :has() pravidlem, scoped jen na tuhle sekci.

   APPROVED IMPROVE DEVIATION (2026-09-08, Petra): živě doměřeno přes
   všechny breakpointy — originál tuhle šedou přepíná teprve od přesně
   1440px (992-1439 nemá žádnou vlastní barvu, splývá se stránkou; ostrý
   skok potvrzen na 1439 vs 1440). Petra to vyhodnotila jako nedomyšlenost
   originálu (Problems sekce je tmavá vždy, ne jen od nějaké šířky) a
   vědomě zvolila --bg-2 JEDNOTNĚ na všech šířkách místo věrné reprodukce
   té nekonzistence — konzistentní světlá/tmavá rytmus sekcí po celé
   stránce, lepší kontrast. Viz DECISIONS.md. */
.section:has(.tech-grid) {
  background-color: var(--bg-2);
}

/* Full-bleed layout, same reasoning as .problems-grid above (retext,
   matches agency's "How do we make it happen?") — here text is the FIRST
   column, so it gets left padding (mirrored formula) instead of right. */
.tech-grid {
  display: flex;
  align-items: stretch;
  /* FIX (2026-09-08, Tech parity audit): was 2rem/32px — reference's row
     gap is a flat 25px through 992-1919px (live-verified 992/1279/1280/
     1439/1440/1919), dropping to 5px only at ≥1920px. Same ladder as
     .problems-grid/.sluzby-grid. */
  gap: 25px;
  width: 100%;
  /* FIX (2026-09-07): reference has NO row min-height for this section at
     ANY width below 1920px (live-verified 992-1440, source-confirmed —
     the bare .flex class here carries no height rule at all). Purely
     content-driven stretch, like .problems-grid below 1280px above. */
}

/* FIX (2026-09-08, Tech parity audit): reference clips 10px off the top
   of this row at 992-1279px — same mechanism already established for
   #sluzby and .problems-grid (row/textWrap both measured at t:-10 inside
   an overflow:hidden section, flat across the whole band). Scoped via
   :has() to .tech-grid specifically. */
@media screen and (min-width: 992px) and (max-width: 1279px) {
  .section:has(.tech-grid) {
    overflow: hidden;
  }

  .tech-grid {
    margin-top: -10px;
  }
}

/* Direction already matched the reference here (text is the FIRST column,
   so its own padding-left both insets it from the viewport edge AND is
   the reference's real gap value — .half-column-text.left, padding-left:
   8% of its own column, uncapped). Only the magnitude changes: 4% of the
   full grid = 8% of a 50%-wide column (see .problems-col above). */
/* BUG-CLASS GUARD (2026-09-08, Tech parity audit): every rule below that
   is specific to the TEXT column is scoped :not(:has(.tech-image)) —
   NEVER bare `.tech-col` — because the image column carries the same
   `tech-col` class and a bare selector silently leaks onto it (exactly
   the regression found live on .problems-col earlier this session: the
   image column got padding + centering it was never meant to have and
   visibly shrank). */
/* FIX (2026-09-08, found live checking mobile after the Qi zigzag work,
   CORRECTED to avoid a gap): :not(:has()) selectors below are MORE
   specific than the bare `.tech-col{width:100%}` in the ≤767px block
   further down, so they silently won there too and broke the mobile
   stacked layout (measured 391/376px columns instead of 767/767 full
   width) — same root cause as the Problems screenshot regression, just
   the specificity-vs-source-order version instead of the bare-selector-
   leak version. First fix attempt wrapped this whole block in
   `@media (min-width:992px)`, which stopped the mobile leak but opened a
   NEW hole at 768-991px (between the ≤767px mobile block and this
   ≥992px one) where NEITHER rule applied — .tech-col:has(.tech-image)
   got no width at all and collapsed to 0, caught live testing exactly
   the "other resolutions" this fix was meant to cover. Reverted to
   unconditional (matches .problems-col's own pattern); the mobile block
   itself is fixed instead, using the SAME :has()-based selectors so
   source order (mobile is later in the file) resolves it correctly at
   every width with no gap — see the ≤767px block below. */
.tech-col:not(:has(.tech-image)) {
  /* FIX (2026-09-08, Tech parity audit): was flat 50% (both columns) —
     reference's actual split is NOT 50/50. Live-verified against
     (row-gap), flat within each tier: 49%/51% (text/image) 992-1439px,
     49.5%/50.5% 1440-1919px, 51%/49% (roles swap) ≥1920px — see the two
     overrides below. */
  width: 49%;
  /* FIX S3 (2026-09-07, live-verified reference 992/1439/1440/1919, flat
     the whole range, no ≥1920 change either — confirmed via cascade
     specificity: .half-column-text.left always wins over the plain
     .half-column-text's ≥1920 padding override). Was wrong shared 48/48. */
  padding: 40px 0 100px 4%;
  /* FIX (2026-09-08, Tech parity audit): reference's outer `.half-column`
     wraps the padded `.half-column-text` with display:flex;align-items:
     center — this centers the whole padded text block vertically within
     the row whenever the row is taller than the text's own natural
     content (only ever happens ≥1920px here, where min-height:650 creates
     real leftover space — live-verified heading top 96/150.5px vs local's
     previous 40/40px flush-to-padding-top). Below that, row height IS the
     text's own natural height (no min-height, no image intrinsic size),
     so centering is a no-op — safe to apply flat rather than gate it to
     ≥1920 like Problems needed. Mathematically identical to reference's
     two-level (outer centers, inner pads) structure since padding stays
     ON this same flex container: centering-content-in-(box-minus-padding)
     ≡ centering-(padding+content)-as-a-unit-in-box. */
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* FIX (2026-09-08, Tech parity audit): found live testing the
   flex-column change above — h3/p are exempt from the new flex-column's
   default align-items:stretch because they have their own max-width
   ceiling, but the CTA button has neither a max-width nor auto margins,
   so it stretched to the column's full content width (636px instead of
   its own ~200px content size — same bug class as the Problems CTA fix
   earlier this session). align-self:flex-start opts it out of stretch.
   Note this only actually renders ≥992px anyway (mobile's .tech-col has
   its own block-flow, no button-stretch risk there), so no gap concern
   here despite being unconditional. */
.tech-col:not(:has(.tech-image)) > a.button {
  align-self: flex-start;
}

.tech-col:has(.tech-image) {
  padding: 0;
  width: 51%;
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .tech-col:not(:has(.tech-image)) {
    width: 49.5%;
  }

  .tech-col:has(.tech-image) {
    width: 50.5%;
  }
}

@media screen and (min-width: 1920px) {
  .tech-col:not(:has(.tech-image)) {
    width: 51%;
  }

  .tech-col:has(.tech-image) {
    width: 49%;
  }
}

.tech-image {
  width: 100%;
  height: 100%;
  min-height: 320px;
  background-image: url("../img/backgrounds/67cacca080996182890240e5_Depositphotos_435071354_XL.jpg");
  background-position: center;
  background-size: cover;
}

@media screen and (max-width: 767px) {
  .tech-grid {
    flex-direction: column;
  }

  /* FIX (2026-09-08, found live): was bare `.tech-col{width:100%;
     padding:1.5rem}` — lower specificity than the desktop `:not(:has())`
     rule above, so at ≤767px the desktop 49% width silently won instead
     of this 100%, collapsing the stacked mobile layout. Matching the
     desktop rule's own :not(:has()) selector gives this equal specificity,
     so normal source order (this block is later in the file) decides
     correctly again — same fix applied to .qi-col below. */
  .tech-col:not(:has(.tech-image)) {
    width: 100%;
    padding: 1.5rem;
    display: block;
  }

  .tech-col:has(.tech-image) {
    width: 100%;
    padding: 0;
  }
}

/* --- QI partner sekce ---
   V originále staticky skrytá (viz komentář v index.html a DECISIONS.md).
   Layout ověřen force-display na originále: identický vzorec jako
   "Řešíme vaše problémy" (bg-2/--section--dark, top:-40px bez radiusu,
   obrázek vlevo, mobil column-reverse) — proto stejná struktura tříd,
   jen s vlastními jmény pro obsah (qi-*), aby se to nepletlo s problems-*. */

/* Full-bleed layout, same reasoning/formula as .problems-grid (retext,
   matches agency's "Who are you searching for?" — confirmed live: image
   left:0, no border-radius). Image is the first column here too. */
.qi-grid {
  display: flex;
  align-items: stretch;
  /* FIX (2026-09-08, Qi parity audit): was 2rem/32px. Reference's row gap
     is flat 25px 992-1439px (live-verified 992/1279/1280/1439, matching
     .problems-grid/.tech-grid), then a DIFFERENT flat 35px from 1440px
     up through 2056 (own value — QI does NOT switch to 5px at ≥1920 like
     the other two sections; verified live, not assumed). */
  gap: 25px;
  width: 100%;
  /* FIX (2026-09-07): reference has NO row min-height for this section
     below 1920px (live-verified 992-1440, source-confirmed — first
     min-height on .flex.slider-section is the 620px rule at 1920px).
     Purely content-driven stretch, same as .tech-grid above. */
}

@media screen and (min-width: 1440px) {
  .qi-grid {
    gap: 35px;
  }
}

/* BUG-CLASS GUARD (2026-09-08, Qi parity audit): every text-column-only
   rule below is scoped :not(:has(.qi-image)) — NEVER bare `.qi-col` —
   same regression class already found on .problems-col and guarded on
   .tech-col earlier this session (bare selector leaks onto the image
   column, which shares the `qi-col` class). */
/* FIX (2026-09-08, found live checking mobile after the zigzag work,
   CORRECTED to avoid a gap): :not(:has()) below is more specific than
   the bare `.qi-col{width:100%}` in the ≤767px block further down, so
   it silently won there too and broke the mobile stacked layout
   (measured ~369/379px columns instead of 767/767 full width). First
   fix attempt wrapped this in `@media (min-width:992px)`, which fixed
   mobile but opened a NEW hole at 768-991px where neither rule applied
   and .qi-col:has(.qi-image) collapsed to 0 width — caught live testing
   "other resolutions". Reverted to unconditional (matches Tech/Problems'
   own pattern); the ≤767px mobile block is fixed instead, using the same
   :has()-based selectors so normal source order resolves it with no gap
   — see that block below. */
.qi-col:not(:has(.qi-image)) {
  /* FIX (2026-09-08, Qi parity audit — CORRECTED): was flat 50% (both
     columns), then briefly 49% here (still wrong — see below). Real
     split live-verified against the row, percentages resolving against
     the FULL row width (confirmed live via the zigzag width test below:
     a 35px gap is large enough to tell "% of row" and "% of row−gap"
     apart, unlike Tech/Problems' small 5px gap where both looked
     identical and the wrong theory went undetected) — 48%/~50%
     (text/image) 992-1439px; 47%/50% 1440-1919px (reference's OWN
     numbers leave ~2-3% of the row unused — a genuine reference-side
     quirk, reproduced as-is, not "fixed" to sum to 100%); 46.6%/51.6%
     ≥1920px — see overrides below. (The image's OWN value at every tier
     is now the zigzag-deviation width instead, see the rule after this
     one — only the text % here still reflects Qi's own reference.) */
  width: 48%;
  /* FIX (2026-09-08, Qi parity audit — found live testing the zigzag
     width below): text% + image% + gap slightly exceeds the row at some
     tiers (they're independently sourced now, not designed to sum to
     100%), which silently triggered default flex-shrink and distorted
     BOTH columns away from their authored percentages. flex-shrink:0 on
     both columns (see the image rule below too) makes every width apply
     exactly as written — verified live, no more silent distortion. */
  flex-shrink: 0;
  /* Same direction fix as .problems-col above (image left, text right —
     gap belongs on the left/image-adjacent side), same 4%-of-grid
     magnitude. FIX S3 (2026-09-07): same mechanism/value as .tech-col
     above (both are the reference's .half-column-text.left), flat
     992-∞, no ≥1920 change. */
  padding: 40px 0 100px 4%;
  /* FIX (2026-09-08, Qi parity audit): same centering mechanism as
     .problems-col/.tech-col — reference's outer `.half-column` wraps the
     padded `.half-column-text` with display:flex;align-items:center.
     Flat here too (no-op below 1920px where row height already equals
     the text's own natural content height). */
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* APPROVED IMPROVE DEVIATION (2026-09-08, Petra — zigzag layout): the
   image's own width no longer follows Qi's own reference value (which
   was 51%/50%/51.6%) — Petra asked for the image's top-right corner to
   line up with the PREVIOUS section's (.tech-grid) photo, whose left
   edge sits at Tech's own text-column width, creating a deliberate
   zigzag down the page instead of reproducing Qi's reference framing.
   Since both rows share the same section width and .qi-image is the
   FIRST column (so its own width IS its right edge's x-position), the
   target per breakpoint is Tech's text-column PIXEL width PLUS Tech's
   OWN gap (that's where Tech's image — and thus the corner to match —
   actually starts, not the bare text width; caught live testing the
   first attempt, which was off by exactly one gap's worth, ~23-25px):
   492/633/711/718.5/955.6/974.1/1043.5 at 992/1280/1439/1440/1919/
   1920/2056. CORRECTED TWICE: (1) flex width percentages here resolve
   against the FULL row, not (row − gap) — confirmed live (Qi's own 35px
   gap is large enough to tell the two hypotheses apart, unlike Tech/
   Problems' small 5px gap where both looked identical); (2) the target
   itself needed Tech's gap added in. One representative value per tier
   keeps every breakpoint within ~2.6px of Tech's actual left edge
   (live-verified all 8 canonical breakpoints), plenty for a decorative
   corner-alignment effect. Qi's TEXT column width and the row gap both
   stay exactly as measured against Qi's own reference above (untouched)
   — only the image moved.
   FOUND LIVE AFTER ADDING flex-shrink:0 ABOVE: 50.1% here + 48% text +
   this tier's 25px gap summed to just over 100% of the row at the
   narrow end (992px), which with shrink disabled caused a real ~6px
   horizontal page overflow (scrollbar) instead of silent distortion.
   Backed off to 49.4% — the safe ceiling given text's own 48% and the
   25px gap at every point in this tier (tightest at 992px:
   100−48−25/977×100 ≈ 49.44%). Costs a little zigzag precision
   (~7-9px off Tech's edge instead of ~2.6px) but a visible layout bug
   isn't an acceptable trade for a decorative effect — verified zero
   overflow at every tested width in this tier. */
.qi-col:has(.qi-image) {
  width: 49.4%;
  flex-shrink: 0;
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .qi-col:not(:has(.qi-image)) {
    width: 47%;
  }

  .qi-col:has(.qi-image) {
    width: 50.3%;
  }
}

@media screen and (min-width: 1920px) {
  .qi-col:not(:has(.qi-image)) {
    width: 46.6%;
  }

  .qi-col:has(.qi-image) {
    width: 51.1%;
  }
}

/* Text content in these 3 retext columns caps at ~450px and uses 15px
   (matching the "subtitle"-equivalent size the reference uses for this
   kind of copy) — confirmed against peaksolutions.agency directly, not
   guessed. Image columns are excluded (they don't have a p/h3 anyway,
   but :not() keeps this future-proof if that ever changes). */
/* max-width belongs on the TEXT, not on the column: the column carries a
   viewport-scaled padding, and with border-box that padding eats into the
   max-width — on a 1920px screen it left ~66px for the text and wrapped
   every word onto its own line.

   The width itself scales with the viewport, matching the reference
   measured live at two widths: ~450px @1280 and ~700px @1920. */
.problems-col > h3,
.problems-col > p,
.tech-col > h3,
.tech-col > p,
.qi-col > h3,
.qi-col > p,
.qi-col > .roles-list {
  max-width: clamp(450px, 36vw, 700px);
}

/* FIX (2026-09-08, Problems parity audit): the shared clamp() above is a
   fluid approximation — reference's Problems text max-width is actually
   a flat per-tier value, live-verified exact at every tested breakpoint
   (992/1279 ceiling 484px, not usually binding since the column itself
   is narrower; 1280-1439 flat 450px; 1440-1919 flat 600px; ≥1920 flat
   700px — no vw-based scaling in between). Scoped to .problems-col only
   (own selector, overriding the shared rule above by source order) —
   tech-col/qi-col keep the fluid clamp() untouched, out of this audit's
   scope. */
.problems-col > h3,
.problems-col > p {
  max-width: 484px;
}

@media screen and (min-width: 1280px) and (max-width: 1439px) {
  .problems-col > h3,
  .problems-col > p {
    max-width: 450px;
  }
}

@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .problems-col > h3,
  .problems-col > p {
    max-width: 600px;
  }
}

@media screen and (min-width: 1920px) {
  .problems-col > h3,
  .problems-col > p {
    max-width: 700px;
  }
}

.problems-col p,
.tech-col p,
.qi-col p {
  font-size: var(--fs-text);
}

/* FIX (2026-09-08, Tech parity audit): --fs-text is a shared sitewide
   variable (subtitle, testimonial, nav — see its own definition) that
   never scales, but reference's OWN paragraph text in these 3 retext
   columns does: live-verified 15px flat below 1440px (matches --fs-text,
   so no override needed there), 19px at 1440-1919px, 22px at ≥1920px —
   confirmed identically on Problems AND Tech (Qi not yet audited, likely
   shares it too since qi-col is grouped in the same base rule above).
   Scoped to these 3 selectors only — does NOT touch --fs-text itself,
   so every other element using that variable is unaffected. */
@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .problems-col p,
  .tech-col p,
  .qi-col p {
    font-size: 19px;
  }
}

@media screen and (min-width: 1920px) {
  .problems-col p,
  .tech-col p,
  .qi-col p {
    font-size: 22px;
  }
}

/* FIX (2026-09-08, Qi parity audit): Qi's own heading font-size ladder
   does NOT follow the shared/generic `h3` ladder (29/32/33px) — live-
   verified flat 29px through 992-1919px (no 32px step at 1440 like
   Problems/Tech get), then jumping straight to 35px at ≥1920px. Higher
   specificity (.qi-col > h3) wins over the bare `h3` rule regardless of
   source order, so placement here is just for proximity to the other Qi
   type fixes above. */
@media screen and (min-width: 1440px) and (max-width: 1919px) {
  .qi-col > h3 {
    font-size: 29px;
  }
}

@media screen and (min-width: 1920px) {
  .qi-col > h3 {
    font-size: 35px;
  }
}

/* FIX S3, CORRECTED (2026-09-07, typography/height parity audit): the
   original S3 value (10px) was measured wrapper-to-wrapper on the
   reference (its heading sits inside a `.hidden` div with
   overflow:hidden) and missed that overflow:hidden blocks normal margin
   collapse — the heading's own 10px margin-bottom renders as trapped
   space INSIDE that wrapper, invisible to a wrapper-to-wrapper
   measurement. The real heading→body gap is that trapped 10px PLUS the
   next element's own 10px margin-top = 20px. Confirmed by reconciling
   the full column height to within 1px at both 992 and 1280 for all
   three sections using this value. body→CTA stays 20px (no such
   wrapper on that side, the original measurement was already correct). */
.problems-col > h3,
.tech-col > h3,
.qi-col > h3 {
  margin-bottom: 20px;
}

/* Reference's Problems/Qi headings use its own h4 class (margin-top:
   20px, trapped the same way), Tech uses h3 (margin-top: 0) — a real
   difference between the two heading levels, not a mistake to unify. */
.problems-col > h3,
.qi-col > h3 {
  margin-top: 20px;
}

.problems-col > p,
.tech-col > p,
.qi-col > p:first-of-type {
  margin-bottom: 20px;
}

/* Reference-only quirk, downloaded and verified directly in its CSS: past
   1920px this ONE section (not tech/qi) drops the image-side gap and
   centres the text block in the remaining column space instead of
   growing the gap forever. tech-col/qi-col deliberately keep scaling
   past 1920 (verified live up to 2560) — this override is not mirrored
   for them. */
@media screen and (min-width: 1920px) {
  /* BUG FIX (2026-09-08, found live via screenshot compare): this whole
     rule was bare `.problems-col`, which ALSO matches the image column
     (its element carries both `problems-col` and `problems-col--image`
     — the image-specific `padding:0` from the base rule is single-class
     specificity too, so this later ≥1920 rule was silently overriding
     it). Result: the image column got 20px top/bottom padding it was
     never meant to have, shrinking `.problems-image` (height:100% of a
     now-40px-shorter box) and leaving a visible grey gap above/below the
     photo — confirmed exactly matching the reported screenshot. Scoped
     to :not(.problems-col--image) like every other text-only rule in
     this section. */
  .problems-col:not(.problems-col--image) {
    /* FIX S3 regression guard (2026-09-07): the base rule above is now
       70/70, correct for 992-1919, but live-verified reference switches
       to 20/20 here specifically (only .half-column-text, not the .left
       variant — confirmed via cascade specificity). Without this, the
       ≥1920 tier would regress from the previous (also wrong, but
       smaller-delta) 48/48. Logged as its own large-desktop parity item,
       not folded into the 992-1919 S3 fix above. */
    padding-top: 20px;
    padding-bottom: 20px;
    padding-left: 0;
    /* FIX (2026-09-08, Problems parity audit): reference's own
       `.half-column-text` becomes `display:flex;align-items:center;
       justify-content:center` at this tier — the text block sits
       vertically CENTERED in the column, not top-padded (live-verified:
       heading top 96px vs local's previous 40px, a ~56px miss). Same
       flex-column-center pattern already approved for .sluzby-col in
       Services. Horizontal centering below (margin:auto on h3/p, the
       calc() on the button) is untouched and keeps working the same way
       inside a flex column, since align-items defaults to stretch and
       max-width still caps each child before its own auto-margins run. */
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  /* The reference centres the TEXT BLOCK as a unit within the column —
     heading/paragraph/button stay left-aligned to EACH OTHER, only the
     block as a whole sits further from the column's own left edge.
     h3/p share a width (both capped at the same clamp), so margin:auto
     happens to give them the same left edge. The button is a narrower,
     content-sized inline-block, so margin:auto on it would centre IT
     independently — same midpoint as h3/p, but a different (indented)
     left edge, breaking the shared alignment (found live at 2056px, a
     MacBook Pro 16" "more space" scaled resolution). Giving it the exact
     same left inset as h3/p's own auto-margin — half the leftover space
     after the identical clamp() width — lines its left edge up with
     theirs instead, without stretching or centering the button itself. */
  .problems-col > h3,
  .problems-col > p {
    margin-left: auto;
    margin-right: auto;
  }

  /* FIX (2026-09-08, Problems parity audit): matches the flat 700px
     ladder value above now used by h3/p at this tier — was still
     referencing the old fluid clamp() (691.2px @1920), which would have
     thrown the button's left edge ~9px out of line with the (now
     corrected) h3/p left edge. align-self:flex-start counters the new
     flex-column .problems-col's default align-items:stretch — h3/p are
     exempt from stretch because BOTH their margins are auto, but the
     button's margin-right is 0 (only margin-left is set), so without
     this it stretched to fill the column's remaining width (815px
     instead of its own content size), found live testing this exact
     fix. */
  .problems-col > a.button {
    margin-left: calc((100% - 700px) / 2);
    align-self: flex-start;
  }
}

/* Photo runs edge to edge and to the section's full height — measured on
   the reference, where .change-this-image._4 spans the dark section top
   to bottom (2329–2806 @1280) and the rounded strip above #reference
   deliberately overlaps its bottom 43px. */
.qi-col:has(.qi-image) {
  padding: 0;
}

.qi-image {
  width: 100%;
  height: 100%;
  min-height: 320px;
  background-image: url("../img/backgrounds/67cacca080996182890240f9_Depositphotos_449599738_XL.jpg");
  background-position: center;
  background-size: cover;
}

@media screen and (max-width: 767px) {
  .qi-grid {
    flex-direction: column-reverse;
  }

  /* FIX (2026-09-08, found live, same fix as .tech-col): was bare
     `.qi-col{width:100%;padding:1.5rem}` — lower specificity than the
     desktop `:not(:has())` rule above, so the desktop 48% width silently
     won at ≤767px instead. Matching selectors give this equal
     specificity, so normal source order (this block is later) wins. */
  .qi-col:not(:has(.qi-image)) {
    width: 100%;
    padding: 1.5rem;
    display: block;
  }

  .qi-col:has(.qi-image) {
    width: 100%;
    padding: 0;
  }
}

/* "Koho hledáte?" role list — retext replacement content for the recycled
   QI slot (see index.html comment + DECISIONS.md). Two-column tag grid,
   no bullets, matches the section's plain-text tone. */
.roles-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  /* FIX S3 (2026-09-07, live-verified reference .div-block-6 + .item,
     source-confirmed via ref.css): reference builds vertical rhythm from
     each item's OWN margin-top:10px (not a shared row-gap), plus the
     list's own margin-top:15px — both flat at every tier. row/column-gap
     is 0 up to 1439px, then 5px from 1440px (source: min-width:1440
     override on .div-block-6). Reproducing this exactly (item margin +
     matching gap ladder below) instead of one compromise gap value.

     margin-top is 25px, not 15px: reference's label→list gap is 25px
     because its flex parent doesn't collapse margins (label's own
     margin-bottom:10px + list's margin-top:15px sum to 25). Our .qi-col
     is plain block flow, where adjacent margins collapse to the LARGER
     value instead of summing — so to render the same 25px gap here, the
     list's own margin-top must BE 25 outright (it already safely wins
     the collapse against the label's smaller margin-bottom). */
  row-gap: 0;
  column-gap: 0;
  list-style: none;
  margin: 25px 0 0;
  padding: 0;
}

.roles-list li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 10px;
  /* FIX S3: reference's .paragraph-medium (item text) font-size ladder —
     15/16/18/20px at base/1280/1440/1920 (source-confirmed in ref.css) —
     drives its line-height and therefore each row's real height. Local
     was inheriting a flat 16px everywhere, which only coincidentally
     matched the 1280-1439 tier and was wrong (too tall/short) elsewhere.
     Reproducing the ladder is the actual fix; a single compromise value
     would keep drifting at every other breakpoint. */
  font-size: 15px;
}

.roles-list li img {
  flex-shrink: 0;
}

@media screen and (min-width: 1280px) {
  .roles-list li {
    font-size: 16px;
  }
}

@media screen and (max-width: 479px) {
  .roles-list {
    grid-template-columns: 1fr;
  }
}

/* --- Reference (testimonials) ---
   V originále staticky skrytá (viz komentář v index.html a DECISIONS.md).
   Wrapper zaoblený + top:-40px jako #sluzby (.section--card), NE
   bezradiusová .section--overlap varianta problems/tech/qi — ověřeno
   computed stylem přes force-display, ne převzato podle sousedních sekcí.
   Karta: bg var(--bg-2), radius var(--radius-card), padding 5% 8% 3% —
   hodnoty ověřené na originálu (.testimonial-box). Výška karet je
   v originále na ≥1280px `height: auto` (obsahem řízená), proto tu žádnou
   pevnou výšku nereplikujeme. */

/* FIX (2026-09-07, Testimonials width audit): this section's reference
   equivalent (.content-wrapper.w-container) is NOT the same container
   mechanism as Problems/Tech/Qi/Contact — live-verified flat 25px
   left/right padding. The shared .container class (var(--container-max)/
   --section-padding-x) doesn't apply here; overriding both, scoped to
   this section only. */
#reference .container {
  max-width: none;
  padding-left: 25px;
  padding-right: 25px;
}

/* CORRECTION (2026-09-07, breakpoint-edge audit): the "no max-width cap
   at every breakpoint" claim above was wrong below 1280px — reference's
   .content-wrapper (plain, no .hero modifier) actually has max-width:
   1225px there, switching to max-width:100% (uncapped, matching the
   override above) only at exactly ≥1280px — live-verified via the exact
   1278/1279/1280 edge (slide width jumps 1175→1215 on the reference
   itself at that boundary, not a smooth/rounding transition). Scoped
   here so ≥1280px keeps the already-matching uncapped behavior. */
@media screen and (max-width: 1279px) {
  #reference .container {
    max-width: 1225px;
  }
}

.testimonials-heading {
  max-width: 600px;
  margin: 0 auto 1.875rem;
  text-align: center;
}

/* FIX (2026-09-08, Testimonials parity audit — now visible, filling the
   ≥1440/1920 gap deliberately left open earlier per DECISIONS.md/TODO.md
   history): reference's own heading here has margin-bottom:10px, not the
   shared h3 default (1rem/16px) every other heading on the page uses —
   live-verified flat at 1280 and 1920, own value confirmed via source,
   not assumed from the shared rule. Scoped to this heading only. */
.testimonials-heading h3 {
  margin-bottom: 10px;
}

.testimonial-slider {
  position: relative;
}

.testimonial-track {
  display: flex;
  flex-direction: column;
}

/* Real slider (added when #reference was un-hidden for the retext — see
   DECISIONS.md/TODO.md history): only .is-active renders, JS in main.js
   swaps the class on prev/next/dot click. Markup (buttons, dots) was
   already prepared; this is the first time it actually needs to work. */
.testimonial-slide {
  display: none;
  background-color: var(--bg-2);
  border-radius: var(--radius-card);
  padding: 5% 8% 3%;
  /* FIX (2026-09-08, found live — same bug class as the fake Tech/Qi
     min-heights this session): the "560px fixed below 1280px" claim
     below was wrong — checked against the live stylesheet directly
     (iterated document.styleSheets, no rule anywhere sets .testimonial-
     box's height), and confirmed by resizing: reference's real height
     is 701.5px @375px, not 560 — 560 was purely a coincidence of ONE
     testimonial's English text at 992px, mistaken for a constraint
     because only that one width was checked. Reference is 100%
     content-driven auto height at every width. The fixed 560px broke
     local's mobile layout: Czech text (longer/more lines than the
     English original) needed more room than 560px, and with
     overflow:visible the text just spilled out below the card's grey
     background/rounded corners instead of the card growing to fit —
     exactly the "height vs. text" break Petra spotted live on mobile.
     .stars-box is display:none on the reference at every breakpoint
     (dead markup, confirmed via source and via its unloaded star
     images) — not reproduced, contributes 0px. */
}

.testimonial-slide.is-active {
  display: block;
}

/* FIX (2026-09-08, Testimonials parity audit): reference's own slider
   arrows (`.w-slider-arrow-left/-right`) are `display:none` at EVERY
   breakpoint tested (992-2056px, live-verified, own `display-none`
   Webflow class) — navigation is dots-only. Local previously only hid
   them ≤767px, showing visible prev/next circle buttons everywhere else
   that the reference never has. Buttons stay in the DOM (keyboard/JS
   still work if ever re-enabled) — just not shown, matching reference's
   own accessible-but-hidden pattern. */
.slider-arrow {
  display: none;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--text-grey);
  background-color: var(--bg);
  cursor: pointer;
  z-index: 2;
}

.slider-arrow::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--font-color);
  border-bottom: 2px solid var(--font-color);
}

.slider-arrow--prev {
  left: -20px;
}

.slider-arrow--prev::before {
  transform: translate(-25%, -50%) rotate(135deg);
}

.slider-arrow--next {
  right: -20px;
}

.slider-arrow--next::before {
  transform: translate(-75%, -50%) rotate(-45deg);
}

.slider-arrow:hover,
.slider-arrow:focus-visible {
  background-color: var(--blue-light);
  border-color: var(--blue-light);
}

.slider-arrow:hover::before,
.slider-arrow:focus-visible::before {
  border-color: var(--bg-text-white);
}

.slider-nav {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
  margin-top: 1.5rem;
}

/* FIX (2026-09-08, Testimonials parity audit): was 10px/blue-themed
   (10x10px, transparent+grey border, active=var(--blue-light)) — the
   reference's real dots (`.w-slider-dot`) are 16x16px, no border, dark
   grey (not blue): inactive rgba(34,34,34,.4), active a solid
   rgb(34,34,34) — live-verified flat at 1280 and 1920. */
.slider-dot {
  width: 16px;
  height: 16px;
  padding: 0;
  border-radius: 50%;
  border: none;
  background-color: rgba(34, 34, 34, 0.4);
  cursor: pointer;
}

.slider-dot.is-active {
  background-color: rgb(34, 34, 34);
}

.testimonial-logo {
  margin-bottom: 0.625rem;
}

.testimonial-text {
  padding-top: 0.9375rem;
  margin-bottom: 0.9375rem;
  font-size: var(--fs-text);
}

.testimonial-name {
  display: flex;
  align-items: center;
  gap: 0.9375rem;
  margin-top: 1.25rem;
  position: relative;
}

.testimonial-name__photo {
  border-radius: 100%;
}

/* Self-hostovaná LinkedIn ikona (assets/img/icons/linkedin.svg) místo
   textového odkazu "LinkedIn" — dřívější kompromis kvůli chybějícímu
   self-hostovanému Font Awesome, viz DECISIONS.md. */
.linkedin-link {
  display: inline-flex;
  margin-top: 0.5rem;
  opacity: 0.85;
  transition: opacity 0.2s ease;
}

.linkedin-link:hover,
.linkedin-link:focus-visible {
  opacity: 1;
}

/* .semi-bold: jméno/pozice u fotky (#reference i #contact). Chybělo tu
   base pravidlo úplně (jen 1440/1920 font-size níže) — --font-heading
   ani font-weight se nikde neaplikovaly, text se vykresloval tělovým
   fontem (Montserrat) místo Lexend Deca a normal váhou místo 500.
   Hodnoty ověřené proti originálu (reference/css), viz DECISIONS.md. */
.semi-bold {
  font-family: var(--font-heading);
  font-size: var(--fs-label); /* 16px */
  font-weight: 500;
  line-height: 21px; /* originál má tu fixní px, ne --line-height-base */
}

@media screen and (max-width: 767px) {
  .semi-bold {
    font-size: 13px; /* jednorázová hodnota, do žádného tokenu nesedí */
    font-weight: 400;
  }
}

@media screen and (max-width: 479px) {
  .testimonial-name {
    flex-direction: column;
    text-align: center;
  }

  .semi-bold {
    font-size: var(--fs-meta); /* 14px */
    font-weight: 500;
    line-height: 1.5;
  }
}

/* --- Kontakt ---
   Grid (Webflow "quick-stack"), ne flexbox jako ostatní sekce — ověřeno
   computed stylem. Kolabuje na 1 sloupec přesně na max-width:991px, stejný
   breakpoint jako jinde v projektu. Karta Marka = `.testimonial-name`
   komponenta z #reference + modifikátor (originál: `class="testimonial-name
   marek"` na tom samém elementu). Nadpis vlevo na desktopu/tabletu,
   centruje se až na ≤479px (ověřeno zvlášť, ne převzato z #reference,
   kde je nadpis centrovaný vždy). */

/* FIX (2026-09-08, Contact parity audit): reference's `.heading-box`
   (wrapping heading+intro+grid) has its own padding-left — NOT the
   shared 25px container padding, an extra amount on top of it. Live-
   verified at three widths (18.22px @1280, 27.82px @1920, and matching
   at 767/992 too) — every value is exactly 1.5% of the box's own
   content width (18.22/1215=1.500%, 27.82/1855=1.500%), confirmed
   clean/deliberate, not a rounding artifact. padding-right/top are 0
   (only padding-bottom:10px, which doesn't affect anything visible
   here — this box wraps everything, nothing follows it). Scoped to
   .contact-intro (the heading/paragraph) — .contact-grid gets the same
   1.5% PLUS its own extra 20px, see that rule below. */
.contact-intro {
  padding-left: 1.5%;
}

.contact-intro h3,
.contact-intro p {
  text-align: left;
}

/* FIX (2026-09-08, Contact parity audit): the shared `p{margin:0 0 1rem}`
   rule's 16px margin-bottom was collapsing through .contact-intro's own
   bottom edge, adding an unwanted 16px on top of .contact-grid's
   margin-top:0 fix above (reference has zero gap here, see that rule's
   comment) — found live re-measuring after the margin-top fix didn't
   fully close the gap. */
.contact-intro p {
  margin-bottom: 0;
}

.testimonial-name--marek {
  margin-top: 1.5625rem; /* 25px, ověřeno computed stylem */
}

/* FIX (2026-09-07, Contact canonical audit): the shared .section padding
   (4rem/4rem) doesn't match reference's own Contact section — its
   .content-section has flat padding:0/0 at every breakpoint tested
   (992-2056, no banding unlike #sluzby). Scoped to #contact only;
   .section itself stays untouched (shared with #sluzby/#reference).
   Reference's own form is ~96px taller than local's (reCAPTCHA widget +
   its surrounding gaps) — an already-approved structural deviation, not
   reproduced here; local will read shorter than reference by roughly
   that amount after this fix, which is the expected, correct result. */
#contact {
  padding-top: 0;
  padding-bottom: 0;
}

/* FIX (2026-09-08, Contact parity audit): #contact's `.container` was
   using the shared, capped rule (1200px base / 1320px ≥1440) — reference
   is uncapped from 1280px up (`.content-wrapper{max-width:none;
   padding:0 25px}`), only capping at 1225px BELOW that. Same exact
   mechanism already fixed for #reference/#sluzby — reused here. */
#contact .container {
  max-width: none;
  padding-left: 25px;
  padding-right: 25px;
}

@media screen and (max-width: 1279px) {
  #contact .container {
    max-width: 1225px;
  }
}

/* IMPROVE DEVIATION (2026-09-08, Petra): reference's own padding here is
   10px flat 480-991px (real, clean value — live-verified matches at
   480/767/991) but breaks down to an unusable ~1px/5px asymmetric value
   at ≤479px (Webflow's own "quick-stack" component quirk at its
   narrowest built-in breakpoint, not deliberate design — confirmed
   flat/broken at both 375 and 479). Petra chose a clean 10px for the
   WHOLE ≤991px range instead of reproducing the ≤479px glitch. */
@media screen and (max-width: 991px) {
  #contact .container {
    padding-left: 10px;
    padding-right: 10px;
  }
}

.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  /* FIX (2026-09-08, Contact parity audit): reference has ZERO gap
     between the intro paragraph and the grid — live-verified flat 0px
     at 992/1280/1920 (subtitle bottom edge and grid top edge exactly
     coincide every time). Was 1.25rem/20px, an unmeasured guess. */
  margin-top: 0;
  /* FIX (2026-09-08, Contact parity audit): reference's grid wrapper
     (Webflow "quick-stack", `.w-layout-layout`) sits at the SAME 1.5%
     inset as .contact-intro above (same left edge as the heading), but
     ALSO carries its own flat 20px padding on every side (top/right/
     bottom too, not just left) — live-verified 20px flat at 767/992/
     1280/1920, unlike the person/form gap (which does change, see the
     ≥1440/≥1920 overrides). Net effect: the grid's visible content
     (person card + form) sits indented ~20px further right/inset than
     the heading text above it — a real, if subtle, reference detail,
     not a mechanism bug to "fix away". */
  padding: 20px;
  padding-left: calc(1.5% + 20px);
}

.contact-form {
  display: flex;
  flex-direction: column;
  margin-top: 1.25rem;
}

/* FIX (2026-09-07, Contact parity audit): reference .field-label is
   13px flat (not the shared --fs-label token, which is 16px and used
   elsewhere for .semi-bold — a different reference class with its own
   size, not the same value by coincidence). */
.contact-form label {
  margin: 0.3125rem 0 0.625rem;
  font-size: 0.8125rem; /* 13px, reference .field-label base */
  font-weight: 600;
}

.contact-form input,
.contact-form textarea {
  border: 1px solid var(--font-color);
  border-radius: var(--radius-input);
  background-color: var(--white);
  padding: 0.5rem 0.75rem 0.5rem 1.25rem;
  margin-bottom: 0.625rem;
  font-family: inherit;
  transition: border-color 0.2s;
}

/* Reference .text-field (input) and .textarea are two different classes
   with two different base font-sizes (14px / 13px) — not one shared
   value, confirmed by direct computed-style measurement. */
.contact-form input {
  font-size: 0.875rem; /* 14px */
}

.contact-form textarea {
  font-size: 0.8125rem; /* 13px */
}

.contact-form input {
  height: 50px; /* ověřeno computed stylem — explicitní výška, ne jen dopočítaná z paddingu */
}

.contact-form input:hover,
.contact-form textarea:hover {
  border-color: var(--blue-light);
}

/* Ověřeno na originále: hover (.text-field:hover) a focus (.w-input:focus)
   používají dva různé odstíny modré — nesjednocovat na jednu barvu. */
.contact-form input:focus,
.contact-form textarea:focus {
  border-color: #3898ec;
  outline: none;
}

.contact-form textarea {
  border-radius: 6px;
  padding: 1.25rem 0.75rem 6.875rem 1.25rem; /* 110px dole, ověřeno computed stylem */
  resize: vertical;
}

.contact-note {
  margin-top: 0.9375rem;
  color: #69707b; /* ověřeno computed stylem — nový odstín šedé, mimo --text-grey */
  font-size: 0.75rem; /* 12px */
  font-weight: 600;
  text-transform: uppercase;
}

.form-message {
  display: none;
}

.form-message.is-visible {
  display: block;
  margin-top: 0.625rem;
  padding: 0.75rem 1.25rem;
  border-radius: var(--radius-input);
  font-size: 0.875rem;
  font-weight: 600;
}

.form-message--success.is-visible {
  background-color: #e3f3e6;
  border: 1px solid #4caf6d;
  color: #1e5631;
}

.form-message--error.is-visible {
  background-color: #fbe7e7;
  border: 1px solid #c0392b;
  color: #7a1f16;
}

.contact-form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.cf-turnstile {
  margin-bottom: 0.625rem; /* same rhythm as .contact-form input's margin-bottom */
}

.contact-form__verifying {
  margin: -0.3125rem 0 0.625rem;
  color: #69707b; /* same grey as .contact-note */
  font-size: 0.75rem;
}

@media screen and (max-width: 991px) {
  .contact-grid {
    grid-template-columns: 1fr;
  }
}

@media screen and (max-width: 479px) {
  .contact-intro h3,
  .contact-intro p {
    text-align: center;
  }
}

/* --- Footer ---
   NENÍ "card" sekce jako #sluzby/#reference/#contact — žádné zaoblení,
   žádné top:-40px, prostý blok v běžném flow (ověřeno computed stylem),
   jen shodné pozadí var(--bg), bezešvě navazuje na #contact. Vlastní širší
   kontejner (max-width:1440px, padding 3%), NE .container (1200px) —
   ověřeno, nepředpokládáno. Copyright text je barvy var(--font-color),
   ne --text-grey (ověřeno computed stylem — předchozí kostra měla špatnou
   barvu). `.site-footer__notice`: flex řádek (copyright vlevo, adresa
   vpravo, space-between) na ≥992px, sloupec (stack, gap) na ≤991px —
   stejný breakpoint jako jinde v projektu. */

/* Footer rebuilt against the reference (measured live): --bg-2 background,
   ~291px tall, three columns (Headquarters / Contact / Trusted by many)
   above the copyright line. */
.site-footer {
  background-color: var(--bg-2);
  /* rounded top corners — the "card" transition into the footer, measured
     on the reference as 45px 45px 0 0 (= our --radius-section token) */
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  padding-top: 60px;
  /* FIX (2026-09-07, Footer parity audit): reference .footer has
     padding-bottom:0 — confirmed live and in source, no override at any
     breakpoint. The 1rem here was unaccounted for and explains +10px of
     the 992-1439 delta on its own (the rest was internal content being
     slightly shorter, a separate, still-open item). */
  overflow: hidden;
}

.site-footer__cols {
  display: flex;
  gap: 3rem;
  flex-wrap: wrap;
  margin-bottom: 2rem;
}

.site-footer__col {
  flex: 1 1 220px;
  font-size: var(--fs-text);
}

.site-footer__title {
  font-size: 1.25rem; /* 20px */
  margin: 0 0 1rem;
}

/* "Follow us on:" sits inline with the LinkedIn icon on the reference */
.site-footer__follow {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.75rem;
}

/* FIX (2026-09-09, Petra design feedback): centered against the full
   line-height box, the icon optically sits a bit low next to the text's
   cap-height (font leading isn't symmetric) — small manual nudge up,
   checked visually rather than derived, same trick as any icon+text
   optical alignment. */
.site-footer__follow .linkedin-link {
  transform: translateY(-2px);
}

/* FIX (2026-09-08, full CSS :hover sweep): reference's footer email/phone
   use `.dark-link:hover{color:var(--text-grey)}` — a plain CSS hover the
   earlier IX2-only audit missed. Scoped to the footer only: the SAME
   `.contact-link` class is also reused on Marek's #contact card, where
   the reference's own (differently-named) `.contact-link` class has no
   hover at all — scoping here keeps that distinction instead of adding
   a hover state the reference doesn't have on the #contact card. */
.site-footer .contact-link {
  transition: color 0.2s;
}

.site-footer .contact-link:hover {
  color: var(--text-grey);
}

.site-footer__badges {
  display: flex;
  align-items: center;
  /* FIX (2026-09-08): 1rem gap made the 3 badges (145+95+104px at their
     reference-matched heights) add up to 376px — wider than the 992-1439
     column (~364px), so they silently wrapped to 2 lines. Reference fits
     all 3 on one line at an even narrower column (~357px) with a ~19px
     gap; reference's own badge PNGs render slightly narrower per-pixel
     than ours at the same height (different source crop), so matching
     its exact gap isn't quite enough on its own — trimmed further to
     keep a safe margin without re-cropping the badge assets. */
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* per-badge heights measured on the reference (40 / 27 / 27) — the three
   logos have very different aspect ratios, a single height looks wrong */
.site-footer__badges img {
  width: auto;
  height: 27px;
}

.site-footer__badges img.badge--provenexpert {
  height: 40px;
}

@media screen and (max-width: 767px) {
  .site-footer__cols {
    flex-direction: column;
    gap: 2rem;
  }

  /* FIX (2026-09-08, full audit): `.site-footer__col{flex:1 1 220px}`
     sets a 220px WIDTH basis for the desktop row layout — but flex-basis
     follows the flex CONTAINER's main axis, and .site-footer__cols just
     switched to flex-direction:column above, so that same 220px turns
     into a forced 220px HEIGHT on every column instead. Shorter columns
     (e.g. "Trusted by many") were stretched to 220px tall regardless of
     their actual content, leaving ~100-150px of dead grey space above/
     below — exactly the "too much gap between paragraphs" symptom.
     Reference has no such fixed height on mobile (columns hug their own
     content, ~13-18px gap between them, confirmed live). */
  .site-footer__col {
    flex-basis: auto;
  }
}

.site-footer__inner {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 3%;
}

.site-footer__divider {
  width: 100%;
  height: 1px;
  background-color: var(--lines);
}

/* copyright: centred, 13px, with the 1px divider above it (reference) */
.site-footer__notice {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  /* FIX (2026-09-08): reference's own .notice box is taller than its text
     (min-height:50px vs. 23px of content = 27px of empty space below the
     copyright line, confirmed live on the reference itself). That slack
     is what lets its scroll-reveal actually fire: the copyright is the
     very last thing on the page, and the pre-reveal translateY(40px)
     offset (see .scroll-reveal below) needs room to scroll INTO — without
     it, the element's transformed position never enters the viewport even
     at max scroll, so the IntersectionObserver never fires and the
     copyright stays invisible forever. Reproduced as padding-bottom
     (rather than reference's min-height) so it stays robust to the
     different copyright string lengths across cs/en/de and to mobile
     line-wrapping, instead of a height value tuned to one string. */
  padding-bottom: 3.5rem;
  border-top: 1px solid var(--lines);
  color: var(--font-color);
  font-size: 0.8125rem; /* 13px */
  text-align: center;
}

.site-footer__notice p {
  margin: 0;
}

@media screen and (max-width: 991px) {
  .site-footer__notice {
    flex-direction: column;
    gap: 0.625rem;
    margin-top: 0.625rem;
  }
}

/* --- Tlačítko „zpět nahoru" ---
   V originále úplně prázdný <a> s ikonou jako CSS background-image (NE
   vnořený <img> jako v předchozí kostře — ověřeno a opraveno). Kruh
   45×45px (≥992px) / 40×40px (≤991px), bg var(--bg-underline-hover).

   Viditelnost je čistě CSS (třída .is-visible přepínaná JS podle směru
   scrollu — viz main.js), NE JS display:none/flex toggle: .go-up má
   display:flex trvale, skrytý stav řeší opacity/transform/visibility/
   pointer-events. visibility:hidden zároveň korektně vyřazuje odkaz
   z tab-order (ověřeno živě), takže není potřeba JS synchronizace
   tabindexu. Klidová (viditelná) opacity 0.9 a hover hodnoty (opacity .7,
   scale 1.13) jsou přesně převzaté z Webflow IX2 definic "Go Up Hover"/
   "Go Up Hover Out" v reference/js — ověřeno přímo v JS bundlu i živě na
   originále (shodná klidová opacity 0.9 na obou). Show/hide přechod sám
   (fade + drobný translateY) je zjednodušený ekvivalent originální
   kombinace transform+scale+opacity, ne 1:1 replika. */

.go-up {
  display: flex;
  position: fixed;
  right: 0;
  bottom: 0;
  z-index: 3;
  width: 45px;
  height: 45px;
  margin: 0 15px 13px 0;
  border-radius: 50%;
  background-color: var(--bg-underline-hover);
  background-image: url("../img/icons/67cacca0809961828902400d_proximo.svg");
  background-position: 50%;
  background-repeat: no-repeat;
  background-size: 18px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* FIX (2026-09-08, animation audit): reference "Go Up Hide" (a-19) —
     translateY 130%, scale 0.2, opacity 0, all 500ms inOutQuad. Was a
     subtle translateY(8px) fade; replaced with the reference's actual
     "shrink + slide away" exit. */
  transform: translateY(130%) scale(0.2);
  transition: opacity 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955),
    transform 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955), visibility 0s linear 0.5s;
}

.go-up.is-visible {
  opacity: 0.9;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0) scale(1);
  /* Reference "Go Up Show" (a-20): translateY 800ms, scale/opacity 500ms,
     all inOutQuad — slower entrance than the 500ms-uniform hide above
     (the reference's own asymmetry, not a typo). CSS can't animate scale
     and translateY at different speeds within one `transform` property,
     so both share the longer 800ms here — closest single-value match. */
  transition: opacity 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955),
    transform 800ms cubic-bezier(0.455, 0.03, 0.515, 0.955), visibility 0s linear 0s;
}

.go-up.is-visible:hover {
  opacity: 0.7;
  transform: scale(1.13);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

@media (prefers-reduced-motion: reduce) {
  .go-up,
  .go-up.is-visible,
  .go-up.is-visible:hover {
    transition: none;
  }
}

@media screen and (max-width: 991px) {
  .go-up {
    width: 40px;
    height: 40px;
  }
}

@media screen and (max-width: 479px) {
  .go-up {
    background-size: 16px;
  }
}

/* --- ≥1440px / ≥1920px: doplnění dříve odloženého bodu (viz #Top výš) ---
   Systematicky projito 73 (1440px) + 78 (1920px) selektorů v originálním
   CSS, spárováno s naším DOMem — ověřeno computed stylem na živém webu
   (ne jen ze staženého CSS souboru). Implementováno je jen to, co má
   u nás odpovídající, viditelně dotčený element; zbytek (Font Awesome,
   nepoužité footer/carousel/div-block-* třídy z jiných částí šablony)
   je mrtvý balast, viz DECISIONS.md.

   #reference/testimonial pravidla ZÁMĚRNĚ VYNECHÁNA (na žádost) — sekce
   je skrytá a wide-breakpoint hodnoty pro slider/hvězdičky/mask by dávaly
   smysl až s pozdější JS implementací slideru, ne pro náš současný
   statický "ready to activate" stav. */

/* --- Preloader (2026-09-08, animation audit) ---
   Reference: `.preloader` (position:fixed, full viewport, z-index max,
   background var(--bg)) + `.preloader-logo` (generic 3-dot pulse SVG,
   "By Sam Herbert" stock loader — not a brand asset, reused verbatim).
   Timing/easing extracted directly from the reference's compiled Webflow
   IX2 JS (webflow.schunk.a5c57590538b3cea.js, interaction "a-16"):
   logo fades/slides in from t=400-1000ms, curtain shrinks 100vh→0vh from
   t=1600-2600ms on a custom cubic-bezier(.977,.095,.111,.989), logo
   fades out in parallel (clipped early once the curtain height collapses).

   Safe-by-default like .js-reveal-ready below: `.preloader` is
   `display:none` unless `.js-preloader-ready` is on <html> (main.js sets
   it as the very first thing it runs) — a failed/blocked script just
   shows the page normally instead of a stuck full-screen overlay. */
.preloader {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  z-index: 9999;
  align-items: center;
  justify-content: center;
  background-color: var(--bg);
  transition: height 1000ms cubic-bezier(0.977, 0.095, 0.111, 0.989);
}

.js-preloader-ready .preloader {
  display: flex;
}

.preloader-logo {
  width: 8%; /* reference: .preloader-logo{width:8%}, confirmed live */
  height: auto;
  opacity: 0;
  transform: translateY(-20px);
}

.preloader.is-visible .preloader-logo {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 600ms ease, transform 500ms ease;
}

.preloader.is-closing {
  height: 0vh;
}

.preloader.is-closing .preloader-logo {
  opacity: 0;
  transition: opacity 1600ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .preloader {
    display: none !important;
  }
}

/* --- Scroll reveal (8 potvrzených elementů, viz DECISIONS.md) ---
   Dvě varianty:
   .scroll-reveal--translate — jen #sluzby a Technologie nadpis, jediné
   dva, kde má Webflow custom (ne preset) definici s přesnou duration/
   delay/easing a BEZ opacity změny. Vlastní duration/delay je vždy
   přepsaný inline JS z data-reveal-duration/data-reveal-delay (viz
   main.js) — 2000/200ms u #sluzby, 1000/400ms u Technologie.
   cubic-bezier(.65,0,.35,1) = standardní pojmenovaná křivka "easeInOut
   Cubic" (Webflow ukládá jen název "inOutCubic", ne čísla).

   .scroll-reveal — zbylých 6 elementů (Problémy nadpis, Technologie CTA,
   #contact nadpis+odstavec, 2× footer notice). Originál používá vestavěný
   Webflow preset "slideInBottom" — jeho přesná duration/easing/opacita
   NEJDE z dat vyčíst (je v runtime kódu, ne v JSON), proto APROXIMACE:
   sdílený slide+fade, 600ms ease-out. Delay hodnoty (400/300/0/0/0/0ms)
   ale ZNÁMÉ jsou (součást eventListeneru, ne presetu) a jsou zachované
   přesně přes data-reveal-delay.

   Graceful degradation: bez .js-reveal-ready (JS neproběhl / IO chybí)
   je obsah vždy plně viditelný — hidden/animovaný stav žije jen uvnitř
   .js-reveal-ready potomků. */

.scroll-reveal,
.scroll-reveal--translate {
  opacity: 1;
  transform: none;
}

.js-reveal-ready .scroll-reveal {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 600ms ease-out, transform 600ms ease-out;
}

.js-reveal-ready .scroll-reveal--translate {
  transform: translateY(115%);
  transition: transform 1000ms cubic-bezier(0.65, 0, 0.35, 1);
}

/* FIX (2026-09-08): třetí varianta — horizontální wipe divideru accordionu.
   Reference (IX2 a-33, SCROLL_INTO_VIEW): translateX -101% → 0, 1000ms,
   delay 600ms, inOutCubic. -101 % (ne -100 %) je referenční hodnota. */
.js-reveal-ready .scroll-reveal--wipe {
  transform: translateX(-101%);
  transition: transform 1000ms cubic-bezier(0.65, 0, 0.35, 1);
}

.js-reveal-ready .scroll-reveal.is-revealed,
.js-reveal-ready .scroll-reveal--translate.is-revealed {
  opacity: 1;
  transform: translateY(0);
}

.js-reveal-ready .scroll-reveal--wipe.is-revealed {
  transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
  .js-reveal-ready .scroll-reveal,
  .js-reveal-ready .scroll-reveal--translate,
  .js-reveal-ready .scroll-reveal--wipe {
    transition: none;
    opacity: 1;
    transform: none;
  }
}

@media screen and (min-width: 1440px) {
  .site-header {
    width: 80vw;
    background-color: var(--bg);
  }

  /* SUPERSEDED (2026-09-08, hero horizontal parity audit): .hero was
     removed from this shared rule. Its width/max-width/centering is now
     driven entirely by --hero-content-width + auto margins (see .hero
     near the top), and leaving it here would have re-capped it at
     --container-wide (1320px) — which actually binds from ~1560px up and
     would silently break the width parity. The old width:100% carve-out
     comment (flex-item fit-content quirk from "Wide desktop polish 2") is
     moot too: #Top is plain block flow again. .container keeps the rule
     unchanged. */
  .container {
    width: 100%;
    max-width: var(--container-wide);
    margin-left: auto;
    margin-right: auto;
  }

  /* .sluzby-grid dělá to samé, co .hero muselo výš — bez tohohle by tenhle
     pozdější .container override (stejná specificita, pozdější v pořadí)
     tiše přepsal base .sluzby-grid{max-width:none}. */
  .sluzby-grid {
    max-width: none;
  }

  /* SUPERSEDED IMPLEMENTATION DECISION (2026-09-07/08, Hero parity audit):
     this tier's own .logo-marquee rule is gone entirely. First the 800px
     cap + margin-top:auto (approved IMPROVE deviation "Wide desktop
     polish 2") was replaced by the per-tier width formula; then the
     formula moved to the shared --hero-content-width and the
     margin-left:calc((100% − container-wide)/2 + section-padding-x)
     left-alignment was replaced by plain auto-margin centering — both now
     live in the single ≥992px .logo-marquee rule near .hero. */

  /* SUPERSEDED IMPLEMENTATION DECISION (2026-09-07, Hero vertical parity
     audit): #Top's display:flex/flex-direction:column (below) is removed —
     it existed only to let .logo-marquee's margin-top:auto push the
     marquee to #Top's bottom edge (previous IMPROVE approximation, see
     .logo-marquee above). With reference's own explicit padding-top +
     margin-top mechanism replicated instead (#Top padding-top, .hero-cta-wrap
     margin-top, .logo-marquee margin-top — all live-verified against the
     reference), #Top no longer needs to be a flex container at all; it goes
     back to plain block flow. (.container/.hero's width:100% just above
     stays — harmless once #Top is block again, and touching it is outside
     this fix's scope.) */
  /* FIX (2026-09-07, Hero height audit): reference .hero-section.home is
     height:755px (explicit, not min-height) at this tier — the previous
     620px was an unmeasured guess, always a no-op since natural content
     (694px) already exceeded it. Safety-checked: 694px still fits under
     755px, no clipping from #Top's own overflow:hidden. */
  /* FIX (2026-09-07, Hero vertical parity audit): padding-top back-solved
     from the live-measured reference headline top (200px) at this tier —
     same method as the base tier above. Verified 0-1px delta. */
  #Top {
    height: 755px;
    padding-top: 152px;
  }

  h3 {
    font-size: 2rem; /* 32px */
  }

  .semi-bold {
    font-size: 1.25rem; /* 20px */
  }

  /* FIX (2026-09-07, Contact parity audit): reference .field-label has
     NO override at 1440 (font-family changes, size doesn't) — removed,
     stays 13px through 1919px. */

  /* CORRECTED (2026-09-08, image-position canonical audit): 580px platí
     1440-1919px, ale patří na DĚTI textového sloupce, ne na .sluzby-col
     samotný (viz base pravidlo výš) — sloupec musí zůstat 50 %. Obrázkový
     sloupec žádný vlastní strop nepotřebuje: .sluzby-image má vlastní
     max-width:392px nezávisle, takže se bez sdíleného .sluzby-col stropu
     nijak neroztáhne (živě ověřeno, viz FINAL CHECK). */
  .sluzby-col:not(.sluzby-col--image) > * {
    max-width: 580px;
  }

  .sluzby-col--image {
    margin: 0 auto;
  }

  /* REVERTED 2026-09-07 (FIX S1, approved width-mechanism audit): the
     560px bump here was an IMPROVE decision assuming the reference itself
     scaled at this breakpoint. Live audit of the reference's actual CSS
     (<img width="392"> driving an unsized flex-child, not a % or cap)
     shows NO change at 1440px at all — flat 392px from 992 clear through
     1439px. Removed; base 392px now carries this range unmodified. */

  /* Zvětšená .sluzby-image (výš) je na wide desktopu vyšší než textový/
     accordion sloupec — s children align-items:center (base pravidlo)
     se levý obsah vertikálně centruje vůči vyšší krabici a vzniká nad
     ním prázdná plocha. flex-start nechá levý obsah začínat nahoře
     (obrázek se tím nijak neposune — jako nejvyšší prvek řádku je i
     s center zarovnaný na vrchol, viz DECISIONS.md). Čisté flex
     zarovnání, žádné pevné top/transform/margin. */
  .sluzby-grid {
    align-items: flex-start;
  }

  /* REVERTED 2026-09-06 (viz DECISIONS.md): dřívější "-16 %/-17 %
     sjednocené" výšky byly vědomá odchylka od originálu, a navíc — jak
     se ukázalo při čerstvém měření — ani od těch správných čísel (byly
     odvozené z 440/520px, live měření originálu ale ukazuje jiné,
     NEsdílené hodnoty pro každou sekci zvlášť). Řádek dostává min-height
     přímo na gridu (ne na obrázku), aby platil shodně pro stretch
     (#problems/#qi) i center (#tech) mechanismus. */
  .problems-grid {
    min-height: 450px; /* ověřeno živě na originále @1440px */
    /* gap FIX (2026-09-08, Problems parity audit): the 3rem/48px here was
       wrong — reference stays flat 25px through the whole 1440-1919
       range (live-verified @1440/1919), same as the base rule now sets.
       No override needed here; removed rather than restated. */
  }

  /* REMOVED (2026-09-08, Tech parity audit): min-height:400px here was
     wrong — fresh live remeasurement shows reference's tech-grid computed
     min-height is 0px at BOTH 1440 and 1919 (the 400px row height seen
     earlier was purely a content-height coincidence with English text,
     not a real CSS constraint — contradicts this section's own base-rule
     comment above, which correctly says no min-height below 1920px). Left
     content-driven now, matching that base comment and the confirmed
     ≥1920px min-height:650px below (which IS real, unlike this one). */

  /* REMOVED (2026-09-08, Qi parity audit): min-height:518px here was
     wrong — same bug class as .tech-grid's 400px above. Fresh live
     remeasurement: reference's computed min-height is 0px at BOTH 1440
     and 1919 (518px row height is a content-height coincidence, not a
     real constraint). Left content-driven; the confirmed ≥1920px
     min-height:620px below IS real and stays. */

  /* FIX S3 (2026-09-07): reference's .div-block-6 row/column-gap changes
     from 0 to 5px exactly at 1440px (source-confirmed), and its item
     text (.paragraph-medium) steps up to 18px here too — see base
     .roles-list rule above for the full mechanism. */
  .roles-list {
    row-gap: 5px;
    column-gap: 5px;
  }

  .roles-list li {
    font-size: 18px;
  }

  /* Wide desktop polish (viz DECISIONS.md): .contact-grid je CSS grid
     s výchozím align-items:stretch — levý sloupec (Marek) se natahoval
     na výšku pravého, delšího formuláře, obsah zůstal nahoře a pod ním
     vznikla prázdná plocha. align-items:start nechá sloupec na jeho
     přirozené výšce — tahle část zůstává (schválená odchylka, DECISIONS.md).
     Šířky sloupců (1fr 1fr), formulář, input styling, barvy, mobil/tablet
     beze změny. */
  .contact-grid {
    /* FIX (2026-09-08, Contact parity audit): 70px byl neověřený odhad —
       reference má na 1440-1919px flat 35px (živě ověřeno 1440/1919). */
    gap: 35px;
    align-items: start;
    /* FIX (2026-09-08, Contact parity audit): margin-top:0.75rem tady byl
       taky neověřený odhad ("zmenšeno, aby to četlo kompaktněji") —
       reference má 0px gap mezi intro textem a gridem na KAŽDÉ šířce
       (992/1280/1920, ne jen tady) — viz base .contact-grid rule výš,
       kde je teď 0 nastavené flat; tenhle override je teď redundantní,
       ale ponechaný explicitně kvůli čitelnosti tier-specific bloku. */
    margin-top: 0;
  }

  /* Portrét Marka — bez explicitní šířky se řídil jen HTML `width="113"`
     atributem; teď explicitní cíl na wide desktopu, jen pro Markovu
     kartu (ne #reference, která sdílí .testimonial-name__photo). */
  .testimonial-name--marek .testimonial-name__photo {
    width: 108px;
  }

  /* Víc vzduchu mezi CTA tlačítkem a poznámkou "Ozveme se vám do
     druhého dne" — jen wide desktop, mobil/tablet drží původních 15px. */
  .contact-note {
    margin-top: 1.75rem;
  }
}

@media screen and (min-width: 1920px) {
  /* REVERTED 2026-09-06 na výslovnou žádost ("držet se víc originálu") —
     předchozí IMPROVE odchylka (80vw místo 66vw) zrušena, viz DECISIONS.md.
     Ověřeno přímo ve staženém CSS originálu: .navbar { max-width: 66vw }
     je jeho VLASTNÍ pravidlo pro ≥1920px (na 1440px originál sám používá
     80vw — to se shoduje s tím, co už máme, beze změny). */
  .site-header {
    width: 66vw;
  }

  /* REVERTED 2026-09-06 (viz DECISIONS.md "Wide desktop ≥1920px: návrat
     k 1:1 originálu"): stažené CSS originálu ukazuje, že jeho ekvivalent
     (.content-wrapper.w-container / .content-wrapper.hero) na ≥1920px
     NEMÁ pevný strop vůbec — jde jen o max-width:100 % s plochým 25px
     paddingem, ověřeno živě až do 2560px (sluzby/kontakt/patička byly
     stažené na plnou šířku). .container tím dál nese jen svůj base
     padding; sluzby-col (37.5 %) a sluzby-image (680px, obojí beze
     změny) dopočítávají svou šířku správně samy, teď že mají k dispozici
     neomezenou šířku rodiče, přesně jako na originále. */

  /* Doplněno hned poté (stejný den) — Petra živě uviděla, že originálův
     plochý 25px okraj na téhle šířce fakt vypadá jako žádný padding (text
     v hero i #sluzby "nalepený" na okraj) — přesně ten důvod, proč tenhle
     strop kdysi vůbec vznikl. Řešení NENÍ vrátit pevný max-width box
     (ten zase dělal jinde nekonzistentní zarovnání vůči headeru) — jen
     samotný okraj škáluje s šířkou, podobně jako u problems/tech/qi
     (ty mají procentuální mezeru u obrázku, a proto tenhle problém
     neměly). --section-padding-x (krmí .container, .hero i pozici
     marquee) předefinovaný jen pro ≥1920px. */
  :root {
    --section-padding-x: clamp(1.5rem, 3vw, 4rem);
  }

  /* .hero dropped from this rule for the same reason as the ≥1440 one
     above — its sizing is --hero-content-width now, not a max-width cap. */
  .container {
    max-width: none;
  }

  /* SUPERSEDED (2026-09-08, hero horizontal parity audit): this tier's
     .logo-marquee rule is gone too. Its width formula (net ratio 60% —
     the reference's hero-column narrows to 60% of its content-wrapper
     exactly here) now lives in --hero-content-width above, and the
     margin-left:var(--section-padding-x) left-alignment is replaced by
     auto-margin centering in the shared ≥992px rule. The "48px" constant
     inside the formula is unchanged and still deliberate: it's local's own
     fixed 24px×2 hero side padding, NOT var(--section-padding-x) (which
     becomes a 3vw clamp only at this tier). */

  /* OPRAVA (bezprostředně po předchozím pokusu) — ten předchozí zastropování
     .sluzby-grid řešilo špatnou příčinu. Přeměřeno znovu přímo na originále:
     jeho obrázek (.change-this-image._1) NENÍ pevných 680px — od 1920px
     roste s šířkou sekce stejně jako text, jen procentuálně:
       1920px: img 677px / section 1905px  = 35.5 %
       2056px: img 727px / section 2041px  = 35.6 %
       2560px: img 911px / section 2545px  = 35.8 %
     Konzistentní ~36 % ze SEKCE (ne ze svého 50% sloupce) při ≥1920px —
     proto se mezera na originále nikdy neroztáhne donekonečna: obrázek
     roste ruku v ruce s textem. Řádek (.sluzby-grid) tedy strop nepotřebuje
     a zůstává bez něj, stejně jako kontakt/patička/hero. */

  /* FIX (2026-09-07, Hero height audit): reference .hero-section.home is
     height:815px (explicit) at this tier — 720px was an unmeasured guess.
     Safety-checked: natural content (720px) still fits under 815px. */
  /* FIX (2026-09-07, Hero vertical parity audit): padding-top back-solved
     from the live-measured reference headline top (213px) at this tier.
     Verified 0-1px delta. */
  #Top {
    height: 815px;
    padding-top: 165px;
  }

  .button {
    height: auto;
    padding-top: 15px;
    padding-bottom: 15px;
    font-size: 1.1875rem; /* 19px */
    line-height: 19px;
  }

  h3 {
    font-size: 2.0625rem; /* 33px */
  }

  .semi-bold {
    font-size: 1.375rem; /* 22px */
    line-height: 1.5; /* originál tu (na rozdíl od 1440px) přepisuje i toto */
  }

  /* FIX (2026-09-07, Contact parity audit): reference .field-label is
     18px at 1920 — same value as input/textarea below, was wrongly 22px
     (matching a different element's, .semi-bold's, scale by mistake). */
  .contact-form label {
    font-size: 1.125rem; /* 18px */
  }

  .contact-form input,
  .contact-form textarea {
    font-size: 1.125rem; /* 18px — already matched reference, unchanged */
  }

  /* FIX (2026-09-07): reference .text-field height stays 50px at EVERY
     breakpoint including 1920 — confirmed live, no override exists in
     its source CSS. The 60px here was a guess; removed. */

  .contact-note {
    text-align: left;
    font-size: 1.125rem; /* 18px */
  }

  .site-footer__notice p {
    font-size: 1.0625rem; /* 17px */
  }

  .site-footer__inner {
    max-width: none;
  }

  /* FIX (2026-09-07, Footer large-desktop audit): reference .footer-title
     20→27px, .footer-text-medium (column body/follow text) 13→19px,
     badges 40→55px (ProvenExpert) / 27→40px (the other two) — all
     confirmed live, all changing at exactly 1920px, no ≥1440 step.
     Gaps/margins around them are unchanged on the reference. */
  .site-footer__title {
    font-size: 1.6875rem; /* 27px */
  }

  .site-footer__col {
    font-size: 1.1875rem; /* 19px */
  }

  .site-footer__badges img {
    height: 40px;
  }

  .site-footer__badges img.badge--provenexpert {
    height: 55px;
  }

  /* FIX (2026-09-09, Petra design feedback): shared 0.5rem badge gap
     (chosen to stop 992-1919px wrapping, see comment on the base rule)
     also applied here, where the badges are much bigger (40/55px vs
     27/40px) — looked cramped/"too close together" at this size.
     Reference itself is inconsistent here (19px @1440, 0px/touching
     @1920, confirmed live) so there's no clean reference value to
     match; picked a value with visible, even breathing room without
     approaching the column edge (564px column, ~494px of badges,
     leaves ~35px per gap at most before touching it). */
  .site-footer__badges {
    gap: 32px;
  }

  /* SUPERSEDED (2026-09-08, Tech parity audit): the 52%/52% bare-selector
     rule that used to live here is gone — it's the exact same bug class
     documented at the top of the Tech section now (bare `.tech-col`
     leaking onto the image column too), and it was ALSO the wrong value
     (52%, not the live-verified 49%/51% split). Real ≥1920px widths
     (text 51%, image 49% — a genuine role-swap from below 1920px, not a
     mistake) now live with the rest of the column-split fix, scoped
     :not(:has(.tech-image)) / :has(.tech-image), right after the base
     .tech-col rules. */

  /* REVERTED 2026-09-06 (viz DECISIONS.md) — hodnoty ověřené živě přímo
     na originále na této šířce (min-height na gridu, ne na obrázku, viz
     zdůvodnění výš u ≥1440px bloku). */
  .tech-grid {
    min-height: 650px;
    /* gap FIX (2026-09-08, Tech parity audit): reference drops to a flat
       5px here (live-verified @1920/2056) — was still 25px from the base
       rule, no ≥1920 override existed yet. Same value/mechanism as
       .problems-grid/.sluzby-grid's own ≥1920 gap. */
    gap: 5px;
  }

  .problems-grid {
    min-height: 500px;
    /* gap FIX (2026-09-08, Problems parity audit): reference drops to a
       flat 5px here (live-verified @1920/2056) — was 3.5rem/56px, way
       off. Same value/mechanism as .sluzby-grid's own ≥1920 gap. */
    gap: 5px;
  }

  .qi-grid {
    min-height: 620px;
  }

  /* FIX S3 (2026-09-07): last step of the .paragraph-medium font-size
     ladder (see base .roles-list rule) — 20px from 1920px. row/column-gap
     stays 5px (no further change in the reference past 1440px). */
  .roles-list li {
    font-size: 20px;
  }

  /* FIX (2026-09-08, Contact parity audit): 55px byl neověřený odhad —
     reference má tady flat 0px (živě ověřeno 1920/2056, sloupce se
     dotýkají bez mezery). */
  .contact-grid {
    gap: 0px;
  }

  /* CORRECTED (2026-09-08, image-position canonical audit): the earlier
     37.5%/33.9%/max-width:100% attempt (see git history) compensated with
     approximated percentages instead of fixing the actual mechanism. Fresh
     canonical remeasurement (spacer force-loaded) gives the real reference
     chain: row gap drops to 5px, columns split 47.6%/52.1% (883/967 @1920,
     948/1038 @2056 — both ≤1px), image column switches justify-content to
     flex-start, and the photo itself is 70% of ITS OWN column (not of the
     row) — 0.701×967=677.8 @1920 and 0.700×1038=726.6 @2056, both matching
     the reference's measured photo width (677/727) to ≤1px. This IS the
     "55%/70%" shape the earlier attempt tried, just with the correct
     column percentage (52.1%, not 55%) — the previous revert was reverting
     bad numbers, not a bad approach. */
  .sluzby-grid {
    gap: 5px;
  }

  .sluzby-col:not(.sluzby-col--image) {
    width: 47.6%;
  }

  .sluzby-col--image {
    width: 52.1%;
    justify-content: flex-start;
  }

  /* Textový blok (ne celý sloupec) zůstává užší než sloupec — na referenci
     606px @1920 / 651px @2056. CORRECTED (živě přeměřeno po implementaci):
     naše `47.6%` sloupec vychází o něco užší než referenční (852px/913px,
     ne 883px/948px — --section-padding-x u nás při ≥1920 spotřebuje víc
     prostoru než referenční fixních 25px), takže cílový podíl SLOUPCE je
     71,1 % / 71,3 %, ne 68,6 %. Živě ověřeno: 71,2 % trefuje oba body do
     ≤1px. */
  .sluzby-col:not(.sluzby-col--image) > * {
    max-width: 71.2%;
  }

  /* CORRECTED (živě přeměřeno po implementaci): stejný důvod jako výš —
     70 % (odvozeno z referenčního sloupce 932px/999px) dávalo jen
     653px/699px místo cílových 677px/727px. Náš sloupec je o něco užší,
     takže cílový podíl je 72,7 %, ověřeno do ≤2px na obou bodech. */
  .sluzby-image {
    max-width: 72.7%;
  }

  /* FIX S2b (2026-09-07, approved row/height-mechanism audit): reference's
     .flex has an explicit min-height:650px here (same shared rule already
     matched on problems-grid/tech-grid/qi-grid) — .sluzby-grid never got
     the equivalent. Without it, neither column's natural height (text
     498, image 588 natural / 839 with aspect-ratio) reaches 650 on its
     own. align-items:stretch (reference's own value here — the base/
     ≥1440 rule above sets flex-start, needs the override at this
     specificity/position to actually win) makes both columns fill that
     floor; aspect-ratio:auto removes the width→height coupling that
     doesn't exist on the reference. Candidate-simulated live before
     implementing: reproduced 650/650/650/650 exactly. Placement matters:
     an earlier attempt put this same rule right after the base
     .sluzby-image definition — same media query, but EARLIER in source
     order than the ≥1440 .sluzby-grid{align-items:flex-start} rule below
     it, which silently won the cascade. Moved here, after it, to fix
     that. */
  .sluzby-grid {
    align-items: stretch;
    min-height: 650px;
  }

  /* FIX (2026-09-07, Services total-height audit): reference's own
     per-section padding at this tier is 40/60 (not the shared .section
     4rem/4rem) — see the 992-1919 override near #sluzby's base rule. */
  #sluzby {
    padding-top: 40px;
    padding-bottom: 60px;
  }

  .sluzby-image {
    aspect-ratio: auto;
  }
}

/* --- Privacy pages (ochrana-osobnich-udaju/, en/privacy/, de/datenschutz/) ---
   Plain text page under the fixed header pill: top padding clears the
   header, a narrow column keeps line length readable. */
.legal-page {
  padding: 140px 0 var(--section-padding-y);
}

.legal-page__inner {
  max-width: 800px;
}

.legal-page h1 {
  margin-bottom: 2rem;
}

.legal-page h2 {
  margin-top: 2rem;
}

.legal-page a,
.contact-privacy a,
.site-footer__privacy a {
  text-decoration: underline;
}

.contact-privacy {
  font-size: 0.875rem;
  margin-top: 1rem;
}

.site-footer__privacy {
  margin: 0;
}
