/* ==========================================================================
   robertmetz.de — no build step, no dependencies.
   Ported from the design handoff in design/Robert Metz Website.dc.html
   ========================================================================== */

/* ---- tokens ------------------------------------------------------------ */
:root {
  --bg: #fbfcfc;
  --panel: #ffffff;
  --chip: #f2f5f5;
  /* nearly opaque on purpose: the bar floats over the full-bleed dark photography
     section, and at the handoff's 0.82 the panel composited down to rgb(211,212,212),
     dropping the nav links to 4.23:1 and the active petrol to 3.46:1. Lifting the
     backdrop with a brightness filter does not help — at that alpha the tint already
     dominates. Over light content this is visually identical to the old value. */
  --nav: rgba(251, 252, 252, 0.96);
  --fg: #0e2a2e;
  --muted: #4e6467;
  --line: #dbe4e2;
  /* Two accents, one hue. --accent keeps the handoff's brilliance and is used
     wherever the colour is a graphic: the hero word, underlines, glows, borders,
     focus rings — all of which only owe 3:1. --accent-text is the same petrol
     taken deep enough to clear 4.5:1, and carries every piece of small accent
     text plus the button fills that sit under a white label. */
  --accent: #0a8aa3;
  --accent-text: #08788d;
  --accent-fg: #f4f7f6;
  --grid: rgba(29, 52, 56, 0.06);
  --eye: rgba(10, 138, 163, 0.34);
  --hero-bg: transparent;
  --hero-line: #dbe4e2;
  /* the ground sits just off white, so hover can simply land on white — the step
     works out at the same deltaL we tuned before (0.028) */
  --hover: #ffffff;
  /* The photography section is dark in both themes, so its accent is fixed here
     rather than themed — the light-theme petrol would sit at 3.31:1 on that ground.
     Taken a step down in lightness (51% -> 44%, hue and saturation unchanged) so it
     stops reading as a shinier colour than the petrol it sits next to. */
  --accent-on-dark: #2ba1b6;

  --font-sans: "Hanken Grotesk", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;
  --pad-x: clamp(24px, 5vw, 80px);
  --maxw: 1400px;
}

:root[data-theme="dark"] {
  --bg: #23262a;
  --panel: #2b2f33;
  --chip: #33383c;
  --nav: rgba(35, 38, 42, 0.8);
  --fg: #e8eaea;
  --muted: #9aa1a3;
  --line: #3a4045;
  --accent: #37b7cc;
  --accent-text: #37b7cc;
  --accent-fg: #08222a;
  --grid: rgba(210, 225, 228, 0.06);
  --eye: rgba(55, 183, 204, 0.26);
  --hero-bg: #0d2b31;
  --hero-line: #164147;
  --hover: var(--panel);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #23262a;
    --panel: #2b2f33;
    --chip: #33383c;
    --nav: rgba(35, 38, 42, 0.8);
    --fg: #e8eaea;
    --muted: #9aa1a3;
    --line: #3a4045;
    --accent: #37b7cc;
    --accent-text: #37b7cc;
    --accent-fg: #08222a;
    --grid: rgba(210, 225, 228, 0.06);
    --eye: rgba(55, 183, 204, 0.26);
    --hero-bg: #0d2b31;
    --hero-line: #164147;
    --hover: var(--panel);
  }
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a {
  color: inherit;
  text-decoration: none;
}
img {
  max-width: 100%;
  display: block;
}
button {
  font: inherit;
  color: inherit;
}
h1,
h2,
h3,
p,
ul,
ol,
figure {
  margin: 0;
}
ul,
ol {
  padding: 0;
  list-style: none;
}

::selection {
  background: rgba(10, 138, 163, 0.24);
}
::-moz-selection {
  background: rgba(10, 138, 163, 0.24);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---- primitives -------------------------------------------------------- */
.container {
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: var(--pad-x);
}

.section {
  padding-block: clamp(64px, 9vw, 132px);
}

.section__title {
  font-size: clamp(30px, 4.4vw, 54px);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.08;
  margin-top: 18px;
}

.section__lead {
  max-width: 62ch;
  margin-top: 22px;
  font-size: clamp(16px, 1.3vw, 18px);
  color: var(--muted);
}

.kicker {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.mono {
  font-family: var(--font-mono);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 100;
  background: var(--accent-text);
  color: var(--accent-fg);
  padding: 10px 16px;
  border-radius: 8px;
  transition: top 0.2s ease;
}
.skip-link:focus {
  top: 8px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border-radius: 999px;
  padding: 13px 24px;
  cursor: pointer;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--fg);
  font-weight: 600;
  transition:
    color 0.2s ease,
    border-color 0.2s ease,
    background 0.2s ease;
}
.btn:hover {
  color: var(--accent-text);
  border-color: var(--accent-text);
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent-text);
  color: var(--accent-fg);
}
.btn--primary:hover {
  background: var(--fg);
  border-color: var(--fg);
  color: var(--bg);
}

