/* =========================================================================
   DAYMON — Site styles
   ========================================================================= */

@font-face {
  font-family: 'Anton SC';
  src: url('fonts/AntonSC-Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Manrope';
  src: url('fonts/Manrope-Variable.ttf') format('truetype-variations'),
       url('fonts/Manrope-Variable.ttf') format('truetype');
  font-weight: 200 800;
  font-display: swap;
}

:root {
  --black: #000000;
  --white: #FFFFFF;
  --blue: #1B296D;
  --blue-bright: #243686;
  --blue-deep: #14204F;
  --blue-glow: rgba(27,41,109,0.45);
  --silver: #C0C0C0;
  --grey-900: #0A0A0A;
  --grey-800: #1A1A1A;
  --grey-700: #232323;
  --grey-600: #2E2E2E;
  --grey-500: #393939;
  --grey-400: #555;
  --grey-300: #7A7A7A;

  --fg-1: var(--white);
  --fg-2: var(--silver);
  --fg-3: var(--grey-300);
  --fg-4: var(--grey-400);

  --border-faint: rgba(192,192,192,0.08);
  --border-soft:  rgba(192,192,192,0.16);
  --border:       rgba(192,192,192,0.24);
  --border-strong:rgba(192,192,192,0.42);

  --font-display: 'Anton SC', 'Anton', 'Impact', sans-serif;
  --font-body:    'Manrope', system-ui, -apple-system, sans-serif;

  --section-y: clamp(64px, 11vh, 140px);
  --gutter: clamp(24px, 4vw, 64px);
  --max-width: 1440px;

  /* Page-wide 3-column rhythm (no visible rules). Left is the widest so
     DAYMON + the bio fit; the middle stays airy for the diamond. */
  --grid-3: minmax(0, 1.35fr) minmax(0, 0.75fr) minmax(0, 1fr);

  --radius: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  --ease-cinema: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 160ms;
  --dur: 280ms;
  --dur-slow: 520ms;
}

/* ---------------- RESET ---------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; scroll-behavior: smooth; overflow-x: hidden; overflow-x: clip; }
body {
  margin: 0;
  background: var(--black);
  color: var(--fg-1);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  font-weight: 400;
  overflow-x: hidden;
  overflow-x: clip; /* clip (not hidden) also blocks iOS sideways panning, without breaking position:sticky */
  position: relative;
}
/* Ambient page background — a soft blue glow at the bottom fading up into
   black, plus a faint pool of light low in the viewport. Fixed to the
   viewport (like lenis.dev's), so it stays put while content scrolls past. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(120% 60% at 50% 108%, rgba(31,46,110,0.22), transparent 62%),
    linear-gradient(to top, #10193f 0%, #080c22 22%, #04050f 45%, #000000 75%);
}
/* Starfield — small drifting particles, behind the diamond/content, in front
   of the base gradient. Rendered by stars.js on every page. */
.starfield-stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.starfield-stage canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
main { position: relative; z-index: 1; background: transparent; }
.site-footer { position: relative; z-index: 100; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
a { color: inherit; text-decoration: none; }

::selection { background: var(--blue); color: #fff; }

/* ---------------- CURSOR (diamond, native movement) ----------------
   A previous version of this replaced the OS cursor with a JS-positioned
   div (`ring.style.transform = translate(mouseX, mouseY)...` on every
   `mousemove`) — removed entirely because it felt laggy compared to the
   real OS cursor, especially with the Three.js diamond running
   continuously on the main thread. The user still wants the diamond
   SHAPE as the cursor, just not the custom movement — this uses the
   plain CSS `cursor: url(...)` property instead: the browser/OS still
   owns 100% of the actual pointer positioning (same hardware-composited
   path as the default arrow, unaffected by page JS/paint work), only
   the IMAGE is swapped. Two static states, both pre-baked (flip+rotate)
   since there's no JS re-transforming them per frame any more: the
   default diamond, and a larger, blue-edged one over interactive
   elements — a plain CSS hover swap, the same mechanism as `cursor:
   pointer`, not a custom animation. */
@media (hover: hover) and (pointer: fine) {
  html, body {
    /* outer <g transform="translate(40,0) scale(-1,1)"> mirrors the whole
       mark horizontally around the viewBox center (20,18, also the
       hotspot below — unaffected by the mirror since it sits ON the
       mirror axis) so the tip leans left, matching a normal OS arrow
       cursor instead of the original's rightward lean. Also sized down
       slightly (36x32 -> 32x28) per request. */
    cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 36' width='32' height='28'><g transform='translate(40,0) scale(-1,1)'><g transform='translate(20,18) rotate(12) translate(-20,-18) scale(1,-1) translate(0,-36)' fill='rgba(255,255,255,0.92)' stroke='rgba(0,0,0,0.28)' stroke-width='0.8' stroke-linejoin='round'><polygon points='2,17 12,5 12,17'/><polygon points='12,17 12,5 17,5 20,17'/><polygon points='20,17 17,5 23,5'/><polygon points='20,17 23,5 28,5 28,17'/><polygon points='28,17 28,5 38,17'/><polygon points='2,17 12,17 20,34'/><polygon points='12,17 28,17 20,34'/><polygon points='28,17 38,17 20,34'/></g></g></svg>") 16 14, auto;
  }
  a, button, [role="button"], input, textarea, select, summary {
    cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 36' width='44' height='40'><g transform='translate(40,0) scale(-1,1)'><g transform='translate(20,18) rotate(12) translate(-20,-18) scale(1,-1) translate(0,-36)' fill='rgba(255,255,255,0.96)' stroke='rgba(27,41,109,0.55)' stroke-width='0.7' stroke-linejoin='round'><polygon points='2,17 12,5 12,17'/><polygon points='12,17 12,5 17,5 20,17'/><polygon points='20,17 17,5 23,5'/><polygon points='20,17 23,5 28,5 28,17'/><polygon points='28,17 28,5 38,17'/><polygon points='2,17 12,17 20,34'/><polygon points='12,17 28,17 20,34'/><polygon points='28,17 38,17 20,34'/></g></g></svg>") 22 20, pointer;
  }
}

/* ---------------- SHELL ---------------- */
.wrap {
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ==========================================================================
   DAYMON HEADER — rebuilt from scratch (2026). Principle adapted from
   cosmos.studio, not copied: centered logo, two links each side, generous
   whitespace, minimal motion. Fully self-contained under the
   .daymon-header prefix — nothing here is shared with .hero or any other
   section, and nothing under .hero references any class defined here.
   Behavior lives in header.js (scroll-compact state + mobile menu).
   ========================================================================== */
.daymon-header {
  /* Single source of truth for "how tall is one row of header content"
     right now — read by both .daymon-header__logo-mark (height) and
     .daymon-header__link (line-height) below, so the logo and every link
     are ALWAYS numerically identical in height, at every point in the
     scroll transition, not just at the two end states. That's what keeps
     them on the same row while both are resizing together. */
  --header-row: 30px;
  --header-logo-w: 26px;
  --header-font: 15px;

  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  padding: 32px var(--gutter);
  transition: padding 500ms var(--ease-cinema);
}
.daymon-header:not(.is-scrolled) {
  /* A bit bigger at the top of the page — logo and links both grow
     together, then shrink back to the values above as soon as .is-scrolled
     is added. */
  --header-row: 37px;
  --header-logo-w: 32px;
  --header-font: 18px;
}
.daymon-header.is-scrolled {
  padding: 16px var(--gutter);
}

/* Background fill — a separate layer, not a property of .daymon-header
   itself, because it needs clip-path to trace the same stepped contour as
   the line below (flat at content-center height in the outer/logo zones,
   dropping to follow the dip under each nav-link cluster). clip-path on
   the header itself would also clip the actual logo/nav text/burger; this
   pseudo sits behind all of that (z-index:-1 within the header's own
   stacking context) so only the tint+blur gets shaped, never the content.
   Same left/right gutter inset as .daymon-header::after below, so the two
   share one coordinate space and their percentages line up exactly. */
.daymon-header::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: var(--gutter); right: var(--gutter);
  z-index: -1;
  background: transparent;
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 70% 50%, 67% 82%, 33% 82%, 30% 50%, 0% 50%);
  transition: background 500ms var(--ease-cinema),
              backdrop-filter 500ms var(--ease-cinema),
              clip-path 500ms var(--ease-cinema);
}
.daymon-header.is-scrolled::before {
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  /* The dip sits proportionally deeper in the scrolled state relative to
     the (shorter) header height here, same reasoning as .after below. */
  clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 70% 50%, 67% 88%, 33% 88%, 30% 50%, 0% 50%);
}

.daymon-header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: 24px;
  max-width: var(--max-width);
  margin: 0 auto;
  position: relative;
  /* Needed so the logo/burger stack ABOVE .daymon-header__mobile (z-index:99,
     also a child of this same .daymon-header stacking context): without an
     explicit z-index here, this being merely position:relative still only
     paints at the "z-index:auto" stacking level, which renders BEFORE any
     descendant with a positive z-index regardless of DOM order — so the
     mobile overlay silently covered the logo/close button the whole time,
     despite .daymon-header itself (z-index:100) outranking the overlay at
     the PAGE level. That page-level ranking never helped two siblings
     inside the same local stacking context. */
  z-index: 101;
  transition: transform 500ms var(--ease-cinema);
}
/* Content is mathematically centered in the padding box (verified: 32px
   top/bottom, identical logo/link height) — but the header's background is
   fully transparent in this state, so there's no visible edge to judge
   that box against. The only visible things are the content and the dip
   line, which extends well below it with nothing to balance it above —
   against that, content reads as sitting too close to the line. Nudging
   it up compensates without touching the line/background, which stay
   anchored to the header's actual (unperceived) box. Not applied once
   scrolled — that state already reads correctly. Desktop only (reset
   below in the mobile query): mobile hides the line entirely, so there's
   nothing to compensate for there. */
.daymon-header:not(.is-scrolled) .daymon-header__inner {
  transform: translateY(-9px);
}

/* Header/body separator — a single symmetric line, not a plain straight
   rule. It runs at the logo's own vertical center (top:50% of .daymon-header
   — symmetric top/bottom padding means the padded box's center is always
   the same point as the content row's center, so this works whether the
   header is in its tall or scrolled-compact state), stepping down just
   before each nav-link cluster and staying low continuously under both —
   no rise back to center in between — mirrored left and right. The dip is
   deep enough (~24px below center) to actually clear past the logo/link
   row, which is ~30px tall and so extends ~15px below its own center —
   without that margin the "low" stretch would sit right under/against the
   content instead of leaving it room. Anchored to the full header (inset
   only by the existing --gutter) rather than the max-width-capped
   .daymon-header__inner, so it reaches almost to the header's own edges.
   One SVG shared across every page via background-image, so no markup is
   duplicated per page. Hidden on mobile, where the two nav groups aren't
   rendered at all.

   preserveAspectRatio='none' stretches the viewBox non-uniformly to fill
   the (much wider than tall) box, which on its own would render the stroke
   thicker on the near-vertical dip transitions than on the flat runs.
   vector-effect='non-scaling-stroke' on the path decouples the stroke
   width from that stretch, so the line stays one even weight end to end
   while the geometry still spans the full width. */
.daymon-header::after {
  content: "";
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  top: 50%;
  height: 36px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 40' preserveAspectRatio='none'%3E%3Cpath d='M0,0 L300,0 L330,26 L670,26 L700,0 L1000,0' fill='none' stroke='rgba(192,192,192,0.55)' stroke-width='2' stroke-linejoin='round' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
}
/* Same SVG, just stretched taller here (background-size:100% 100% scales
   it uniformly) — the header's own padding is 2x bigger in this state
   (32px vs 16px), so there's more room below the content, and the dip
   should use more of it rather than clearing by a fixed, now-proportionally
   smaller margin. */
.daymon-header:not(.is-scrolled)::after {
  height: 50px;
}

/* Scroll-fill — the same line, traced again in blue and revealed
   left-to-right as the page scrolls (--scroll-pct, written by header.js).
   A real element, not a third pseudo-element: .daymon-header already uses
   both ::before (background shape) and ::after (the grey line), and a
   selector can only have one of each. Positioned identically to ::after
   (same inset, same height per scroll state) so the two stay pixel-
   aligned; clip-path reveals only the left --scroll-pct% of it, so the
   blue appears to "fill in" over the grey rather than just fading in.
   z-index:1 keeps it painting above the grey ::after line. */
.daymon-header__line-fill {
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  top: 50%;
  height: 36px;
  z-index: 1;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  clip-path: inset(0% calc(100% - var(--scroll-pct, 0%)) 0% 0%);
  transition: clip-path 120ms linear;
  filter: drop-shadow(0 0 4px rgba(36,54,134,0.55));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 40' preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient id='lf' x1='0' y1='0' x2='1' y2='0'%3E%3Cstop offset='0%25' stop-color='%23243686'/%3E%3Cstop offset='100%25' stop-color='%231b296d'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M0,0 L300,0 L330,26 L670,26 L700,0 L1000,0' fill='none' stroke='url(%23lf)' stroke-width='2.5' stroke-linejoin='round' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
}
.daymon-header:not(.is-scrolled) .daymon-header__line-fill {
  height: 50px;
}

