:root {
  --bg: #0a0801;
  --fg: #d9d7d4;
  --fg-dim: #8c8a85;
  --line: rgba(217, 215, 212, 0.14);
  --accent: #b8956a;
  --font: "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-weight: 400;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

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

ul { list-style: none; margin: 0; padding: 0; }

/* ---------- loader ---------- */

/* full-black cover until the window load event (all images, styles and fonts
   in), then fades away and removes itself. sits above the fixed header. */
.loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.6s ease;
}

.loader.is-done {
  opacity: 0;
  pointer-events: none;
}

.loader img {
  width: min(220px, 45vw);
  height: auto;
}

/* ---------- header ---------- */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 28px 5vw;
  /* no mix-blend-mode here: a blended header would isolate the logo images'
     own screen blend, painting their black boxes instead of dropping them out */
  transition: background-color 0.45s ease, backdrop-filter 0.45s ease;
}

/* past the hero: frosted bar — content scrolling under the icon line is
   blurred and dimmed so it never collides visually with the logo */
.site-header.is-past-hero {
  background-color: rgba(10, 8, 1, 0.4);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.logo {
  position: relative;
  display: block;
}

/* the -alpha PNGs carry white artwork on true transparency (luminance baked
   into the alpha channel), so no blend modes are needed — blending could never
   work here anyway, as the fixed header forms a stacking context that isolates
   it. color comes from filters: brightness(0) = black over the hero photo,
   none = white once past it on the dark page. */
.logo img {
  display: block;
  height: 88px;
  width: auto;
  filter: brightness(0);
  transition: opacity 0.45s ease;
}

.site-header.is-past-hero .logo img {
  filter: none;
}

.logo__icon {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
}

/* past the hero image: full lockup out, icon only */
.site-header.is-past-hero .logo__full {
  opacity: 0;
}

.site-header.is-past-hero .logo__icon {
  opacity: 1;
}

/* over the kontakt section's forest background: full lockup, WHITE artwork
   (the -alpha PNGs are natively white, so no filter), no frosted bar. these
   sit after the is-past-hero rules so they win while both classes are on. */
.site-header.is-over-kontakt {
  background-color: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.site-header.is-over-kontakt .logo img {
  filter: none;
}

.site-header.is-over-kontakt .logo__full {
  opacity: 1;
}

.site-header.is-over-kontakt .logo__icon {
  opacity: 0;
}

.menu-toggle {
  width: 36px;
  height: 20px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.menu-toggle span {
  display: block;
  height: 1px;
  width: 100%;
  background: var(--fg);
  transition: transform 0.4s ease, opacity 0.4s ease;
}

.menu-toggle.is-open span:first-child {
  transform: translateY(9.5px) rotate(45deg);
}
.menu-toggle.is-open span:last-child {
  transform: translateY(-9.5px) rotate(-45deg);
}

/* ---------- overlay nav ---------- */

/* slides in from the RIGHT and covers half the screen's width, full height.
   the transition-duration is overridden inline by script.js when a section
   link is clicked, so the panel closes over the whole scroll to the target. */
.overlay-nav {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 50vw;
  z-index: 50;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  /* `safe` keeps the top reachable if the content ever outgrows the panel */
  justify-content: center;
  justify-content: safe center;
  padding: 10vh 4vw;
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}

.overlay-nav.is-open {
  transform: translateX(0);
}

.overlay-nav__list {
  font-size: clamp(20px, 2.6vw, 34px);
  font-weight: 300;
  letter-spacing: 0.02em;
  line-height: 1.45;
}

.overlay-nav__list > li {
  border-bottom: 1px solid var(--line);
  padding: 12px 0;
}

@media (max-width: 640px) {
  /* a half-width drawer is too narrow on phones */
  .overlay-nav {
    width: 100%;
  }
  /* the 88px desktop lockup is ~206px wide — over half a phone's width */
  .logo img {
    height: 52px;
  }
  .site-header {
    padding: 18px 6vw;
  }
  .overlay-nav {
    padding: 12vh 8vw;
  }
}

.overlay-nav__list a {
  display: inline-block;
  transition: opacity 0.3s ease, letter-spacing 0.3s ease;
}

.overlay-nav__list a:hover {
  opacity: 0.55;
}

.overlay-nav__sublist {
  font-size: 15px;
  color: var(--fg-dim);
  display: flex;
  gap: 22px;
  padding-top: 10px;
  letter-spacing: 0.08em;
}

.overlay-nav__privacy {
  margin-top: 20px;
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--fg-dim);
}

.overlay-nav__addresses {
  margin-top: 12px;
  display: flex;
  gap: 40px;
  font-size: 12px;
  color: var(--fg-dim);
  letter-spacing: 0.04em;
}

.overlay-nav__addresses p { margin: 2px 0; }

.loc-name {
  color: var(--fg);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: 6px !important;
}

/* ---------- hero ---------- */

.hero {
  position: relative;
  height: 150vh;
  overflow: hidden;
  background-color: var(--bg);
  background-image: url("assets/forest.avif");
  background-size: cover;
  background-position: center top;
  background-repeat: no-repeat;
}

/* title + scroll cue occupy only the first screen of the 150vh hero */
.hero__content {
  position: relative;
  z-index: 3;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* warm darkening pass over the photo. sits BENEATH the fog (z-index 0 vs 1) so
   the mist keeps its full strength and only the image is knocked back. slightly
   heavier top and bottom to frame the composition and seat the title. */
.hero__tint {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(41, 26, 13, 0.32) 0%,
    rgba(34, 21, 10, 0.22) 48%,
    rgba(41, 26, 13, 0.36) 100%
  );
}

/* fog overlay: footage shot on black, so screen blending drops the background
   out and composites only the fog over the photo. must NOT be wrapped in a
   transformed/will-change element — that forms an isolated group and the black
   would paint over the photo instead of blending away. */
.fog-video {
  position: absolute;
  /* headroom so the scroll drift never pulls an edge into frame */
  top: -6%;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 112%;
  object-fit: cover;
  object-position: center center;
  pointer-events: none;
  mix-blend-mode: screen;
  /* footage averages 41/255 luminance; a light lift brings the composite to
     ~+37 average over the photo, matching the density of the earlier pass */
  filter: brightness(1.3) contrast(1.1);
  opacity: 0.9;
}

/* sits above the fog so both image and mist sink into black together. the half
   of the image below the fold does the fading: transparent through 58% (87vh),
   which is just shy of the 100vh fold so no seam shows at the screen edge, then
   deepening to solid page background by the bottom of the 150vh hero. */
.hero__fade {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 58%,
    rgba(10, 8, 1, 0.55) 80%,
    rgba(10, 8, 1, 0.9) 93%,
    var(--bg) 100%
  );
}

/* black over the photo, in the logo's geometric sans (Poppins ≈ the lockup's
   lettering). sized so the longer 40-char line stays on one line: at 3.4vw it
   spans ~72vw against the 88vw available inside the padding. */
.hero__title {
  position: relative;
  z-index: 1;
  padding: 0 6vw;
  text-align: center;
  font-family: "Poppins", var(--font);
  font-weight: 300;
  font-size: clamp(16px, 3.4vw, 48px);
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: #000;
}

.hero__title span {
  display: block;
  white-space: nowrap;
}

/* the 16px clamp floor plus 6vw gutters overflows below ~360px, and the lines
   must stay unwrapped — so drop the floor and tighten the gutters instead of
   letting a horizontal scrollbar appear */
@media (max-width: 480px) {
  .hero__title {
    padding: 0 3vw;
    font-size: clamp(12px, 4.2vw, 48px);
  }
}

.hero__scroll {
  position: absolute;
  z-index: 1;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  letter-spacing: 0.2em;
  color: #33322f;
  text-transform: uppercase;
}

.hero__scroll i {
  width: 1px;
  height: 40px;
  background: linear-gradient(#33322f, transparent);
}

/* ---------- shared section styles ---------- */

section:not(.hero) {
  padding: 14vh 6vw;
  max-width: 1100px;
  margin: 0 auto;
}

.eyebrow {
  font-size: 12px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin: 0 0 28px;
}

.section-title {
  font-weight: 300;
  font-size: clamp(28px, 5vw, 54px);
  line-height: 1.3;
  margin: 0 0 32px;
}

.section-title--small {
  font-size: clamp(20px, 3vw, 30px);
  margin-bottom: 20px;
}

.section-copy {
  max-width: 62ch;
  font-size: clamp(15px, 1.6vw, 18px);
  color: #c3c1bd;
  font-weight: 300;
  margin: 0 0 28px;
}

.glyph {
  font-size: 1.3em;
  color: var(--accent);
}

/* ---------- philosophy: text left, sticky photo stack right ---------- */

/* section.philosophy (0-1-1) so this outranks the shared section:not(.hero)
   rule — a bare .philosophy (0-1-0) loses and the 1100px centered cap wins,
   which is invisible on narrow screens but recenters everything on wide ones */
section.philosophy {
  /* single source for the duo's geometry — the photo sizes, the text width and
     the vertical centring all derive from these. 31.35vw is 5% off the original
     33vw. the vh cap only engages on short/wide screens, where it is what keeps
     the dots from falling below the fold; on a normal 16:9 desktop the vw value
     wins, so the photos really are ~5% smaller rather than vh-shrunk. */
  /* --duo-ratio is width/height: 1 = square. heights derive from it, so a
     section can carry non-square photos without cropping. the vh cap scales
     with the ratio, since a wider box is proportionally shorter. */
  --duo-ratio: 1;
  --duo-primary: min(31.35vw, calc(55vh * var(--duo-ratio)));
  --duo-secondary: calc(var(--duo-primary) * 0.66);
  --duo-primary-h: calc(var(--duo-primary) / var(--duo-ratio));
  --duo-secondary-h: calc(var(--duo-secondary) / var(--duo-ratio));

  max-width: none;
  margin: 0;
  /* text hugs the left edge, photos push toward the right edge */
  padding: 14vh 3vw 10vh 4vw;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--duo-primary);
  column-gap: 4vw;
  align-items: start;
}

/* let the copy use the full text column instead of the global 62ch cap */
.philosophy .section-copy {
  max-width: none;
}

/* text holds its place while the photos stack up alongside. capped at 2/3 of
   its column, which leaves it comfortably clear of the secondary card even
   though that card now hangs further left with the bigger photos. */
.philosophy__text {
  position: sticky;
  top: 28vh;
  max-width: 66.6%;
}

.philosophy__stack {
  display: flex;
  flex-direction: column;
}

/* two-photo composition: one big square spot and one small card at a
   third of that size, straddling the big photo's top border — half above the
   edge, half over the image. clicking swaps which photo holds which spot.
   the whole duo is a single sticky element, so photos and dots always move
   and release together. the ::after spacer holds it pinned for ~50vh. */
.photo-duo {
  position: sticky;
  /* centres the whole composition — the card's overhang above and the dots
     below — in the space under the fixed header, so the dots always land
     inside the viewport. derived from: 50vh - primary/2 + secondary/4 + 46px */
  top: calc(50vh - var(--duo-primary-h) / 2 + var(--duo-secondary-h) / 4 + 46px);
  width: var(--duo-primary);
  height: var(--duo-primary-h);
  cursor: pointer;
}

.philosophy__stack::after {
  content: "";
  display: block;
  height: 50vh;
}

.duo-photo {
  position: absolute;
  margin: 0;
  transition: top 0.6s ease, left 0.6s ease, width 0.6s ease, height 0.6s ease;
}

/* spots are addressed by data-spot, which script.js rotates on click — so any
   number of photos can share the composition and every one behaves alike.
   spot 1 = big. spot 2 = card on the big photo's top-left corner (top and left
   both minus half its size, so half hangs above the border and its centreline
   lands on the left border). spot 3 = the extra card. */
.duo-photo[data-spot="1"] {
  top: 0;
  left: 0;
  width: var(--duo-primary);
  height: var(--duo-primary-h);
  z-index: 1;
}

.duo-photo[data-spot="2"] {
  top: calc(var(--duo-secondary-h) / -2);
  left: calc(var(--duo-secondary) / -2);
  width: var(--duo-secondary);
  height: var(--duo-secondary-h);
  z-index: 2;
}

/* dots live inside the sticky duo — release sync is automatic */
.stack-dots {
  position: absolute;
  top: calc(100% + 26px);
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  gap: 14px;
}

.stack-dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  opacity: 0.55;
  transform: scale(0.35);
  transition: transform 0.35s ease, opacity 0.35s ease;
}