.link-arrow {
  display: inline-block;
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--accent-text);
  border-bottom: 1px solid var(--accent-text);
  padding-bottom: 2px;
  transition: opacity 0.2s ease;
}
.link-arrow:hover {
  opacity: 0.7;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--chip);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 13px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg);
}

/* ---- keyframes (ported verbatim from the handoff) ---------------------- */
@keyframes rmGlow {
  0% {
    transform: translate(-14%, -10%) scale(0.95);
    opacity: 0.85;
  }
  50% {
    transform: translate(26%, 16%) scale(1.45);
    opacity: 1;
  }
  100% {
    transform: translate(-14%, -10%) scale(0.95);
    opacity: 0.85;
  }
}
@keyframes rmGlow2 {
  0% {
    transform: translate(14%, 22%) scale(1.2);
    opacity: 0.7;
  }
  50% {
    transform: translate(-18%, -8%) scale(0.8);
    opacity: 1;
  }
  100% {
    transform: translate(14%, 22%) scale(1.2);
    opacity: 0.7;
  }
}
@keyframes rmBounce {
  0%,
  100% {
    transform: translateY(-3px);
  }
  50% {
    transform: translateY(3px);
  }
}
@keyframes rmDrift {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}
/* travels well past the dot's own 16px halo, which rmPulse's 2.4x does not.
   The ring holds most of its opacity through the first half of the travel —
   a linear fade from the start reads as nothing at all on the dark panel. */
@keyframes rmPing {
  0% {
    transform: scale(1);
    opacity: 0.55;
  }
  45% {
    opacity: 0.3;
  }
  80% {
    transform: scale(3);
    opacity: 0;
  }
  100% {
    transform: scale(3);
    opacity: 0;
  }
}
@keyframes rmPulse {
  0% {
    transform: scale(1);
    opacity: 0.5;
  }
  70% {
    transform: scale(2.4);
    opacity: 0;
  }
  100% {
    opacity: 0;
  }
}
@keyframes rmBeat {
  0%,
  100% {
    transform: scale(1);
  }
  15% {
    transform: scale(1.25);
  }
  30% {
    transform: scale(1);
  }
  45% {
    transform: scale(1.18);
  }
}
@keyframes rmBlink {
  0%,
  48% {
    opacity: 1;
  }
  49%,
  100% {
    opacity: 0;
  }
}
@keyframes rmSpin {
  to {
    transform: rotate(360deg);
  }
}
@keyframes rmBreath {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.16);
  }
}
@keyframes rmSignal {
  from {
    stroke-dashoffset: 12;
  }
  to {
    stroke-dashoffset: -400;
  }
}

/* ---- nav --------------------------------------------------------------- */
.nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid transparent;
  box-shadow: none;
  transition:
    border-color 0.3s ease,
    box-shadow 0.3s ease;
}
/* The bar's tint and blur sit on a pseudo-element, never on .nav itself: an
   element carrying a backdrop-filter becomes the containing block for its
   fixed-position descendants, and the mobile menu panel is one of those. With
   the filter on .nav, opening the menu while scrolled trapped the fullscreen
   panel inside the bar (500x195 instead of 500x813). */
.nav::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: transparent;
  transition: background 0.3s ease;
}
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-block: 26px;
  transition: padding-block 0.3s ease;
}
.nav--scrolled {
  border-bottom-color: var(--line);
  box-shadow: 0 8px 30px -14px rgba(0, 0, 0, 0.28);
}
.nav--scrolled::before {
  background: var(--nav);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}
.nav--scrolled .nav__inner {
  padding-block: 14px;
}

/* legal pages have no hero to sit on — keep the panel from the start */
.nav--plain::before {
  background: var(--nav);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: 12px;
}
.nav__mark {
  display: block;
  overflow: visible;
  height: 42px;
  transition: height 0.25s ease;
}
.nav__mark text {
  font-family: var(--font-sans);
  font-weight: 800;
  font-size: 72px;
}
.nav--scrolled .nav__mark {
  height: 34px;
}
.nav__name {
  display: flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1.05;
}
.nav__title {
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.01em;
}
.nav__tagline {
  font-size: 10.5px;
  letter-spacing: 0.02em;
  color: var(--muted);
  margin-top: 2px;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.6vw, 34px);
}
.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  font-size: 14px;
  letter-spacing: 0.01em;
  padding: 4px 2px;
  color: var(--muted);
  transition: color 0.25s ease;
}
.nav__num {
  font-size: 11px;
  font-weight: 600;
  margin-right: 7px;
  opacity: 0.45;
  transition:
    opacity 0.25s ease,
    transform 0.25s ease;
}
/* The underline travels with the reading direction of the scroll: it grows from
   the side you are coming from and retreats toward the side you are heading to,
   so a leaving and an arriving underline never move against each other.
   --bar-enter / --bar-exit are flipped by site.js on scroll direction change;
   the defaults below are the no-JS case (in from left, out to the right). */