/* Progress glow — ported from ciaoenergy.com's scroll indicator and
   recoloured to the DAYMON blue. Two stacked layers on top of the blue
   __line-fill:
     ::before — a brighter, blurred re-trace of the same line geometry,
       clipped to the filled length and masked to a left→right ramp so
       only the stretch approaching the current position actually glows
       (their __mid + __halo layers, combined). It re-traces the real
       path, so it dips with the line for free.
     ::after — the bright elongated "hotspot" that marks exactly where you
       are (their __hotspot), dropped into the line's dip via
       --scroll-line-dip (0..1, from header.js) × 26/40 of the box height.
   opacity on the wrapper is the master dimmer. Own element because
   __line-fill's own clip-path would otherwise clip a child glow. Hidden
   on mobile with the rest of the line. */
.daymon-header__line-glow {
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  top: 50%;
  height: 36px;
  z-index: 2;
  pointer-events: none;
  opacity: 0.85;
}
.daymon-header:not(.is-scrolled) .daymon-header__line-glow { height: 50px; }

.daymon-header__line-glow::before {
  content: "";
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 40' preserveAspectRatio='none'%3E%3Cpath d='M0,0 L300,0 L330,26 L670,26 L700,0 L1000,0' fill='none' stroke='rgba(150,178,255,0.9)' stroke-width='4' stroke-linejoin='round' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
  clip-path: inset(0 calc(100% - var(--scroll-pct, 0%)) 0 0);
  /* Ramp is measured across the filled length (scaled by --scroll-pct),
     so the tail is always faint at the page top and full at the head,
     whatever the progress — same behaviour as ciao's per-bar gradients. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, rgba(0,0,0,0.15) calc(var(--scroll-pct, 0%) * 0.5), #000 var(--scroll-pct, 0%));
          mask-image: linear-gradient(to right, transparent 0%, rgba(0,0,0,0.15) calc(var(--scroll-pct, 0%) * 0.5), #000 var(--scroll-pct, 0%));
  filter: blur(3px);
  transition: clip-path 120ms linear;
}

.daymon-header__line-glow::after {
  content: "";
  position: absolute;
  /* Flat line sits at this box's top edge (SVG path y=0); the dip factor
     pushes the hotspot down to meet the line where it drops. 36px box. */
  top: calc(var(--scroll-line-dip, 0) * 0.65 * 36px);
  left: var(--scroll-pct, 0%);
  width: 30px;
  height: 7px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
              rgba(198,214,255,0.95) 0%,
              rgba(74,101,210,0.55) 35%,
              rgba(74,101,210,0) 70%);
  filter: blur(2px);
  transition: left 120ms linear, top 120ms linear;
}
.daymon-header:not(.is-scrolled) .daymon-header__line-glow::after {
  /* Taller line box in the top-of-page state — same 26/40 ratio, 50px. */
  top: calc(var(--scroll-line-dip, 0) * 0.65 * 50px);
}

/* Two link groups, one each side of the logo. Both get the same min-width
   so the two outer grid tracks stay equal width regardless of the actual
   text length on each side — that's what keeps the logo column exactly
   centered in the viewport rather than merely "between the links". */
.daymon-header__nav {
  display: flex;
  align-items: center;
  gap: 44px;
}
/* justify-content here is what actually packs the links against the
   logo-facing edge of each group's box. Without it (default flex-start)
   the left group's min-width overflow lands on the wrong side — right next
   to the logo — pushing "Projekte" further from the logo than "Über" was
   on the other side. flex-end/flex-start below make both hug the logo
   equally regardless of how much min-width overflow either side has. */
.daymon-header__nav--left  { grid-column: 1; justify-self: end;   min-width: 190px; justify-content: flex-end; }
.daymon-header__nav--right { grid-column: 3; justify-self: start; min-width: 190px; justify-content: flex-start; }

.daymon-header__link {
  font-size: var(--header-font);
  font-weight: 500;
  /* line-height (not padding) sets the box height, matched to
     .daymon-header__logo-mark's height via the same --header-row custom
     property, so the logo and every link sit on the same row exactly —
     same height, not just same center — through the whole resize. */
  line-height: var(--header-row);
  color: var(--fg-2);
  transition: color 220ms var(--ease-cinema),
              font-size 500ms var(--ease-cinema),
              line-height 500ms var(--ease-cinema);
}
.daymon-header__link:hover,
.daymon-header__link:focus-visible {
  color: #fff;
}
.daymon-header__link.is-active {
  color: #fff;
  font-weight: 600;
}

.daymon-header__logo {
  grid-column: 2;
  justify-self: center;
  display: inline-flex;
  align-items: center;
}
.daymon-header__logo-mark {
  width: var(--header-logo-w);
  height: var(--header-row);
  color: #fff;
  display: block;
  transition: transform 300ms var(--ease-cinema), filter 300ms var(--ease-cinema),
              width 500ms var(--ease-cinema), height 500ms var(--ease-cinema);
}
.daymon-header__logo:hover .daymon-header__logo-mark {
  transform: translateY(-1px);
  filter: drop-shadow(0 0 12px rgba(27,41,109,0.45));
}

.daymon-header__burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 8px;
}
.daymon-header__burger-line {
  width: 100%;
  height: 1px;
  background: #fff;
  transition: transform 260ms var(--ease-cinema), opacity 260ms var(--ease-cinema);
}
.daymon-header__burger[aria-expanded="true"] .daymon-header__burger-line:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.daymon-header__burger[aria-expanded="true"] .daymon-header__burger-line:nth-child(2) { opacity: 0; }
.daymon-header__burger[aria-expanded="true"] .daymon-header__burger-line:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* Mobile overlay menu — plain fade, toggled by header.js via the [hidden]
   attribute (removes it from layout + the accessibility tree while closed)
   plus the .is-open class (drives the opacity transition while open). */
.daymon-header__mobile {
  position: fixed;
  inset: 0;
  z-index: 99;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,0.96);
  opacity: 0;
  transition: opacity 320ms var(--ease-cinema);
}
.daymon-header__mobile[hidden] { display: none; }
.daymon-header__mobile.is-open { opacity: 1; }
.daymon-header__mobile-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
}
.daymon-header__mobile-link {
  font-size: 20px;
  font-weight: 500;
  color: var(--fg-2);
  transition: color 220ms var(--ease-cinema);
}
.daymon-header__mobile-link:hover,
.daymon-header__mobile-link:focus-visible,
.daymon-header__mobile-link.is-active {
  color: #fff;
}
.daymon-header__mobile-link.is-active { font-weight: 600; }

@media (max-width: 880px) {
  .daymon-header__inner { display: flex; justify-content: space-between; }
  .daymon-header__nav { display: none; }
  .daymon-header__logo { grid-column: auto; justify-self: auto; }
  .daymon-header__burger { display: inline-flex; }
  .daymon-header::after { display: none; }
  .daymon-header__line-fill { display: none; }
  .daymon-header__line-glow { display: none; }
  .daymon-header::before, .daymon-header.is-scrolled::before { clip-path: none; }
  .daymon-header:not(.is-scrolled) .daymon-header__inner { transform: none; }

  /* While the mobile nav overlay is open (header.js toggles .nav-open on
     #daymonHeader), keep the white logo visible and centered — the header
     itself sits above the overlay (z-index 100 vs 99) — and give the
     burger a round bordered "X" treatment matching .pjs-ov__close, instead
     of the bare crossed lines it morphs into on its own. */
  .daymon-header.nav-open .daymon-header__inner {
    grid-template-columns: 1fr auto 1fr;
    display: grid;
  }
  .daymon-header.nav-open .daymon-header__logo {
    grid-column: 2;
    justify-self: center;
  }
  .daymon-header.nav-open .daymon-header__burger {
    grid-column: 3;
    justify-self: end;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: rgba(10, 10, 16, 0.5);
    transition: border-color var(--dur) var(--ease-cinema), background var(--dur) var(--ease-cinema);
  }
  .daymon-header.nav-open .daymon-header__burger:hover { border-color: var(--border-strong); }
}

/* ---------------- BUTTONS ---------------- */
.btn {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 18px 32px;
  border-radius: var(--radius);
  transition: background var(--dur) var(--ease-cinema), border-color var(--dur) var(--ease-cinema),
              color var(--dur) var(--ease-cinema), box-shadow var(--dur) var(--ease-cinema),
              transform var(--dur) var(--ease-cinema);
  white-space: nowrap;
}
.btn .arrow {
  width: 16px; height: 16px;
  transition: transform var(--dur) var(--ease-cinema);
}
.btn:hover .arrow { transform: translateX(4px); }
.btn--primary {
  background: transparent;
  color: #fff;
  border: 1px solid var(--border-strong);
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.10),
    0 8px 28px rgba(255,255,255,0.12),
    0 0 28px rgba(255,255,255,0.08);
}
.btn--primary:hover {
  background: rgba(255,255,255,0.08);
  border-color: #fff;
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.45),
    0 12px 40px rgba(255,255,255,0.22),
    0 0 48px rgba(255,255,255,0.18);
  transform: translateY(-1px);
}
.btn--primary:active { background: rgba(255,255,255,0.12); transform: translateY(1px); }
.btn--secondary {
  background: var(--blue);
  color: #fff;
  border: 1px solid transparent;
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.10),
    0 8px 28px rgba(255,255,255,0.12),
    0 0 28px rgba(255,255,255,0.08);
}
.btn--secondary:hover {
  background: var(--blue-bright);
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.45),
    0 12px 40px rgba(255,255,255,0.22),
    0 0 48px rgba(255,255,255,0.18),
    0 8px 32px rgba(27,41,109,0.45);
  transform: translateY(-1px);
}
.btn--secondary:active { background: var(--blue-deep); transform: translateY(1px); }

/* glow starts off and snaps on like a light switch, a beat after the button
   appears — same technique piloted on the hero CTAs (see .hero__ctas
   .btn--primary/--secondary below), generalized to every .btn--primary/
   --secondary on the site via the existing .reveal/.in scroll-reveal
   system: while an ancestor .reveal hasn't gotten its .in class yet, the
   glow is fully transparent; once .in lands, it falls back to the button's
   own resting box-shadow, animating in via the slower back-eased curve
   below for that "settle after overshoot" bloom. A button rendered outside
   any .reveal wrapper (e.g. straight into an already-open overlay) has no
   reveal moment to time against, so it just keeps its glow on by default —
   nothing regresses there. */
.btn--primary,
.btn--secondary {
  transition-property: transform, background, border-color, color, box-shadow;
  transition-duration: var(--dur), var(--dur), var(--dur), var(--dur), 460ms;
  transition-timing-function:
    var(--ease-cinema), var(--ease-cinema), var(--ease-cinema), var(--ease-cinema),
    cubic-bezier(0.3, 1.6, 0.4, 1);
}
.reveal:not(.in) .btn--primary,
.reveal:not(.in) .btn--secondary {
  box-shadow: 0 0 0 1px transparent, 0 0 0 transparent, 0 0 0 transparent;
}
.btn--ghost {
  padding: 6px 0;
  border-radius: 0;
  color: #fff;
}
.btn--ghost .lbl {
  background-image: linear-gradient(#fff,#fff);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  padding-bottom: 3px;
  transition: background-size var(--dur-slow) var(--ease-cinema);
}
.btn--ghost:hover .lbl { background-size: 100% 1px; }

/* ---------------- HERO ---------------- */
.hero {
  position: relative;
  min-height: 100svh;
  /* Three vertical columns: left = DAYMON + bio + buttons, middle = empty
     (the fixed diamond floats there), right = the process path. Rows:
     DAYMON, then the intro block; the path spans both. align-content:end
     keeps the whole thing in the bottom-left. */
  display: grid;
  grid-template-columns: var(--grid-3);
  grid-template-rows: auto auto;
  align-content: end;
  justify-items: start;
  column-gap: clamp(24px, 4vw, 72px);
  row-gap: clamp(0px, 0.2vw, 4px);
  padding: 120px var(--gutter) var(--section-y);
  /* Cap + centre the content box exactly like a .section__inner, so the
     left column lines up with the section titles' left edge (and shares
     the same right boundary). +2*gutter because .hero adds its own gutter
     padding inside this cap, whereas .section__inner has none. */
  max-width: calc(var(--max-width) + 2 * var(--gutter));
  margin-inline: auto;
  overflow: hidden;
  z-index: 1;
}
.hero__title {
  grid-column: 1 / -1;   /* free to bleed past its column, behind the diamond */
  grid-row: 1;
  justify-self: start;
}
.hero__intro {
  grid-column: 1 / 2;
  grid-row: 2;
  position: relative;
  z-index: 2;
}
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}
.hero__bg::before {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(70% 80% at 50% 45%, rgba(36,54,134,0.45), transparent 65%),
    radial-gradient(45% 55% at 12% 90%, rgba(27,41,109,0.30), transparent 70%),
    radial-gradient(45% 55% at 88% 90%, rgba(20,32,79,0.32), transparent 70%);
}
.hero__bg::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, transparent 0%, transparent 50%, rgba(0,0,0,0.55) 100%),
    radial-gradient(85% 70% at 50% 50%, transparent 35%, rgba(0,0,0,0.55) 100%);
}

/* ---------------- 3D diamond canvas (Three.js) — fixed bg, follows scroll ---------------- */
.diamond-stage {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: var(--ds-opacity, 0.9);
  transition: opacity 600ms var(--ease-cinema);
  will-change: opacity;
}
.diamond-stage canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  filter:
    drop-shadow(0 50px 90px rgba(15, 25, 75, 0.45))
    drop-shadow(0 0 120px rgba(36, 54, 134, 0.30));
}

