/* ==========================================================================
   Digital Surfer — digitalsurfer.pl
   1:1 reproduction of the approved Canva design (source-canva/design.pdf).
   Design reference viewport: 1366px (= 1024.5pt page width * 4/3).
   Colors, type and copy are verbatim from the source; the only additions are
   the ones explicitly agreed with the owner (fluid full-viewport scenes,
   down-arrow scroll nav, fade-ins, mobile stack, brand-grid wave dim).
   ========================================================================== */

:root {
  --c-graphite: #323136;
  --c-accent: #38b6ff;
  --c-black: #000000;
  --c-white: #ffffff;

  --font-head: 'Poppins', 'Segoe UI', Arial, sans-serif;
  --font-body: 'Open Sans', 'Segoe UI', Arial, sans-serif;

  /* fluid section padding, derived from the design's ~72-85pt margins */
  --pad-x: clamp(20px, 6.2vw, 90px);
  --pad-y: clamp(28px, 6vw, 64px);

  /* single corner offset shared by ALL corner elements — logo, hamburger,
     ↑ and ↓ nav-arrows: top = bottom = left = right, same value on every
     axis, so all four corners sit on matching symmetric axes (owner
     directive, 2026-07-24). */
  --corner-offset: clamp(16px, 5vw, 60px);
  /* HIT-AREA box for every corner/nav icon (↑, ↓, hamburger, carousel ←/→)
     — always ≥44px, the touch-target minimum (owner directive 2026-07-24,
     reaffirmed 2026-07-25). Kept fluid/unchanged; only the VISUAL circle
     drawn inside it (--icon-visual-size, below) was resized. */
  --corner-icon-size: clamp(44px, 3.2vw, 48px);
  /* VISUAL circle diameter shared by every corner/nav icon — ↑, ↓, hamburger
     AND the recommend-carousel ←/→ (owner directive 2026-07-25: unify all
     four to the carousel arrows' own, smaller, original size instead of
     growing the carousel arrows up to the old 44-48px). Value = the
     carousel arrows' prior clamp, unchanged. Sits centered inside the
     ≥44px --corner-icon-size hit box (see .nav-arrow/.menu-mark/
     .carousel-arrow svg rules) so the touch target never shrinks with it. */
  --icon-visual-size: clamp(34px, 2.9vw, 40px);

  /* type scale — clamp(min, vw-at-1366-baseline, design-px-cap) */
  --fs-eyebrow:      clamp(14px, 1.50vw, 20.5px);
  --fs-h1-a:          clamp(30px, 3.88vw, 53px);
  --fs-h1-b:          clamp(22px, 2.74vw, 37.5px);
  --fs-body:          clamp(15px, 1.26vw, 17.2px);
  --fs-mural-1:       clamp(26px, 2.82vw, 38.5px);
  --fs-mural-2:       clamp(18px, 2.04vw, 27.9px);
  --fs-mural-3:       clamp(14.5px, 1.55vw, 21.2px);
  --fs-h2:            clamp(21px, 1.93vw, 26.3px);
  --fs-service-title: clamp(15px, 1.37vw, 18.65px);
  --fs-service-desc:  clamp(14px, 1.37vw, 18.65px);
  --fs-timeline-h:    clamp(14.5px, 1.27vw, 17.33px);
  --fs-timeline-body: clamp(12.5px, 0.98vw, 13.33px);
  --fs-contact-h2:    clamp(17px, 1.37vw, 18.65px);
  --fs-contact-body:  clamp(15px, 1.37vw, 18.65px);
}

* { box-sizing: border-box; }
/* Deck navigation (js/main.js) moves focus programmatically to each incoming
   scene's heading/section via tabindex="-1", so keyboard/AT users land in
   the new scene. That's not a user-initiated Tab focus, so the browser's
   default focus ring (blue box) shouldn't render for it — real interactive
   elements (links, buttons, menu) keep their own :focus-visible treatment
   below, unaffected by this rule. */
[tabindex="-1"]:focus { outline: none; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--c-white);
  color: var(--c-graphite);
  font-family: var(--font-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--c-white);
  color: var(--c-black);
  padding: 12px 18px;
  z-index: 999;
}
.skip-link:focus { left: 8px; top: 8px; }

b, strong { font-weight: 700; }
.accent { color: var(--c-accent); font-weight: 700; }

/* ==========================================================================
   Slides — full-viewport scenes (the only agreed structural addition)
   ========================================================================== */
.slide {
  position: relative;
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
  background: var(--c-white);
  overflow: hidden;
}
.slide__inner {
  position: relative;
  width: 100%;
  padding: var(--pad-y) var(--pad-x);
}

/* fade-in — visible by default; JS only arms the transition when the user
   allows motion (no-JS / reduced-motion => fully visible, no layout shift) */
.fade-in {
  opacity: 1;
  transform: none;
}
.js-motion .fade-in {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s ease, transform .7s ease;
}
.js-motion .fade-in.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   Nav arrows (▼) — circle + arrow glyph, exactly as in the source design.
   Color variant follows the background it sits on, same as the Canva file.
   ========================================================================== */
/* ↓ sits bottom-left, ↑ sits top-left — independent corners, not stacked.
   --corner-offset is the distance from the scene edge to each element's
   CENTER (not its edge) — owner directive 2026-07-24 amendment — so that
   logo (~36px), hamburger (44-48px) and arrows (44-48px) share the exact
   same corner axes despite their different sizes. left:offset anchors the
   box's left edge at that distance, then translateX(-50%) shifts the box
   back by half its own (possibly fluid/clamped) width so its CENTER lands
   on the offset instead. */
.nav-arrows { display: contents; }
/* Box (.nav-arrow) = the ≥44px touch-safe HIT AREA, invisible, unchanged
   size/position mechanics. The VISIBLE filled circle moved onto the <svg>
   itself (owner directive 2026-07-25: shrink the visible circle to
   --icon-visual-size — same size as the carousel arrows — while keeping the
   hit area at --corner-icon-size); svg accepts background/border-radius
   like any element, so this needs no extra markup. */