.stack-dots span.is-active {
  transform: scale(1);
  opacity: 1;
}

/* waterdrop reveal: photo A opens on section entry; photo B chains in after
   A's ripple ends (script.js listens for the transition end). the overshoot
   bezier lets the radius swell slightly past full and settle — a liquid
   ripple; 75% covers the corners (circle() % resolves against the diagonal). */
.duo-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  clip-path: circle(0% at 50% 50%);
  transform: scale(1.15);
  transition:
    clip-path 1.1s cubic-bezier(0.3, 1.36, 0.35, 1),
    transform 1.1s cubic-bezier(0.3, 1.36, 0.35, 1);
}

/* both ripple from the one class on the initial scroll-in; the secondary waits
   out the primary's animation via transition-delay, so the timing is exact and
   needs no extra scrolling or JS chaining */
.duo-photo--b img {
  transition-delay: 0.3s;
}

.photo-duo.is-open .duo-photo img {
  clip-path: circle(75% at 50% 50%);
  transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .duo-photo img {
    clip-path: none;
    transform: none;
    transition: none;
  }
  .duo-photo {
    transition: none;
  }
}

/* ---------- section 2: the duo composition mirrored — photos left, text right ----------
   the secondary card moves to the primary's top-RIGHT corner, so its overhang
   points into the column gap and text column rather than off the left edge of
   the screen. everything else (sizes, reveal, swap, dots) is inherited. */