.hero__inner {
  position: relative;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 24px;
}
.hero__eyebrow {
  display: inline-flex; align-items: center; gap: 14px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--fg-2);
}
.hero__eyebrow .bar { width: 28px; height: 1px; background: var(--fg-2); }
.hero__title {
  font-family: var(--font-display);
  /* Same size as every .section__title on the site. */
  font-size: clamp(48px, 6.5vw, 104px);
  line-height: 0.9;
  letter-spacing: 0;
  text-transform: uppercase;
  margin: 0;
  color: #fff;
  /* Kept deliberately tight and low-alpha — this is inherited by every
     .char__inner span, and the earlier, wider/softer glow (0 0 32px) was
     washing across the whole glyph rather than just separating it from
     the busy background, reading as grey/metallic instead of white. */
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  /* Behind the diamond: .hero is a stacking context (position:relative;
     z-index:1) holding the fixed .diamond-stage (z-index:0), so a negative
     z-index drops DAYMON below the diamond. .hero__intro and .hero__path
     (z-index:2) stay in front of it. */
  position: relative;
  z-index: -1;
}
.hero__subtitle {
  font-family: var(--font-display);
  font-size: clamp(18px, 2vw, 28px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #fff;
  text-shadow:
    0 2px 12px rgba(0, 0, 0, 0.65),
    0 0 32px rgba(0, 0, 0, 0.55);
  margin: 0;
}
.hero__lead {
  max-width: 52ch;
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.7;
  font-weight: 300;
  color: var(--fg-2);
  margin: 0;
}
.hero__ctas {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: stretch;
  margin-top: 8px;
}
.hero__ctas .btn {
  flex: 0 0 auto;
  min-width: 240px;
  justify-content: center;
}

/* ---------------- SECTION CHROME ---------------- */
.section {
  padding: var(--section-y) var(--gutter);
  position: relative;
  z-index: 1;
}
.section::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(60% 40% at var(--bx, 50%) var(--by, 0%), rgba(27,41,109,0.10), transparent 70%);
  z-index: 0;
}
/* #ueber is the only .section--alt — used to have its own half/quarter
   padding here, dropped 2026-09-30 so every section shares one uniform
   --section-y rhythm top and bottom (.section--alt now only changes the
   background gradient below, not the spacing). */
.section--alt::before {
  background:
    radial-gradient(50% 45% at 100% 50%, rgba(36,54,134,0.14), transparent 70%),
    radial-gradient(45% 40% at 0% 100%, rgba(20,32,79,0.10), transparent 70%);
}
.section > * { position: relative; z-index: 1; }
.section__head {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 48px;
  align-items: end;
  margin-bottom: clamp(48px, 6vw, 96px);
  max-width: var(--max-width);
  margin-left: auto; margin-right: auto;
}
/* For a .section__head with no .section__intro (just eyebrow + title) —
   drops the 1fr/2fr grid so the left column doesn't leave a dead gap. */
.section__head--solo { display: block; }
.section__inner { max-width: var(--max-width); margin: 0 auto; }
.section__title {
  font-family: var(--font-display);
  font-size: clamp(48px, 6.5vw, 104px);
  line-height: 0.95;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
  color: #fff;
}
.section__intro {
  font-size: clamp(15px, 1.2vw, 18px);
  line-height: 1.7;
  color: var(--fg-2);
  font-weight: 300;
  max-width: 50ch;
}
.eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-2);
  margin-bottom: 20px;
}
.eyebrow .bar { display: none; }   /* dropped site-wide — no dash before eyebrow labels */

/* the whole #ueber content sits in one compact card that lifts + glows
   gently on hover (and sits a touch raised even at rest). */
.about-card {
  padding: clamp(22px, 3vw, 48px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: rgba(192, 192, 192, 0.035);
  box-shadow:
    0 20px 54px -22px rgba(0, 0, 0, 0.55),
    0 0 46px rgba(27, 41, 109, 0.16);
  transition:
    transform var(--dur-slow) var(--ease-cinema),
    opacity var(--dur-slow) var(--ease-cinema),
    border-color var(--dur-slow) var(--ease-cinema),
    background var(--dur-slow) var(--ease-cinema),
    box-shadow var(--dur-slow) var(--ease-cinema);
}
@media (hover: hover) {
  .about-card:hover {
    transform: translateY(-6px);
    border-color: var(--border-strong);
    background: rgba(192, 192, 192, 0.055);
    box-shadow:
      0 36px 96px -28px rgba(0, 0, 0, 0.62),
      0 0 0 1px rgba(36, 54, 134, 0.42),
      0 0 96px rgba(27, 41, 109, 0.36);
  }
}
/* #ueber is treated as ONE card (not text + portrait revealing separately)
   — a fifth, distinct scroll device: a straight slide-in from the right,
   no blur, no stagger. A solid object entering the frame, rather than
   content resolving into focus like everywhere else. */
.about-card.reveal { opacity: 0; transform: translateX(56px); }
.about-card.reveal.in { opacity: 1; transform: translateX(0); }
@media (prefers-reduced-motion: reduce) {
  .about-card { transition: none; }
  .about-card:hover { transform: none; }
  .about-card.reveal { opacity: 1; transform: none; }
}

/* ---------------- ABOUT ----------------
   Left: eyebrow + title + copy (Fliesstext). Right: the portrait
   (assets/damian-portrait.jpg, 1000×667) with the quote laid over it.
   The portrait stretches to the text height -> compact. */
.about {
  display: grid;
  grid-template-columns: minmax(0, 1.28fr) minmax(0, 0.72fr);
  gap: clamp(16px, 2.2vw, 32px);
  align-items: stretch;
}
.about__text {
  display: flex;
  flex-direction: column;
}
.about__text .section__title { margin: 0 0 clamp(18px, 2.2vw, 30px); }
.about__copy p {
  color: var(--fg-2);
  font-size: 15px;
  line-height: 1.6;
  font-weight: 300;
  max-width: 80ch;
  margin: 0 0 11px;
}
.about__copy p:last-child { margin-bottom: 0; }

.about__portrait {
  position: relative;
  height: 100%;
  min-height: 340px;
  margin: 0;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-soft);
  background: var(--grey-800);
  overflow: hidden;
}
.about__portrait img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 18%;
}
/* the quote, laid over the lower part of the photo — big serif quote marks,
   Anton statement type, one phrase pulled out in glowing white, attribution */
.about__quote {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: clamp(48px, 6vw, 78px) clamp(20px, 2.6vw, 36px) clamp(18px, 2.2vw, 30px);
  background: linear-gradient(to top,
    rgba(4, 6, 14, 0.9) 0%,
    rgba(4, 6, 14, 0.58) 52%,
    transparent 100%);
}
.about__quote::before,
.about__quote::after {
  position: absolute;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(52px, 6vw, 84px);   /* both marks same size */
  line-height: 1;
  color: #fff;
  text-shadow:
    0 0 14px rgba(255, 255, 255, 0.45),
    0 0 34px rgba(255, 255, 255, 0.28),
    0 0 52px rgba(36, 54, 134, 0.5);
  pointer-events: none;
}
.about__quote::before {
  content: "\201C";
  left: clamp(12px, 1.8vw, 26px);
  top: clamp(4px, 1vw, 12px);
  opacity: 0.92;
}
.about__quote::after {
  content: "\201D";
  right: clamp(12px, 1.7vw, 24px);
  bottom: clamp(4px, 1vw, 14px);
  opacity: 0.92;
}
.about__quote-text {
  position: relative;
  margin: 0;
  padding-right: clamp(40px, 5vw, 66px);   /* leave the bottom-right corner for the closing mark */
  font-family: var(--font-display);
  font-size: clamp(18px, 1.9vw, 27px);
  line-height: 1.14;
  letter-spacing: 0.015em;
  text-transform: uppercase;
  text-wrap: balance;
  overflow-wrap: break-word;
  color: #fff;
}
.about__quote-text .hl {
  color: #fff;
  /* dark-blue halo (no black glow) — sets the phrase off from the rest */
  text-shadow:
    0 0 12px rgba(20, 32, 79, 0.95),
    0 0 26px rgba(20, 32, 79, 0.6);
}
.about__quote-by {
  display: block;
  margin-top: clamp(10px, 1.3vw, 15px);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* ---------------- PROJECTS GRID ---------------- */
.projects {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(40px, 5vw, 80px) 32px;
}
.project {
  display: flex;
  flex-direction: column;
  gap: 20px;
  text-decoration: none;
  color: inherit;
  transition: transform var(--dur-slow) var(--ease-cinema);
}
.project:hover { transform: translateY(-6px); }
.project--lg { grid-column: span 8; }
.project--md { grid-column: span 6; }
.project--sm { grid-column: span 4; }
.project--off { grid-column: 8 / span 5; }

.project__thumb {
  width: 100%;
  aspect-ratio: 16/10;
  border-radius: var(--radius-md);
  position: relative;
  overflow: hidden;
  background: var(--grey-700);
  border: 1px solid var(--border-soft);
  isolation: isolate;
}
.project__thumb::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--thumb-grad, linear-gradient(135deg, #1B296D 0%, #000 60%, #232323 100%));
  transition: transform 1200ms var(--ease-cinema);
  z-index: 0;
}
.project__thumb::after {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(60% 40% at 50% 100%, rgba(0,0,0,0.55), transparent 60%),
    radial-gradient(40% 30% at 30% 30%, rgba(255,255,255,0.06), transparent 60%);
  z-index: 1;
  transition: opacity var(--dur) var(--ease-cinema);
}
.project__thumb .num {
  position: absolute;
  top: 18px; right: 22px;
  font-family: 'Manrope', monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  color: rgba(255,255,255,0.5);
  z-index: 2;
}
.project__thumb .glow {
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: 0 0 0 1px transparent, 0 0 60px transparent;
  transition: box-shadow var(--dur-slow) var(--ease-cinema);
  z-index: 3;
}
.project:hover .project__thumb::before { transform: scale(1.04); }
.project:hover .project__thumb::after  { opacity: 0.7; }
.project:hover .project__thumb .glow {
  box-shadow: 0 0 0 1px rgba(27,41,109,0.6), 0 0 80px rgba(27,41,109,0.35);
}

.project__meta { display: flex; flex-direction: column; gap: 8px; }
.project__row {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.project__title {
  font-family: var(--font-display);
  font-size: clamp(28px, 3vw, 44px);
  line-height: 1.05;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
  color: #fff;
  transition: color var(--dur) var(--ease-cinema);
}
.project__desc {
  font-size: 14px;
  color: var(--fg-2);
  line-height: 1.6;
  font-weight: 300;
  max-width: 48ch;
  margin: 0;
}

/* All-projects (grid layout for /projekte page) */
.projects--all { gap: 64px 32px; }
.projects--all .project { grid-column: span 6; }
@media (max-width: 880px) {
  .projects--all .project { grid-column: span 12; }
}

/* ---------------- SERVICES ---------------- */
.services {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  border-top: 1px solid var(--border-faint);
  border-left: 1px solid var(--border-faint);
}
.service {
  padding: clamp(32px, 4vw, 56px) clamp(24px, 3vw, 40px);
  border-right: 1px solid var(--border-faint);
  border-bottom: 1px solid var(--border-faint);
  display: flex; flex-direction: column; gap: 18px;
  position: relative;
  transition: background var(--dur-slow) var(--ease-cinema);
}
.service:hover { background: rgba(27,41,109,0.06); }
.service:hover .service__num { color: var(--blue); }
.service__num {
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 0.24em;
  color: var(--fg-3);
  transition: color var(--dur) var(--ease-cinema);
}
.service__title {
  font-family: var(--font-display);
  font-size: clamp(28px, 3vw, 44px);
  line-height: 1.1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
  color: #fff;
}
.service__desc {
  font-size: 15px;
  line-height: 1.7;
  color: var(--fg-2);
  font-weight: 300;
  max-width: 44ch;
  margin: 0;
}
.service__list {
  list-style: none;
  padding: 0; margin: 8px 0 0;
  display: flex; flex-direction: column; gap: 6px;
}
.service__list li {
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-3);
  position: relative;
  padding-left: 18px;
}
.service__list li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.7em;
  width: 8px; height: 1px;
  background: var(--fg-3);
}

@media (max-width: 720px) {
  .services { grid-template-columns: 1fr; }
}

/* ================================================================
   WAS ICH ANBIETE (.wia) — interactive service index.
   Left: editorial list. Right: crossfading preview stage (each
   .wia__preview is absolutely placed into the right half of
   .wia__body on desktop; on mobile it flows inline under its item).
   All classes are wia-* — nothing here touches other sections.
   ================================================================ */
.wia { padding: 0; }           /* the sticky wrapper owns the spacing */

/* Tall scroll track: one screen to read + a scroll budget per service, so
   scrolling steps through all six automatically. Plain native scroll — no
   pinning/hijack; the layout just sticks while you pass through. */
.wia__scroll {
  position: relative;
  height: 340svh;              /* ≈ 40svh of scroll per service — tune this
                                 to speed up / slow down the step-through */
}
.wia__sticky {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* now numerically identical to --section-y (see root tokens) — was its
     own hardcoded vh-based clamp before every other section's gap was
     brought down to match this one, so just reuse the shared token now */
  padding: var(--section-y) var(--gutter);
}
.wia__inner {
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  /* head -> body gap, unified 2026-09-30 to the same value every other
     section uses for its own title -> content gap (see
     [[spacing-unification]]) */
  gap: clamp(18px, 2.2vw, 30px);
}