.nav-arrow {
  position: absolute;
  left: var(--corner-offset);
  width: var(--corner-icon-size);
  height: var(--corner-icon-size);
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  z-index: 5;
}
.nav-arrow--down { bottom: var(--corner-offset); transform: translate(-50%, 50%); }
.nav-arrow--up   { top: var(--corner-offset); transform: translate(-50%, -50%); }
.nav-arrow svg { width: var(--icon-visual-size); height: var(--icon-visual-size); border-radius: 50%; }
.nav-arrow--dark    { color: var(--c-white); }
.nav-arrow--light   { color: var(--c-graphite); }
.nav-arrow--accent  { color: var(--c-white); }
.nav-arrow--dark svg    { background: var(--c-black); }
.nav-arrow--light svg   { background: var(--c-white); }
.nav-arrow--accent svg  { background: var(--c-accent); }
.nav-arrow:hover,
.nav-arrow:focus-visible {
  color: var(--c-white);
  outline: none;
}
.nav-arrow:hover svg,
.nav-arrow:focus-visible svg {
  background: var(--c-accent);
}

/* Owner directive 2026-07-25: moved onto the SAME corner axes as the ↑/↓
   nav-arrows (left) and the hamburger (right) — --corner-offset from the
   scene edge to each icon's CENTER, identical mechanism to .nav-arrow/
   .menu-mark above (left/right anchors the edge, translate(±50%) recentres
   on the offset). Box = the shared ≥44px hit area (--corner-icon-size); the
   svg (drawing its own stroked circle + chevron, same as before) is resized
   to --icon-visual-size so its rendered circle now matches the ↑/↓/hamburger
   circles exactly, per owner spec. Functionally still disabled by markup
   (aria-disabled/tabindex="-1"/aria-hidden in index.html — one
   recommendation, nothing to page through yet); js/main.js flips that state
   on once ≥2 <li> land in .recommend__list. */
.carousel-arrow {
  position: absolute;
  top: 50%;
  width: var(--corner-icon-size);
  height: var(--corner-icon-size);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-black);
  text-decoration: none;
  z-index: 5;
}
.carousel-arrow svg { width: var(--icon-visual-size); height: var(--icon-visual-size); border-radius: 50%; }
.carousel-arrow--prev { left: var(--corner-offset); transform: translate(-50%, -50%); }
.carousel-arrow--next { left: auto; right: var(--corner-offset); transform: translate(50%, -50%); }
.carousel-arrow[aria-disabled="true"] { pointer-events: none; }
.carousel-arrow:hover,
.carousel-arrow:focus-visible {
  color: var(--c-white);
  outline: none;
}
.carousel-arrow:hover svg,
.carousel-arrow:focus-visible svg {
  background: var(--c-accent);
}
.carousel-arrow:hover svg circle,
.carousel-arrow:focus-visible svg circle {
  stroke: none;
}

/* ==========================================================================
   Header mark — D logo (hero only, as in the source page) + hamburger
   (fixed, every scene — owner directive 2026-07-24). Both sit on
   --corner-offset so hero's top-left/top-right corners stay symmetric —
   see the --corner-offset = center-point amendment note above .nav-arrow.
   ========================================================================== */
.brand-mark {
  position: absolute;
  top: var(--corner-offset);
  left: var(--corner-offset);
  width: clamp(30px, 2.6vw, 36px);
  transform: translate(-50%, -50%);
  z-index: 5;
}

/* Hamburger — position:fixed so it follows the viewport across every scene.
   No menu exists yet: real <button>, focusable (hover/focus states below
   need it in the tab order), marked aria-disabled (not native `disabled`,
   which would drop focus-visible) — the correct "feature coming soon"
   pattern. Color follows the scene currently under it: black on light
   backgrounds, white on dark ones (.is-dark-bg, toggled by main.js from each
   section's data-theme), blue accent on hover/focus regardless of theme. */
.menu-mark {
  position: fixed;
  top: var(--corner-offset);
  right: var(--corner-offset);
  transform: translate(50%, -50%);
  width: var(--corner-icon-size);
  height: var(--corner-icon-size);
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  cursor: pointer;
  color: var(--c-black);
  z-index: 50;
}
/* Box (.menu-mark, the <button>) = the ≥44px touch-safe hit area, same
   mechanism as .nav-arrow. The svg is sized to --icon-visual-size (owner
   directive 2026-07-25: unify to the carousel arrows' smaller size, was
   100% of the 44-48px box) — its own drawn circle (stroke, cx/cy/r in a
   40×40 viewBox) scales with IT now, decoupled from the hit box. */
.menu-mark svg { width: var(--icon-visual-size); height: var(--icon-visual-size); border-radius: 50%; }
.menu-mark.is-dark-bg { color: var(--c-white); }
/* Hover/focus — same solid-circle pattern as .nav-arrow: filled accent
   circle + white glyph. The fill now lives on the svg (matches its own
   --icon-visual-size, not the wider 44-48px hit box, so the circle doesn't
   visibly jump in size on hover) instead of the button's own box; the SVG's
   own hairline circle (drawn for the resting outline look) is suppressed so
   it doesn't show as a seam inside the filled disc. */
.menu-mark:hover,
.menu-mark:focus-visible {
  color: var(--c-white);
  outline: none;
}
.menu-mark:hover svg,
.menu-mark:focus-visible svg {
  background: var(--c-accent);
}
.menu-mark:hover svg circle,
.menu-mark:focus-visible svg circle {
  stroke: none;
}
/* Lines morph into an X when the menu opens (.is-open, toggled by main.js).
   transform-box:fill-box so rotation pivots on each line's own center
   (SVG default origin is the viewport, not the shape). Amounts match the
   glyph's own coordinates: lines sit 4.5 viewBox-units apart, so translating
   top/bottom by 4.5 stacks all three on the middle line's axis before the
   45°/-45° rotation forms the X. */