section.philosophy--flipped {
  /* photos are 1536x1024 — kept at their native 3:2, uncropped */
  --duo-ratio: 1.5;
  padding: 14vh 4vw 10vh 3vw;
  grid-template-columns: var(--duo-primary) minmax(0, 1fr);
}

/* no-crop sections: the photos here have mixed orientations, so a photo rotating
   into a differently-shaped spot would be centre-cropped by `cover`.
   `contain` fits it whole inside the spot instead,
   preserving its native ratio; the unfilled part of the box is transparent and
   the page behind is black, so the letterboxing is invisible. keeping the spot
   boxes fixed also means the card anchors stay put no matter which photo is
   where — sizing spots to each photo's width would detach them from the corner. */
.philosophy--nocrop .duo-photo img {
  object-fit: contain;
}

/* section 4 reuses the flipped composition wholesale — its own photos are 4:3
   (1448x1086) rather than 3:2, and it carries the third photo, which is portrait
   (3686x5574) and keeps its own ratio. sized to the card's height so the trio
   reads as a set. */
/* realizace: one primary + a 2x2 grid of four satellites to its right.
   row 1 pairs the two landscape 4:3 shots, row 2 the two portraits (3:4 and
   0.6613). row heights are derived so both rows span the SAME width and the
   grid's total height equals the primary's — every outer edge lines up:
     row1 width = 2·(4/3)·h1 = 2.6667·h1
     row2 width = (0.75 + 0.6613)·h2 = 1.4113·h2
     equal widths  → h2 = 1.8895·h1
     h1 + h2 + gap = primary height → h1 = (Ph − gap) / 2.8895 */