/* head — eyebrow, then the big title */
.wia__head {
  position: relative;
  z-index: 1;
}
.wia__title {
  margin: 0;   /* size inherits from .section__title — same as every other section */
}
.wia__eyebrow {
  display: block;
  margin-bottom: clamp(8px, 1.4vw, 16px);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-2);
}

/* body */
.wia__body {
  position: relative;
  z-index: 1;
  min-height: clamp(360px, 40vw, 560px);
}
.wia__list {
  list-style: none;
  margin: 0;
  padding: 0;
  width: min(48%, 640px);
}

/* one service row — the divider draws left→right on reveal
   (same background-size technique as .btn--ghost .lbl) */
.wia__item {
  background-image: linear-gradient(var(--border-soft), var(--border-soft));
  background-repeat: no-repeat;
  background-position: bottom left;
  background-size: 0% 1px;
  transition: background-size 1000ms var(--ease-cinema);
}
.wia.is-inview .wia__item { background-size: 100% 1px; }
.wia__item:nth-child(1) { transition-delay: 120ms; }
.wia__item:nth-child(2) { transition-delay: 205ms; }
.wia__item:nth-child(3) { transition-delay: 290ms; }
.wia__item:nth-child(4) { transition-delay: 375ms; }
.wia__item:nth-child(5) { transition-delay: 460ms; }
.wia__item:nth-child(6) { transition-delay: 545ms; }

.wia__trigger {
  position: relative;
  width: 100%;
  display: flex;
  align-items: baseline;
  gap: clamp(12px, 1.4vw, 22px);
  padding: clamp(10px, 1.15vw, 15px) 0;
  text-align: left;
  color: inherit;
}
.wia__trigger:focus-visible {
  outline: 1px solid var(--border-strong);
  outline-offset: 4px;
}
/* subtle DAYMON-blue accent on the active row */
.wia__trigger::before {
  content: "";
  position: absolute;
  left: calc(-1 * clamp(12px, 1.4vw, 22px));
  top: 50%;
  width: 2px;
  height: 0;
  background: var(--blue-bright);
  transform: translateY(-50%);
  transition: height 480ms var(--ease-cinema);
}
.wia__item.is-active .wia__trigger::before { height: 56%; }

.wia__num {
  flex: none;
  font-size: clamp(11px, 0.9vw, 13px);
  letter-spacing: 0.2em;
  color: var(--fg-4);
  transform: translateY(-0.15em);
  transition: color 320ms var(--ease-cinema);
}
.wia__item.is-active .wia__num { color: var(--fg-1); }

.wia__name {
  font-family: var(--font-display);
  font-size: clamp(17px, 2vw, 26px);       /* collapsed rows stay compact */
  line-height: 1.04;
  letter-spacing: 0.015em;
  text-transform: uppercase;
  color: var(--fg-3);
  text-wrap: balance;
  transition: color 320ms var(--ease-cinema), font-size 360ms var(--ease-cinema);
}
.wia__item.is-active .wia__name {
  color: var(--fg-1);
  font-size: clamp(20px, 2.5vw, 34px);     /* active row a step larger */
}

/* expanding detail — fr-row animation, no fixed height, no layout jank */
.wia__detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 560ms var(--ease-cinema);
}
.wia__detail-inner {
  overflow: hidden;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 380ms var(--ease-cinema), transform 380ms var(--ease-cinema);
}
.wia__item.is-active .wia__detail { grid-template-rows: 1fr; }
.wia__item.is-active .wia__detail-inner {
  opacity: 1;
  transform: none;
  transition-delay: 110ms;
}
.wia__desc {
  margin: 0 0 14px;
  padding-left: calc(clamp(11px, 0.9vw, 13px) + clamp(14px, 1.6vw, 26px));
  max-width: 44ch;
  font-size: clamp(14px, 1vw, 15.5px);
  font-weight: 300;
  line-height: 1.65;
  color: var(--fg-2);
}
.wia__tags {
  margin: 0 0 clamp(18px, 2vw, 30px);
  padding-left: calc(clamp(11px, 0.9vw, 13px) + clamp(14px, 1.6vw, 26px));
  font-size: clamp(10.5px, 0.8vw, 12px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-3);
}
/* spaces inside the generated content give wrap opportunities between tags */
.wia__tags span:not(:last-child)::after {
  content: " · ";
  color: var(--fg-4);
}

/* ---- preview stage (right half of .wia__body on desktop) ---- */
.wia__preview {
  position: absolute;
  top: 0;
  right: 0;
  width: min(50%, 660px);
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transform: scale(1.02);
  filter: blur(6px);
  transition:
    opacity 640ms var(--ease-cinema),
    transform 720ms var(--ease-cinema),
    filter 640ms var(--ease-cinema),
    visibility 0s linear 720ms;
}
.wia__item.is-active .wia__preview {
  opacity: 1;
  visibility: visible;
  transform: none;
  filter: blur(0);
  transition:
    opacity 640ms var(--ease-cinema),
    transform 720ms var(--ease-cinema),
    filter 640ms var(--ease-cinema),
    visibility 0s;
}

.wia__media {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  overflow: hidden;
  background:
    radial-gradient(120% 120% at 12% 8%, rgba(36,54,134,0.16), transparent 55%),
    linear-gradient(160deg, #101019 0%, #08080c 58%, #0c0c14 100%);
}
.wia__media > img,
.wia__media > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The YouTube-embed tile (01, Video): the API replaces #wia-yt-01 with its
   own <div><iframe>…</iframe></div> wrapper — target both so it fills the
   16:9 box exactly like the img/video previews. Decorative + autoplaying,
   so no pointer interaction with YouTube's own UI underneath. */
.wia__media--video > div,
.wia__media--video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.wia__media--video iframe { pointer-events: none; }
/* Below the frame now, not overlaid on the image — normal flow (adds to
   .wia__preview's own height instead of floating over the media), left-
   aligned. Used to be position:absolute/bottom-left OVERLAID on every
   tile; moved out for all 6 uniformly per the user's request ("alle
   kleinen Titel unten links, außerhalb des Rahmens") — this started as a
   one-off fix just for the grafik (04) tile, whose frame is narrower than
   .wia__preview so the label's old overlay anchor no longer lined up
   under it, then got generalized here once the user liked the look. */
.wia__pv-label {
  display: block;
  margin-top: clamp(10px, 1.4vw, 16px);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--fg-3);
  opacity: 0.8;
}
/* grafik (04) only: its frame (.wia-book__stage) is narrower than
   .wia__preview and centered within it (see .wia__media--grafik), so the
   label's default left:0 — correct for the other 5 tiles, whose frames
   ARE flush with .wia__preview's own edge — lines up with .wia__preview's
   edge instead, closer to the book's prev-arrow than to the frame itself.
   Shifts it right by the frame's own horizontal inset: half of
   (1 − frame-width/preview-width) — with preview width W, frame width is
   (W × 9/16) × 1269/900 = W × 0.793125, so each side's gap is
   (1 − 0.793125) / 2 ≈ 10.34% of W. */
.wia__media--grafik ~ .wia__pv-label { margin-left: 10.34%; }

/* "Ganzes Projekt ansehen" — a first-draft CTA (Branding + Content tiles
   only, so far), outside .wia__media rather than overlaid on it, anchored
   bottom-right below the frame. Positioned absolute (top:100% of
   .wia__preview + a margin gap, right:0) rather than normal flow, so it
   doesn't push .wia__preview even taller by stacking under .wia__pv-label
   (which, as of the label's move to normal flow below the frame, already
   adds its own height there) — this keeps the CTA visually anchored a
   fixed gap below the label consistently, rather than compounding two
   in-flow elements' heights. Small/quiet (ghost-underline style, same
   technique as .btn--ghost .lbl) since this is explicitly a draft, not a
   full CTA button yet — links to #projekte for now (no per-project anchor
   exists there yet for either case study). */
.wia__project-cta {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: clamp(10px, 1.4vw, 16px);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-2);
  white-space: nowrap;
  transition: color 260ms var(--ease-cinema);
}
.wia__project-cta .lbl {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  padding-bottom: 2px;
  transition: background-size 420ms var(--ease-cinema);
}
.wia__project-cta .arrow {
  width: 14px;
  height: 14px;
  transition: transform 260ms var(--ease-cinema);
}
@media (hover: hover) {
  .wia__project-cta:hover { color: #fff; }
  .wia__project-cta:hover .lbl { background-size: 100% 1px; }
  .wia__project-cta:hover .arrow { transform: translateX(3px); }
}
.wia__project-cta:focus-visible { outline: 1px solid var(--border-strong); outline-offset: 3px; }

/* 01 — video: ring + play triangle (placeholder only — hidden once real
   footage is loaded, via .has-media on the same element; a looping
   background clip doesn't need a "click to play" affordance). */
.wia__media--video.has-media::before,
.wia__media--video.has-media::after { display: none; }
.wia__media--video::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(46px, 5vw, 66px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 1px solid var(--border);
  border-radius: 50%;
}
.wia__media--video::after {
  content: "";
  position: absolute;
  left: calc(50% - 2px);
  top: 50%;
  transform: translate(-50%, -50%);
  border-style: solid;
  border-width: 7px 0 7px 12px;
  border-color: transparent transparent transparent var(--fg-2);
}

/* 02 — web: abstract wireframe blocks */
.wia__media--web > i {
  position: absolute;
  display: block;
  background: var(--border-faint);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
}
.wia__media--web > i:nth-child(1) { left: 8%; top: 11%; right: 8%; height: 9%; }
.wia__media--web > i:nth-child(2) { left: 8%; top: 28%; width: 46%; bottom: 12%; }
.wia__media--web > i:nth-child(3) { right: 8%; top: 28%; width: 30%; height: 24%; }
.wia__media--web > i:nth-child(4) { right: 8%; bottom: 12%; width: 30%; height: 30%; }

/* 02 — web, real build: laptop mockup (assets/computer-greenscreen.png)
   with the site screen-recording corner-pinned onto its display.
   - .wia-laptop fills the tile; the image sizes via object-fit so the
     laptop is never cropped.
   - .wia-laptop__mask sits over the SAME full box (not just the screen
     area) and clip-paths to the display's four corners (as % of the
     image — matched 1:1 with the corner fractions script.js uses for the
     matrix3d target, so the visible video edge and the transform's own
     target edge are the same four points, not two independently-eyeballed
     shapes).
   - .wia-laptop__vid fills that mask; script.js applies a matrix3d
     transform (computed from the mask's live pixel size + the same corner
     fractions) that warps the whole rectangle onto the four corners — a
     real projective (homography) warp, not a skew hack, so it's exact at
     any size. transform-origin stays at 0 0 to match the math. */
.wia-laptop {
  position: absolute;
  inset: 0;
  /* A bit bigger than the tile itself, so the screen content reads more
     clearly — scaling the whole group (not just the image) keeps the
     video's corner-pin matrix3d correctly aligned to the mask, since
     script.js measures THIS element's unscaled layout size (transform
     doesn't affect clientWidth/clientHeight) and a uniform scale on the
     whole subtree preserves every relative position within it. The
     overflow this creates is cropped by .wia__media's own
     overflow:hidden, same as any other tile's image. A small extra
     translateY nudges it down slightly — at 1.16x the crop otherwise
     left noticeably more empty space below the laptop than above it;
     listed before scale() so the % is a fixed share of the tile's own
     (unscaled) height at any size, not multiplied by the zoom too. */
  transform: translateY(2.5%) scale(1.16);
}
.wia-laptop__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none;
}
.wia-laptop__mask {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  clip-path: polygon(45.1722% 10.3549%, 78.7859% 15.3411%, 73.7225% 62.9267%, 40.5969% 51.6536%);
}
.wia-laptop__vid {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: 0 0;
}
@media (prefers-reduced-motion: reduce) {
  .wia-laptop__vid { animation: none; }
}

/* 03 — foto: soft light + crop marks (placeholder only — suppressed by
   .has-media once the real photo library below is in place). */
.wia__media--foto {
  background:
    radial-gradient(90% 90% at 72% 18%, rgba(36,54,134,0.20), transparent 60%),
    linear-gradient(200deg, #14141c, #070709 72%);
}
.wia__media--foto::before,
.wia__media--foto::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border: 1px solid var(--border);
}
.wia__media--foto::before { left: 16px; top: 16px; border-right: 0; border-bottom: 0; }
.wia__media--foto::after  { right: 16px; bottom: 34px; border-left: 0; border-top: 0; }
.wia__media--foto.has-media::before,
.wia__media--foto.has-media::after { display: none; }

/* 03 — foto, real build: small before/after photo library, reduced to
   just the image + an automatic wipe + a small "next motif" arrow (no
   side panel, no labels, no manual drag any more).
   .wia-photo fills the tile. .wia-photo__compare holds the "after"
   (edited) image as the base layer; .wia-photo__before sits on top,
   clipped to --pos (a % custom property) via clip-path — so it's one
   real compare view, not two images side by side. --pos is animated by
   a CSS @keyframes loop (registered via @property so it interpolates
   smoothly) — slow wipe unedited -> edited -> unedited, continuously,
   no JS driving it per-frame. */