.menu-mark .ham-line {
  transition: transform .3s ease, opacity .3s ease;
  transform-box: fill-box;
  transform-origin: center;
}
.menu-mark.is-open .ham-line--top { transform: translateY(4.5px) rotate(45deg); }
.menu-mark.is-open .ham-line--mid { opacity: 0; }
.menu-mark.is-open .ham-line--bottom { transform: translateY(-4.5px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .menu-mark .ham-line { transition: none !important; }
}

/* ==========================================================================
   Fullscreen menu overlay — tuxdi.com/en pattern (owner-approved reference),
   adapted to this site's design system: no new fonts/colors, reuses
   --c-black/--c-white/--c-accent, Poppins Bold, the circle-glyph icon
   language already used by nav-arrow/carousel-arrow. Hidden via
   opacity+visibility (not `hidden`/display:none) so the opacity/transform
   transition can run; visibility:hidden also removes its links from the tab
   order while closed, so no extra JS is needed to un-trap focus on close.
   ========================================================================== */
.site-menu {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow-y: auto;
  background: var(--c-black);
  color: var(--c-white);
  /* fluid, height-aware safety padding (owner fix 2026-07-25 — was a fixed
     px budget off --corner-offset alone, which didn't shrink on short
     windows and ate up to ~250px before a single link was drawn). The
     visual top/bottom breathing room on tall windows still comes from the
     margin-top:auto / margin-bottom:auto pair below, not from this padding;
     this padding is only the hard floor kept once content is tall enough to
     scroll. */
  padding: clamp(14px, 3.2svh, 60px) var(--pad-x) clamp(14px, 3.2svh, 60px);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition: opacity .35s ease, transform .35s ease, visibility 0s linear .35s;
}
.site-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity .35s ease, transform .35s ease;
}
/* margin-top:auto here + margin-bottom:auto on .site-menu__social (the last
   flow child) is the "safe centering" pair (owner fix 2026-07-25): when the
   nav+lang+social stack is shorter than 100svh the two auto margins split
   the leftover space evenly, reproducing the old justify-content:center
   look; the moment the stack is taller than the viewport both margins
   collapse to 0 together, so the FIRST link starts flush at the scrolled-to-
   top edge (reachable at scrollTop 0) instead of being centre-clipped above
   an unreachable negative offset — the bug on short/landscape windows. */
.site-menu__nav { width: 100%; max-width: 900px; margin-top: auto; }
.site-menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(2px, 1.3svh, 10px);
  text-align: center;
}
/* Solid white type (owner directive 2026-07-24 amendment: the earlier
   -webkit-text-stroke hollow/outline treatment is removed). Hover/focus AND
   the current scroll-spy section (.is-current, set by main.js when the
   overlay opens) fill with the site's one accent.
   Font-size (owner fix 2026-07-25): now also height-aware — min(vw, svh)
   inside the clamp's preferred value means on short/wide windows the svh
   term takes over and shrinks the type with the viewport's HEIGHT, not just
   its width, so 8 links + Blog + PL/EN + socials keep fitting 100svh. */
.site-menu__link {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.3rem, min(5.2vw, 4.6svh), 3.6rem);
  line-height: 1.15;
  color: var(--c-white);
  text-decoration: none;
  padding: .05em .25em;
  transition: color .2s ease;
}
a.site-menu__link:hover,
a.site-menu__link:focus-visible,
a.site-menu__link.is-current {
  color: var(--c-accent);
  outline: none;
}
/* Blog — same typography/size/color as the active links (owner directive
   2026-07-24 amendment), just inactive: no hover (pointer-events:none also
   keeps `a.site-menu__link:hover` from ever matching since it's a <span>),
   dimmed to ~0.5 opacity so its disabled state still reads clearly. */
.site-menu__link--disabled {
  color: rgba(255, 255, 255, .5);
  cursor: default;
  pointer-events: none;
}
.site-menu__lang { margin: clamp(10px, 2.6svh, 40px) 0 0; }
/* PL / EN switcher (owner directive 2026-07-24 amendment, replaces the
   former "English version — soon" placeholder; owner directive 2026-07-25:
   EN site shipped, the non-current slot is now a REAL <a> link on both
   language pages, role-swapped — see index.html/en/index.html markup
   comments). Current-language slot = filled accent (same fill-on-active
   language as the menu links' own .is-current). Other-language slot =
   visually clickable (hover feedback, pointer cursor). text-decoration:none
   added (2026-07-25) so the now-real <a> doesn't pick up the browser's
   default underline — same treatment .site-menu__link already has. */
.site-menu__lang-pl,
.site-menu__lang-sep,
.site-menu__lang-en {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: clamp(13px, 1.3vw, 16px);
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
}
.site-menu__lang-pl { color: var(--c-accent); }
.site-menu__lang-sep { color: rgba(255, 255, 255, .5); }
.site-menu__lang-en {
  color: var(--c-white);
  cursor: pointer;
  transition: color .2s ease;
}
.site-menu__lang-en:hover,
.site-menu__lang-en:focus-visible {
  color: var(--c-accent);
  outline: none;
}
/* In normal flow (owner fix 2026-07-25 — was position:absolute/bottom-pinned
   to the overlay's own box, which could sit on top of/overlap the nav list
   once the list grew taller than the viewport, since it didn't move with
   the rest of the stack). margin-bottom:auto pairs with .site-menu__nav's
   margin-top:auto above for the safe-centering behaviour: bottom-of-panel
   placement is preserved on windows tall enough to fit everything, and it
   degrades to "last item after the list, reachable by scrolling" instead of
   overlapping anything once the window is too short. */
.site-menu__social {
  width: 100%;
  margin: clamp(10px, 2.6svh, 28px) 0 auto;
  list-style: none;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(16px, 2.4vw, 28px);
}
.site-menu__social a {
  display: flex;
  color: var(--c-white);
  transition: color .2s ease;
}
.site-menu__social a:hover,
.site-menu__social a:focus-visible {
  color: var(--c-accent);
  outline: none;
}
@media (prefers-reduced-motion: reduce) {
  .site-menu,
  .site-menu * { transition: none !important; }
}

/* body scroll-lock while the overlay is open; hamburger stays on top of it
   (its own z-index is 50, below the overlay's 90) and forced to a
   contrasting color against the overlay's black, regardless of whatever
   .is-dark-bg scroll-theme it had underneath. */
body.menu-open { overflow: hidden; }
body.menu-open .menu-mark {
  z-index: 100;
  color: var(--c-white);
}
body.menu-open .menu-mark:hover,
body.menu-open .menu-mark:focus-visible {
  color: var(--c-white);
}
body.menu-open .menu-mark:hover svg,
body.menu-open .menu-mark:focus-visible svg {
  background: var(--c-accent);
}