section.philosophy--realizace {
  --duo-ratio: 1.3333;
  /* primary trimmed so primary + grid clear the text column comfortably */
  --duo-primary: min(26vw, calc(50vh * var(--duo-ratio)));
  --duo-grid-gap: 12px;
  --duo-r1h: calc((var(--duo-primary-h) - var(--duo-grid-gap)) / 2.8895);
  --duo-r2h: calc(var(--duo-r1h) * 1.8895);
  --duo-grid-x: calc(var(--duo-primary) + 2vw);
}

/* grid spots — top row flush with the primary's top edge, bottom row with its
   bottom edge. these outrank the generic spot-2 card rule by order and
   specificity, so realizace's card joins the grid instead of straddling. */
section.philosophy--realizace .duo-photo[data-spot="2"] {
  top: 0;
  left: var(--duo-grid-x);
  width: calc(var(--duo-r1h) * 1.3333);
  height: var(--duo-r1h);
  z-index: 1;
}

section.philosophy--realizace .duo-photo[data-spot="3"] {
  top: 0;
  left: calc(var(--duo-grid-x) + var(--duo-r1h) * 1.3333 + var(--duo-grid-gap));
  width: calc(var(--duo-r1h) * 1.3333);
  height: var(--duo-r1h);
  z-index: 1;
}