@property --pos {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 50%;
}
.wia-photo {
  position: absolute;
  inset: 0;
}
.wia-photo__compare {
  position: absolute;
  inset: 0;
  --pos: 50%;
  animation: wia-photo-wipe 10s ease-in-out infinite;
}
@keyframes wia-photo-wipe {
  0%, 8%   { --pos: 4%; }
  50%, 58% { --pos: 96%; }
  100%     { --pos: 4%; }
}
.wia-photo__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.wia-photo__before {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--pos)) 0 0);
}
/* the wipe line — purely decorative, tracks --pos */
.wia-photo__handle {
  position: absolute;
  inset: 0;
  left: var(--pos);
  width: 1px;
  background: rgba(255, 255, 255, 0.55);
  box-shadow: 0 0 16px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  z-index: 2;
}

.wia-photo__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-soft);
  border-radius: 50%;
  background: rgba(8, 8, 12, 0.5);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  color: var(--fg-2);
  transition: border-color 260ms var(--ease-cinema), color 260ms var(--ease-cinema), background 260ms var(--ease-cinema);
}
.wia-photo__nav--prev { left: clamp(10px, 1.4vw, 16px); }
.wia-photo__nav--next { right: clamp(10px, 1.4vw, 16px); }
.wia-photo__nav .arrow { width: 15px; height: 15px; }
@media (hover: hover) {
  .wia-photo__nav:hover { border-color: var(--border-strong); color: #fff; background: rgba(8, 8, 12, 0.72); }
}
.wia-photo__nav:focus-visible { outline: 1px solid var(--border-strong); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .wia-photo__compare { animation: none; --pos: 50%; }
  .wia-photo__nav { transition: none; }
}

/* 04 — grafik: editorial type + rule + shape (placeholder only —
   suppressed by .has-media once the compendium flip-book below is in
   place). The outer frame keeps the standard shared 16:9 SIZE — same
   height as every other tile's frame at this width, per the user's own
   correction ("Höhe von den anderen Rahmen übernehmen") — but not the
   shared paint: once has-media, .wia-book__stage draws its own
   border/background/radius sized to the book's real 1269:900 shape (see
   the .wia-book comment below), so this box is left fully transparent —
   otherwise its own 16:9 border/background would show as empty painted
   margin on either side of the narrower book. */
.wia__media--grafik.has-media { border: none; background: none; }
.wia__media--grafik > b {
  position: absolute;
  left: 7%;
  bottom: 4%;
  font-family: var(--font-display);
  font-size: clamp(72px, 13vw, 150px);
  line-height: 0.8;
  color: var(--fg-2);
}
.wia__media--grafik > i:nth-of-type(1) {
  position: absolute;
  left: 7%;
  right: 7%;
  top: 24%;
  height: 1px;
  background: var(--border);
}
.wia__media--grafik > i:nth-of-type(2) {
  position: absolute;
  right: 10%;
  top: 12%;
  width: clamp(40px, 6vw, 72px);
  aspect-ratio: 1;
  border: 1px solid var(--border);
  border-radius: 50%;
}

/* 04 — grafik, real build: the design compendium (assets/kompendium/, one
   image per PDF page) as a small flip-book, now via StPageFlip (a real
   page-turn library — genuine curl/bend + shadow, drag/swipe/click built
   in) instead of a hand-rolled rotateY. See index.html for why the 4
   .wia-book__mask panels exist (papering over a hard-coded white the
   library paints into empty canvas regions — confirmed not fixable via
   styling the library itself). The book's own spread ratio (1269:900,
   ≈1.41:1) doesn't match the shared 16:9 .wia__media frame every other
   tile uses — rather than reshaping the frame itself to fit (tried once,
   reverted per the user's own correction: the frame should keep the same
   height as its siblings), .wia-book__stage below is sized to `height:
   100%` of the (still-16:9) frame with `width` auto from its own
   aspect-ratio — narrower than the frame, centered via .wia-book's flex
   centering. The stepped "stacked pages" box-shadow this used to have
   (when the book sat smaller, at 80% height, with margin all around) was
   removed — at this bigger size it read as a mismatched extra frame
   rather than page depth.
   The VISIBLE border/background/radius (normally painted by .wia__media
   itself for every other tile) moved from .wia__media--grafik onto
   .wia-book__stage instead, per the user's follow-up ("passen wir die
   Breite an, auf die Broschüre") — .wia__media--grafik keeps its 16:9 box
   only as an invisible sizing reference (matches siblings' height/layout
   position), stripped of its own paint, so nothing draws in the empty
   side margins any more — the frame you actually SEE is exactly the
   book's own 1269:900 shape now, not a wider box with the book floating
   inside it. */
.wia-book {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.wia-book__stage {
  position: relative;
  height: 100%;
  aspect-ratio: 1269 / 900;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  overflow: hidden;
  background:
    radial-gradient(120% 120% at 12% 8%, rgba(36,54,134,0.16), transparent 55%),
    linear-gradient(160deg, #101019 0%, #08080c 58%, #0c0c14 100%);
}
.wia-book__canvas { position: absolute; inset: 0; }
/* Covers the whole empty half during a single-cover view (front cover ->
   --l, back cover -> --r). A previous version also masked a narrow sliver
   on the opposite edge, assumed to be extra canvas padding around a hard
   cover — pixel-measured across several render sizes and that assumption
   was wrong (the cover image reaches its edge exactly, no gap there) —
   that extra mask was quietly cropping real cover artwork ("abgeschnitten"
   at the very edge). Removed; only the genuinely-empty half is masked
   now. */
.wia-book__mask {
  position: absolute;
  top: 0;
  bottom: 0;
  background: #0c0c10;
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transition: opacity 200ms linear;
}
.wia-book__mask--l  { left: 0; width: 50%; }
.wia-book__mask--r  { right: 0; width: 50%; }
@media (prefers-reduced-motion: reduce) {
  .wia-book__mask { transition: none; }
}

.wia-book__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 6;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-soft);
  border-radius: 50%;
  background: rgba(8, 8, 12, 0.5);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  color: var(--fg-2);
  transition: border-color 260ms var(--ease-cinema), color 260ms var(--ease-cinema), background 260ms var(--ease-cinema);
}
.wia-book__nav--prev { left: clamp(10px, 1.4vw, 16px); }
.wia-book__nav--next { right: clamp(10px, 1.4vw, 16px); }
.wia-book__nav .arrow { width: 15px; height: 15px; }
@media (hover: hover) {
  .wia-book__nav:hover { border-color: var(--border-strong); color: #fff; background: rgba(8, 8, 12, 0.72); }
}
.wia-book__nav:focus-visible { outline: 1px solid var(--border-strong); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .wia-book__leaf { transition: none; }
  .wia-book__nav { transition: none; }
}

/* ---- reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  .wia__item,
  .wia__preview,
  .wia__detail,
  .wia__detail-inner,
  .wia__trigger::before,
  .wia__num,
  .wia__name { transition-duration: 1ms !important; transition-delay: 0s !important; }
  .wia__preview { filter: none; transform: none; }
  .wia.is-inview .wia__item { background-size: 100% 1px; }
}

/* ---- mobile: no hover system, everything stacked + expanded ---- */
@media (max-width: 880px) {
  /* no sticky-scroll on mobile — everything stacked + expanded, normal flow */
  .wia__scroll { height: auto; }
  .wia__sticky {
    position: static;
    min-height: 0;
    overflow: visible;
    padding: var(--section-y) var(--gutter);
  }
  .wia__inner { display: block; }
  .wia__name,
  .wia__item.is-active .wia__name { font-size: clamp(22px, 6vw, 34px); }
  .wia__body { min-height: 0; }
  .wia__list { width: 100%; }

  .wia__trigger { padding: clamp(20px, 5.5vw, 26px) 0 clamp(10px, 3vw, 14px); }
  .wia__trigger::before { display: none; }
  .wia__num { color: var(--fg-2); }
  .wia__name { color: var(--fg-1); }

  /* preview flows inline: trigger → preview → detail (DOM order) */
  .wia__preview {
    position: static;
    width: 100%;
    opacity: 1;
    visibility: visible;
    transform: none;
    filter: none;
    margin: clamp(14px, 4vw, 20px) 0 clamp(6px, 2vw, 10px);
    /* .wia__project-cta's desktop rule (position:absolute, top:100% of
       .wia__preview) only works while .wia__preview itself is the
       positioned ancestor — once it goes position:static above, the CTA
       falls back to whichever ancestor further up IS positioned, landing
       it wherever that box's top/right happen to be and overlapping the
       running text below. Rejoin it to normal flow instead, reordered so
       it always lands last regardless of its DOM position (it sits
       between .wia__media and .wia__pv-label in markup, see index.html). */
    display: flex;
    flex-direction: column;
  }
  .wia__media { order: 1; }
  .wia__pv-label { order: 2; }
  .wia__project-cta {
    position: static;
    order: 3;
    margin: clamp(10px, 3vw, 16px) 0 0;
  }
  /* every detail open */
  .wia__detail { grid-template-rows: 1fr; }
  .wia__detail-inner { opacity: 1; transform: none; }
  .wia__desc,
  .wia__tags { padding-left: 0; }
  .wia__desc { max-width: none; }
  .wia__tags { line-height: 2; }
}

@media (max-width: 480px) {
  .wia__media--grafik > b { font-size: clamp(60px, 20vw, 96px); }
}

/* short desktop viewports (≈768–860px laptops): shrink everything so the
   whole pinned layout fits one screen without scrolling within the row */
@media (min-width: 881px) and (max-height: 860px) {
  .wia__sticky { padding-block: clamp(24px, 4vh, 52px); }
  .wia__inner { gap: clamp(10px, 1.6vw, 18px); }
  /* the .wia section is a sticky one-screen layout, so its title still
     shrinks on short laptops even though every other section stays big */
  .wia__title { font-size: clamp(30px, 3.3vw, 46px); }
  .wia__eyebrow { margin-bottom: clamp(6px, 1vw, 12px); }
  .wia__trigger { padding-block: clamp(7px, 0.9vw, 12px); }
  .wia__name { font-size: clamp(16px, 1.8vw, 22px); }
  .wia__item.is-active .wia__name { font-size: clamp(18px, 2.1vw, 27px); }
  .wia__desc { font-size: clamp(13px, 0.95vw, 14.5px); line-height: 1.55; margin-bottom: 10px; }
  .wia__tags { margin-bottom: clamp(10px, 1.4vw, 18px); }
  .wia__body { min-height: 0; }
}

/* ---------------- WHY ME ---------------- */
/* title → tiles gap pulled in tighter than the generic .section__head
   default (48-96px) — same tight value #ueber uses between its own title
   and body copy, per the user's own reference point */
#warum .section__head { margin-bottom: clamp(18px, 2.2vw, 30px); }
.why {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--border-faint);
  border: 1px solid var(--border-faint);
  border-radius: var(--radius-md);
  overflow: hidden;
  --warum-progress: 0;
}
/* Third scroll device on the page: the cards reveal one after another,
   SCRUBBED to actual scroll position (script.js sets --warum-progress 0..1
   as #warum crosses the viewport, clamped to its own max so it never
   un-reveals on scroll-up) rather than either the site's one-shot
   .reveal-on-enter or a fixed-timer cascade — same --i-staggered calc
   pattern as the hero title's --hero-exit/--lp (see that section below).
   Scoped to .js so a no-JS visitor just sees the cards, never stuck
   hidden. */
.why__cell:nth-child(1) { --i: 0; }
.why__cell:nth-child(2) { --i: 1; }
.why__cell:nth-child(3) { --i: 2; }
.js .why__cell {
  --wcp: clamp(0, calc(var(--warum-progress, 0) * 1.6 - var(--i, 0) * 0.25), 1);
  opacity: var(--wcp);
  transform: translateY(calc((1 - var(--wcp)) * 28px)) scale(calc(0.97 + var(--wcp) * 0.03));
  filter: blur(calc((1 - var(--wcp)) * 9px));
}
@media (prefers-reduced-motion: reduce) {
  .js .why__cell { opacity: 1; transform: none; filter: none; }
}
.why__cell {
  position: relative;
  background: var(--black);
  padding: clamp(32px, 4vw, 56px);
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow: hidden;
  transition: background var(--dur-slow) var(--ease-cinema), box-shadow var(--dur-slow) var(--ease-cinema),
              opacity 100ms linear, transform 100ms linear, filter 100ms linear;
}
.why__cell::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 100% at 50% 0%, var(--blue-glow), transparent 65%);
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-cinema);
  pointer-events: none;
}
.why__cell:hover {
  background: linear-gradient(160deg, var(--blue-deep) 0%, var(--grey-800) 55%, var(--black) 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), inset 0 0 0 1px rgba(89, 118, 214, 0.28);
}
.why__cell:hover::before { opacity: 1; }
.why__num {
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 0.32em;
  color: var(--fg-3);
  position: relative;
  transition: color var(--dur-slow) var(--ease-cinema);
}
.why__cell:hover .why__num { color: var(--blue-bright); }
.why__head {
  position: relative;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.15;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
  color: #fff;
}
.why__copy {
  position: relative;
  font-size: 14px;
  line-height: 1.7;
  color: var(--fg-2);
  font-weight: 300;
  margin: 0;
}
@media (max-width: 880px) { .why { grid-template-columns: 1fr; } }

/* ---------------- CONTACT ---------------- */
/* title → .contact__lead gap (the "Ich nehme aktuell..." intro paragraph
   that used to sit here was removed) — matched to #ueber's own
   title-to-body-copy gap, same value used for #warum above */