/* ==========================================================================
   1. HERO
   ========================================================================== */
.hero__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4vw;
  min-height: 100svh;
  min-height: 100vh;
}
.hero__text { max-width: 46%; }
.hero__eyebrow {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-eyebrow);
  color: var(--c-graphite);
  margin: 0 0 .6em;
}
.hero__h1 { margin: 0 0 .5em; font-family: var(--font-head); font-weight: 700; line-height: 1.08; }
.hero__h1-line1 { display: block; font-size: var(--fs-h1-a); color: var(--c-graphite); }
.hero__h1-line2 { display: block; font-size: var(--fs-h1-b); color: var(--c-accent); }
.hero__body {
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--c-graphite);
  max-width: 44ch;
  margin: 0;
}
/* position:absolute + bottom:0 so the photo sits flush on the slide's own
   bottom edge regardless of .hero__inner's own top/bottom padding (padding
   is inside a positioned ancestor's containing block, so it doesn't push an
   absolutely-positioned child off the true edge). WIDTH-driven (not
   height-driven) sizing, per fidelity-pass measurement against
   source-canva/design-2x.png (2026-07-24 visual-fidelity pass): the source
   photo's visible subject spans ~53%-89% of the slide's width and ~26%-100%
   of its height (bottom-bleed, no right-edge bleed). Since the source PNG
   asset itself (assets/photos/hero.webp) has the subject inset within its
   own transparent canvas (~36%-71% x, ~35%-100% y), solving the linear
   left/width that reproduces the SAME subject position on screen gives
   left:14.7% width:104.7% of the slide — deliberately wider than 100% (its
   own blank/transparent right margin bleeds past the slide edge, clipped by
   .slide's overflow:hidden; the opaque subject itself stays inset,
   unchanged from before). Percent-of-slide units (not vh) so the photo's
   proportion holds while the window scales, per owner directive. */
.hero__photo {
  position: absolute;
  left: 14.7%;
  width: 104.7%;
  bottom: 0;
  overflow: hidden;
}
.hero__photo img {
  display: block;
  width: 100%;
  height: auto;
  max-width: none;
}

/* ==========================================================================
   2. MANIFEST / MURAL — coloured exception, per source
   ========================================================================== */
.slide--mural { background: #7a6a52; }
.mural__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.mural__inner {
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.mural__text {
  position: relative;
  z-index: 2;
  text-align: right;
  max-width: 56%;
  font-family: var(--font-body);
  font-weight: 700;
  color: var(--c-white);
  text-shadow: 0 2px 14px rgba(0,0,0,.35);
}
.mural__l1 { font-size: var(--fs-mural-1); margin: 0 0 .15em; }
.mural__l2 { font-size: var(--fs-mural-2); margin: 0 0 .8em; }
.mural__l3 { font-size: var(--fs-mural-3); line-height: 1.35; margin: 0 0 .8em; }
.mural__l3:last-child { margin-bottom: 0; }

/* ==========================================================================
   3. W CZYM MOGĘ POMÓC — black section
   ========================================================================== */
.slide--pomoc { background: var(--c-black); color: var(--c-white); }
.pomoc__inner {
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
  gap: 4vw;
}
/* Composition rebuild (owner directive 2026-07-25): the previous geometry
   mixed vw (width) with vh (height) on the img, which DEFORMS the photo the
   moment the window's own aspect ratio departs from the 16:9 the numbers
   were measured against (vw and vh scale independently) — a hard bug per
   owner rule ("never change a photo's proportions, only move/scale it
   uniformly"). Fixed by driving size off a SINGLE dimension (height, in
   svh/vh) with width:auto — the img's own intrinsic 1086:724 ratio then
   fixes the other dimension, so no window ratio can ever stretch it.
   .pomoc__photo (wrapper) now only carries POSITION (left:25% + translateX
   -50% = the photo's vertical center axis sits at 25% of the window's own
   width, per owner spec — measured against .pomoc__inner's padding box,
   which spans the full slide/viewport width since .slide__inner has no
   positioned ancestor narrower than the slide) + bottom:0 (flush to the
   scene's own bottom edge, no top/bottom bleed — "mieści się na wysokość").
   The translateX(-50%) axis-centering lives on the WRAPPER, not the <img>,
   because the <img> instead carries .fade-in (moved off the wrapper,
   index.html) — .fade-in's own `transform: translateY(...)` would otherwise
   collide with an axis transform on the same element (a transformed element
   also becomes the containing block for its own positioned descendants,
   which would break the img's percentage-based left/bottom math during the
   motion state). */
.pomoc__photo {
  position: absolute;
  left: 25%;
  bottom: 0;
  transform: translateX(-50%);
  height: 100svh;
  height: 100vh;
}
.pomoc__photo img {
  display: block;
  width: auto;
  height: 100%;
  max-width: none;
}
/* .pomoc__content used to be sized by the flex row's remaining space next to
   .pomoc__photo's 43%-flex-basis column; now that the photo is taken out of
   flow (position:absolute, above), .pomoc__content is the flex row's ONLY
   item — margin-left:50% therefore resolves to exactly 50% of the OUTER
   slide width (not just the inner content-box), because .pomoc__inner's
   left/right padding (--pad-x) are equal: for a single flex child spanning
   the container's content box, margin-left:50% of (100%-2×pad-x) content
   width, plus the pad-x offset already baked into the content box's own
   start, algebraically reduces to exactly 50% of the full border-box width
   regardless of pad-x's value — i.e. the text block's own left edge lands
   on the window's 50% line, per owner spec, at every viewport/pad-x
   combination. align-items:center on .pomoc__inner (unchanged) keeps the
   vertical centering exactly as before. */
.pomoc__content { flex: 1 1 auto; min-width: 0; margin-left: 50%; position: relative; z-index: 1; }
.pomoc__h2 {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-h2);
  color: var(--c-white);
  margin: 0 0 .4em;
}
.pomoc__intro {
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--c-white);
  max-width: 46ch;
  margin: 0 0 1.6em;
}
.services { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: clamp(10px, 1.8vw, 22px); }
.services__item { display: flex; align-items: center; gap: clamp(14px, 1.8vw, 24px); }
.services__icon { width: clamp(30px, 3vw, 42px); height: auto; flex: 0 0 auto; }
.services__title { font-family: var(--font-head); font-weight: 700; font-size: var(--fs-service-title); margin: 0; color: var(--c-white); }
.services__desc { font-family: var(--font-head); font-weight: 400; font-size: var(--fs-service-desc); margin: .15em 0 0; color: var(--c-white); opacity: .92; }