.nav__bar {
  position: absolute;
  left: 2px;
  right: 2px;
  bottom: -1px;
  height: 1.5px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: var(--bar-exit, right) center;
  transition: transform 0.32s cubic-bezier(0.7, 0, 0.2, 1);
}
.nav__link:hover,
.nav__link:focus-visible,
.nav__link.is-active {
  color: var(--accent-text);
}
.nav__link:hover .nav__num,
.nav__link:focus-visible .nav__num,
.nav__link.is-active .nav__num {
  opacity: 1;
}
.nav__link.is-active .nav__num {
  transform: translateY(-1px);
}
.nav__link:hover .nav__bar,
.nav__link:focus-visible .nav__bar,
.nav__link.is-active .nav__bar {
  transform: scaleX(1);
  transform-origin: var(--bar-enter, left) center;
}

.nav__tools {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-left: 6px;
}
.nav__toggle {
  font-size: 12px;
  letter-spacing: 0.06em;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 12px;
  cursor: pointer;
  transition:
    color 0.2s ease,
    border-color 0.2s ease;
}
.nav__toggle:hover {
  color: var(--accent-text);
  border-color: var(--accent-text);
}

/* ---- hero -------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  background: var(--hero-bg);
  padding: clamp(110px, 14vh, 150px) 0 clamp(48px, 7vh, 84px);
}
.hero__grid,
.hero__wash,
.hero__glow,
.hero__fade {
  position: absolute;
  pointer-events: none;
}
.hero__grid {
  inset: 0;
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 58px 58px;
  -webkit-mask-image: radial-gradient(
    130% 105% at 22% 30%,
    #000 25%,
    transparent 100%
  );
  mask-image: radial-gradient(130% 105% at 22% 30%, #000 25%, transparent 100%);
}
.hero__wash {
  top: -20%;
  left: -10%;
  width: 75%;
  height: 120%;
  background: radial-gradient(closest-side, var(--eye), transparent);
}
.hero__glow--a {
  top: -6%;
  left: -6%;
  width: 66%;
  height: 96%;
  background: radial-gradient(closest-side, var(--eye), transparent);
  filter: blur(34px);
}
.hero__glow--b {
  top: 8%;
  left: 26%;
  width: 46%;
  height: 78%;
  background: radial-gradient(closest-side, var(--eye), transparent);
  filter: blur(40px);
}
.hero__fade {
  left: 0;
  right: 0;
  bottom: 0;
  height: clamp(90px, 16vh, 200px);
  background: linear-gradient(to bottom, transparent, var(--bg));
}
.hero__inner {
  position: relative;
  width: 100%;
}
.hero__eyebrow {
  font-weight: 700;
  color: var(--accent-text);
  font-size: 14px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  margin-bottom: clamp(22px, 3vh, 34px);
}
.hero__title {
  font-weight: 800;
  font-size: clamp(40px, 10.5vw, 132px);
  line-height: 0.9;
  letter-spacing: -0.035em;
  margin-bottom: clamp(28px, 4vh, 40px);
}
.hero__title span {
  display: block;
}
.hero__accent {
  color: var(--accent);
}
.hero__lead {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--muted);
  max-width: 46ch;
  margin-bottom: clamp(30px, 4vh, 44px);
}

.cta {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  background: var(--accent-text);
  color: var(--accent-fg);
  font-size: 15px;
  font-weight: 700;
  padding: 15px 26px;
  border-radius: 10px;
  cursor: pointer;
  transition:
    transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1),
    box-shadow 0.28s ease;
}
.cta--round {
  border-radius: 999px;
}
.cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 32px -12px var(--eye);
}
.cta__icon {
  flex: none;
  display: flex;
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.cta:hover .cta__icon {
  transform: scale(1.18);
}

.scroll-hint {
  position: fixed;
  /* hugs the viewport edge rather than the content column, so it stays clear of
     the hero copy, the CTA and the section text underneath it */
  right: clamp(14px, 1.8vw, 28px);
  bottom: clamp(48px, 7vh, 84px);
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
  height: 96px;
  color: var(--muted);
  transition: color 0.2s ease;
}
.scroll-hint:hover {
  color: var(--accent-text);
}
.scroll-hint__label {
  font-size: 11px;
  letter-spacing: 0.22em;
  writing-mode: vertical-rl;
  opacity: 1;
  transform: translateY(0) scale(1);
  transition:
    opacity 0.4s ease,
    transform 0.5s cubic-bezier(0.7, 0, 0.2, 1);
}
.scroll-hint__circle {
  flex: none;
  width: 34px;
  height: 34px;
  border: 1px solid var(--hero-line);
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: rotate(0deg);
  transition:
    transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1),
    border-color 0.4s ease;
}
.scroll-hint__arrow {
  display: flex;
}

/* scrolled: label fades away, the circle flips so the arrow points back up */
.scroll-hint.is-scrolled .scroll-hint__label {
  opacity: 0;
  transform: translateY(14px) scale(0.9);
}
.scroll-hint.is-scrolled .scroll-hint__circle {
  transform: rotate(180deg);
  border-color: var(--line);
}