#kontakt .section__head { margin-bottom: clamp(18px, 2.2vw, 30px); }
.contact {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(40px, 6vw, 96px);
}
.contact__lead {
  font-size: 16px;
  line-height: 1.7;
  color: var(--fg-2);
  font-weight: 300;
  max-width: 38ch;
  margin: 24px 0 36px;
}
.contact__meta { display: flex; flex-direction: column; gap: 18px; margin-top: 24px; }
.contact__meta a {
  display: inline-flex;
  align-items: baseline;
  gap: 16px;
  color: #fff;
  font-size: 14px;
  transition: color var(--dur) var(--ease-cinema);
  width: fit-content;
}
.contact__meta a:hover { color: var(--fg-2); }
.contact__meta .lab {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-3);
  width: 80px;
  flex-shrink: 0;
}

.form { display: flex; flex-direction: column; gap: 26px; }
.field { display: flex; flex-direction: column; gap: 10px; }
.field label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-2);
}
.field input, .field textarea {
  font-family: var(--font-body);
  font-size: 16px;
  color: #fff;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  padding: 14px 0;
  outline: none;
  resize: none;
  transition: border-color var(--dur) var(--ease-cinema);
}
.field textarea { min-height: 100px; }
.field input:focus, .field textarea:focus { border-bottom-color: #fff; }
.field input::placeholder, .field textarea::placeholder { color: var(--fg-4); }
.form__actions { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; gap: 24px; flex-wrap: wrap; }
.form__hint { font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--fg-4); transition: color var(--dur) var(--ease-cinema); }
.form__hint.is-success { color: var(--blue-bright); }
.form__hint.is-error { color: var(--fg-2); }
/* Honeypot — visually hidden but present in the DOM/tab order is
   deliberately avoided (tabindex="-1" + not in normal flow), so it never
   costs a real visitor a keystroke; bots that fill every field still trip
   it. See the field's own HTML comment for the full reasoning. */
.field--hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.form.is-sending .btn--primary { opacity: 0.6; pointer-events: none; }

/* ---------------- FOOTER ---------------- */
.site-footer {
  padding: var(--section-y) var(--gutter) 32px;
  border-top: 1px solid var(--border-faint);
  background: var(--black);
}
.footer__inner { max-width: var(--max-width); margin: 0 auto; }
.footer__top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 48px;
  align-items: start;
}
.footer__tag {
  font-family: var(--font-display);
  font-size: clamp(36px, 4.8vw, 80px);
  line-height: 0.95;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #fff;
  margin: 0 0 24px;
}
.footer__col h4 {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-3);
  margin: 0 0 18px;
}
.footer__col a {
  font-size: 14px;
  color: var(--fg-1);
  display: block;
  padding: 6px 0;
  transition: color var(--dur) var(--ease-cinema);
}
.footer__col a:hover { color: var(--fg-2); }
.footer__bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 80px;
  padding-top: 24px;
  border-top: 1px solid var(--border-faint);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-4);
  gap: 24px;
  flex-wrap: wrap;
}
/* the copyright line reads oddly in full caps ("DAYMON.CH", "HUGI DAMIAN") —
   keep it as normal mixed case while the Impressum/Datenschutz links (and
   everything else in this row) stay uppercase */
.footer__bottom span { text-transform: none; }
.footer__bottom .links { display: flex; gap: 32px; }
.footer__bottom a { color: var(--fg-4); transition: color var(--dur) var(--ease-cinema); }
.footer__bottom a:hover { color: #fff; }

@media (max-width: 880px) {
  /* minmax(0,…) + tagline on its own row: "KOMPROMISSLOS." is wider than
     half the phone width and was pushing the right column off-screen */
  .footer__top { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px 24px; }
  .footer__top > :first-child { grid-column: 1 / -1; }
  .footer__tag { font-size: clamp(32px, 11vw, 56px); }
  .footer__col a { overflow-wrap: anywhere; }
  .footer__bottom { flex-direction: column; align-items: flex-start; }
  .footer__bottom .links { flex-wrap: wrap; gap: 12px 32px; }
}

/* ---------------- LEGAL PAGE ---------------- */
.legal {
  padding: 160px var(--gutter) var(--section-y);
  max-width: 880px;
  margin: 0 auto;
}
.legal h1 {
  font-family: var(--font-display);
  font-size: clamp(48px, 6vw, 96px);
  line-height: 0.95;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0 0 48px;
  color: #fff;
}
.legal h2 {
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 28px);
  line-height: 1.15;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 48px 0 16px;
  color: #fff;
}
.legal p, .legal ul {
  font-size: 15px;
  line-height: 1.75;
  color: var(--fg-2);
  font-weight: 300;
  max-width: 64ch;
  margin: 0 0 16px;
}
.legal ul { padding-left: 18px; }
.legal a { color: #fff; border-bottom: 1px solid var(--border-soft); transition: border-color var(--dur) var(--ease-cinema); }
.legal a:hover { border-bottom-color: #fff; }

/* ---------------- PROJECTS PAGE HERO ---------------- */
.page-hero {
  padding: 200px var(--gutter) 80px;
  max-width: var(--max-width);
  margin: 0 auto;
}
.page-hero__title {
  font-family: var(--font-display);
  font-size: clamp(64px, 9vw, 168px);
  line-height: 0.92;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 24px 0 0;
  color: #fff;
}
.page-hero__lead {
  font-size: clamp(16px, 1.4vw, 20px);
  line-height: 1.6;
  color: var(--fg-2);
  font-weight: 300;
  max-width: 56ch;
  margin: 32px 0 0;
}

/* ---------------- FADE-IN ON SCROLL ---------------- */
/* Same "resolve out of a soft blur" language as the hero title's own
   letter-by-letter intro (.hero__title .char__inner, see below) — a gentle
   lift + focus-pull + scale-settle rather than a plain slide-up, so every
   section after the hero reads as the same cinematic signature instead of
   a generic fade. --ease-cinema (expo-out, no overshoot) keeps it precise/
   restrained rather than bouncy, matching the brand's "kompromisslos" tone. */
.reveal {
  opacity: 0;
  transform: translateY(28px) scale(0.97);
  filter: blur(9px);
  transition: opacity 900ms var(--ease-cinema), transform 900ms var(--ease-cinema), filter 900ms var(--ease-cinema);
  will-change: opacity, transform, filter;
}
.reveal.in { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
.reveal[data-delay="1"] { transition-delay: 80ms; }
.reveal[data-delay="2"] { transition-delay: 160ms; }
.reveal[data-delay="3"] { transition-delay: 240ms; }
.reveal[data-delay="4"] { transition-delay: 320ms; }

@media (prefers-reduced-motion: reduce) {
  .reveal { transform: none; filter: none; transition: opacity 400ms linear; }
  .reveal.in { transform: none; filter: none; }
}

/* Project cards get their OWN device — a "pop" scale+fade instead of the
   blur/scale resolve everything else uses, staggered left-to-right across
   the grid via the existing data-delay attribute (script.js already
   assigns it by array index, which IS grid-left-to-right order, so no JS
   change needed here). cubic-bezier with a slight overshoot (back-ease) is
   what sells "pop" — everywhere else on the site uses --ease-cinema
   (no overshoot, deliberately restrained), so this is a genuinely distinct
   feel for the project grid specifically. Two classes beats .reveal's
   specificity, so no !important needed. */
.pj-card.reveal {
  opacity: 0;
  transform: scale(0.82);
  transition: opacity 550ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 550ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pj-card.reveal.in { opacity: 1; transform: scale(1); }
/* Bigger steps than the generic .reveal[data-delay] rule (80/160/240/320ms) —
   at those values, combined with this 550ms pop, all 4 cards read as one
   near-simultaneous cluster instead of a clear left-to-right sequence. */
.pj-card.reveal[data-delay="1"] { transition-delay: 160ms; }
.pj-card.reveal[data-delay="2"] { transition-delay: 320ms; }
.pj-card.reveal[data-delay="3"] { transition-delay: 480ms; }
.pj-card.reveal[data-delay="4"] { transition-delay: 640ms; }
@media (prefers-reduced-motion: reduce) {
  .pj-card.reveal { transform: none; transition: opacity 400ms linear; }
}

/* ---------------- HERO TITLE — SHINE FADE-IN + SCROLL-SCRUBBED EXIT ---- */
/* Two independent layers on the same glyphs:
   - .char__inner runs the INTRO on load: each glyph gently lifts out of a
     soft blur and fades in dim, staggered by --i via transition-delay (no
     falling motion). Once .is-shining lands, each letter then flashes
     dim -> white -> plain white in turn (also staggered by --i) — that
     letter-by-letter brightening sweep *is* the shine, no separate
     overlay layer needed.
   - .char is the SCROLL layer: script.js sets --hero-exit (0..1) from the
     scroll position and each glyph derives --lp from it (staggered by
     --i), sliding back up behind the header + fading + blurring as you
     scroll down, scrubbed. A separate element so it composes with, rather
     than fights, the intro transition.
   Without JS the raw text just renders normally. */
@property --hero-exit {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}
/* Pre-hide only when JS is confirmed live (see script.js) — without JS
   the raw text must stay visible. */
.js .hero__title[data-splittext] { visibility: hidden; }

.hero__title { --hero-exit: 0; }

.hero__title .char {
  display: inline-block;
  vertical-align: top;
  /* scroll-exit progress: 0 in place, 1 gone up behind the header. The
     -(--i)*0.11 term staggers the letters; 1.55 makes every letter reach
     a full 1 by the time --hero-exit hits 1. */
  --lp: clamp(0, calc(var(--hero-exit) * 1.55 - var(--i, 0) * 0.11), 1);
  transform: translateY(calc(-200% * var(--lp)));
  opacity: calc(1 - var(--lp));
  filter: blur(calc(12px * var(--lp)));
  transition: transform 110ms linear, opacity 110ms linear, filter 110ms linear;
  will-change: transform, opacity, filter;
}
.hero__title .char__inner {
  display: inline-block;
  /* Elegant fade-in: a soft lift out of a faint blur, no falling motion —
     each glyph gently resolves into place, staggered by --i. Sped up
     (was 900/680ms + 55ms step) so the whole hero intro reads faster. */
  transform: translateY(20px);
  opacity: 0;
  color: rgba(203, 213, 255, 0.9);
  filter: blur(11px);
  transition:
    transform 700ms cubic-bezier(0.16, 1, 0.3, 1),
    opacity 520ms ease-out,
    color 700ms cubic-bezier(0.16, 1, 0.3, 1),
    filter 700ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 40ms);
  will-change: transform, opacity, filter;
}
.hero__title.is-revealed { line-height: 1; }
.hero__title.is-revealed .char__inner {
  transform: none;
  opacity: 1;
  /* Lands dim — .is-shining below flashes each letter to white in turn
     (see the keyframes), left to right. */
  color: rgba(210, 219, 242, 0.6);
  filter: blur(0);
}

/* The brightening IS the shine, and only ever happens on the real letters
   themselves — no duplicate copy of the word laid on top any more (that
   was ghosting visibly, since a plain text run and individually-boxed
   .char__inner spans don't kern/measure identically, so the two copies
   never quite lined up). Each letter just flashes from dim to a bright
   glow and settles on plain white, staggered by --i (same 40ms step as
   the intro reveal) — the sequence of letters lighting up left-to-right
   *is* the sweep, no separate visual layer needed to sell it. */
.hero__title.is-shining .char__inner {
  animation: hero-letter-shine 380ms ease-out both;
  animation-delay: calc(var(--i, 0) * 40ms);
}
@keyframes hero-letter-shine {
  0%   { color: rgba(210, 219, 242, 0.6); text-shadow: none; }
  35%  {
    color: #fff;
    text-shadow: 0 0 26px rgba(255, 255, 255, 0.9), 0 0 46px rgba(255, 255, 255, 0.5);
  }
  100% { color: #fff; text-shadow: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__title .char { transform: none; opacity: 1; filter: none; }
  /* .is-revealed. (not just .hero__title.) to match the dim-color rule's
     specificity (3 classes each) — script.js never adds .is-shining under
     reduced motion (there's no sweep to sync to), so without this the
     dim .is-revealed color would win the tie by source order and DAYMON
     would stay dim forever for reduced-motion users. */
  .hero__title.is-revealed .char__inner {
    transition: none;
    transform: none;
    opacity: 1;
    filter: none;
    color: #fff;
  }
}

/* ---------------- HERO — LEFT BODY ---------------- */
/* A short bio + the two CTAs, stacked under DAYMON. */
.hero__intro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(8px, 1.2vw, 18px);
}
.hero__bio {
  max-width: min(44ch, 100%);
  margin: 0;
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.7;
  font-weight: 300;
  color: var(--fg-2);
}

/* Entrance — one sequential timeline, chained entirely in script.js off
   real transitionend/animationend events (not independent CSS delays):
   title letters -> shine -> bio types out -> CTA group rises + fades in
   -> button glow. Each class below is added exactly when its phase should
   start, so every transition/animation here runs with NO css delay of its
   own — the gap between phases is the small fixed buffer baked into the
   JS chain. */

/* Bio: real typewriter reveal. script.js splits the text into one <span
   class="tc"> per character (each carrying --i, its index). Every
   character sits at opacity:0 until .is-live, then each one's
   transition-delay (staggered by --i) pops it in in turn — letters never
   move or blur, they just appear in sequence, like being typed. Wrapping
   stays exactly as plain text would (plain inline spans), so multi-line
   bios type out line by line naturally. */
.js .hero__bio[data-typewrite] { visibility: hidden; }
.hero__bio .tc {
  opacity: 0;
  transition: opacity 1ms linear;
  transition-delay: calc(var(--i, 0) * 8ms);
}
.js .hero.is-live .hero__bio .tc { opacity: 1; }

/* CTA group: rise + fade in, triggered by .is-cta once the bio has
   finished typing. */
.js .hero__ctas {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 550ms var(--ease-cinema), transform 550ms var(--ease-cinema);
  will-change: opacity, transform;
}
.js .hero.is-cta .hero__ctas { opacity: 1; transform: none; }

/* The hero buttons' own glow starts switched off and snaps on afterwards,
   like a light switch — a beat after they've appeared (.hero.is-lit, added
   by script.js after .is-live, deliberately later than the CTA fade-in
   itself for a dramatic pause). The bouncy box-shadow transition this reads
   through now lives on the base .btn--primary/--secondary rule (generalized
   site-wide from this original hero-only version) — this block only still
   needs the actual on/off condition, since the hero's own timing is
   JS-driven (.is-lit) rather than the generic .reveal/.in scroll-reveal
   used everywhere else. */
.js .hero:not(.is-lit) .hero__ctas .btn--primary,
.js .hero:not(.is-lit) .hero__ctas .btn--secondary {
  box-shadow: 0 0 0 1px transparent, 0 0 0 transparent, 0 0 0 transparent;
}
@media (prefers-reduced-motion: reduce) {
  .js .hero__bio[data-typewrite] { visibility: visible; }
  .hero__bio .tc { transition: none; opacity: 1; }
  .js .hero__ctas { transition: none; opacity: 1; transform: none; }
  .js .hero:not(.is-lit) .hero__ctas .btn--primary,
  .js .hero:not(.is-lit) .hero__ctas .btn--secondary { box-shadow: none; }
}

@media (max-width: 880px) {
  .hero {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    row-gap: clamp(28px, 7vw, 48px);
  }
  .hero__title,
  .hero__intro {
    grid-column: 1;
    grid-row: auto;
    align-self: start;
  }
  /* the two CTAs side by side, smaller */
  .hero__inner { width: 100%; }
  .hero__ctas {
    flex-wrap: nowrap;
    gap: 10px;
    width: 100%;
  }
  .hero__ctas .btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 14px 10px;
    gap: 6px;
    font-size: 10px;
    letter-spacing: 0.08em;
  }
  .hero__ctas .btn .arrow { width: 12px; height: 12px; }
}
@media (max-width: 380px) {
  .hero__ctas .btn { letter-spacing: 0.04em; }
  .hero__ctas .btn .arrow { display: none; }
}

/* ---------------- RESPONSIVE ---------------- */
@media (max-width: 880px) {
  .section__head { grid-template-columns: 1fr; gap: 24px; }
  .about-card { padding: clamp(20px, 6vw, 34px); }
  .about, .contact { grid-template-columns: 1fr; }
  .about { gap: clamp(24px, 7vw, 40px); }
  .about__text .section__title { margin-bottom: clamp(16px, 5vw, 24px); }
  /* Safari collapses a stretched grid item whose only content is
     absolutely positioned (aspect-ratio alone isn't enough) — so on mobile
     the photo sits in normal flow and gives the figure its real height. */
  .about__portrait {
    height: auto;
    min-height: 0;
    align-self: start;
  }
  .about__portrait img {
    position: relative;
    inset: auto;
    height: auto;
    aspect-ratio: 4 / 3;
  }
  .project--lg, .project--md, .project--sm, .project--off { grid-column: span 12; }
  .hero__title {
    font-size: clamp(52px, 18vw, 180px);
    /* Desktop deliberately sinks DAYMON behind the diamond (z-index:-1, see
       the rule above) — on mobile the diamond is smaller/off-center and the
       title reading on top of it is wanted instead, so bring it back in front. */
    z-index: 3;
  }
}


/* ================================================================
   AUSGEWÄHLTE PROJEKTE (.pjs) — editorial, on the 4-column rhythm,
   with a same-page overlay. Self-contained: .pjs* / .pj-* /
   .pjs-media* / .pjs-ov*. Does not touch .projects / .project.
   ================================================================ */
.pjs__head {
  max-width: var(--max-width);
  /* title/lead -> grid gap, unified 2026-09-30 to the same value every
     other section uses for its own title -> content gap (see
     [[spacing-unification]]) */
  margin-bottom: clamp(18px, 2.2vw, 30px);
}
/* small label above the heading — same style as .wia__eyebrow */
.pjs__eyebrow {
  display: block;
  margin-bottom: clamp(10px, 1.4vw, 16px);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-2);
}
/* size inherits from .section__title — same as every other section */
.pjs__h2 {
  margin: 0 0 clamp(14px, 1.6vw, 22px);
}
.pjs__lead {
  margin: 0;
  max-width: 56ch;
  font-size: clamp(15px, 1.2vw, 18px);
  line-height: 1.7;
  font-weight: 300;
  color: var(--fg-2);
}

/* small, deliberately plain preview cards — the project itself only shows
   once the overlay opens; auto-fill collapses to 1 column on narrow
   viewports on its own, no breakpoint override needed */
.pjs__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: clamp(28px, 3vw, 44px);
}