/* ==========================================================================
   4. DLACZEGO JA — timeline dowodowy
   ========================================================================== */
.timeline__inner {
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
}
.timeline { list-style: none; margin: 0; padding: 0; flex: 1 1 56%; max-width: 640px; position: relative; z-index: 1; }
/* Fidelity-pass amendment (2026-07-24, owner screen-w-screen vs Canva): the
   fixed 2.2em padding-left put every item's TEXT on one shared margin while
   only the dot/dash moved via --indent — so items 2 & 4 (deep indent) showed
   their dot floating in mid-air, well right of the paragraph it belongs to.
   Source measurement (design.pdf vector geometry): the text always starts a
   near-constant ~29-36px (avg ~30px) after the DOT's own left edge, for
   every item — i.e. the text's indent is var(--indent) + that fixed gap, not
   a constant on its own. Per-item --indent values (below) are unchanged. */
.timeline__item {
  position: relative;
  padding-left: calc(var(--indent, 0px) + clamp(24px, 2.3vw, 32px));
  padding-top: clamp(10px, 1.6vw, 18px);
  padding-bottom: clamp(10px, 1.6vw, 18px);
}
/* Connector dashes: SVG path (stroke-dasharray) tiled as a background so the
   dots stay perfectly round at any zoom, per design-2x.png. The line is
   drawn a few px wider than --indent so it tucks in behind the dot instead
   of stopping short of it. Fidelity-pass measurement (2026-07-24, vs
   source-canva/design-2x.png): the dash bleeds all the way to the SLIDE's
   own left edge (not just the .timeline column's left edge, which is
   already inset by --pad-x) — left/width extended by --pad-x to reach it.
   Vertically, both the dash and the dot sit level with the CENTER of the
   headline's FIRST LINE (not above it, level with the "25 lat…" text) —
   top pushed down from the item's own padding-top + roughly half the
   headline's line-height. */
.timeline__item::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--pad-x));
  top: calc(clamp(10px, 1.6vw, 18px) + 0.32em);
  width: calc(var(--indent, 0px) + var(--pad-x) + 5px);
  height: 2px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='2'%3E%3Cline x1='0' y1='1' x2='8' y2='1' stroke='%23323136' stroke-width='2' stroke-linecap='round' stroke-dasharray='0.001 7'/%3E%3C/svg%3E");
  background-repeat: repeat-x;
  background-size: 8px 2px;
}
.timeline__dot {
  position: absolute;
  left: var(--indent, 0px);
  top: calc(clamp(10px, 1.6vw, 18px) + 0.32em - 3.5px);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--c-black);
}
/* Items 1 and 5 sit at the leftmost column, but the source still draws a
   short dash into their dot (it isn't zero-length) — matches design-2x.png. */
.timeline__item--1 { --indent: clamp(9px, 1.15vw, 20px); }
.timeline__item--2 { --indent: clamp(60px, 8vw, 108px); }
.timeline__item--3 { --indent: clamp(14px, 1.9vw, 26px); }
.timeline__item--4 { --indent: clamp(46px, 6.1vw, 82px); }
.timeline__item--5 { --indent: clamp(9px, 1.15vw, 20px); }
.timeline__headline {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-timeline-h);
  color: var(--c-graphite);
  margin: 0 0 .35em;
}
.timeline__body {
  font-family: var(--font-head);
  font-weight: 400;
  font-size: var(--fs-timeline-body);
  line-height: 1.5;
  color: var(--c-graphite);
  margin: 0;
  max-width: 56ch;
}
/* Composition rebuild (owner directive 2026-07-25) — same rationale and
   method as .pomoc__photo above: the previous vw-width/vh-height geometry
   deforms the photo off-16:9, a hard bug per owner rule. Rebuilt on a single
   dimension (height, svh/vh) + width:auto (intrinsic 1086:724 ratio fixes
   the other side, so no window ratio can stretch it). The wrapper carries
   ONLY position: left:75% + translateX(-50%) puts the photo's vertical
   center axis at 75% of the window's own width (owner spec — measured
   against .timeline__inner's padding box, full slide width, same reasoning
   as .pomoc__photo); bottom:0 flush to the scene's bottom edge. Height is
   96.319% (not 100%) to keep the small top inset the source design has for
   this photo (measured off design.pdf's placed-image geometry, 2026-07-24
   fidelity pass: 3.681% top inset — 100% - 3.681% = 96.319%); leaving `top`
   unset lets the browser solve it from height+bottom, landing the gap at
   the top automatically. The .fade-in class lives on the <img> (not this
   wrapper) for the same reason as .pomoc__photo: avoids a transform clash
   between the axis-centering translateX and .fade-in's own translateY. */
.timeline__photo {
  position: absolute;
  left: 75%;
  bottom: 0;
  transform: translateX(-50%);
  height: 96.319svh;
  height: 96.319vh;
}
.timeline__photo img {
  display: block;
  width: auto;
  height: 100%;
  max-width: none;
}

/* ==========================================================================
   5. MARKI — grid ~38 logo, fala dim/brighten
   ========================================================================== */
/* Fix (2026-07-24, owner: nav-arrows drifting off the shared corner axes on
   #brands/#companies): this rule used to carry its own padding-top/padding-bottom
   directly on the outer .slide box. .nav-arrow is absolutely positioned
   against its nearest positioned ancestor, which is .slide__inner/.brands__
   inner (.nav-arrows itself is display:contents) — but every OTHER scene's
   .slide has zero padding of its own, so that .slide__inner box happens to
   exactly fill the full-viewport .slide box the arrows are supposed to sit
   in. This rule's 40px padding shrank/offset that box on #brands only,
   pushing both arrows ~40px off the shared axes. Removed — .brands__inner's
   own padding-bottom below already reserves the room the arrows need, so
   nothing is lost visually. */
