/* saschahermanns.com — dark, monochrome, oversized display type; normal capitalisation (only the logo is lowercase). */

/* Inter 4.1 (static/fonts/inter, Latin subset), the same version as in Figma. Variable in weight and optical
   size: the browser picks the optical size from the font size (font-optical-sizing: auto), so large type gets
   Inter's Display cut — tighter and narrower — exactly as Figma renders it. */
@font-face {
  font-family: "Inter"; src: url(/fonts/inter/InterVariable-599009e51df0c8aa7d5adf73c11d3514.woff2) format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter"; src: url(/fonts/inter/InterVariable-Italic-93e4eb184243109d5d5323d93e676c6e.woff2) format("woff2");
  font-weight: 100 900; font-style: italic; font-display: swap;
}

:root {
  color-scheme: dark;
  --bg: #0E0F0E;
  --fg: #F2F3F7; /* Ghost White: the only white on the site */
  --muted: #A3A3A4;
  --dim: #7D7D7D; /* original #7A7A7A, nudged to pass WCAG AA (4.5:1) */
  --idle: #666666; /* original #4A4A4A, raised to 3.4:1 for large text */
  --curtain: #1B1C1B; /* a touch lighter than --bg, so the page transition reads as a surface */

  /* one typeface everywhere: Inter Variable, Medium (500), self-hosted (top of this file) */
  --font: "Inter", "Helvetica Neue", Arial, sans-serif;
  --display: var(--font);
  --hero: var(--font);
  --body: var(--font);

  --gutter: 104px; /* side padding on desktop: header, content, menu, footer */
  --wrap: 1700px; /* max content width: content, header, menu and footer */
  /* side padding that keeps header, menu and footer aligned with the content column (substituted where used,
     so 100% is the element's own containing block) */
  --edge: max(var(--gutter), (100% - var(--wrap)) / 2);
  --radius: 8px;
  --gap: 40px;
  --ease: cubic-bezier(.22, .61, .36, 1);

  --fs-display: clamp(54px, 2.1rem + 6.2vw, 140px);
  --fs-xl: clamp(34px, 0.97rem + 4.94vw, 100px);
  --fs-nav: clamp(54px, 1.4rem + 9.7vw, 188px);
  --fs-lead: clamp(23px, 0.9rem + 2.32vw, 54px);
  --fs-title: clamp(18px, 0.77rem + 1.53vw, 34px);
  --fs-text: clamp(17px, 0.93rem + 0.57vw, 23px);
  --fs-ui: 17px;
  --logo-h: clamp(24px, 21.7714px + .5714vw, 30px); /* SVG logo (Header & Menü): 30 at 1440, 24 at 390; Typografie → Abstände */
}
@media (max-width: 1023px) { :root { --gutter: 48px; } }
@media (max-width: 768px) { :root { --gutter: 24px; --gap: 16px; --fs-ui: 15px; } }

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%; text-size-adjust: 100%; background: var(--bg); color: var(--fg);
  /* where the browser shows a scrollbar (mouse on macOS, Windows), its room stays when the menu or a page change
     stops the scrolling: header, menu bar and X don't jump sideways */
  scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