.pj-card {
  position: relative;
  display: flex;
  flex-direction: column;
}
.pj-card__media { display: block; position: relative; }
/* darkens the whole preview evenly so the centered favicon + arrow cluster
   stay legible over bright thumbnails (a centered vignette was tried first
   but read as an odd localized spot rather than a deliberate treatment) */
.pj-card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: var(--radius-lg);
  background: rgba(0, 0, 0, 0.38);
}
/* favicon chip, centered on the preview — same round glass-chip look as
   .pjs-media-expand in the overlay, for visual consistency between the two
   "icon on media" patterns in this section. The 2 expand arrows (below)
   flank it at rest and glide out to the corners on hover. */
.pj-card__favicon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(6, 7, 10, 0.85);
  border: 1px solid var(--border);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  overflow: hidden;
}
.pj-card__favicon img { width: 60%; height: 60%; object-fit: contain; display: block; }
.pj-card__body { padding-top: clamp(14px, 1.6vw, 18px); }
.pj-card__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.pj-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.5vw, 19px);
  line-height: 1.15;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: #fff;
}
.pj-card__year { flex: none; font-size: 11px; letter-spacing: 0.16em; color: var(--fg-4); }
.pj-card__tags {
  margin: 6px 0 0;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* "you can open this" cue — two of the site's own arrow glyphs, flanking
   the centered favicon. Idle: a slow, gentle "breathing" loop (drift a bit
   apart, drift back) via @keyframes — not static, but not the aggressive
   full-corner hover jump either. Hover: pauses the loop and eases to a
   moderately wider (not maximal) spread — half the distance of an earlier,
   too-aggressive corner-hugging version. */
.pj-card__expand { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.pj-card__expand-arrow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 26px;
  color: #fff;
  opacity: 0.85;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
  transition: transform 500ms var(--ease-cinema), opacity var(--dur) var(--ease-cinema);
}
.pj-card__expand-arrow--tl { animation: pj-arrow-breathe-tl 4.4s ease-in-out infinite; }
.pj-card__expand-arrow--br { animation: pj-arrow-breathe-br 4.4s ease-in-out infinite; }
@keyframes pj-arrow-breathe-tl {
  0%, 100% { transform: translate(calc(-50% - 25px), calc(-50% - 25px)) rotate(225deg); }
  50% { transform: translate(calc(-50% - 30px), calc(-50% - 30px)) rotate(225deg); }
}
@keyframes pj-arrow-breathe-br {
  0%, 100% { transform: translate(calc(-50% + 25px), calc(-50% + 25px)) rotate(45deg); }
  50% { transform: translate(calc(-50% + 30px), calc(-50% + 30px)) rotate(45deg); }
}
.pj-card:hover .pj-card__expand-arrow,
.pj-card:focus-within .pj-card__expand-arrow { opacity: 1; }
.pj-card:hover .pj-card__expand-arrow--tl,
.pj-card:focus-within .pj-card__expand-arrow--tl { animation: none; transform: translate(calc(-50% - 50px), calc(-50% - 50px)) rotate(225deg); }
.pj-card:hover .pj-card__expand-arrow--br,
.pj-card:focus-within .pj-card__expand-arrow--br { animation: none; transform: translate(calc(-50% + 50px), calc(-50% + 50px)) rotate(45deg); }

/* whole card is the click target */
.pj-card__open {
  position: absolute;
  inset: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.pj-card__open:focus-visible { outline: 1px solid var(--border-strong); outline-offset: 6px; }

.pj-card:hover .pjs-media,
.pj-card:focus-within .pjs-media { border-color: var(--blue-bright); }
.pj-card:hover .pjs-media__fill,
.pj-card:focus-within .pjs-media__fill { transform: scale(1.03); }

/* --- media placeholder base --- */
.pjs-media {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  background: linear-gradient(165deg, #0e0e15 0%, #08080c 60%, #0b0b12 100%);
  transition: border-color var(--dur-slow) var(--ease-cinema);
}
.pjs-media__fill {
  position: absolute;
  inset: 0;
  display: block;
  transition: transform var(--dur-slow) var(--ease-cinema);
}
img.pjs-media__fill,
video.pjs-media__fill {
  width: 100%; height: 100%;
  /* contain, not cover — real project media (video/poster) must never be
     cropped; mismatched aspect ratios just letterbox on the dark bg instead */
  object-fit: contain;
}
iframe.pjs-media__fill { width: 100%; height: 100%; border: 0; }
/* Logo tiles: white backdrop (for a dark/transparent logo file). Fills the
   box the same way every other gallery tile does (object-fit:contain, no
   inset padding) — scales up until it touches an edge, no artificial
   breathing room. The base .pjs-media border (--border-soft, a translucent
   light grey tuned for the dark tiles everywhere else) all but disappears
   on white, so this swaps in a translucent BLACK line instead — same
   "light border" weight, just legible against a light backdrop. */
.pjs-media--bg-light { background: #fff; border-color: rgba(0, 0, 0, 0.14); }

/* "Groß ansehen" — small always-visible expand icon on real overlay media
   (site/yt/video/src), opens .pjs-lb. Sits above the media itself (e.g. the
   YouTube iframe stays directly playable underneath; this is just a corner
   affordance, not a full-card click-catcher). */
.pjs-media-expand {
  position: absolute;
  top: clamp(10px, 1.2vw, 14px);
  right: clamp(10px, 1.2vw, 14px);
  z-index: 3;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: #fff;
  background: rgba(6, 7, 12, 0.6);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  opacity: 0.82;
  transition: opacity var(--dur) var(--ease-cinema), border-color var(--dur) var(--ease-cinema), transform var(--dur) var(--ease-cinema);
}
.pjs-media-expand svg { width: 15px; height: 15px; }
.pjs-media-expand:hover,
.pjs-media-expand:focus-visible { opacity: 1; border-color: var(--border-strong); transform: scale(1.06); }

/* optional caption ABOVE a small real-media preview (e.g. gallery items in
   the overlay) — only present when the visual carries a "label" */
.pjs-media-item { display: flex; flex-direction: column; gap: 8px; }
.pjs-media-cap { margin: 0; font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--fg-3); }

.pjs__label {
  position: absolute;
  left: clamp(12px, 1.4vw, 18px);
  bottom: clamp(10px, 1.2vw, 16px);
  z-index: 2;
  margin: 0;
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--fg-3);
  opacity: 0.72;
  pointer-events: none;
}

/* --- per-discipline placeholder art --- */
.pjs-media--film::after {
  content: "";
  position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0.5;
  background: repeating-linear-gradient(0deg, rgba(255,255,255,0.025) 0 1px, transparent 1px 4px);
}
.pjs-media--film .pjs-media__fill { background: radial-gradient(120% 90% at 28% 0%, rgba(36,54,134,0.2), transparent 55%); }
.pjs-media--film .pjs-media__fill i {
  position: absolute; width: 44%; aspect-ratio: 16 / 9; border: 1px solid var(--border); border-radius: 2px;
}
.pjs-media--film .pjs-media__fill i:nth-child(1) { left: 12%; top: 18%; opacity: 0.35; }
.pjs-media--film .pjs-media__fill i:nth-child(2) { left: 28%; top: 30%; opacity: 0.6; }
.pjs-media--film .pjs-media__fill i:nth-child(3) { left: 44%; top: 42%; background: linear-gradient(160deg, rgba(36,54,134,0.16), transparent); }

.pjs-media--web .pjs-media__fill i {
  position: absolute; display: block; background: var(--border-faint); border: 1px solid var(--border-soft);
}
.pjs-media--web .pjs-media__fill i:nth-child(1) { left: 8%; top: 12%; right: 8%; height: 10%; }
.pjs-media--web .pjs-media__fill i:nth-child(2) { left: 8%; top: 30%; width: 46%; bottom: 12%; }
.pjs-media--web .pjs-media__fill i:nth-child(3) { right: 8%; top: 30%; width: 30%; height: 26%; }
.pjs-media--web .pjs-media__fill i:nth-child(4) { right: 8%; bottom: 12%; width: 30%; height: 30%; }

.pjs-media--foto .pjs-media__fill {
  background:
    radial-gradient(80% 80% at 72% 20%, rgba(36,54,134,0.26), transparent 60%),
    linear-gradient(205deg, #16161f, #060608 72%);
}
.pjs-media--foto::before {
  content: "";
  position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0.5;
  background-image: radial-gradient(rgba(255,255,255,0.05) 0.5px, transparent 0.6px);
  background-size: 3px 3px;
}
.pjs-media--foto .pjs-media__fill i { position: absolute; width: 20px; height: 20px; border: 1px solid var(--border); }
.pjs-media--foto .pjs-media__fill i:nth-child(1) { left: 14px; top: 14px; border-right: 0; border-bottom: 0; }
.pjs-media--foto .pjs-media__fill i:nth-child(2) { right: 14px; bottom: 14px; border-left: 0; border-top: 0; }

.pjs-media--grafik .pjs-media__fill b {
  position: absolute; left: 6%; bottom: 2%;
  font-family: var(--font-display); font-size: clamp(56px, 15vw, 150px); line-height: 0.8; color: var(--fg-2);
}
.pjs-media--grafik .pjs-media__fill i:nth-of-type(1) { position: absolute; left: 6%; right: 6%; top: 26%; height: 1px; background: var(--border); }
.pjs-media--grafik .pjs-media__fill i:nth-of-type(2) { position: absolute; right: 9%; top: 12%; width: clamp(38px, 7vw, 80px); aspect-ratio: 1; border: 1px solid var(--border); border-radius: 50%; }

.pjs-media--brand .pjs-media__fill {
  display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 1px; background: var(--border-faint);
}
.pjs-media--brand .pjs-media__fill > * { background: #09090f; display: flex; align-items: center; justify-content: center; gap: 8px; }
.pjs-media--brand .pjs-media__fill > b { font-family: var(--font-display); font-size: clamp(22px, 4vw, 46px); }
.pjs-media--brand .pjs-media__fill > b:nth-child(1) { color: var(--fg-2); }
.pjs-media--brand .pjs-media__fill > b:nth-child(2) { color: var(--fg-3); }
.pjs-media--brand .pjs-media__sw i { width: clamp(11px, 2vw, 19px); aspect-ratio: 1; border-radius: 50%; }
.pjs-media--brand .pjs-media__sw i:nth-child(1) { background: var(--blue); }
.pjs-media--brand .pjs-media__sw i:nth-child(2) { background: var(--blue-bright); }
.pjs-media--brand .pjs-media__sw i:nth-child(3) { background: var(--blue-deep); }
.pjs-media--brand .pjs-media__grid {
  background-image:
    repeating-linear-gradient(90deg, var(--border-faint) 0 1px, transparent 1px 14px),
    repeating-linear-gradient(0deg, var(--border-faint) 0 1px, transparent 1px 14px);
}

.pjs-media--content .pjs-media__fill { display: flex; align-items: center; justify-content: center; gap: clamp(10px, 2.4vw, 26px); }
.pjs-media--content .pjs-media__fill i { display: block; border: 1px solid var(--border); background: linear-gradient(160deg, rgba(36,54,134,0.12), transparent 70%); }
.pjs-media--content .pjs-media__fill i:nth-child(1) { width: 15%; aspect-ratio: 9 / 16; }
.pjs-media--content .pjs-media__fill i:nth-child(2) { width: 22%; aspect-ratio: 1; }
.pjs-media--content .pjs-media__fill i:nth-child(3) { width: 17%; aspect-ratio: 4 / 5; }

/* ================= OVERLAY ================= */
.pjs-ov {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 56px);
}
.pjs-ov[hidden] { display: none; }
.pjs-ov__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(3, 4, 10, 0.72);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity 440ms var(--ease-cinema);
}
.pjs-ov__panel {
  position: relative;
  width: min(1080px, 100%);
  /* outer margin trimmed (was clamp(32px,8vw,112px)) to reclaim panel
     height — the scroll-hint below the hero needs to stay inside this
     budget even when a 2-line title eats extra vertical space */
  max-height: min(calc(100vh - clamp(24px, 5vw, 72px)), 960px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: linear-gradient(180deg, #0c0c13, #070709);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  opacity: 0;
  transform: translateY(14px) scale(0.986);
  transition: opacity 460ms var(--ease-cinema), transform 540ms var(--ease-cinema);
}
.pjs-ov.is-open .pjs-ov__backdrop { opacity: 1; }
.pjs-ov.is-open .pjs-ov__panel { opacity: 1; transform: none; }
.pjs-ov__scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(28px, 5vw, 60px);
  padding-top: clamp(48px, 6vw, 68px);
}
.pjs-ov__close {
  position: absolute;
  top: clamp(14px, 2vw, 20px);
  right: clamp(14px, 2vw, 20px);
  z-index: 5;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--fg-2);
  background: rgba(10, 10, 16, 0.5);
  transition: color var(--dur) var(--ease-cinema), border-color var(--dur) var(--ease-cinema), background var(--dur) var(--ease-cinema);
}
.pjs-ov__close svg { width: 18px; height: 18px; }
.pjs-ov__close:hover { color: #fff; border-color: var(--border-strong); }
.pjs-ov__close:focus-visible { outline: 1px solid var(--border-strong); outline-offset: 3px; }

.pjs-ov__body { transition: opacity 220ms var(--ease-cinema); }
.pjs-ov__body.is-swapping { opacity: 0; }
.pjs-ov__num { margin: 0 0 12px; font-size: 12px; letter-spacing: 0.24em; color: var(--fg-4); }
.pjs-ov__title {
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-size: clamp(30px, 4.6vw, 64px);
  line-height: 1.0;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #fff;
}
.pjs-ov__tags { margin: 0 0 clamp(16px, 2.2vw, 28px); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--fg-3); }
/* Plain full width, no vh cap — the cap (and the matching left-align +
   hint-width work that went with it) was reverted per request. NOTE: this
   reintroduces the original risk it was added to fix — a 2-line title on a
   short viewport can push the scroll-hint below the panel's initial visible
   area again (see [[projekte-editorial-section]] for the full history). Not
   re-added since the user explicitly asked for plain full width; revisit if
   that resurfaces. */
.pjs-ov__hero {
  margin: 0 0 clamp(14px, 2vw, 26px);
  aspect-ratio: 16 / 9;
  width: 100%;
}
/* Scroll-down hint, in normal flow directly BELOW the hero preview, centered
   — visible the instant the overlay opens, no scrolling needed. Was tried
   overlaid on top of the image first (absolute + white + drop-shadow, for
   contrast against arbitrary photo content) — moved into the flow instead
   since sitting under the image reads cleaner, and pushes the rest of the
   panel (Fließtext, gallery, "Nächstes Projekt") down slightly to make room,
   which is fine, matches the requested effect. White again (was briefly
   --blue-bright while it sat in the flow on the panel's own dark chrome,
   but plain white reads more like an icon/affordance and less like a
   decorative brand flourish — no drop-shadow needed, the panel background
   is uniformly dark unlike a photo). Two chevrons drifting down + fading on
   a loop, reusing the site's own chevron-only arrow language (see
   .pj-card__expand) just pointed down instead of diagonal. Fades out once
   the visitor actually scrolls (.pjs-ov__scroll.is-scrolled, toggled in
   script.js) — deliberate exception to the sitewide "no scroll cue" rule
   (see [[scroll-craft-audit]]): that rule is about the page's own scroll,
   this is a MODAL whose scrollability isn't otherwise obvious.
   Margins here and on .pjs-ov__hero/.pjs-ov__title/.pjs-ov__scroll were all
   trimmed together (see those rules) to reclaim vertical headroom — a
   2-line title (e.g. "Pulse — Nightclub Markenentwicklung") was pushing this
   hint below the panel's initial visible area, defeating the whole point
   ("sobald man öffnet"). */
.pjs-ov__scroll-hint {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin: 0 0 clamp(12px, 1.8vw, 22px);
  color: #fff;
  opacity: 0.9;
  transition: opacity var(--dur-slow) var(--ease-cinema);
}
.pjs-ov__scroll-hint svg { width: 20px; height: 20px; }
.pjs-ov__scroll-hint svg:first-child { animation: pjs-scroll-hint-drift 1.8s var(--ease-cinema) infinite; }
.pjs-ov__scroll-hint svg:last-child { animation: pjs-scroll-hint-drift 1.8s var(--ease-cinema) infinite 0.25s; margin-top: -11px; }
.pjs-ov__scroll.is-scrolled .pjs-ov__scroll-hint { opacity: 0; }
/* single flowing description paragraph — replaces the old 3-column
   Projekt/Ziel/Meine-Rolle grid (dropped per the user's own request: "ich
   will einfach ein Fließtext... eine kurze Beschreibung"). Capped width
   keeps a long line length readable, same way .section__intro does
   elsewhere on the site. */
.pjs-ov__text {
  margin: 0 0 clamp(28px, 3.5vw, 48px);
  font-size: clamp(14px, 1.05vw, 16px);
  line-height: 1.7;
  font-weight: 300;
  color: var(--fg-2);
}
@keyframes pjs-scroll-hint-drift {
  0%, 100% { opacity: 0.3; transform: translateY(0); }
  50% { opacity: 1; transform: translateY(5px); }
}
@media (prefers-reduced-motion: reduce) {
  .pjs-ov__scroll-hint svg { animation: none; }
}
.pjs-ov__link { margin: 0 0 clamp(28px, 3.5vw, 48px); }
.pjs-ov__gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(10px, 1.6vw, 20px); }
.pjs-ov__gallery .pjs-media { aspect-ratio: 4 / 3; }
.pjs-ov__next {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  width: 100%;
  margin-top: clamp(32px, 4vw, 56px);
  padding: clamp(20px, 2.4vw, 28px) 0 0;
  border-top: 1px solid var(--border-soft);
  text-align: left;
  color: var(--fg-2);
  transition: color var(--dur) var(--ease-cinema);
}
.pjs-ov__next:hover,
.pjs-ov__next:focus-visible { color: #fff; }
.pjs-ov__next-k { font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--fg-4); }
.pjs-ov__next-name { font-family: var(--font-display); font-size: clamp(18px, 2vw, 30px); text-transform: uppercase; }
.pjs-ov__next svg { width: 18px; height: 18px; margin-left: auto; transition: transform var(--dur) var(--ease-cinema); }
.pjs-ov__next:hover svg { transform: translateX(4px); }

/* ---- media lightbox (.pjs-lb) — near-fullscreen "Groß ansehen" viewer,
   sits above .pjs-ov (z-index 300) ---- */
.pjs-lb {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: grid;
  place-items: center;
  padding: clamp(12px, 3vw, 40px);
}
.pjs-lb[hidden] { display: none; }
.pjs-lb__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(2, 2, 5, 0.92);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 380ms var(--ease-cinema);
}
.pjs-lb.is-open .pjs-lb__backdrop { opacity: 1; }
.pjs-lb__close {
  position: fixed;
  top: clamp(14px, 2vw, 24px);
  right: clamp(14px, 2vw, 24px);
  z-index: 5;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--fg-2);
  background: rgba(10, 10, 16, 0.6);
  transition: color var(--dur) var(--ease-cinema), border-color var(--dur) var(--ease-cinema);
}
.pjs-lb__close svg { width: 20px; height: 20px; }
.pjs-lb__close:hover { color: #fff; border-color: var(--border-strong); }
.pjs-lb__close:focus-visible { outline: 1px solid var(--border-strong); outline-offset: 3px; }
.pjs-lb__stage {
  position: relative;
  width: min(94vw, 1600px);
  height: min(90vh, 900px);
  opacity: 0;
  transform: scale(0.98);
  transition: opacity 420ms var(--ease-cinema), transform 460ms var(--ease-cinema);
}
.pjs-lb.is-open .pjs-lb__stage { opacity: 1; transform: none; }
.pjs-lb__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: var(--radius-lg);
  background: #000;
}
.pjs-lb__frame--img,
.pjs-lb__frame--video { object-fit: contain; }