/* ---- sections ---------------------------------------------------------- */
.section[id],
.photo[id] {
  scroll-margin-top: 90px;
}
.container--inset {
  padding-inline: calc(var(--pad-x) + clamp(0px, 6vw, 64px));
}
.section--dev {
  padding-bottom: clamp(48px, 6vw, 64px);
}
.section--projects {
  padding-top: clamp(48px, 6vw, 64px);
}
.kicker--accent {
  color: var(--accent-text);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: none;
}
.subsection__title {
  font-weight: 700;
  font-size: clamp(22px, 2.6vw, 28px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin: 14px 0 28px;
}

/* ---- cards ------------------------------------------------------------- */
.grid-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 16px;
  overflow: hidden;
  margin-top: clamp(36px, 4vw, 48px);
}
.card {
  background: var(--bg);
  padding: clamp(26px, 3vw, 32px) clamp(24px, 2.5vw, 30px);
  display: flex;
  flex-direction: column;
  transition: background 0.25s ease;
}
.card:hover {
  background: var(--hover);
}
.card__num {
  font-size: 13px;
  color: var(--accent-text);
  margin-bottom: 16px;
}
.card__title {
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
}
.card--project .card__title {
  font-size: 19px;
  margin-bottom: 4px;
}
.card__sub {
  font-size: 12px;
  color: var(--accent-text);
  margin-bottom: 14px;
}
.card__body {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--muted);
}
.card--project .card__body {
  font-size: 14px;
  margin-bottom: 20px;
}
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: auto;
}
.tag {
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 4px 9px;
}

/* ---- KI box ------------------------------------------------------------ */
.ai-box {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  margin-top: clamp(40px, 5vw, 56px);
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--panel);
  padding: clamp(28px, 3.5vw, 40px);
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: clamp(20px, 3vw, 44px);
  align-items: start;
}
/* blueprint grid, faded out toward the text column */
.ai-box::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: radial-gradient(
    120% 130% at 78% 20%,
    #000 0%,
    transparent 72%
  );
  mask-image: radial-gradient(120% 130% at 78% 20%, #000 0%, transparent 72%);
}
.ai-box > div {
  position: relative;
  z-index: 2;
}
.ai-box__label {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}
.ai-box__label .mono {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.ai-box__dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--eye);
}
/* The ring the dot pulses. It is an outline, not a filled circle: the dot sits
   in a static 16px --eye halo, and a filled ring fading out inside that halo is
   invisible. Transparent at rest, so reduced motion sees nothing. */