/* Fix (2026-07-24, owner: grid drifted above the screen's vertical center):
   padding-bottom carried the arrows' +120px reservation ALONE, so the flex
   column's centered content sat ~60px above true center. Split that 120px
   evenly across top+bottom instead — same total clearance reserved for the
   arrows, but now symmetric, so justify-content:center lands on the actual
   screen center. */
.brands__inner {
  min-height: auto;
  padding-top: calc(var(--pad-y) + 60px);
  padding-bottom: calc(var(--pad-y) + 60px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* Source rows are 8/8/7/7/8 logos wide (row 3 and 4 have one fewer logo).
   Fix (2026-07-24, owner: container too narrow — 944px squeezed the
   long wordmarks (always/MARS/Snickers, SIEMENS/BOSCH/B/S/H/,
   Kropla/OSHEE/Jurajska) into each other): justify-content:space-between
   is GONE (it forced every row to the same total width, which shrank the
   horizontal gap on 8-logo rows well below the vertical row-to-row gap —
   that earlier judgment call is superseded by this directive). Rows now
   center via an explicit `gap`, the SAME clamp() as the vertical row gap
   below, so the grid breathes equally in both axes; a 7-logo row is simply
   narrower and centered, not stretched to match the 8-logo rows (owner-
   approved deviation from the source's equal-width rows). Container widened
   back up so that gap has room: 944px -> a responsive ~1200-1360px. */
.brands {
  margin: 0 auto;
  padding: 0;
  max-width: clamp(1024px, 88vw, 1360px);
  display: flex;
  flex-direction: column;
  gap: clamp(19.2px, 3.6vw, 48px);
}
.brands__row { list-style: none; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; gap: clamp(19.2px, 3.6vw, 48px); }
/* Fidelity-pass measurement (2026-07-24, owner feedback vs design-2x.png):
   a HEIGHT-only box let wide/thin wordmarks (Bosch, Oshee…) render far
   larger than square/icon marks at the same cap-height, and per-asset PNGs
   carried inconsistent baked-in white-canvas padding (Generali, Alior,
   Intersport…), making the row look chaotic. Assets were re-cropped to
   their true ink bounds (see assets/brands/*.png), and each slot is now a
   shared WIDTH+HEIGHT box (not height alone) so object-fit:contain shrinks
   wide wordmarks by width the same way the source grid does. */
/* Fix (2026-07-24, owner: logos too large) — slot box scaled ×0.7. */
.brands__row li { display: flex; align-items: center; justify-content: center; width: clamp(50.4px, 6.02vw, 82.6px); height: clamp(23.8px, 2.94vw, 40.6px); flex: 0 0 auto; }
.brands img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }

.js-motion .brands li { animation: brandWave 7s ease-in-out infinite; }
@keyframes brandWave {
  0%, 100% { opacity: .75; }
  50% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .brands li { animation: none !important; opacity: 1 !important; }
}

/* ==========================================================================
   6. REKOMENDACJA
   ========================================================================== */
.recommend__inner {
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* .recommend__list: structural wrapper only, ready for N cards (owner
   directive 2026-07-25) — same box .recommend__card used to be sized as
   directly, so the single-card state today is visually unchanged. */
.recommend__list { list-style: none; margin: 0; padding: 0; position: relative; max-width: 620px; width: 100%; }
/* Multi-card crossfade, same is-active/is-leaving opacity pattern as the
   deck engine (js/main.js) reuses for scene transitions — only the active
   card is in normal flow (defines .recommend__list's height); others are
   stacked via position:absolute so they don't add height/reflow the layout.
   With today's single card (marked is-active in index.html so it renders
   correctly without JS) this is a no-op: it's already the only, in-flow,
   fully-opaque item. */
.recommend__card { width: 100%; }
.recommend__list.is-carousel .recommend__card {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 400ms ease, visibility 0s linear 400ms;
}
.recommend__list.is-carousel .recommend__card.is-active {
  position: relative;
  inset: auto;
  opacity: 1;
  visibility: visible;
  transition: opacity 400ms ease;
}
.recommend__list.is-carousel .recommend__card.is-leaving { position: absolute; inset: 0; opacity: 1; visibility: visible; }
/* Plain HTML text card (owner directive 2026-07-25 — replaces the former
   per-reviewer Canva screenshot images so all 11 recommendations share one
   typographic system): name (bold) + title (smaller) on top, quote below,
   same layout the source LinkedIn screenshots use. */
.recommend__name {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-contact-h2);
  line-height: 1.3;
  color: var(--c-black);
  margin: 0 0 .2em;
}
.recommend__title {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-timeline-body);
  line-height: 1.4;
  color: var(--c-graphite);
  margin: 0 0 1.1em;
}
.recommend__quote {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--c-graphite);
}
.recommend__quote p { margin: 0 0 .75em; }
.recommend__quote p:last-child { margin-bottom: 0; }

/* ==========================================================================
   6b. BLOG — zajawki (working preview, owner directive 2026-07-24). Same
   system as every other scene: light (white, default .slide bg — no
   data-theme, matches #recommendations/#kontakt), heading in the shared
   --font-head/--fs-h2 pattern (.contact__h2 twin), three plain text blocks
   (no cards/frames/shadows/images) sitting side by side — mirrors
   .recommend__inner's single centered block, just with a row of 3 instead
   of 1 card. */
.blog__inner {
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.blog__content { width: 100%; max-width: 1180px; }
.blog__h2 {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-h2);
  color: var(--c-black);
  margin: 0 0 1em;
}
.blog__list { list-style: none; margin: 0; padding: 0; display: flex; gap: clamp(24px, 4vw, 56px); }
.blog__item { flex: 1 1 0; min-width: 0; }
.blog__category {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-timeline-body);
  color: var(--c-accent);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin: 0 0 .6em;
}
.blog__title {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-service-title);
  line-height: 1.35;
  color: var(--c-black);
  margin: 0 0 .5em;
}
.blog__lead {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--c-graphite);
  margin: 0;
}

/* ==========================================================================
   7. KONTAKT
   ========================================================================== */