@media (prefers-reduced-motion: reduce) {
  .pjs-media, .pjs-media__fill, .pj-card__expand-arrow,
  .pjs-ov__backdrop, .pjs-ov__panel, .pjs-ov__body,
  .pjs-ov__next, .pjs-ov__next svg, .pjs-ov__close,
  .pjs-media-expand, .pjs-lb__backdrop, .pjs-lb__stage { transition: none !important; }
  .pj-card__expand-arrow--tl, .pj-card__expand-arrow--br { animation: none !important; }
  .pjs-ov__panel, .pjs-lb__stage { transform: none; }
  .pj-card:hover .pjs-media__fill,
  .pj-card:focus-within .pjs-media__fill { transform: none; }
}

@media (max-width: 880px) {
  .pjs-ov__gallery { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .pjs-ov { padding: 0; }
  /* panel is full-screen here — the blur behind it is never seen, only paid for */
  .pjs-ov__backdrop, .pjs-lb__backdrop { -webkit-backdrop-filter: none; backdrop-filter: none; }
  .pjs-ov__panel { width: 100%; max-height: none; height: 100dvh; border: 0; border-radius: 0; }
  .pjs-ov__scroll { padding: 20px 20px 44px; padding-top: 66px; }
  .pjs-ov__gallery { grid-template-columns: 1fr; }
  .pjs-lb { padding: 0; }
  .pjs-lb__stage { width: 100%; height: 100dvh; max-height: none; }
  .pjs-lb__frame { border-radius: 0; }
}