.ai-box__dot::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 999px;
  box-shadow: 0 0 0 1.5px var(--accent);
  opacity: 0;
}
.ai-box__body {
  font-size: 16px;
  line-height: 1.7;
  max-width: 62ch;
  /* The graph is cropped to the box height, not scaled to it, so the box has to
     stay about five lines tall or the outer nodes press into the rounded
     corners. English runs a line shorter than German at most widths, which is
     what exposed it — reserving the height here keeps both languages framing
     the graph the same way. */
  min-height: 8.5em;
}
.ai-box__tools {
  /* start level with the paragraph rather than the label: the label is one
     12px mono line plus its 16px gap, so the list is pushed down by exactly
     that much and the first tool lines up with "Die Programmierung …" */
  margin-top: calc(12px * 1.55 + 16px);
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.ai-box__tools li {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  color: var(--muted);
}
/* Claude and Scribe are single-colour art, so they are painted as masks like
   the client logos and follow the accent, hover included. Solo cannot: its
   layered stack only reads if the layers keep their step, and a mask would
   flatten it to a filled square — it ships as an <img> with the ramp baked in
   (a petrol that clears both grounds), which is why it takes no hover tint. */
.ai-box__icon {
  flex: none;
  display: block;
  width: 26px;
  height: 26px;
}
.ai-box__icon--claude,
.ai-box__icon--scribe {
  background: var(--accent-text);
  -webkit-mask: var(--logo) center / contain no-repeat;
  mask: var(--logo) center / contain no-repeat;
  transition: background 0.2s ease;
}
.ai-box__icon--claude {
  --logo: url("/assets/img/logos/claude.svg");
}
.ai-box__icon--scribe {
  --logo: url("/assets/img/logos/scribe.svg");
}
.ai-box__icon--solo {
  /* the artwork is 25.2x28, so the square box would stretch it without this */
  object-fit: contain;
  border-radius: 6px;
}

/* The graph sits in the right half only; the mask keeps it off the paragraph,
   and the whole svg is decoration — the pulses are the only animated part. */
.ai-box__net {
  position: absolute;
  z-index: 1;
  inset: 0;
  width: 100%;
  height: 100%;
  color: var(--accent);
  -webkit-mask-image: linear-gradient(90deg, transparent 55%, #000 76%);
  mask-image: linear-gradient(90deg, transparent 55%, #000 76%);
}
.ai-box__links {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  opacity: 0.3;
  vector-effect: non-scaling-stroke;
}
.ai-box__nodes {
  fill: currentColor;
  opacity: 0.45;
}
.ai-box__pulses {
  display: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  opacity: 0.9;
}
/* Below ~1024 the two-column box gets narrow enough that the 55% fade starts
   on the tool column itself — the curves crossed the Claude/Solo/Scribe labels
   and a node landed on "Scribe". The graph keeps its place; only the fade moves
   right, past where the longest label ends. */
@media (min-width: 860px) and (max-width: 1023px) {
  .ai-box__net {
    -webkit-mask-image: linear-gradient(90deg, transparent 72%, #000 90%);
    mask-image: linear-gradient(90deg, transparent 72%, #000 90%);
  }
}

/* ---- marquee ----------------------------------------------------------- */
.marquee {
  margin-top: clamp(44px, 6vw, 64px);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding-block: clamp(30px, 4vw, 42px);
}
.marquee__mask {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent,
    #000 6%,
    #000 94%,
    transparent
  );
  mask-image: linear-gradient(
    90deg,
    transparent,
    #000 6%,
    #000 94%,
    transparent
  );
}
.marquee__track {
  display: flex;
  width: max-content;
  align-items: stretch;
}
.marquee__group {
  display: flex;
  flex-direction: column;
  gap: 16px;
  justify-content: center;
  padding: 0 clamp(30px, 4vw, 48px);
  border-right: 1px solid var(--line);
}
.marquee__label {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.marquee__logos {
  display: flex;
  align-items: center;
  gap: clamp(26px, 3.2vw, 40px);
}
.marquee__logo {
  flex: none;
  color: var(--muted);
  opacity: 0.5;
  transition:
    color 0.3s ease,
    opacity 0.3s ease;
}
.marquee__logo:hover {
  color: var(--accent-text);
  opacity: 1;
}
.marquee__logo svg {
  display: block;
}

/* ---- clients ----------------------------------------------------------- */
.clients {
  margin-top: clamp(44px, 6vw, 60px);
}
.clients__logos {
  /* Grows with the viewport so the row fills the column on wide screens
     instead of trailing off; the cap keeps it from outgrowing the gaps. */
  --logo-h: clamp(26px, 2.05vw, 30px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* centred rather than left-aligned: on desktop the row fills the column, so
     it makes no visible difference there, but every wrapped row below that
     stays balanced instead of trailing off to the left. */
  justify-content: center;
  /* Capped well below the column: past 1400px the container stops growing
     while its padding keeps going, so the row loses width exactly where a
     vw-scaled gap would gain it — at 44px the eight logos wrapped again
     around 1480px. */
  column-gap: clamp(26px, 2.8vw, 34px);
  row-gap: clamp(28px, 3vw, 36px);
  margin-top: 26px;
}

/* Each logo is painted as a silhouette: the file supplies only its alpha via
   mask, the colour comes from the theme. That way white-art PNGs and colour
   SVGs behave identically and both themes are handled without per-file rules.
   Width is capped so very wide wordmarks end up optically shorter rather than
   dwarfing the stacked ones. */
.client-logo {
  display: block;
  height: calc(var(--logo-h, 26px) * var(--scale, 1));
  width: min(
    calc(var(--logo-h, 26px) * 5.8),
    calc(var(--logo-h, 26px) * var(--scale, 1) * var(--ratio, 3))
  );
  background: var(--muted);
  -webkit-mask: var(--logo) center / contain no-repeat;
  mask: var(--logo) center / contain no-repeat;
  opacity: 0.62;
  transition:
    background 0.25s ease,
    opacity 0.25s ease;
}
.client-logo:hover {
  background: var(--accent-text);
  opacity: 1;
}

/* no mask support: show nothing rather than solid blocks — the aria-label
   still carries the brand name */
@supports not ((mask-image: url("")) or (-webkit-mask-image: url(""))) {
  .client-logo {
    background: none;
  }
}

/* ---- photography (full-bleed dark) ------------------------------------- */
.photo {
  background: #1c1c1e;
  color: #ececed;
  padding-block: clamp(80px, 10vw, 120px);
}
.photo__top {
  display: flex;
  gap: clamp(32px, 5vw, 72px);
  align-items: flex-end;
  margin-bottom: clamp(38px, 5vw, 52px);
}
.photo__intro {
  flex: 1 1 auto;
  min-width: 0;
}
.kicker--photo {
  color: var(--accent-on-dark);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: none;
}
.photo__body {
  font-size: 17px;
  line-height: 1.7;
  color: #a2a2a6;
  max-width: 56ch;
  margin-top: 18px;
}
.photo__tools {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  gap: clamp(28px, 3vw, 44px);
}
.photo__toolgroup {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}
.photo__toolgroup + .photo__toolgroup {
  border-left: 1px solid rgba(255, 255, 255, 0.12);
  padding-left: clamp(24px, 3vw, 44px);
}
.photo__toollabel {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #8a8a8e;
}
.photo__logos {
  display: flex;
  align-items: center;
  gap: clamp(22px, 2.4vw, 34px);
}
.photo__logo {
  flex: none;
  display: flex;
  align-items: center;
  color: #7d7d81;
  opacity: 0.75;
  transition:
    color 0.3s ease,
    opacity 0.3s ease;
}
.photo__logo:hover {
  color: var(--accent-on-dark);
  opacity: 1;
}
.photo__logo svg {
  display: block;
}
.photo__cta {
  text-align: center;
  margin-top: 26px;
}
.link-arrow--photo {
  color: var(--accent-on-dark);
  border-bottom-color: var(--accent-on-dark);
}

/* ---- slider ------------------------------------------------------------ */
.slider {
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  background: #111113;
}
.slider__viewport {
  overflow: hidden;
  /* the horizontal drag belongs to the slider, the vertical one to the page —
     without this Safari can claim the swipe for its own panning */
  touch-action: pan-y;
}
.slider__track {
  display: flex;
  transition: transform 0.55s cubic-bezier(0.7, 0, 0.2, 1);
}
.slider__slide {
  flex: 0 0 100%;
  aspect-ratio: 16 / 9;
}
.slider__slide picture,
.slider__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Shown only without JS, where the track stays empty. */
.slider__fallback {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
.slider__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(20, 20, 22, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background 0.2s ease,
    border-color 0.2s ease,
    transform 0.2s ease;
}
.slider__nav--prev {
  left: 16px;
}
.slider__nav--next {
  right: 16px;
}
.slider__nav:hover,
.slider__nav:focus-visible {
  background: rgba(70, 70, 74, 0.82);
  border-color: rgba(255, 255, 255, 0.55);
  transform: translateY(-50%) scale(1.06);
}
.slider__status {
  position: absolute;
  left: 18px;
  bottom: 16px;
  font-size: 12px;
  letter-spacing: 0.08em;
  color: #fff;
  background: rgba(20, 20, 22, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  padding: 6px 11px;
  border-radius: 999px;
}
.slider__dots {
  display: flex;
  justify-content: center;
  gap: 9px;
  margin-top: 20px;
}
.slider__dot {
  width: 8px;
  height: 8px;
  border: 0;
  padding: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.28);
  cursor: pointer;
  transition:
    width 0.2s ease,
    background 0.2s ease;
}
.slider__dot[aria-current="true"] {
  width: 22px;
  background: var(--accent-on-dark);
}

/* ---- contact ----------------------------------------------------------- */
.section--contact {
  padding-bottom: clamp(40px, 6vw, 56px);
}
.contact__inner {
  /* No rule on top: the dark photography section above already ends the
     page's flow, and no other section opens with one. The section's own
     padding carries the separation. */
  padding-top: clamp(6px, 1vw, 12px);
}
.contact__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 14px 24px;
  margin-bottom: clamp(24px, 3.5vw, 36px);
}
.avail {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  color: var(--muted);
}
.avail__dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: #3fa66a;
  flex: none;
}
.avail__dot::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: #3fa66a;
}
.contact__statement {
  font-weight: 800;
  font-size: clamp(32px, 5.2vw, 58px);
  line-height: 1.08;
  letter-spacing: -0.03em;
  max-width: 18ch;
  margin-bottom: clamp(30px, 4vw, 44px);
}
.contact__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 24px;
  margin-bottom: clamp(34px, 5vw, 52px);
}
.contact__mail {
  font-weight: 700;
  font-size: clamp(19px, 2.4vw, 26px);
  letter-spacing: -0.01em;
  transition: color 0.2s ease;
}
.contact__mail:hover {
  color: var(--accent-text);
}
/* Below ~449px the button (199px) and the address (178px) can no longer share
   the 24px-gapped line, so they stack — and a pill sized to its label left an
   orphan above an equally short address. Widening the button to the column
   gives the stack an edge to sit against. This threshold is the content's,
   not the layout's: the 859px breakpoint still owns every structural change. */
@media (max-width: 448px) {
  .contact__actions {
    row-gap: 18px;
  }
  .contact__actions .cta {
    width: 100%;
    justify-content: center;
  }
  /* Centred with the button's label rather than with the copy above: at this
     width the two read as one call to action, and the address left-aligned
     under a full-width button looked like a stray line. */
  .contact__mail {
    flex: 1 1 100%;
    text-align: center;
  }
}
.socials {
  /* The band between the two rules is inset by the same amount top and
     bottom (see .meta), so the address does not sit closer to the lower
     line than the links sit to the upper one. */
  padding-top: clamp(26px, 3vw, 32px);
  border-top: 1px solid var(--line);
}
.socials__list {
  display: flex;
  flex-wrap: wrap;
  /* Rows of the list stay tighter than the space below it, so the address
     reads as its own thing rather than as one more wrapped row. */
  gap: 16px 44px;
  align-items: center;
}
.socials__list a {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  font-weight: 600;
  font-size: 18px;
  transition: color 0.2s ease;
}
.socials__list a:hover {
  color: var(--accent-text);
}
.socials__list .mono {
  font-size: 12px;
  color: var(--muted);
}
.contact__addr {
  margin-top: clamp(30px, 3.6vw, 38px);
  font-size: 12.5px;
  color: var(--muted);
  font-style: normal;
}

/* ---- meta row ---------------------------------------------------------- */
.meta {
  margin-top: clamp(26px, 3vw, 32px);
  padding-top: clamp(22px, 2.4vw, 26px);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 14px 26px;
  font-size: 12px;
  color: var(--muted);
}
.meta__links {
  display: flex;
  gap: 22px;
}
.meta__links a {
  transition: color 0.2s ease;
}
.meta__links a:hover {
  color: var(--accent-text);
}

/* ---- signoff ----------------------------------------------------------- */
.signoff {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 5px clamp(20px, 5vw, 80px);
  border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--fg) 4%, var(--bg));
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  line-height: 1.25;
  color: var(--fg);
}
:root[data-theme="dark"] .signoff {
  background: color-mix(in srgb, #fff 4%, var(--bg));
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .signoff {
    background: color-mix(in srgb, #fff 4%, var(--bg));
  }
}
.signoff__mark,
.signoff__spin {
  display: inline-flex;
  align-items: center;
}
.signoff__sep {
  opacity: 0.4;
}
.signoff__role {
  color: var(--muted);
}

/* ---- animation (opt-in) ------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  .hero__glow--a {
    animation: rmGlow 11s ease-in-out infinite;
  }
  .hero__glow--b {
    animation: rmGlow2 15s ease-in-out infinite;
  }
  .scroll-hint:not(.is-scrolled) .scroll-hint__arrow {
    animation: rmBounce 2s ease-in-out infinite;
  }
  .marquee__track {
    animation: rmDrift 46s linear infinite;
  }
  .avail__dot::before {
    animation: rmPulse 2.4s ease-out infinite;
  }
  .signoff__mark {
    animation: rmBreath 3.4s ease-in-out infinite;
  }
  .signoff__spin {
    animation: rmSpin 16s linear infinite;
  }
  /* four short signals travelling the graph, staggered so the box never goes
     completely still — the dash pattern is one visible segment on a very long
     gap, so the stroke reads as a pulse rather than a marching-ants line */
  .ai-box__pulses {
    display: block;
  }
  .ai-box__pulses path {
    stroke-dasharray: 12 400;
    animation: rmSignal 5.2s linear infinite;
  }
  .ai-box__pulses path:nth-child(2) {
    animation-delay: 1.3s;
  }
  .ai-box__pulses path:nth-child(3) {
    animation-delay: 2.6s;
  }
  .ai-box__pulses path:nth-child(4) {
    animation-delay: 3.9s;
  }
  /* the status dot, at every width so the label behaves the same in both
     layouts. Deliberately quiet: it is a sign of life next to the graph, not a
     second thing competing with it. */
  .ai-box__dot::before {
    animation: rmPing 3.2s ease-out infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .slider__track {
    transition: none;
  }
}

/* ---- mobile ------------------------------------------------------------ */
.nav__burger {
  display: none;
}

@media (max-width: 859px) {
  .container--inset {
    padding-inline: clamp(20px, 5vw, 80px);
  }
  /* the logos below it are centred at every width; on phones, where they
     always wrap, the label would otherwise hang off to one side */
  .clients .kicker {
    text-align: center;
  }
  .grid-cards {
    grid-template-columns: 1fr;
  }
  .ai-box {
    grid-template-columns: 1fr;
  }
  /* One column leaves no room for the graph beside the paragraph, so it moves
     to the space beside the tool list and is centred on it: the list is three
     26px rows with 16px gaps, so its middle sits (padding + 55px) above the
     bottom edge. The graph is taller than that band and runs past the box on
     both sides — .ai-box clips it. Masked in from the left so the curves enter
     as loose ends clear of the labels. Width must be explicit: an svg with
     width:auto takes its intrinsic 400:120 ratio from the viewBox and
     overflows. */
  .ai-box__net {
    inset: auto 0 calc(clamp(28px, 3.5vw, 40px) - 50px) auto;
    width: 65%;
    height: 210px;
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 26%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 26%);
  }
  /* nothing left to line up with once the columns stack */
  .ai-box__tools {
    margin-top: 0;
  }
  /* the reserve frames the desktop crop; here the graph sits against the tool
     list instead, and the text runs longer than five lines anyway */
  .ai-box__body {
    min-height: 0;
  }
  .hero {
    justify-content: center;
  }
  .scroll-hint {
    display: none;
  }
  .photo__top {
    flex-direction: column;
    align-items: flex-start;
  }
  /* Both groups stay on one line: the wide Canon wordmark is what forced the
     break, so the marks scale down by height and the gaps tighten. At 20px
     the pair needs ~261px, which clears the narrowest phone column (272px at
     a 320px viewport). */
  .photo__tools {
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 16px;
  }
  .photo__toolgroup + .photo__toolgroup {
    padding-left: 16px;
  }
  .photo__logos {
    gap: 16px;
  }
  .photo__logo svg {
    height: 20px;
    width: auto;
  }
  /* "Kamera & Ausrüstung" is wider than the marks under it, so the label sets
     the group's width — it has to come down too. */
  .photo__toollabel {
    font-size: 10px;
    letter-spacing: 0.04em;
  }
  .slider__slide {
    aspect-ratio: 3 / 2;
  }
  /* Swiping is the primary gesture on a phone, so the controls step back:
     smaller, tighter to the edge, and the counter down to a marker. They
     still clear the 24px minimum target size. */
  .slider__nav {
    width: 34px;
    height: 34px;
    font-size: 14px;
  }
  .slider__nav--prev {
    left: 8px;
  }
  .slider__nav--next {
    right: 8px;
  }
  .slider__status {
    left: 10px;
    bottom: 10px;
    font-size: 10px;
    letter-spacing: 0.06em;
    padding: 3px 8px;
  }

  .nav__burger {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    padding: 0;
    background: transparent;
    border: 0;
    cursor: pointer;
    position: relative;
    z-index: 60;
  }
  .nav__burger-bar {
    display: block;
    width: 24px;
    height: 2px;
    margin: 0 auto;
    border-radius: 2px;
    background: var(--fg);
    transition:
      transform 0.35s cubic-bezier(0.7, 0, 0.2, 1),
      opacity 0.25s ease;
  }
  #menu-toggle[aria-expanded="true"] .nav__burger-bar:nth-child(2) {
    transform: translateY(7px) rotate(45deg);
  }
  #menu-toggle[aria-expanded="true"] .nav__burger-bar:nth-child(3) {
    opacity: 0;
    transform: scaleX(0.4);
  }
  #menu-toggle[aria-expanded="true"] .nav__burger-bar:nth-child(4) {
    transform: translateY(-7px) rotate(-45deg);
  }

  .nav__links {
    position: fixed;
    inset: 0;
    z-index: 55;
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
    gap: 0;
    padding: clamp(90px, 16vh, 140px) clamp(28px, 8vw, 64px)
      clamp(40px, 8vh, 72px);
    background: var(--nav);
    -webkit-backdrop-filter: blur(22px);
    backdrop-filter: blur(22px);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-12px);
    pointer-events: none;
    transition:
      opacity 0.4s ease,
      transform 0.45s cubic-bezier(0.7, 0, 0.2, 1),
      visibility 0.4s;
  }
  .nav__links.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
  }
  .nav__links .nav__link {
    display: flex;
    align-items: baseline;
    gap: 18px;
    padding-block: clamp(12px, 2vh, 18px);
    border-bottom: 1px solid var(--line);
    color: var(--fg);
  }
  .nav__links .nav__link.is-active {
    color: var(--accent-text);
  }
  .nav__links .nav__num {
    font-size: 14px;
    opacity: 1;
    color: var(--accent-text);
  }
  .nav__label {
    font-weight: 700;
    font-size: clamp(30px, 8vw, 46px);
    letter-spacing: -0.02em;
    line-height: 1;
  }
  .nav__bar {
    display: none;
  }
  .nav__links .nav__tools {
    margin-top: auto;
    padding-top: clamp(28px, 5vh, 44px);
    padding-left: 0;
    gap: 10px;
  }
  .nav__links .nav__toggle {
    font-size: 13px;
    padding: 10px 18px;
    color: var(--fg);
  }
  body.menu-open {
    overflow: hidden;
  }
}