section.philosophy--realizace .duo-photo[data-spot="4"] {
  top: calc(var(--duo-r1h) + var(--duo-grid-gap));
  left: var(--duo-grid-x);
  width: calc(var(--duo-r2h) * 0.75);
  height: var(--duo-r2h);
  z-index: 1;
}

section.philosophy--realizace .duo-photo[data-spot="5"] {
  top: calc(var(--duo-r1h) + var(--duo-grid-gap));
  left: calc(var(--duo-grid-x) + var(--duo-r2h) * 0.75 + var(--duo-grid-gap));
  width: calc(var(--duo-r2h) * 0.6613);
  height: var(--duo-r2h);
  z-index: 1;
}

/* the composition has no overhang above the primary here, so centre plainly */
.philosophy--realizace .photo-duo {
  top: calc(50vh - var(--duo-primary-h) / 2);
}

.philosophy--realizace .philosophy__text {
  padding-top: 0;
}

/* reveal cascade for the fourth and fifth photos */
.duo-photo--d img {
  transition-delay: 0.9s;
}

.duo-photo--e img {
  transition-delay: 1.2s;
}

/* realizace: the primary keeps its trigger timing, but the rest of the set
   follows in quick succession — 0.1s steps, a third of the standard interval,
   so all five are in well under a second of the first */
.philosophy--realizace .duo-photo--b img { transition-delay: 0.1s; }
.philosophy--realizace .duo-photo--c img { transition-delay: 0.2s; }
.philosophy--realizace .duo-photo--d img { transition-delay: 0.3s; }
.philosophy--realizace .duo-photo--e img { transition-delay: 0.4s; }

/* ripples in after the secondary (which is delayed 0.3s) */
.duo-photo--c img {
  transition-delay: 0.6s;
}

/* flipped sections put the card on the primary's top-RIGHT corner instead */
.philosophy--flipped .duo-photo[data-spot="2"] {
  left: calc(var(--duo-primary) - var(--duo-secondary) / 2);
}

/* text sits at the right end of its column, clear of the card's overhang, and
   vertically centred on the pinned photo. a full-height sticky flex box centres
   at 50vh; the padding-top shifts that centre down to the photo's centre, which
   sits secondary-h/4 + 46px below the viewport middle (padding splits, so the
   shift is half the padding). */
.philosophy--flipped .philosophy__text {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: calc(var(--duo-secondary-h) / 2 + 92px);
  margin-left: auto;
}

/* ---------- process: philosophy mirrored — picture left, text right ---------- */

section.process {
  max-width: none;
  margin: 0;
  /* text hugs the left edge, picture toward the right — same hand as section 1 */
  padding: 14vh 3vw 14vh 4vw;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 40vw;
  column-gap: 4vw;
  align-items: center;
}

/* positioned so the single dot beneath the picture can anchor to it */
.process__media {
  position: relative;
}

.process__media img {
  display: block;
  width: 100%;
  height: auto; /* native ratio, no square crop */
}

.process .section-copy {
  max-width: none;
}

@media (max-width: 900px) {
  section.process {
    grid-template-columns: 1fr;
  }
  /* stacked: text first, then the picture below it */
  .process__media {
    margin-top: 6vh;
  }
}