/* lenis smooth scroll (site.js): native smooth scrolling would fight its animation */
html.lenis { scroll-behavior: auto; }
html.lenis, html.lenis body { height: auto; }
.lenis [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }
body {
  margin: 0; min-height: 100vh; display: flex; flex-direction: column;
  background: var(--bg); color: var(--fg);
  font: 500 var(--fs-ui)/1.53 var(--body); letter-spacing: -0.1px;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  overflow-x: clip; /* full-bleed covers must not cause sideways scrolling */
}
body:has(dialog[open]) { overflow: hidden; }
main { flex: 1; }
/* headings get focus after a page swap only so screen readers announce them: not interactive, no ring */
main:focus, main [tabindex="-1"]:focus { outline: none; }
.js:not(.ready) main { visibility: hidden; } /* until site.js has set up the reveals (boot.js) */
img, video { display: block; max-width: 100%; height: auto; border-radius: var(--radius); } /* width/height attributes only reserve space (no layout shift) */
a { color: inherit; text-decoration: none; transition: color .3s var(--ease); }
a:hover { color: var(--fg); }
:focus-visible { outline: 1px solid var(--fg); outline-offset: 4px; }
h1, h2, h3, p, dl, dd, figure, ul { margin: 0; }
button, [popover] { color: inherit; } /* no browser-default white */
ul { padding: 0; list-style: none; }
.muted { color: var(--muted); }
.display { font: 500 var(--fs-display)/1 var(--display); letter-spacing: -0.01em; }
.wrap { width: min(var(--wrap), 100% - 2 * var(--gutter)); margin-inline: auto; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip { position: absolute; left: var(--edge); top: -100px; z-index: 100; background: var(--fg); color: var(--bg); padding: 8px 16px; border-radius: 999px; }
.skip:focus { top: 16px; }

/* ---------- header ---------- */
.site-header, .menu-bar {
  display: flex; align-items: center; gap: 16px;
  min-height: 120px; padding: 0 var(--edge);
}
.site-header {
  position: sticky; top: 0; z-index: 10; view-transition-name: site-header;
  /* stays on top while scrolling and inverts over light images; only its controls take clicks */
  mix-blend-mode: exclusion; pointer-events: none;
}
.site-header > * { pointer-events: auto; }
/* with JS (site.js, "header contrast") each item measures what lies beneath it and turns dark over light pictures
   instead of blending; the grey breadcrumb is white over pictures and dark grey over light ones */
.has-tone .site-header { mix-blend-mode: normal; }
.has-tone .site-header :is(.logo, .crumb) { transition: color .3s var(--ease); }
.has-tone .site-header .logo-img { transition: filter .3s var(--ease); }
.has-tone .site-header .burger span::before, .has-tone .site-header .burger span::after { transition-property: translate, rotate, background-color; }
.site-header .logo.tone-dark { color: var(--bg); }
.site-header .logo.tone-dark .logo-img.mono { filter: url(#ghost-white) invert(1); }
.site-header .logo.tone-dark .logo-img:not(.mono) { filter: invert(1) hue-rotate(180deg); }
.site-header .crumb.tone-media { color: var(--fg); } /* grey only over the plain page; over pictures white … */
.site-header .crumb.tone-dark { color: color-mix(in srgb, var(--bg) 85%, var(--fg)); } /* … or dark grey (from the colour tokens) */
.site-header .burger.tone-dark span::before, .site-header .burger.tone-dark span::after { background: var(--bg); }
.logo { font: 500 20px/30px var(--display); text-transform: lowercase; white-space: nowrap; }
/* uploaded SVG logo: as high as --logo-h, width from its own proportions; one colour (Ghost White) unless
   "Originalfarben" is on, so it inverts over light images like the text did */
.logo.has-svg { display: block; line-height: 0; }
.logo-img { display: block; height: var(--logo-h); width: auto; max-width: min(60vw, 520px); border-radius: 0; }
.logo-img.mono { filter: url(#ghost-white); } /* every visible pixel exactly #F2F3F7 (filter in layout.html) */
.crumb {
  margin-left: clamp(24px, 18vw, 262px); align-self: center; padding-top: 4px;
  color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; /* as written: project titles, menu names */
}
.header-actions { margin-left: auto; display: flex; align-items: center; gap: 28px; }
.icon-btn svg { width: 22px; height: 22px; display: block; stroke: var(--fg); stroke-width: 1.5; fill: none; transition: opacity .3s; }
.icon-btn:hover svg { opacity: .5; }
.burger { width: 40px; height: 40px; margin-right: -5px; /* lines (30px) stay flush with the gutter */ padding: 0; border: 0; background: none; cursor: pointer; display: grid; place-items: center; }
/* two lines (30 x 1.5px, 8px apart) that morph into an X of the same lines: first they meet in the middle, then they
   turn (and back the other way round) */
.burger span, .burger span::before, .burger span::after { display: block; width: 30px; height: 1.5px; }
.burger span { position: relative; }
.burger span::before, .burger span::after {
  content: ""; position: absolute; left: 0; background: var(--fg);
  transition: translate .22s var(--ease) .16s, rotate .22s var(--ease);
}
.burger span::before { translate: 0 -4.75px; } /* 8px between the lines */
.burger span::after { translate: 0 4.75px; }
/* the X turns on the spot: the lines meet in the middle of the burger and turn there, nothing moves sideways (the X is
   narrower than the lines, so it sits centred where they were) */
.burger.is-open span::before, .burger.is-open span::after { translate: 0 0; transition: translate .22s var(--ease), rotate .3s var(--ease) .16s; }
/* the X (menu and project): the same lines, 30px long and 1.5px thick */
.burger.is-open span::before { rotate: 45deg; }
.burger.is-open span::after { rotate: -45deg; }
a.burger { color: inherit; } /* the project's X is a link: same lines as the menu's X */
.menu-bar .burger { margin-left: auto; }
@media (max-width: 767px) {
  .site-header, .menu-bar { min-height: 96px; }
  .crumb { display: none; }
}

/* ---------- menu (modal dialog) ---------- */
.menu {
  position: fixed; inset: 0; width: 100%; max-width: none; height: 100dvh; max-height: none;
  margin: 0; padding: 0; border: 0; background: none; color: var(--fg);
  flex-direction: column; overflow-y: auto;
}
/* the background is its own layer, so the wipe can run underneath the bar: logo and burger never move */
.menu::before { content: ""; position: fixed; inset: 0; z-index: -1; background: var(--bg); }
/* opening/closing is animated in site.js: an opaque wipe from the top, links rise out of a mask;
   the page header hides while the menu's own bar (same place, same size) stands in for it */
html.menu-open .site-header { visibility: hidden; }
.menu[open] { display: flex; }
.menu:focus { outline: none; }
.menu::backdrop { background: transparent; }
.menu-body { /* footer stays at the bottom edge; the links size themselves to this box's height (container) */
  flex: 1; min-height: 0; display: flex; align-items: center; gap: var(--gutter); padding: 0 var(--edge); container-type: size;
}
.menu-contact { display: grid; gap: 26px; }
.menu-contact hr { width: 252px; margin: 0; border: 0; border-top: 1px solid var(--fg); }
.socials { display: grid; grid-template-columns: repeat(2, max-content); gap: 12px 40px; }
.socials a { display: inline-flex; align-items: center; gap: 10px; }
.socials svg { width: 12px; height: 12px; stroke: currentColor; stroke-width: 1.4; fill: none; }
/* desktop (mouse, from 1024px): the arrow shows on hover only (its room stays, nothing moves); touch screens and
   narrow windows always show it */
@media (hover: hover) and (pointer: fine) and (min-width: 1024px) {
  .socials svg { opacity: 0; translate: -3px 3px; transition: opacity .3s var(--ease), translate .4s cubic-bezier(.16, 1, .3, 1); }
  .socials a:is(:hover, :focus-visible) svg { opacity: 1; translate: none; }
}
.menu-nav { margin-left: auto; display: grid; justify-items: end; padding: 2rem 0; }
.menu-nav a { /* names as written in the admin (Header & Menü) */
  --nav-lh: 1; /* line height as a ratio (Typografie sets it), so it shrinks with the size */
  display: block; overflow: hidden; font: 500 var(--fs-nav)/var(--nav-lh) var(--display); color: var(--idle); letter-spacing: -0.02em;
  /* the mask (the links rise out of it as the menu opens) reaches .24em below the line, so descenders (j, p, g, y)
     are never cut; the negative margin keeps the links exactly as far apart as before */
  padding-bottom: .24em; margin-bottom: -.2em;
}
/* the menu is always exactly one screen and never scrolls: the size from Typografie holds while it fits; on shorter
   screens the links shrink to the height between bar and footer (4 links of --nav-lh + .04em mask each; nav padding
   2rem top and bottom), so the footer always sits at the bottom edge */
.menu-nav a { font-size: min(var(--fs-nav), (100cqh - 4rem) / (4 * var(--nav-lh) + .3)); }
.menu-nav a span { display: block; }
.menu-nav a:hover, .menu-nav a[aria-current="page"] { color: var(--fg); }
@media (max-width: 767px) {
  .menu { --menu-gap: 48px; --nav-gap: 22px; }
  .menu-body { flex-direction: column-reverse; align-items: stretch; justify-content: center; gap: var(--menu-gap); }
  .menu-contact > p:not(.muted) { display: none; } /* address and phone; the white line above "Follow me:" stays, like on desktop */
  /* data protection, imprint: only in the page footer. Hidden but kept as the first row, so
     "all rights reserved" sits at the bottom and the footer keeps its height (lines up with the page's) */
  .menu > .site-footer nav { visibility: hidden; order: -1; flex-basis: 100%; } /* own row: the line stays left, also on small tablets */
  .menu-nav { margin: 0; justify-items: start; gap: var(--nav-gap); padding: 0; }
  /* phones: under the links the line and the social links (~130px), the gap to them and between the links */
  .menu-nav a { font-size: max(24px, min(var(--fs-nav), (100cqh - 140px - var(--menu-gap) - 3 * var(--nav-gap)) / (4 * var(--nav-lh) + .16))); }
}
/* phones up to 780px high (Safari with its bars: iPhone SE … Pro Max): everything moves a little closer, so the links stay large */
@media (max-width: 767px) and (max-height: 780px) {
  .menu { --menu-gap: 28px; --nav-gap: 10px; }
  .menu > .site-footer { min-height: 104px; padding-block: 1.5em; }
}

/* ---------- footer ---------- */
.site-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px 40px; flex-wrap: wrap; min-height: 140px; padding: 2em var(--edge); }
.site-footer nav { display: flex; gap: 40px; }
@media (max-width: 767px) { .site-footer nav { gap: 24px; } }

/* ---------- home ---------- */
.home { --home-gap: clamp(64px, 9vw, 140px); display: grid; align-content: start; gap: var(--home-gap); margin-block: 0; }
/* hero: the page slides under the transparent header; 250px from the page top to the headline and from it to the intro */
body:has(.home) .site-header { margin-bottom: -120px; }
.home-title {
  /* grows smoothly from 144 / 80 (phones, 390px) to 250 / 250 (1440px and up) */
  --hero-top: clamp(144px, 10.1vw + 105px, 250px); --hero-bottom: clamp(80px, 16.2vw + 17px, 250px);
  padding-top: var(--hero-top); margin-bottom: max(0px, var(--hero-bottom) - var(--home-gap)); /* never below 0: both can be set in the admin */
}
.home-title { /* spans the content width; design: 96px / 120px / -2% from 1440px up, 38px / 40px / -2% on phones (390px),
                 size and line height scale linearly in between */
  --hero-fs: clamp(38px, 16.46px + 5.524vw, 96px);
  --hero-lh: clamp(40px, 10.29px + 7.619vw, 120px);
  font: 500 var(--hero-fs)/var(--hero-lh) var(--hero); letter-spacing: -.02em; text-align: center; text-wrap: balance;
  max-width: 12.6em; margin-inline: auto; /* a little narrower than the content: four lines on every large screen, in em so it grows with the type */
}
/* hero images: scale with the type, no jumps between breakpoints. 88% of a line high (~180 x 106 at 120px
   line height), at least 40px (phones: 68 x 40); radius 8px -> 2px. Optically centred on the lowercase letters; the negative block margin keeps
   them out of the line height, so every line is exactly one line-height apart. */
.inline-media {
  display: inline-flex; white-space: nowrap;
  margin: -.5em .06em; vertical-align: middle;
}
.inline-img { display: block; height: max(40px, calc(.88 * var(--hero-lh))); width: auto; aspect-ratio: 1.7; object-fit: cover; border-radius: max(2px, .067em); } /* 88% of a line: stacked images keep a gap */
/* showcase image/video under the hero (admin: "Showreel"): 1700 x 927 at full content width (same 1420:774 ratio as the design), square on phones */
.showreel { position: relative; overflow: hidden; border-radius: var(--radius); }
.showreel img, .showreel video { width: 100%; height: auto; aspect-ratio: 1420 / 774; object-fit: cover; border-radius: 0; }
.home .home-intro { /* full content width; design: 72px / 80px / -1% from 1440px up, 26px / 26px / -2% on phones (390px),
                       size, line height and letter spacing scale linearly in between */
  color: var(--fg);
  font: 500 clamp(26px, 8.914px + 4.381vw, 72px)/clamp(26px, 5.943px + 5.143vw, 80px) var(--hero);
  letter-spacing: clamp(-.72px, -.4457px - .01905vw, -.52px);
}
/* intro with its eyebrow ("Latest project"): at most 1400px wide, so the lines don't run too long */
.home-about { max-width: 1400px; }
.home .home-intro a { text-decoration: underline; text-decoration-color: var(--idle); text-underline-offset: 4px; }
.home .home-intro a:hover { text-decoration-color: currentColor; }
.lr a .w, .lr a .wi { text-decoration: inherit; } /* a link's underline does not reach the masked words by itself */
.home-eyebrow { /* 16 / 22 at 1440, 14 / 20 on phones (Typografie: Eyebrow) */
  margin: 0 0 clamp(16px, 10.0571px + 1.5238vw, 32px);
  font: 500 clamp(14px, 13.2571px + .1905vw, 16px)/clamp(20px, 19.2571px + .1905vw, 22px) var(--body); color: var(--muted);
}
.section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; margin-bottom: 40px; }
.section-head h2 { /* Owners Medium: 34 / 54 at 1440 and up, 23 / 34 on phones */
  font: 500 clamp(23px, 2.37vw, 34px)/calc(54 / 34) var(--hero); letter-spacing: -.01em; color: var(--fg);
}
@media (max-width: 767px) { .section-head h2 { line-height: 34px; } }
.section-head a { display: inline-flex; align-items: baseline; color: var(--muted); transition: color .3s var(--ease); }
/* "show all projects": only the label, flush with the content edge; on hover it turns white and the 16px
   arrow slides in from the right, pushing the label over */
.section-head svg {
  flex: none; width: 0; height: 16px; margin-left: 0; overflow: visible; opacity: 0;
  /* optical centre: the arrow's middle (8px up in the box) sits on the middle of the lowercase letters
     (Inter x-height .546em → .273em above the baseline), not on the middle of the line box, which looked high */
  margin-bottom: calc(.273em - 8px);
  stroke: currentColor; stroke-width: 1.4; fill: none;
  transition: width .35s var(--ease), margin-left .35s var(--ease), opacity .25s var(--ease);
}
.section-head a:is(:hover, :focus-visible) { color: var(--fg); }
.section-head a:is(:hover, :focus-visible) svg { width: 16px; margin-left: 10px; opacity: 1; }
.home .work { margin: 0; }
/* closing section (home and About): 250px above and below (to the footer); 120px on phones. On the home page the
   grid gap above already counts */
.home-contact { --contact-pad: 250px; padding: var(--contact-pad) 0; }
.home .home-contact { min-height: 0; padding-top: calc(var(--contact-pad) - var(--home-gap)); }
@media (max-width: 767px) { .home-contact { --contact-pad: 120px; } }
@media (max-width: 767px) {
  .home { --home-gap: 64px; }
  body:has(.home) .site-header { margin-bottom: -96px; }
  .showreel img, .showreel video { aspect-ratio: 1; }
  .home .work { grid-template-columns: 1fr; } /* latest projects: stacked on phones */
  .home .card img, .home .card video { aspect-ratio: 4 / 3; }
  .section-head { margin-bottom: 24px; }
}

/* ---------- all work ---------- */
.work { display: grid; grid-template-columns: 1fr 1fr; grid-auto-flow: dense; gap: 80px var(--gap); margin-block: 40px 120px; }
.card { display: grid; grid-template-columns: 1fr auto; align-content: start; column-gap: 16px; }
.card.wide { grid-column: 1 / -1; }
.card figure { grid-column: 1 / -1; margin-bottom: 20px; overflow: hidden; border-radius: var(--radius); transform: translateZ(0); } /* own layer: rounded corners clip a playing, zooming video (Safari too) */
.card video { will-change: transform, opacity; } /* the hover zoom runs on the graphics card */
.card img, .card video { width: 100%; aspect-ratio: 690 / 600; object-fit: cover; transition: opacity .4s var(--ease), transform 1.2s var(--ease); }
.card.wide img, .card.wide video { aspect-ratio: 1420 / 646; }
.card:hover img, .card:hover video { opacity: .5; transform: scale(1.02); }
.card-title { font: 500 var(--fs-title)/1.2 var(--display); align-self: end; }
.card-year { font: 500 28px/1.2 var(--display); color: var(--dim); align-self: center; }
.card-cat { grid-column: 1 / -1; margin-top: 2px; font-size: var(--fs-text); line-height: 1.2; color: var(--dim); }
@media (max-width: 767px) {
  .work { gap: 56px 16px; margin-block: 20px 64px; }
  .card figure { margin-bottom: 13px; }
  .card img, .card video { aspect-ratio: 3 / 4; }
  .card.wide img, .card.wide video { aspect-ratio: 4 / 3; }
  .card-year { display: none; }
}

/* ---------- case study ---------- */
.case {
  /* content column (max 1700) between two flexible margins; cover and next-project stage span the full row.
     No 100vw / negative margins: older Safari doesn't stretch grid items that way (cover cut off on the right). */
  display: grid; row-gap: var(--gap);
  grid-template-columns: [full-start] 1fr [content-start] min(var(--wrap), 100% - 2 * var(--gutter)) [content-end] 1fr [full-end];
}
.case > * { grid-column: content; min-width: 0; }
.case > .cover, .case > .next-stage { grid-column: full; }
.case-head { min-height: min(750px, 80vh); display: grid; align-content: center; justify-items: center; gap: 96px; text-align: center; }
.case-title { display: grid; gap: 32px; }
.tagline, .next-project .label { color: var(--muted); }
.meta dt { color: var(--muted); }
.case-head .display { line-height: 1; }
/* project facts: full content width (like header and title), five equal columns with a 1px rule on top;
   17 / 20 on every screen */
.meta {
  justify-self: stretch; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 40px var(--gap);
  text-align: left; font-size: 17px; line-height: 20px;
}
.meta > div { border-top: 1px solid var(--idle); padding-top: 24px; overflow-wrap: anywhere; }
.meta dd { margin-top: 8px; color: var(--fg); }
.case-info { display: grid; justify-items: start; gap: 64px; margin-block: 80px; } /* flush left with the content edge */
.case-info > div { width: 100%; max-width: 830px; display: grid; gap: 11px; }
/* project texts: 18 / 24, no letter spacing, Inter Regular; the headings stay Medium at the same size */
.case-info h2 { font: 500 18px/24px var(--body); letter-spacing: 0; color: var(--muted); }
.case-info .prose { font-size: 18px; line-height: 24px; font-weight: 400; letter-spacing: 0; color: var(--fg); }
.prose { color: var(--muted); font-size: var(--fs-text); line-height: 1.3; }
.prose p + p { margin-top: 1em; }
.gallery { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--gap); }
.gallery figure { grid-column: span 6; overflow: hidden; border-radius: var(--radius); transform: translateZ(0); } /* rounded corners for playing videos (see .card figure) */
/* gallery images unfold from the bottom once they scroll into view (class set in site.js) */
@media (prefers-reduced-motion: no-preference) {
  /* images: a curtain draws the image up from the bottom edge while the picture inside settles from a slight
     zoom. Starts moving at once (ease-out quart: half the wipe in ~0.15s, so there's no lag behind the scroll)
     and lands softly over the rest of the second. The mask sits on the image, not the figure: a fully clipped
     figure would never count as visible for the observers in site.js. */
  .unfold {
    --draw: cubic-bezier(.25, 1, .5, 1);
    --settle: cubic-bezier(.16, 1, .3, 1);
    translate: 0 40px; transition: translate 1.1s var(--settle);
  }
  .unfold > :is(img, video) {
    clip-path: inset(100% 0 0 0 round var(--radius)); scale: 1.15;
    transition: clip-path 1s var(--draw), scale 1.4s var(--settle), opacity .4s var(--ease), transform 1.2s var(--ease); /* keeps card hover */
  }
  .unfold.is-in { translate: none; }
  .unfold.is-in > :is(img, video) { clip-path: inset(0 round var(--radius)); scale: 1; } /* round: iOS Safari ignores the radius on playing videos */
  /* unfolded (site.js): the clip-path goes, the figure's own rounded corners take over (translateZ below); with a
     clip-path a playing video had to be clipped again on every frame of the hover zoom and stuttered */
  .unfold.is-settled > :is(img, video) { clip-path: none; }

  /* text lines rise out of a mask (words wrapped in site.js) */
  /* the mask reaches .25em below the line box, so descenders (g, y, p) are never cut off */
  .lr .w { display: inline-block; text-indent: 0; padding-bottom: .25em; margin-bottom: -.25em; clip-path: inset(-.4em -.2em 0 -.2em); }
  .lr .wi { display: inline-block; text-indent: 0; translate: 0 135%; transition: translate 1s cubic-bezier(.16, 1, .3, 1); }
  .lr.is-in .wi { translate: none; }
}
.gallery .w-half { grid-column: span 3; }
.gallery .w-third { grid-column: span 2; }
.gallery img, .gallery video { width: 100%; height: auto; }
/* a video sharing a row with an image takes that image's format (cropped, not stretched; --fit set in site.js),
   on phones too, where the figures stack */
.gallery figure[style*="--fit"] > video { aspect-ratio: var(--fit); object-fit: cover; }
.next-project { min-height: 628px; display: grid; align-content: center; justify-items: center; gap: 20px; text-align: center; }
.next-project .display { margin-bottom: 16px; transition: color .3s var(--ease); }
/* all circles: a 1.5px ring in #A3A3A4, drawn as an inset shadow (browsers round borders to whole pixels) */
.circle { width: 86px; height: 86px; box-shadow: inset 0 0 0 1.5px var(--muted); border-radius: 50%; display: grid; place-items: center; transition: background .3s var(--ease), box-shadow .3s var(--ease), color .3s var(--ease), transform .3s var(--ease); }
.circle svg { width: 36px; height: 36px; stroke: currentColor; stroke-width: 1.5; fill: none; }
.next-project:hover .circle, .circle:hover { background: var(--fg); box-shadow: inset 0 0 0 1.5px var(--fg); color: var(--bg); }
.next-project:hover .circle svg { transform: translateX(3px); transition: transform .3s var(--ease); }
@media (max-width: 1366px) {
  .case-head { gap: 64px; }
  .case-info { margin-block: 48px; }
}
@media (max-width: 1024px) {
  .meta { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .case-head { min-height: 0; margin-block: 40px; gap: 48px; }
  .case-title { gap: 24px; }
  .meta { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; }
  .case-info { margin-block: 32px; gap: 40px; }
  .case-info > div { gap: 4px; }
  .gallery .w-half, .gallery .w-third { grid-column: span 6; }
  .next-project { min-height: 420px; gap: 16px; }
  .next-project .display { margin-bottom: 8px; }
  .circle { width: 72px; height: 72px; }
  .circle svg { width: 28px; height: 28px; }
}

/* ---------- project cover + next-project stage ---------- */
/* the cover sits under the transparent header; the stage's last frame is pixel-identical to the next cover */
body:has(.cover) .site-header { margin-bottom: -120px; }
.cover, .next-sticky { position: relative; height: 100svh; min-height: 560px; overflow: hidden; background: var(--bg); color: var(--fg); }
.cover-media { position: absolute; inset: 0; }
.cover-media > :is(img, video) { width: 100%; height: 100%; object-fit: cover; border-radius: 0; }
.cover-media::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, color-mix(in srgb, var(--bg) 65%, transparent), color-mix(in srgb, var(--bg) 0%, transparent) 55%); }
.cover-text {
  position: absolute; bottom: clamp(32px, 5vw, 72px);
  left: max(var(--gutter), (100% - var(--wrap)) / 2); right: max(var(--gutter), (100% - var(--wrap)) / 2);
  display: grid; justify-items: start; gap: 20px; }
/* project title on the cover: 144 / 144 / -2% from 1440px (Figma), scaling with the screen width below that
   (10vw: 39px on a 390px phone). site.js pulls it left by the first letter's side bearing, so the ink is flush
   with the content edge. */
.cover-title { font: 500 clamp(36px, 10vw, 144px)/1 var(--display); letter-spacing: -.02em; }
.has-cover .case-head { min-height: 0; margin-block: 80px 0; }

.next-stage { --p: 0; --enter: 1; --t: min(var(--p) / .9, 1); height: 250svh; margin-top: 120px; }
.next-sticky { position: sticky; top: 0; }
.next-media { translate: 0 calc((1 - var(--enter)) * -30%); }
.next-media > :is(img, video) { scale: calc(1.12 - .12 * var(--t)); }
.next-shade { position: absolute; inset: 0; background: var(--bg); opacity: calc(.45 * (1 - var(--t))); }
.next-stage .cover-text { translate: 0 calc((1 - var(--enter)) * 25%); }
.next-stage .cover-title a:hover { color: inherit; }
/* loading line: fills left to right, then fades so the last frame equals the next project's cover */
.next-progress {
  position: absolute; left: 0; right: 0; bottom: 100%; margin-bottom: 28px; display: grid; gap: 12px;
  font-size: var(--fs-ui); opacity: clamp(0, (.97 - var(--p)) / .09, 1);
}
.next-bar { display: block; height: 1px; background: color-mix(in srgb, var(--fg) 30%, transparent); }
.next-bar > span { display: block; height: 100%; background: var(--fg); transform-origin: 0 50%; scale: min(var(--p) / .85, 1) 1; }
@media (prefers-reduced-motion: no-preference) {
  .js body:has(.next-stage) > .site-footer { display: none; } /* the stage scrolls on to the next project */
  .cover > .cover-media > :is(img, video) { animation: cover-in 1.6s var(--ease) both; }
  .cover.is-seamless > .cover-media > :is(img, video) { animation: none; } /* swapped in by the next-project stage */
}
@media (prefers-reduced-motion: reduce) {
  .next-stage { --p: 1; height: auto; }
  .next-stage .next-progress { display: none; }
}
@keyframes cover-in { from { scale: 1.08; } to { scale: 1; } }
/* cover title rises out of a mask as the curtain lifts (not after the seamless next-project swap) */
.cover-title { padding-bottom: .22em; margin-bottom: -.22em; clip-path: inset(-50% -10% 0 -10%); } /* room for descenders */
.cover-line { display: block; }
@media (prefers-reduced-motion: no-preference) {
  .cover:not(.is-seamless) .cover-line { animation: line-in 1.1s cubic-bezier(.16, 1, .3, 1) .25s both; }
}
/* explicit end values: Safari can otherwise freeze a finished "from"-only animation on its start value */
@keyframes line-in { from { translate: 0 125%; } to { translate: 0 0; } }
@media (max-width: 767px) {
  body:has(.cover) .site-header { margin-bottom: -96px; }
  .has-cover .case-head { margin-top: 48px; }
  .next-stage { margin-top: 64px; height: 180svh; } /* shorter scroll to the next project on phones */
}

/* ---------- about ---------- */
.about { margin-top: 3rem; }
.prose-about h2, .prose-about h3 { font: 500 var(--fs-xl)/1 var(--display); letter-spacing: -0.01em; }
.prose-about h2 { max-width: 1400px; } /* the opening sentences (## in the admin): lines not longer than 1400px */
.prose-about h2 + h2 { margin-top: 80px; }
.prose-about p { font-size: var(--fs-lead); line-height: 1.2; letter-spacing: -0.5px; max-width: 1128px; margin-top: 80px; }
.prose-about h3 { margin: 200px 0 40px; } /* "Working experience", "Awards" (### in the admin) */
.prose-about table { display: block; border-collapse: collapse; }
.prose-about thead { display: none; }
/* working experience and awards: one list, one row each (year · role or award · company or category), all on the
   text's left edge, lines between the rows */
.prose-about tbody { display: grid; border-top: 1px solid var(--muted); }
.prose-about tr {
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 7fr) minmax(0, 4fr); align-items: baseline; column-gap: var(--gap);
  padding-block: clamp(16px, 13.0286px + .7619vw, 24px); border-bottom: 1px solid var(--muted);
}
.prose-about td { padding: 0; text-align: left !important; }
.prose-about td:first-child { font-size: clamp(17px, 0.82rem + 1.05vw, 28px); line-height: 1.1; color: var(--muted); }
.prose-about td:nth-child(2) { font: 500 clamp(23px, 1.19rem + 1.05vw, 34px)/1 var(--display); letter-spacing: -0.02em; }
.prose-about td:nth-child(3) { font-size: var(--fs-text); line-height: 1.3; color: var(--muted); }
/* "Services & Tools" (admin: About): the title like "Working experience", two columns (one on phones) of entries
   that open and close one at a time per column (site.js: .is-ready, .is-open; without JS all stay open) */