.contact__inner {
  min-height: 100svh;
  min-height: 100vh;
  display: flex;
  align-items: center;
  gap: 5vw;
}
.contact__photo { flex: 0 0 34%; max-width: 380px; }
.contact__photo img { width: 100%; height: auto; }
.contact__text { flex: 1 1 auto; }
.contact__h2 {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-contact-h2);
  color: var(--c-black);
  margin: 0 0 .3em;
}
.contact__lead {
  font-family: var(--font-head);
  font-weight: 400;
  font-size: var(--fs-contact-body);
  line-height: 1.5;
  color: var(--c-black);
  margin: 0 0 1.6em;
}
.social { list-style: none; margin: 0; padding: 0; display: flex; gap: clamp(10px, 1.5vw, 18px); }
.social a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(44px, 4vw, 52px);
  height: clamp(44px, 4vw, 52px);
  background: #f0f0f0;
  color: var(--c-black);
  border-radius: 4px;
  text-decoration: none;
}

/* ==========================================================================
   8. STOPKA — loga firm własnych
   ========================================================================== */
.slide--footer { min-height: auto; }
/* Fix (2026-07-24, owner: nav-arrows drifting off the shared corner axes on
   #brands/#companies): the vertical breathing room used to be padding directly on
   the outer .slide box (.slide--footer). .nav-arrow anchors to its nearest
   positioned ancestor, .footer__inner (.nav-arrows/.nav-arrow itself sits
   right in it here) — every other scene's .slide carries no padding of its
   own, so .slide__inner's box exactly fills the full-viewport .slide box the
   arrow offsets assume. This padding shrank/offset that box on #companies only
   (by up to 130px), pushing the ↑ arrow well off the shared axis. Moved onto
   .footer__inner itself (same clamp values, vertical only) so the visual
   spacing is unchanged but .slide--footer stays full-bleed like every other
   scene. */