@media (max-width: 900px) {
  section.philosophy {
    /* text sits above the photos here, so the card can be larger relative to
       the screen; still capped against vh so the dots stay in view */
    --duo-primary: min(66.5vw, 50vh);
    grid-template-columns: 1fr;
  }
  .philosophy__text {
    position: static;
    /* clear air between the copy and the composition below it */
    margin-bottom: 12vh;
    /* stacked layout — no photo alongside to clear, so use the full width */
    max-width: none;
  }
  /* NOT sticky on mobile: pinning a stacked composition just forces the reader
     through a long dead scroll before the next section arrives. relative (not
     static!) so it still forms the containing block its absolute children need. */
  .photo-duo {
    position: relative;
    top: auto;
    margin-left: auto;
    margin-right: auto;
    /* the card overhangs above the primary; reserve that room so it cannot
       ride up into the text */
    margin-top: calc(var(--duo-secondary-h) / 2);
  }
  /* with nothing pinned there is nothing to hold in place — the spacer only
     delayed the following section */
  .philosophy__stack::after {
    height: 0;
  }
  /* the card's left overhang is clamped so it cannot run off the viewport: the
     duo is centred in a ~93vw column, and a full half-width overhang would
     introduce horizontal scrolling */
  .duo-photo[data-spot="2"] {
    left: max(-15vw, calc(var(--duo-secondary) / -2));
  }
  .stack-dots {
    top: calc(100% + 20px);
  }

  /* comfortable gutters — the desktop 3-4vw is only ~12px on a phone */
  section.philosophy,
  section.philosophy--flipped,
  section.philosophy--realizace,
  section.process {
    padding-left: 6vw;
    padding-right: 6vw;
  }

  /* flipped variant, stacked: header above the photos to match section 1, and
     the right-hand overhang clamped so the card cannot run off the viewport.
     the desktop centring (sticky, 100vh box, padding offset) MUST be undone
     here — it is set at 0-2-0, so the plain .philosophy__text reset above
     cannot reach it and the block would stay a 100vh sticky flex box. */
  .philosophy--flipped .philosophy__text {
    order: -1;
    margin-left: 0;
    position: static;
    height: auto;
    display: block;
    padding-top: 0;
  }
  .philosophy--flipped .duo-photo[data-spot="2"] {
    left: min(
      calc(var(--duo-primary) - var(--duo-secondary) / 2),
      calc(var(--duo-primary) - var(--duo-secondary) + 15vw)
    );
  }
  /* realizace, narrow screens: smaller primary and the duo pinned to the left
     edge of its column so the grid's width stays on-screen. spot 2 must be
     re-asserted here — the flipped card clamp above this point would otherwise
     win by order and drag the top-left cell out of the grid. */
  /* stacked on mobile: primary above, 2x2 grid below — script.js measures the
     column and sets the duo's width/height. `relative`, never `static`: the five
     photos are absolutely positioned and would otherwise escape to a distant
     ancestor and vanish from the section entirely. */
  .philosophy--realizace .photo-duo {
    position: relative;
    top: auto;
    margin-left: 0;
    margin-top: 0;
    width: 100%;
  }
  .philosophy--realizace .duo-photo {
    /* let the JS-supplied box drive size; no transition on first layout */
    transition: top 0.6s ease, left 0.6s ease, width 0.6s ease, height 0.6s ease;
  }
  .philosophy--realizace .duo-photo[data-spot="2"] {
    left: var(--duo-grid-x);
  }
}

.text-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}

.text-link__arrow {
  transition: transform 0.3s ease;
}

.text-link:hover .text-link__arrow {
  transform: translateX(6px);
}

/* ---------- typewriter ---------- */

/* every character is pre-wrapped in a span, so revealing them causes no reflow
   — the text occupies its final layout from the start and letters just appear */
.tw-char {
  opacity: 0;
}

.tw-char.is-typed {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .tw-char {
    opacity: 1;
  }
}

/* ---------- fade-up scroll animation ---------- */

.fade-up {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}

.fade-up.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ---------- projects ---------- */

.project-list {
  margin-top: 10vh;
  display: flex;
  flex-direction: column;
  gap: 90px;
}

.project-card {
  border-top: 1px solid var(--line);
  padding-top: 40px;
}

.project-card__eyebrow {
  font-size: 11px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin: 0 0 18px;
}

.project-card__title {
  font-weight: 300;
  font-size: clamp(24px, 3.4vw, 36px);
  margin: 0 0 12px;
}

.project-card__tagline {
  font-size: 16px;
  color: var(--accent);
  margin: 0 0 18px;
}

.project-card__copy {
  max-width: 58ch;
  font-size: 15px;
  color: #c3c1bd;
  font-weight: 300;
  margin: 0 0 24px;
}