/* logo band (About, design: byq nimbus logos-2): one row running left at an even pace, 40px high logos (at most
   200px wide) 128px apart, fading out over 180px at both ends; site.js repeats the row (.logos-row) until the band
   plus one row is filled, the animation moves exactly one row's width (--row-w) and starts over unseen */
.logos { margin-top: 200px; overflow: clip; -webkit-mask-image: linear-gradient(90deg, transparent, #000 180px, #000 calc(100% - 180px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 180px, #000 calc(100% - 180px), transparent); }
.logos-track { --logo-gap: 128px; display: flex; width: max-content; }
.logos-row { display: flex; align-items: center; gap: var(--logo-gap); padding-right: var(--logo-gap); margin: 0; }
.logos-row img { display: block; height: 40px; width: auto; max-width: 200px; object-fit: contain; filter: url(#ghost-white); } /* every visible pixel #F2F3F7 (filter in layout.html) */
.logos.is-running .logos-track { animation: logos-run var(--logos-time, 30s) linear infinite; }
@keyframes logos-run { to { translate: calc(-1 * var(--row-w, 0px)) 0; } }
@media (max-width: 767px) {
  .logos { margin-top: 100px; -webkit-mask-image: linear-gradient(90deg, transparent, #000 60px, #000 calc(100% - 60px), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 60px, #000 calc(100% - 60px), transparent); }
  .logos-track { --logo-gap: 64px; }
  .logos-row img { height: 28px; max-width: 140px; }
}
/* "Bewegung reduzieren": the logos stand still, centred and wrapping, without the fade */
@media (prefers-reduced-motion: reduce) {
  .logos { -webkit-mask-image: none; mask-image: none; }
  .logos-track { width: auto; justify-content: center; }
  .logos-row { flex-wrap: wrap; justify-content: center; row-gap: 40px; padding-right: 0; }
}
.services-title { font: 500 var(--fs-xl)/1 var(--display); letter-spacing: -0.01em; margin: 200px 0 40px; }
.services-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; column-gap: clamp(40px, 21.4286px + 4.7619vw, 90px); row-gap: 64px; }
.services-label { margin: 0; padding-bottom: 16px; border-bottom: 1px solid var(--muted); font-size: 17px; line-height: 22px; color: var(--muted); }
.service { border-bottom: 1px solid var(--idle); }
.service-head { margin: 0; font: inherit; }
.service-toggle {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 24px;
  padding: 24px 0; border: 0; background: none; color: var(--fg); text-align: left; cursor: pointer;
  font: 500 clamp(22px, 19.7714px + .5714vw, 28px)/1.1 var(--display); letter-spacing: -0.02em;
}
.service-sign { position: relative; flex: none; width: 20px; height: 20px; }
.service-sign::before, .service-sign::after { content: ""; position: absolute; left: 0; top: calc(50% - .75px); width: 100%; height: 1.5px; background: var(--fg); transition: rotate .5s var(--ease), opacity .3s var(--ease); }
.service-sign::after { rotate: 90deg; }
.service.is-open .service-sign::after { rotate: 0deg; opacity: 0; } /* + turns into − */
.service-body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .7s cubic-bezier(.16, 1, .3, 1); }
.services.is-ready .service:not(.is-open) .service-body { grid-template-rows: 0fr; }
.service-inner { min-height: 0; overflow: hidden; display: flex; align-items: flex-end; justify-content: space-between; gap: var(--gap); }
.service-inner > * { transition: opacity .5s var(--ease); }
.services.is-ready .service:not(.is-open) .service-inner > * { opacity: 0; }
.service-text { margin: 0 0 24px; max-width: 440px; white-space: pre-line; font-size: clamp(17px, 15.8857px + .2857vw, 20px); line-height: 1.4; color: var(--muted); }
.service-icon { flex: none; display: grid; place-items: center; width: 80px; height: 80px; margin-bottom: 24px; border-radius: var(--radius); background: var(--curtain); }
.service-icon img { width: 55%; height: 55%; object-fit: contain; border-radius: 0; }
@media (max-width: 767px) {
  .services-title { margin: 100px 0 16px; }
  .services-cols { grid-template-columns: minmax(0, 1fr); }
  .service-toggle { padding: 20px 0; }
  .service-icon { width: 64px; height: 64px; }
}
@media (prefers-reduced-motion: reduce) {
  .service-body, .service-inner > *, .service-sign::before, .service-sign::after { transition: none; }
}
@media (max-width: 767px) {
  .prose-about h2 + h2, .prose-about p { margin-top: 48px; }
  .prose-about h3 { margin: 100px 0 16px; }
  /* phones: year on the left, role and company stacked next to it */
  .prose-about tr { grid-template-columns: 96px minmax(0, 1fr); column-gap: 16px; row-gap: 6px; }
  .prose-about td:first-child { white-space: nowrap; } /* "2025–today" on one line */
  .prose-about td:nth-child(3) { grid-column: 2; }
}

/* ---------- contact page (admin: Kontakt) ----------
   Desktop and tablet: exactly one screen with the footer, no scrolling. The page fills the space between header and
   footer (body is a flex column, main grows); the portrait (a cut-out PNG) scales into that height, standing in the
   middle on the bottom edge, the title bottom left, the details bottom right. Tablets held upright and phones: the
   portrait on top fading into the page, title and details below it (phones may scroll). */
body:has(.contact-page) .site-header { margin-bottom: -120px; } /* the page starts under the transparent header */
body:has(.contact-page) .crumb { display: none; }
.contact-page {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; column-gap: var(--gap);
  padding: 120px var(--edge) clamp(32px, 5svh, 64px);
}
.contact-portrait { position: absolute; left: 0; right: 0; top: 88px; bottom: 0; margin: 0; pointer-events: none; display: flex; justify-content: center; } /* as wide as the page, the picture in its middle (any aspect ratio) */
.contact-portrait img { display: block; height: 100%; width: auto; max-width: none; border-radius: 0; }
/* crop from the admin (Kontakt → Ausschnitt; site.js sets the variables from data-crop): the picture zooms around its
   focus point. On the desktop and when stacked the frame is as wide as the page (the picture in its middle), so a
   zoomed picture is only cut at the page's edges and the frame's top and bottom */
.contact-portrait { overflow: clip; }
.contact-portrait img { flex-shrink: 0; }
.contact-portrait img { transform: scale(var(--pz, 1)); transform-origin: var(--px, 50%) var(--py, 25%); }
/* desktop: the portrait stands on the bottom of the screen, behind the footer, and fades out on a long, eased curve
   (less and less opaque towards the bottom), so the footer stays readable on it */
.contact-portrait {
  bottom: -140px; /* the footer's height: down to the screen's edge */
  -webkit-mask-image: linear-gradient(#000 58%, rgba(0, 0, 0, .94) 66%, rgba(0, 0, 0, .8) 73%, rgba(0, 0, 0, .6) 80%, rgba(0, 0, 0, .38) 87%, rgba(0, 0, 0, .18) 93%, rgba(0, 0, 0, .05) 98%, transparent);
  mask-image: linear-gradient(#000 58%, rgba(0, 0, 0, .94) 66%, rgba(0, 0, 0, .8) 73%, rgba(0, 0, 0, .6) 80%, rgba(0, 0, 0, .38) 87%, rgba(0, 0, 0, .18) 93%, rgba(0, 0, 0, .05) 98%, transparent);
}
.contact-page { overflow: visible; overflow-x: clip; } /* the portrait may reach below into the footer, never sideways out of the page (phones: it is wider than the screen) */
body:has(.contact-page) > .site-footer { position: relative; z-index: 1; } /* over the portrait */
.contact-title { /* 140 / 126 at 1440, 72 / 65 on phones (Typografie: Kontaktseite → Titel) */
  position: relative; margin: 0; font: 500 clamp(72px, 46.7429px + 6.4762vw, 140px)/.9 var(--display); letter-spacing: -.02em;
  overflow: clip; overflow-clip-margin: .3em; /* its words rise from below: they must not make the one-screen page scroll meanwhile */
}
.contact-info { position: relative; min-width: 300px; }
.contact-info p { margin: 0; }
.contact-label { margin-bottom: 16px !important; color: var(--muted); }
.contact-address { margin-bottom: 24px !important; }
.contact-links { margin-bottom: 40px !important; line-height: 2; }
@media (max-width: 1023px) and (orientation: portrait), (max-width: 599px) {
  .contact-page { display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start; }
  .contact-portrait { /* fades into the page from about the middle, on a soft eased curve */
    top: 0; bottom: auto; height: 72%; left: 0; right: 0; translate: none; display: flex; justify-content: center;
    -webkit-mask-image: linear-gradient(#000 38%, rgba(0, 0, 0, .86) 50%, rgba(0, 0, 0, .55) 62%, rgba(0, 0, 0, .25) 74%, rgba(0, 0, 0, .07) 86%, transparent 96%);
    mask-image: linear-gradient(#000 38%, rgba(0, 0, 0, .86) 50%, rgba(0, 0, 0, .55) 62%, rgba(0, 0, 0, .25) 74%, rgba(0, 0, 0, .07) 86%, transparent 96%);
  }
  .contact-title { margin-bottom: 40px; }
  .contact-info { min-width: 0; }
  .contact-portrait img { transform: scale(var(--pz-m, 1)); transform-origin: var(--px-m, 50%) var(--py-m, 25%); }
}
@media (max-width: 767px) { body:has(.contact-page) .site-header { margin-bottom: -96px; } }
@media (max-width: 599px) { /* phones: the portrait on top, everything below it; the page may scroll */
  .contact-page { display: block; padding: 46svh var(--edge) 72px; }
  .contact-portrait { height: 78svh; }
}
.contact-page.no-portrait { padding-top: 160px; }
/* desktop (mouse, wide): the page is one full screen, the footer follows below; the details start at 77% of the
   content width, never past the right edge (site.js: --info-x) */
@media (hover: hover) and (pointer: fine) and (min-width: 1024px) {
  .contact-page { min-height: 100svh; }
  .contact-portrait { bottom: 0; }
  .contact-page[data-info-x] .contact-info { position: absolute; left: var(--info-x); bottom: clamp(32px, 5svh, 64px); width: max-content; min-width: 0; }
  /* the portrait stands on the bottom of the screen and stays there (fixed) while the page scrolls the footer in:
     title and details glide up, the footer slides over the fading portrait. Full height, in the middle of the screen;
     title and details lie over it (design Contact_2.0_Desktop) */
  .contact-page[data-info-x] .contact-portrait {
    --h: calc(100svh - 88px);
    position: fixed; top: auto; bottom: 0; height: var(--h); left: 0; right: 0; translate: none; display: flex; justify-content: center;
  }
}
/* entrance (site.js adds .will-reveal, then .is-in): the portrait is drawn up from the bottom edge while it settles
   from a slight zoom, like the project images; the details rise one after another */
@media (prefers-reduced-motion: no-preference) {
  .contact-page.will-reveal .contact-portrait img {
    clip-path: inset(100% 0 0 0); scale: 1.06; /* settles towards the crop's focus point (transform-origin above) */
    transition: clip-path 1.5s cubic-bezier(.25, 1, .5, 1), scale 2s cubic-bezier(.16, 1, .3, 1);
  }
  .contact-page.will-reveal.is-in .contact-portrait img { clip-path: inset(0); scale: 1; }
  .contact-page.will-reveal .contact-info > * {
    opacity: 0; translate: 0 24px;
    transition: opacity .9s var(--ease), translate 1.2s cubic-bezier(.16, 1, .3, 1);
  }
  .contact-page.will-reveal.is-in .contact-info > * { opacity: 1; translate: none; }
  .contact-page.will-reveal.is-in .contact-info > :nth-child(1) { transition-delay: .35s; }
  .contact-page.will-reveal.is-in .contact-info > :nth-child(2) { transition-delay: .42s; }
  .contact-page.will-reveal.is-in .contact-info > :nth-child(3) { transition-delay: .49s; }
  .contact-page.will-reveal.is-in .contact-info > :nth-child(4) { transition-delay: .56s; }
  .contact-page.will-reveal.is-in .contact-info > :nth-child(5) { transition-delay: .63s; }
}

/* ---------- contact & error ---------- */
.contact { display: grid; align-content: center; justify-items: center; gap: 64px; padding: 4rem var(--gutter); text-align: center; min-height: 60vh; }
/* "let's work together": Owners Medium 140 / 140 at 1440 and up, 54 / 54 on phones */
.contact > .display { font: 500 clamp(54px, 9.73vw, 140px)/1 var(--hero); letter-spacing: -.01em; }
/* end of the home page and of About: a big title, left-aligned, and two links (call, write) underneath */
.home-contact { display: grid; justify-items: start; gap: clamp(32px, 26.0571px + 1.5238vw, 48px); } /* Typografie → Abstände: Abschluss */
.home-cta-title { /* 87 / 96 at 1440, 38 / 44 on phones (Typografie: Kontakt → Abschluss Startseite) */
  margin: 0; font: 500 clamp(38px, 19.8px + 4.6667vw, 87px)/clamp(44px, 24.6857px + 4.9524vw, 96px) var(--display); letter-spacing: -.01em;
}
.home-cta-links { margin: 0; display: flex; flex-wrap: wrap; gap: 8px 24px; }

/* ---------- legal ---------- */
.legal { margin-block: 3rem 80px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 60px; } /* long URLs must not widen the column */
.legal h1 { font: 500 54px/64px var(--body); letter-spacing: -0.5px; }
.legal .prose { color: var(--fg); font-size: 16px; line-height: 1.6; max-width: 72ch; overflow-wrap: break-word; }
.legal .prose :where(p, ul) + :where(p, ul) { margin-top: 1.2em; }
.legal .prose ul { padding-left: 1.2em; list-style: disc; }
.legal .prose a { overflow-wrap: anywhere; }
@media (max-width: 767px) { .legal { margin-top: 0; gap: 32px; } .legal h1 { font-size: 40px; line-height: 1.1; } }

/* ---------- custom cursor over project cards and the closing section (site.js; mouse only) ---------- */
/* the native pointer hides; a disc ("View", "Contact": data-cursor) follows it with a little inertia. Over the
   call and mail links of the closing section the disc steps aside and the normal hand shows */
.cursor {
  position: fixed; left: 0; top: 0; z-index: 50; pointer-events: none;
  width: 92px; height: 92px; margin: -46px 0 0 -46px; border-radius: 50%;
  display: grid; place-items: center; background: var(--fg); color: var(--bg);
  font: 500 16px/1 var(--display);
  scale: 0; transition: scale .5s cubic-bezier(.16, 1, .3, 1);
}
.cursor.is-on { scale: 1; }
.cursor.is-on.is-down { scale: .86; transition-duration: .2s; }
.has-cursor [data-cursor], .has-cursor [data-cursor] * { cursor: none; }
.has-cursor .home-contact[data-cursor] a { cursor: pointer; }
@media (hover: none), (pointer: coarse) { .cursor { display: none; } }

/* ---------- motion ---------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .cover { view-timeline: --cover block; }
    .cover .cover-media, .cover .cover-text { animation: linear both; animation-timeline: --cover; animation-range: exit; }
    .cover .cover-media { animation-name: cover-out-media; }
    .cover .cover-text { animation-name: cover-out-text; }
  }
}
@keyframes cover-out-media { to { translate: 0 30%; } }
@keyframes cover-out-text { to { translate: 0 -40%; } } /* moves with the scroll, stays fully opaque */

@view-transition { navigation: auto; }
::view-transition-old(root) { animation: .45s var(--ease) both page-out; }
::view-transition-new(root) { animation: .7s var(--ease) both page-in; }

/* ---------- loader: first visit of the home page (boot.js adds .loading, site.js counts and lifts it) ----------
   Only the count, black on Ghost White (0% to 100%), bottom left on the content edge; it also covers the header.
   The panels then lift slowly, one after the other, and the dark page with header and menu shows underneath. */
.loader {
  position: fixed; inset: 0; z-index: 11; display: none; /* over the header too: only the count */
  align-content: end;
  padding: 0 var(--edge) clamp(24px, 3.33vw, 48px);
}
.js.loading .loader { display: grid; }
.loader-panels { position: absolute; inset: 0; z-index: -1; display: flex; overflow: hidden; }
.loader-panels span { flex: 1; margin-right: -1px; background: var(--fg); } /* light while loading, the dark page shows as they lift */
.loader p { margin: 0; }
.loader-count { /* rises out of its mask as the page shows (site.js) */
  padding-bottom: .06em; margin-bottom: -.06em; clip-path: inset(-10% -5% 0 -5%);
  color: var(--bg); font: 500 clamp(96px, 51.8px + 11.3333vw, 215px)/.86 var(--display); letter-spacing: -.045em; font-variant-numeric: tabular-nums; /* 215 at 1440, 96 at 390 (Typografie: Lade-Zähler) */
  margin-left: -.06em; /* the 1's side bearing: the ink sits on the content edge */
}
.loader-count span { display: block; }

/* page transition curtain (animated in site.js); a popover, so it also covers the open menu */
.curtain {
  position: fixed; inset: 0; z-index: 100; width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: none; overflow: hidden; display: none; pointer-events: none;
}
.curtain:popover-open, .curtain.is-active { display: flex; pointer-events: auto; }
.curtain span { flex: 1; margin-right: -1px; background: var(--curtain); transform: translateY(101%); }
@keyframes page-out { to { opacity: 0; transform: translateY(-24px); } }
@keyframes page-in { from { opacity: 0; transform: translateY(32px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  @view-transition { navigation: none; }
}

/* ---------- small underlined links (project link, legal texts, error page, social links in the menu) ----------
   Like the footer of nivara-architecture-template: a 1px line at the bottom of the text, below the descenders,
   always there; on hover it shrinks away to the right and draws in again from the left. Every line of a wrapped link
   gets its own line. */
:where(.meta, .legal .prose, .error-link, .contact-links, .home-cta-links) a, .socials .u {
  --u: currentColor;
  text-decoration: none; padding-bottom: .08em;
  background: linear-gradient(var(--u), var(--u)) no-repeat 0 100% / 100% 1px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.socials .u { line-height: 1.2; } /* a flex item: as high as its text, like an inline link, so the line sits as close */
:where(.meta) a { --u: var(--idle); } /* grey line, white on hover */
:where(.meta) a:hover { --u: currentColor; }
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  :where(.meta, .legal .prose, .error-link, .contact-links, .home-cta-links) a:hover, .socials a:hover .u { animation: link-redraw .7s cubic-bezier(.65, 0, .35, 1); }
}
@keyframes link-redraw {
  0% { background-position-x: right; background-size: 100% 1px; }
  45% { background-position-x: right; background-size: 0% 1px; }
  46% { background-position-x: 0; background-size: 0% 1px; }
  100% { background-position-x: 0; background-size: 100% 1px; }
}