.footer__inner {
  min-height: auto;
  padding-top: clamp(48px, 9vw, 130px);
  padding-bottom: clamp(48px, 9vw, 130px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.companies {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 1000px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(28px, 5vw, 64px);
}
.companies img { height: clamp(28px, 3.2vw, 44px); width: auto; }

/* ==========================================================================
   Desktop scene-fit — each full-bleed scene is EXACTLY one viewport tall
   (height, not just min-height), so it composes to the window and its arrows
   never fall below the fold. Below ~600px of viewport height the base rules
   above (min-height + natural scroll) apply instead — matches the mobile
   breakpoint's own min-height:auto fallback for short/mobile windows.
   ========================================================================== */
@media (min-width: 821px) and (min-height: 600px) {
  .slide--hero, .slide--mural, .slide--pomoc, .slide--timeline,
  .slide--recommend, .slide--blog, .slide--contact, .slide--brands, .slide--footer {
    height: 100svh;
    height: 100vh;
    min-height: 0;
    align-items: stretch;
  }
  .hero__inner, .mural__inner, .pomoc__inner, .timeline__inner,
  .recommend__inner, .blog__inner, .contact__inner, .brands__inner, .footer__inner {
    height: 100%;
    min-height: 0;
  }
  /* .hero__photo excluded here on purpose — position:absolute now (own
     height:90svh, bottom:0), not a flex item of .hero__inner anymore.
     .timeline__photo excluded too (2026-07-25): it needs 96.319% (its own
     top-inset value), not a flat 100% — its base rule (svh/vh, always
     correct regardless of container height) already covers every state, so
     no override is needed here. */
  .pomoc__photo { height: 100%; }
}

/* ==========================================================================
   PRESENTATION DECK — desktop crossfade (owner directive 2026-07-24)
   The site behaves as a slide deck: scenes are STACKED (position:fixed,
   one visible), and a scroll gesture CROSSFADES the current scene out /
   the next scene in — no vertical scrolling of the scenes themselves.
   Activated only under html.deck, which main.js adds solely when JS runs
   AND this same desktop breakpoint matches. No-JS keeps the normal
   scrollable one-pager (the class is never added); mobile / short windows
   fall through the (min-width:821px) and (min-height:600px) gate and keep
   the natural-scroll layout + fade-in reveals.
   Only the TRANSITION MECHANICS live here — no design/type/color/layout of
   any scene is changed.
   ========================================================================== */
@media (min-width: 821px) and (min-height: 600px) {
  html.deck, html.deck body { height: 100%; }
  /* the deck owns the viewport; scenes are fixed, so the document itself
     must not scroll (each scene scrolls INTERNALLY when it is taller than
     the window — see overflow-y below). */
  html.deck body { overflow: hidden; }

  html.deck .slide {
    position: fixed;
    inset: 0;
    height: 100svh;
    height: 100vh;
    min-height: 0;
    /* internal scroll only kicks in when a scene is taller than the window
       (e.g. the brands grid on 1366×768); otherwise nothing scrolls. */
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    opacity: 0;
    visibility: hidden;
    z-index: 0;
    /* opacity-only transition; visibility flips to hidden AFTER the fade so
       the outgoing scene stays rendered while it dissolves. */
    transition: opacity 400ms ease, visibility 0s linear 400ms;
  }
  /* the visible scene */
  html.deck .slide.is-active {
    opacity: 1;
    visibility: visible;
    z-index: 2;
    transition: opacity 400ms ease;
  }
  /* the scene being left: held fully OPAQUE beneath the incoming one (which
     fades in on top) for the duration of the swap, so the crossfade never
     shows a flash of the page background. main.js removes this class once
     the fade completes. */
  html.deck .slide.is-leaving {
    opacity: 1;
    visibility: visible;
    z-index: 1;
  }

  /* Tall-scene support: let the scene inners grow past one viewport so the
     fixed scene can scroll internally (reverts the desktop scene-fit rule's
     height:100% cap back to a min-height, restoring the base behaviour). */
  html.deck .hero__inner, html.deck .mural__inner, html.deck .pomoc__inner,
  html.deck .timeline__inner, html.deck .recommend__inner,
  html.deck .blog__inner, html.deck .contact__inner {
    height: auto;
    min-height: 100svh;
    min-height: 100vh;
  }
  /* keep the photo exactly one viewport tall even when the text column
     grows taller than the window (percentage height would collapse against
     the now-auto inner). .timeline__photo excluded (2026-07-25): its own
     base rule is already svh/vh (not %), so it's immune to the inner's
     height:auto here — restating it would wrongly override its 96.319%
     back to a flat 100%. */
  html.deck .pomoc__photo {
    height: 100svh;
    height: 100vh;
  }
  /* Scenes whose SLIDE box centers its own content (brands, footer — they
     are not in the stretch list above): safe-center so that when the content
     is taller than the window the top isn't clipped and internal scroll can
     reach it (plain center clips the overflowing start). */
  html.deck .slide--brands, html.deck .slide--footer { align-items: safe center; }

  /* In the deck the scene "opens whole" — the per-element fade-in rise is
     suppressed (pure scene crossfade, no internal movement). */
  html.deck .fade-in {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  /* Reduced motion: hard cut, no crossfade (main.js also drops the timing). */
  @media (prefers-reduced-motion: reduce) {
    html.deck .slide,
    html.deck .slide.is-active { transition: none !important; }
  }
}

/* ==========================================================================
   Mobile — same klimat, single column
   ========================================================================== */
@media (max-width: 820px) {
  :root {
    --pad-x: 24px;
    --pad-y: 32px;
    --fs-eyebrow: 15px;
    --fs-h1-a: 34px;
    --fs-h1-b: 25px;
    --fs-body: 16px;
    --fs-mural-1: 24px;
    --fs-mural-2: 18px;
    --fs-mural-3: 15px;
    --fs-h2: 24px;
    --fs-service-title: 16px;
    --fs-service-desc: 15px;
    --fs-timeline-h: 16px;
    --fs-timeline-body: 14.5px;
    --fs-contact-h2: 20px;
    --fs-contact-body: 16px;
  }

  /* No top/bottom padding here (was 8px): .slide is the positioned
     ancestor's flex container for .slide__inner, so padding on .slide
     pushes .slide__inner's border-box down/up by that amount — while the
     corner elements (logo, nav-arrows) are positioned against
     .slide__inner's box via --corner-offset. That extra 8px only applied on
     the vertical axis (.slide has no horizontal padding), breaking the
     top/bottom vs. left/right corner symmetry vs. desktop. Removing it
     restores identical offsets on all four axes; .slide__inner already
     carries its own generous padding below, so no content-spacing changes. */
  .slide { min-height: auto; }
  /* bottom reserve enlarged for the ↑/↓ two-arrow stack (was sized for a
     single ↓ arrow only) so it never overlaps the last line of content. */
  .slide__inner { padding-top: calc(var(--pad-y) + 50px); padding-bottom: calc(var(--pad-y) + 130px); }

  .hero__inner { flex-direction: column-reverse; align-items: stretch; gap: 28px; min-height: auto; }
  .hero__text { max-width: none; }
  /* Back to a normal-flow flex item on mobile (desktop's position:absolute
     bottom-right placement is out of scope here — stacked column layout
     unchanged, owner directive 2026-07-24 concerned desktop only). Restores
     the flex centering the shared base rule used to provide before it
     became position:absolute for desktop. */
  .hero__photo {
    position: static;
    width: 100%;
    height: 58vh;
    align-self: center;
    display: flex;
    align-items: flex-end;
    justify-content: center;
  }
  .hero__photo img { width: auto; height: 100%; }

  .mural__inner { min-height: auto; padding-top: 46vh; }
  .mural__text { max-width: none; text-align: left; }
  .slide--mural { min-height: 100svh; min-height: 100vh; }
  .mural__bg { object-position: 30% center; }

  .pomoc__inner { flex-direction: column; align-items: stretch; gap: 24px; min-height: auto; }
  .pomoc__content { margin-left: 0; }
  /* Reset the desktop axis-positioned wrapper (2026-07-25: position:absolute
     + left:25%/bottom:0/transform:translateX(-50%)) back to a normal
     stacked-column flex item — mobile keeps the full, uncropped frame like
     before, no axis/bleed at this width. */
  .pomoc__photo { position: static; transform: none; flex: 0 0 auto; max-height: 46vh; overflow: hidden; display: flex; align-items: center; justify-content: center; }
  .pomoc__photo img { width: 100%; height: 100%; object-fit: contain; object-position: center; }

  .timeline__inner { flex-direction: column-reverse; align-items: stretch; gap: 24px; min-height: auto; }
  /* Reset the desktop axis-positioned wrapper (2026-07-25: position:absolute
     + left:75%/bottom:0/transform:translateX(-50%)) back to a normal
     stacked-column flex item — mobile keeps the full, uncropped frame like
     before, no axis/bleed at this width. */
  .timeline__photo { position: static; transform: none; flex: 0 0 auto; max-height: 40vh; overflow: hidden; display: flex; align-items: center; justify-content: center; }
  .timeline__photo img { width: 100%; height: 100%; object-fit: contain; object-position: center; }
  .timeline__item--2, .timeline__item--4 { --indent: 30px; }

  .brands { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 3vw, 40px) clamp(10px, 2.4vw, 32px); }
  .brands__row { display: contents; }
  /* height pinned explicitly (2026-07-24): the desktop ×0.7 logo-size fix
     above must not cascade into mobile, which was never part of that spec —
     restores the pre-fix mobile logo height unchanged. */
  .brands__row li { width: 100%; height: clamp(34px, 4.2vw, 58px); }

  /* .carousel-arrow's own mobile left/right override removed (2026-07-25):
     it now shares .nav-arrow's --corner-offset axis mechanism (needs no
     per-breakpoint tuning of its own — --corner-offset already scales down
     for mobile) instead of the old fixed clamp(10px,4vw,110px) positioning
     that override was compensating for. The reserved padding below still
     keeps the quote text clear of the icons at this width. */
  .recommend__inner { padding-left: 56px; padding-right: 56px; }

  .blog__inner { min-height: auto; }
  .blog__list { flex-direction: column; gap: 24px; }

  .contact__inner { flex-direction: column; align-items: stretch; gap: 24px; min-height: auto; }
  .contact__photo { max-width: 220px; margin: 0 auto; }

  .companies { gap: 28px 36px; }
}

@media (max-width: 480px) {
  .brands { grid-template-columns: repeat(2, 1fr); }
}

/* Screen-reader-only section headings (spec §9.2, fix M2) — zero visual change */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