.sticky-stack {
  margin-top: 12vh;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.sticky-panel {
  position: sticky;
  top: 90px;
  height: 34vh;
  border-radius: 4px;
  display: flex;
  align-items: flex-end;
  padding: 24px;
  font-size: 13px;
  letter-spacing: 0.2em;
  color: var(--fg-dim);
}

.sticky-panel--1 {
  background: linear-gradient(155deg, #1b1912, #0a0801 70%);
}
.sticky-panel--2 {
  background: linear-gradient(155deg, #191712, #0a0801 70%);
  top: 110px;
}
.sticky-panel--3 {
  background: linear-gradient(155deg, #17150f, #0a0801 70%);
  top: 130px;
}

/* ---------- company ---------- */

.company__wordmark {
  font-size: clamp(48px, 9vw, 120px);
  font-weight: 200;
  letter-spacing: 0.06em;
  margin: 0 0 8vh;
  color: rgba(217, 215, 212, 0.14);
}

/* ---------- footer ---------- */

.site-footer {
  padding: 12vh 6vw 6vh;
  border-top: 1px solid var(--line);
}

.site-footer__mark {
  font-size: 12px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

.site-footer__headline {
  display: block;
  font-size: clamp(30px, 6vw, 64px);
  font-weight: 300;
  margin: 20px 0 8vh;
}

.site-footer__nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  font-size: 14px;
  letter-spacing: 0.06em;
  margin-bottom: 6vh;
}

.site-footer__nav a:hover { color: var(--accent); }

.site-footer__social {
  display: flex;
  gap: 24px;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 5vh;
}

.site-footer__privacy {
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--fg-dim);
  display: inline-block;
  margin-bottom: 8vh;
}

.site-footer__addresses {
  display: flex;
  flex-wrap: wrap;
  gap: 60px;
  font-size: 12px;
  color: var(--fg-dim);
  margin-bottom: 8vh;
}

.site-footer__addresses p { margin: 2px 0; max-width: 32ch; }

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 24px;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--fg-dim);
  border-top: 1px solid var(--line);
  padding-top: 24px;
}

.back-to-top {
  margin-left: auto;
  text-transform: uppercase;
}

.back-to-top:hover { color: var(--accent); }

/* ---------- about: video left, text right ---------- */

section.about {
  max-width: none;
  margin: 0;
  padding: 14vh 4vw 14vh 3vw;
  display: grid;
  grid-template-columns: 40vw minmax(0, 1fr);
  column-gap: 4vw;
  align-items: center;
}

.about__video {
  display: block;
  width: 100%;
  height: auto; /* native ratio */
}

.about .section-copy {
  max-width: none;
}

@media (max-width: 900px) {
  section.about {
    grid-template-columns: 1fr;
  }
  /* stacked: text reads first, video below */
  .about__media {
    order: 2;
    margin-top: 6vh;
  }
}

/* ---------- kontakt: hero reprise, exactly one screen, page ends here ---------- */

section.kontakt {
  position: relative;
  max-width: none;
  margin: 0;
  padding: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--bg);
  background-image: url("assets/forest.avif");
  background-size: cover;
  background-position: center top;
  background-repeat: no-repeat;
}

/* same warm darkening pass as the hero, beneath the fog */
.kontakt__tint {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(41, 26, 13, 0.32) 0%,
    rgba(34, 21, 10, 0.22) 48%,
    rgba(41, 26, 13, 0.36) 100%
  );
}

.kontakt__fog {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  pointer-events: none;
  mix-blend-mode: screen;
  filter: brightness(1.3) contrast(1.1);
  opacity: 0.9;
}

/* the TOP edge sinks into the page black, blending out of the o mě section —
   the hero's bottom fade mirrored */
.kontakt__fade {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    var(--bg) 0%,
    rgba(10, 8, 1, 0.9) 7%,
    rgba(10, 8, 1, 0.55) 20%,
    transparent 42%
  );
}

/* contact line in the hero's face and black, a step smaller, and seated in the
   upper part of the screen rather than dead centre */
.kontakt__line {
  position: relative;
  z-index: 3;
  align-self: flex-start;
  margin: 22vh 0 0;
  display: flex;
  justify-content: center;
  gap: 8ch;
  white-space: nowrap;
  padding: 0 6vw;
  font-family: "Poppins", var(--font);
  font-weight: 300;
  font-size: clamp(15px, 2.2vw, 30px);
  letter-spacing: 0.02em;
  color: #000;
}

.kontakt__line a:hover {
  color: var(--accent);
}

@media (max-width: 640px) {
  /* hero-sized type cannot hold both on one narrow line — stack them centred */
  .kontakt__line {
    flex-direction: column;
    align-items: center;
    gap: 3vh;
  }
}

/* ---------- responsive ---------- */

@media (max-width: 640px) {
  section:not(.hero) { padding: 10vh 7vw; }
  .overlay-nav__addresses { flex-direction: column; gap: 20px; }
  .site-footer__addresses { flex-direction: column; gap: 20px; }
}