/* ---- legal pages ------------------------------------------------------- */
.nav--plain .nav__inner {
  gap: 16px;
}
.section--legal {
  padding-top: clamp(120px, 16vh, 170px);
}
.legal {
  max-width: 115ch;
  margin-inline: 0;
}
/* match the breathing room the home page gets from .section--contact */
.footer--legal .container {
  padding-bottom: clamp(40px, 6vw, 56px);
}
.legal h1 {
  font-size: clamp(30px, 4vw, 46px);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.1;
  margin-bottom: 28px;
}
.legal h2 {
  margin-top: 2.4em;
  margin-bottom: 0.5em;
  font-size: clamp(19px, 2.2vw, 24px);
  font-weight: 700;
  letter-spacing: -0.015em;
  scroll-margin-top: 100px;
}
.legal h3 {
  margin-top: 1.8em;
  margin-bottom: 0.4em;
  font-size: 16px;
  font-weight: 600;
  color: var(--accent-text);
}
.legal p {
  margin-bottom: 1em;
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--muted);
}
.legal strong {
  color: var(--fg);
  font-weight: 600;
}
.legal a {
  color: var(--accent-text);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.legal a:hover {
  opacity: 0.75;
}
.legal__term {
  margin-top: 1.6em;
  margin-bottom: 0.3em;
}
.legal__term strong {
  color: var(--fg);
}
.legal__list {
  list-style: disc;
  padding-left: 1.3em;
  margin-bottom: 1.2em;
}
.legal__list li {
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--muted);
  margin-bottom: 0.4em;
}
.legal hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 2.6em 0 1.6em;
}
.legal small {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--muted);
  display: block;
}
.legal__toc {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 22px 26px;
  background: var(--panel);
  margin-bottom: 2.4em;
}
.legal__toc ol {
  list-style: none;
  counter-reset: none;
  margin-top: 14px;
}
.legal__toc li {
  margin-bottom: 7px;
}
.legal__toc a {
  font-size: 14.5px;
  color: var(--fg);
  text-decoration: none;
  transition: color 0.2s ease;
}
.legal__toc a:hover {
  color: var(--accent-text);
  text-decoration: underline;
}
.legal__back {
  margin-top: 3em;
}
