/* Selbst gehostete Schriften (Inter, Noto Serif JP; beide SIL Open Font License).
   Variable Fonts: eine Datei pro Unicode-Bereich deckt alle Gewichte ab. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../assets/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../assets/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Noto Serif JP';
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url('../assets/fonts/noto-serif-jp-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Noto Serif JP';
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url('../assets/fonts/noto-serif-jp-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Besucher-Cursor: schwarzer Pfeil mit weißem Rand (gleiche Form wie der
   blaue Portfolio-Cursor), Hotspot an der Pfeilspitze. Nur für Geräte mit
   Maus; klickbare Elemente behalten ihren Hand-Zeiger (cursor: pointer). */
@media (hover: hover) and (pointer: fine) {
  html {
    cursor: url('../assets/icons/cursor-visitor.svg') 2 2, auto;
  }
}

:root {
  --color-black: #000000;
  --color-white: #ffffff;
  --color-light-grey: #f4f5f3;

  --font-heading: 'Noto Serif JP', serif;
  --font-body: 'Inter', sans-serif;

  --space-300: 8px;
  --space-400: 16px;
  --space-500: 24px;
  --space-600: 32px;
  --space-700: 48px;
  --space-800: 64px;
  --space-1000: 128px;
  --space-1200: 256px;

  --radius-full: 200px;

  --content-max-width: 960px;
  --breakpoint-tablet: 770px;
  --breakpoint-mobile: 560px;

  /* Projekt-Akzentfarben (Projekt 1 & 7 final: #4C79FA / #126BFE; Rest Platzhalter, bis echte Projektfotos die Farben vorgeben) */
  --project-1-accent: 162, 158, 177;
  --project-2-accent: 212, 80, 96;
  --project-3-accent: 0, 123, 255;
  --project-4-accent: 45, 168, 108;
  --project-5-accent: 93, 92, 98;
  --project-6-accent: 100, 189, 151;
  --project-7-accent: 18, 107, 254;
}

* {
  box-sizing: border-box;
}

/* Auf Mobile (v.a. iOS Safari) ist html das eigentliche Scroll-Element --
   overflow-x:hidden nur auf body reicht dort oft nicht, um seitliches
   Rausscrollen/-ziehen zu verhindern. "clip" statt "hidden": setzt man nur
   overflow-x, erzwingt die Spec für overflow-y "auto" -- das macht html/body
   zu offiziellen Scroll-Containern und würde dadurch jedes position:sticky
   im Rest der Seite brechen (sticky bezieht sich dann auf diesen falschen
   Container statt auf den echten Viewport). "clip" unterbindet das seitliche
   Scrollen genauso, erzeugt aber keinen Scroll-Container -- unabhängig davon,
   ob die Seite gerade sticky-Elemente nutzt, ist das die sicherere Wahl.
   Eigene background-color (nicht nur auf body): iOS Safari füllt den
   Overscroll-/Bounce-Bereich am oberen/unteren Rand mit der Farbe von html,
   nicht von body -- ohne das würde dort beim Scrollen durch den Hero-Verlauf
   kurz Blau/Gelb statt Weiß durchscheinen. */
html {
  overflow-x: clip;
  background-color: var(--color-white);
}

/* Sperrt Scrollen komplett, solange eine Page-Transition-Ladeanimation läuft
   (siehe page-transition.js: lockScroll/unlockScroll) -- ohne das könnte man
   während Intro-Zählung/Ankunfts-Halt/Wisch-rein parallel schon durch die
   verdeckte Seite scrollen. */
html.is-scroll-locked {
  overflow: hidden;
}

/* Sanftes Scrollen zu den Anker-Sections (#home, #projekte, #ueber-mich) --
   nur auf Mobile (<768px). Ab Tablet/Desktop übernimmt page-transition.js
   den Scroll selbst per GSAP (langsamer, eigenes Easing) -- natives
   scroll-behavior:smooth ist dort zu schnell und nicht anpassbar. Nicht bei
   reduzierter Bewegung erzwingen. */
@media (prefers-reduced-motion: no-preference) and (max-width: 767px) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  min-height: 100vh;
  overflow-x: clip;
  font-family: var(--font-body);
  color: var(--color-black);
  background-color: var(--color-white);
}

/* ---------- Page Transition ---------- */

/* Overlay für Erstbesuch (Prozentzahl) und Seitenwechsel (Zielseiten-Name).
   Liegt über allem (z-index höher als der Header), startet standardmäßig
   voll sichtbar/deckend -- so zeigt es beim allerersten Laden sofort die
   Ladeanimation, ohne auf JS warten zu müssen. clip-path steuert den
   "von oben nach unten"-Wisch-Effekt beim Seitenwechsel (page-transition.js
   fährt ihn vor der eigentlichen Navigation von unsichtbar auf sichtbar
   hoch); beim Erstbesuch bleibt er ungenutzt, da dort nichts "aufgedeckt"
   werden muss. Bei einem echten Reload (per Navigation-Timing-API erkannt,
   siehe Inline-Script) zeigt sich derselbe Ankunfts-Effekt erneut mit dem
   eigenen Seitennamen. [data-skip] blendet ihn nur noch bei Zurück/Vorwärts
   im selben Tab sofort aus, gesetzt vom Inline-Script direkt nach diesem
   Markup, damit es keinen Blitz-Flash gibt. */
.page-transition {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(180deg, #bcd6e8 0%, #ffffff 100%);
  clip-path: inset(0% 0 0% 0);
  opacity: 1;
  pointer-events: none;
}

.page-transition[data-skip="true"] {
  display: none;
}

.page-transition__label {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(32px, 6vw, 72px);
  color: var(--color-black);
  text-align: center;
  padding: 0 var(--space-500);
  max-width: 90vw;
}

/* Fortschrittsbalken unter der Intro-Zahl (JS legt ihn nur beim Erstbesuch an). */
.page-transition__bar {
  position: absolute;
  left: 50%;
  top: calc(50% + clamp(32px, 6vw, 72px) * 0.6 + 20px);
  width: 150px;
  height: 6px;
  margin-left: -75px;
  border-radius: 999px;
  background: #ffffff;
  overflow: hidden;
}

@media (max-width: 767px) {
  .page-transition__bar {
    height: 4px;
  }
}

.page-transition__bar-fill {
  width: 100%;
  height: 100%;
  border-radius: 999px;
  background: #bcd6e8;
  transform-origin: left center;
}

/* Ohne inline-block greift translateY auf dem Span nicht zuverlässig --
   die Prozentzahl/der Name soll beim Fade sichtbar nach oben/unten wandern. */
.page-transition__value {
  display: inline-block;
}

.page-container {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1000);
}

/* Wie bei .project-slider__inner: die Content-Spalte ist bis 960px breit ohne
   Rand-Padding -- erst darunter braucht es einen Sicherheitsabstand zur
   Bildschirmkante. */
@media (max-width: 767px) {
  .page-container {
    padding: 0 var(--space-600);
  }
}

a {
  color: inherit;
}

/* ---------- Header ---------- */

.site-header {
  position: fixed;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 100vw;
  z-index: 100;

  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 20px;

  background-color: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  transition: max-width 0.5s ease, width 0.5s ease, background-color 0.5s ease, border-color 0.5s ease;
}

/* "Floating" Variante: schrumpft von Seitenbreite auf Content-Breite und
   bekommt einen weißen Hintergrund + Border. Wird per Scroll (siehe header.js)
   ab der halben Viewport-Höhe zugeschaltet; der Wechsel läuft dank der
   Transitions oben als Morph statt als hartem Cut. */
.site-header.is-floating {
  /* min() statt nur max-width: unterhalb von content-max-width + 2x32px
     (also auf Tablet) würde die Pille sonst bis an den Viewport-Rand laufen,
     weil max-width dort noch nicht greift (100% ist ja schon kleiner). */
  width: calc(100% - (var(--space-600) * 2));
  max-width: var(--content-max-width);
  background-color: var(--color-white);
  border-color: #f4f5f3;
}

/* Bold-Hover würde die Textbreite ändern und dadurch Nav/Lang-Switch daneben
   verschieben. Fix: ein unsichtbarer Bold-"Ghost" (::after) liegt im selben
   Grid-Feld wie der sichtbare Text und reserviert dessen (größere) Breite
   dauerhaft, damit der Link nie schmaler/breiter wird als im Hover-Zustand. */
.site-header__logo {
  display: inline-grid;
  margin: 0;
  font-family: var(--font-heading);
  font-size: 16px;
  white-space: nowrap;
  text-decoration: none;
  color: var(--color-black);
}

.site-header__logo::after {
  content: attr(data-text);
  grid-area: 1 / 1;
  font-weight: 700;
  visibility: hidden;
}

.site-header__logo-text {
  grid-area: 1 / 1;
  font-weight: 600;
  transition: font-weight 0.3s ease;
}

.site-header__logo:hover .site-header__logo-text {
  font-weight: 700;
}

.site-header__nav {
  /* Absolut zur Header-Mitte statt per space-between: Logo und Lang-Switch
     sind unterschiedlich breit und würden die Nav sonst aus der Mitte schieben
     (gilt für fixed und floating). */
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 8px;
}

.nav-link {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  letter-spacing: -0.32px;
  text-decoration: none;
  white-space: nowrap;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  background-color: transparent;
  border: 1px solid transparent;
  transition: background-color 0.5s ease, border-color 0.5s ease;
}

/* Fixed: Inactive per default, Hover = weißer Fill. */
.nav-link:hover {
  background-color: var(--color-white);
}

/* Floating: Inactive per default, Hover = "Primary/Active"-Look (hellgrauer Fill). */
.site-header.is-floating .nav-link:hover {
  background-color: #f4f5f3;
}

.lang-switch-wrap {
  position: relative;
}

.lang-switch {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border: none;
  border-radius: var(--radius-full);
  background-color: transparent;
  font-family: var(--font-body);
  font-size: 16px;
  letter-spacing: -0.32px;
  color: var(--color-black);
  cursor: pointer;
  transition: background-color 0.5s ease;
}

/* Fixed: Inactive per default, Hover = weißer Fill (wie die Nav-Buttons). */
.lang-switch:hover {
  background-color: var(--color-white);
}

/* Floating: steht per default schon im "Hover"-Look (weiß), Hover selbst
   geht dann einen Schritt weiter auf "Primary/Active" (hellgrauer Fill). */
.site-header.is-floating .lang-switch {
  background-color: var(--color-white);
}

.site-header.is-floating .lang-switch:hover {
  background-color: #f4f5f3;
}

.lang-switch__icon {
  width: 10px;
  height: 10px;
  transition: transform 0.5s ease;
}

/* Öffnen: gegen den Uhrzeigersinn (-180°). Schließen: derselbe Weg zurück,
   also im Uhrzeigersinn in die Ausgangsposition. */
.lang-switch-wrap.is-open .lang-switch__icon {
  transform: rotate(-180deg);
}

/* Unsichtbare Hover-Brücke über die 12px-Lücke zwischen DE und EN: ohne sie
   verlässt der Mauszeiger beim Durchqueren der Lücke kurz den Hover-Bereich
   (.lang-switch-wrap), was sofort ein "mouseleave" und damit ein Zuklappen
   auslöst. Position:absolute -- beeinflusst nicht die Höhe des Headers. */
.lang-switch-bridge {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 12px;
}

/* "Andere Sprache" (EN) -- exakt so breit wie der DE-Button (left+right:0
   spannt sie über die volle Breite des Wrappers, der sich an dessen Größe
   orientiert) und mit demselben Padding/Layout linksbündig, damit "DE" und
   "EN" exakt untereinander stehen. Vertikal hängt sie nicht an der
   Unterkante des DE-Buttons, sondern an der Unterkante des ganzen Headers
   (dessen Padding 10px + Border 1px + Rest aus der vertikalen Zentrierung
   des Flex-Headers = 12px, empirisch gemessen), damit sie bündig mit dem
   sichtbaren Header-Rand abschließt statt im Padding zu "kleben". */
.lang-switch-alt {
  position: absolute;
  top: calc(100% + 12px);
  left: 0;
  right: 0;
  margin: 0;
  display: flex;
  align-items: center;
  padding: 6px 14px;
  border: none;
  border-radius: var(--radius-full);
  background-color: transparent;
  font-family: var(--font-body);
  font-size: 16px;
  letter-spacing: -0.32px;
  color: var(--color-black);
  cursor: pointer;
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s ease, background-color 0.5s ease, visibility 0s linear 0.5s;
}

.lang-switch-wrap.is-open .lang-switch-alt {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.5s ease, background-color 0.5s ease;
}

.lang-switch-alt:hover {
  background-color: var(--color-white);
}

.site-header.is-floating .lang-switch-alt:hover {
  background-color: #f4f5f3;
}

.menu-toggle {
  display: none;
  position: relative;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  /* Ohne das legt Mobile Safari/Chrome beim Antippen kurz einen grau/
     schwarzen Highlight-Rahmen über den Button. */
  -webkit-tap-highlight-color: transparent;
}

/* Balken statt zweier gegen-eingeblendeter Icons: beide bleiben im DOM und
   drehen/verschieben sich per transform nahtlos zwischen Hamburger und X --
   dadurch ein echter Morph statt einer Überblendung. Ein und derselbe Balken
   bildet offen wie geschlossen den jeweiligen Strich, die Länge (30px) gilt
   also identisch für Hamburger-Balken und X-Schenkel. */
.menu-toggle__bar {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30px;
  height: 3.83333px;
  background-color: var(--color-black);
  transform: translate(-50%, -50%) translateY(-4.79167px);
  transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}

.menu-toggle__bar--bottom {
  transform: translate(-50%, -50%) translateY(4.79167px);
}

.menu-toggle.is-open .menu-toggle__bar--top {
  transform: translate(-50%, -50%) rotate(45deg);
}

.menu-toggle.is-open .menu-toggle__bar--bottom {
  transform: translate(-50%, -50%) rotate(-45deg);
}

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

/* Auf Mobile gibt es nur noch die "Fixed"-Pille (siehe Figma-Komponente
   "Menue", Property 1=Fixed) -- oben wie am Desktop, 12px unter der
   Geräte-Kante, komplett transparent (kein weißer Fill, auch nicht im
   geöffneten Flyout -- die Komponente hat in keinem der beiden States
   einen Background/Border). Kein Scroll-abhängiger Fixed/Floating-Wechsel
   mehr: is-floating wird zwar weiterhin (per header.js) getoggled, hier
   aber explizit mit-selektiert und auf denselben transparenten Look
   zurückgesetzt, damit auf Mobile immer nur die "Fixed"-Variante zu sehen
   ist. Auch offen (menu-open) bleibt der Look identisch -- nur das Icon
   morpht (siehe .menu-toggle). Breite orientiert sich an der Seitenbreite:
   gleicher 32px-Seitenabstand (--space-600) wie bei den anderen
   Mobile-Sections (z. B. .about__inner), Name links / Icon rechts also
   wieder per justify-content: space-between wie in der Desktop-Basisregel
   -- bleibt dabei via left:50%/translateX(-50%) (siehe Basis-Regel)
   horizontal zentriert. Die Komponente selbst hat KEIN horizontales
   Innenpadding mehr (nur noch py-300/8px vertikal) -- der 32px-Abstand zum
   Screen-Rand kommt also ausschließlich von der Außenbreite oben, nicht
   zusätzlich von innen. */
@media (max-width: 767px) {
  .site-header,
  .site-header.is-floating {
    width: calc(100% - (var(--space-600) * 2));
    max-width: none;
    justify-content: space-between;
    padding: var(--space-300) 0;
    background-color: transparent;
    border-color: transparent;
  }

  .site-header__logo {
    font-size: 16px;
  }

  .site-header__nav,
  .lang-switch-wrap {
    display: none;
  }

  .menu-toggle {
    display: flex;
  }
}

/* ---------- Blur am oberen Bildschirmrand ---------- */

/* Rein dekorativer Scrim, der den Content beim Scrollen unter dem oberen
   Bildschirmrand weichzeichnet (mit Fade nach unten, statt harter Kante).
   Breitenunabhängig (wie im Vorbild): gleiche Werte auf Mobile, Tablet
   und Desktop. */
.scroll-top-blur {
  display: block;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 112px;
  z-index: 45;
  pointer-events: none;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  mask-image: linear-gradient(#000 30%, transparent 100%);
  -webkit-mask-image: linear-gradient(#000 30%, transparent 100%);
  /* Eigener Compositing-Layer: ohne das kann iOS Safari dieses fixed
     backdrop-filter-Element mit seiner eigenen (adaptiven) Tab-/
     Adressleisten-Färbung verwechseln, die dann nach dem Scrollen über
     den Hero-Verlauf "hängen bleibt" statt wieder auf Weiß zurückzuspringen. */
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
}

/* ---------- Mobile Flyout Menü ---------- */

.mobile-flyout {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}

.mobile-flyout.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.3s ease;
}

.mobile-flyout__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, #bcd6e8 0%, #ffffff 100%);
}

.mobile-flyout__nav {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-600);
}

.mobile-flyout__links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-500);
}

.mobile-flyout__link {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 24px;
  text-decoration: none;
  color: var(--color-black);
  white-space: nowrap;
}

.mobile-flyout__lang {
  display: flex;
  align-items: center;
  gap: var(--space-400);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
}

.mobile-flyout__lang-active {
  color: var(--color-black);
}

.mobile-flyout__lang-inactive {
  color: rgba(0, 0, 0, 0.5);
  cursor: pointer;
}

/* Englisch aktiv: Seite bleibt bis zur Übersetzung (i18n.js) unsichtbar,
   damit nicht kurz Deutsch aufblitzt. */
.tf-pending body {
  visibility: hidden;
}

.mobile-flyout__divider {
  align-self: stretch;
  height: 1px;
  background: var(--color-black);
  border: none;
  margin: 0;
}

.mobile-flyout__contact {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-300);
}

.mobile-flyout__contact a {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  text-decoration: none;
  color: var(--color-black);
  white-space: nowrap;
}

.mobile-flyout__legal {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  text-decoration: none;
  color: var(--color-black);
}

@media (min-width: 768px) {
  .mobile-flyout {
    display: none;
  }
}

/* ---------- Hero ---------- */

/* .hero ist reiner "Scrollstrecken"-Wrapper (200vh = 1x sichtbarer Hero +
   1x zusätzliche Scrollstrecke für den Farbverlauf) -- nur ab Tablet/Desktop
   (min-width:768px), siehe Mobile-Override weiter unten. .hero__pin darin
   hält per nativem position:sticky Inhalt und Hintergrund fest, während der
   Wrapper durchgescrollt wird (siehe hero-gradient.js für den reinen
   Farbverlauf-Scrub, kein JS-Pin). Auf allen getesteten Geräten sichtbar
   wackelnder Hintergrund führte dazu, JEDEN JS-berechneten Pin (sowohl
   position:fixed- als auch transform-basiert) zu verwerfen -- nur natives,
   komplett vom Compositor gehandhabtes position:sticky läuft ruckelfrei.
   Das bringt auf iPhones den bekannten Nebeneffekt zurück, dass iOS Safaris
   adaptive Tab-/Adressleisten-Färbung nach dem Scrollen einen Farbrest
   "hängen" lässt -- deshalb der komplette Verzicht auf den Effekt unter
   768px (siehe unten): das betrifft gerade die Geräte mit dieser Safari-
   Eigenheit, während Tablet/Desktop davon nicht/kaum betroffen sind. */
.hero {
  position: relative;
  width: 100%;
  height: 200vh;
  margin-bottom: 0;
}

/* background-size ist relativ zur eigenen Box (300% Höhe = 3x eigene Höhe)
   statt in vh, damit es unabhängig von mobiler Browser-Chrome/Adressleiste
   exakt zur (von ScrollTrigger anhand von .hero gemessenen) Scrollstrecke
   passt. Bei 300% zeigt background-position 0% nur das erste Drittel (0-33%)
   des Verlaufs und 100% nur das letzte Drittel (67-100%) -- die Farbstopps
   sind so gesetzt, dass darin jeweils reines Blau/Weiß liegt und Gelb nur in
   der Mitte des Scrollens auftaucht (siehe hero-gradient.js für den Scrub). */
.hero__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  background-image: linear-gradient(180deg, #bcd6e8 0%, #bcd6e8 28%, #faf6e4 50%, #ffffff 72%, #ffffff 100%);
  background-repeat: no-repeat;
  background-size: 100% 300%;
  background-position: 0% 0%;
  /* .hero__cursor fährt beim Ein-/Ausblenden bewusst außerhalb des
     sichtbaren Bereichs (x = Hero-Breite + 80px) -- ohne overflow:hidden
     erzeugt das zusätzliche, seitlich rausscrollbare Layout-Breite. */
  overflow: hidden;
}

.hero__inner {
  width: 100%;
  max-width: var(--content-max-width);
  height: 100vh;
  margin: 0 auto;
  padding: 0 var(--space-600);
  display: flex;
  justify-content: center;
  align-items: center;
}

.hero__content {
  /* min-content statt 100%: Container schrumpft auf die breiteste Zeile
     (Name, nowrap), sodass der Block mit linksbündigem Inhalt exakt mittig
     sitzt. fit-content reicht nicht, da die Zeitleisten-Texte sonst auf die
     volle max-width aufgehen. */
  width: min-content;
  max-width: 447px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 48px;
}

.hero__intro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.hero__name {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 72px;
  white-space: nowrap;
}

.hero__role {
  position: relative;
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 32px;
  padding: 8px 4px;
  border: 2px solid transparent;
}

.hero__role.is-editing {
  border-color: #57afee;
}

.hero__role-text {
  white-space: nowrap;
}

.hero__role-word.is-selected {
  background: #3390ff;
  color: var(--color-white);
  border-radius: 2px;
}

.hero__role-handle {
  display: none;
  position: absolute;
  width: 6px;
  height: 6px;
  background: var(--color-white);
  border: 2px solid #57afee;
  transform: translate(-50%, -50%);
}

.hero__role.is-editing .hero__role-handle {
  display: block;
}

.hero__role-handle--tl {
  top: 0;
  left: 0;
}

.hero__role-handle--tr {
  top: 0;
  left: 100%;
}

.hero__role-handle--bl {
  top: 100%;
  left: 0;
}

.hero__role-handle--br {
  top: 100%;
  left: 100%;
}

.hero__timeline {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.hero__timeline-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.hero__timeline-item p {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
}

.hero__logo-box {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero__logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.hero__cursor {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  pointer-events: none;
  z-index: 50;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  will-change: transform;
}

.hero__cursor-arrow {
  width: 28px;
  height: 28px;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.25));
}

.hero__cursor-row {
  display: flex;
  align-items: flex-start;
  gap: 4px;
}

.hero__cursor-spacer {
  width: 17px;
  height: 23px;
  flex-shrink: 0;
}

.hero__cursor-morph {
  position: relative;
}

.hero__cursor-tag {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: left bottom;
  background: #0072F7;
  color: var(--color-white);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.36px;
  padding: 2px 4px;
  border-radius: 4px;
  white-space: nowrap;
}

/* ---------- Cursor variant: Name_Message ---------- */

.hero__cursor-bubble {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: left bottom;
  display: none;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  background: #0072F7;
  padding: 8px 16px;
  border-radius: 4px 4px 4px 24px;
}

.hero__cursor-bubble-name {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  color: var(--color-white);
  white-space: nowrap;
}

.hero__cursor-bubble-message {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
  color: var(--color-white);
  white-space: nowrap;
}

/* Dauerhafter Scroll-Hinweis unten im Hero (Icon + "Scrollen"). fixed statt
   absolute, damit er auch auf Mobile (100vh vs. sichtbare Höhe) immer am
   sichtbaren unteren Rand sitzt; blendet per JS beim ersten Scrollen aus. */
.hero__scroll-hint {
  position: fixed;
  left: 50%;
  bottom: calc(var(--space-600) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: var(--space-300);
  color: var(--color-black);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 13px;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.hero__scroll-hint.is-hidden {
  opacity: 0;
}

/* Maus-Icon: hochkantes, oben/unten voll abgerundetes Rechteck mit einem
   Punkt. Alle Maße sind ganzzahlig (1px Rand, 2px Punkt, Punkt mit festem
   left), damit der Punkt auf keiner Pixeldichte zur Seite wegrundet. Der
   Punkt läuft alle 5s: nach unten, ausfaden, oben einfaden (Klasse is-playing
   wird per JS gesetzt, sobald der Seitenübergang vorbei ist). */
.hero__scroll-hint-icon,
.slider-scroll-hint__icon {
  position: relative;
  box-sizing: border-box;
  width: 10px;
  height: 16px;
  margin: 0 3px;
  border: 1px solid currentColor;
  border-radius: 999px;
}

/* Der gemeinsame Icon-Rand (3px links/rechts) verschiebt die sichtbare Gruppe
   sonst um 1,5px aus der Mitte -- links kein Rand, rechts 3px wie bisher. */
.hero__scroll-hint-icon {
  margin-left: 0;
}

.hero__scroll-hint-icon::after,
.slider-scroll-hint__icon::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 3px;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background-color: currentColor;
}

.hero__scroll-hint.is-playing .hero__scroll-hint-icon::after,
.slider-scroll-hint.is-visible .slider-scroll-hint__icon::after {
  animation: heroScrollHintDot 5s ease-in-out 1s infinite;
}

@keyframes heroScrollHintDot {
  0%      { transform: translateY(0);   opacity: 1; }
  18%     { transform: translateY(8px); opacity: 1; }
  30%     { transform: translateY(8px); opacity: 1; }
  38%     { transform: translateY(8px); opacity: 0; }
  38.01%  { transform: translateY(0);   opacity: 0; }
  46%     { transform: translateY(0);   opacity: 1; }
  100%    { transform: translateY(0);   opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__scroll-hint.is-playing .hero__scroll-hint-icon::after,
  .slider-scroll-hint.is-visible .slider-scroll-hint__icon::after {
    animation: none;
  }
}

/* Hero-Hinweis gibt es auf Mobile nicht (nur Desktop und Tablet). Der Hinweis
   im Projekt-Slider (.slider-scroll-hint) gilt dagegen auf allen Viewports. */
@media (max-width: 767px) {
  .hero__scroll-hint {
    display: none;
  }
}

/* Hinweis "Nach unten scrollen" unten im gepinnten Projekt-Slider: gleiche
   Optik wie der Hero-Hinweis (Maus-Icon + Text), aber im Slider verankert,
   weil man dort sonst versucht, die Karten seitlich zu wischen/ziehen. */
.slider-scroll-hint {
  position: absolute;
  left: 50%;
  bottom: calc(var(--space-600) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--space-300);
  color: var(--color-black);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 13px;
  white-space: nowrap;
  pointer-events: none;
  /* Nur sichtbar, wenn project-slider.js einen seitlichen Gestenversuch
     erkannt hat (Klasse is-visible), bis normal gescrollt wird. */
  opacity: 0;
  transition: opacity 0.4s ease;
}

.slider-scroll-hint.is-visible {
  opacity: 1;
}

/* Auf Mobile sitzen die Karten (inkl. mehrzeiliger Tags) tiefer und der
   Platz ist knapp -- Hinweis deshalb näher an den unteren Rand. */
@media (max-width: 767px) {
  .slider-scroll-hint {
    bottom: calc(var(--space-400) + env(safe-area-inset-bottom, 0px));
  }
}

/* Kurze Handys (z. B. 375x667): Kartenreihe mit mehrzeiligen Tags reicht sonst
   bis in den Hinweis -- Inhalt um ca. 24px nach oben schieben. */
@media (max-width: 767px) and (max-height: 740px) {
  .project-slider {
    padding-bottom: 48px;
  }
}

/* Scroll-Hinweis: Bubble wächst beim Tippen mit dem Text (max-content), ist
   aber auf 250px begrenzt; darüber bricht der Text um. */
.hero__cursor-bubble.is-hint {
  width: max-content;
  max-width: 250px;
}

.hero__cursor-bubble.is-hint .hero__cursor-bubble-message {
  white-space: normal;
}

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

@media (max-width: 767px) {
  /* Auf Mobile kein Pin/Scroll-Effekt (siehe hero-gradient.js, dort per
     ScrollTrigger.matchMedia auf min-width:768px begrenzt) -- der animierte
     Hintergrund führte auf allen getesteten Geräten zu sichtbarem Wackeln
     beim Scrollen. Stattdessen: ganz normaler 1-Viewport-Hero mit dem
     Verlauf statisch in einem Durchgang (kein background-size/-position-
     Scrub mehr nötig). */
  .hero {
    height: 100vh;
    /* svh statt vh/dvh: die kleinste sichtbare Höhe (Adressleiste
       eingeblendet) füllt das Hero am Seitenanfang exakt, sodass von den
       Projekten darunter nichts durchblitzt -- und bleibt beim Scrollen
       stabil. dvh würde bei jedem Ein-/Ausfahren der Leiste die Höhe ändern
       und damit die ganze Seite darunter verschieben. vh = Fallback. */
    height: 100svh;
    margin-bottom: 0;
  }

  .hero__pin {
    position: static;
    height: 100%;
    background-image: linear-gradient(180deg, #bcd6e8 0%, #ffffff 100%);
    background-size: 100% 100%;
    background-position: 0% 0%;
  }

  /* Menü sitzt auf Mobile wieder oben (nicht mehr unten) -- reine Zentrierung
     (align-items: center) passt daher wieder, nur minimal (~1.5%) nach unten
     versetzt. padding-top ohne Gegenstück unten verschiebt den zentrierten
     Content etwas nach unten (Versatz = padding-top / 2). Cursor/Rolle
     positionieren sich zur Laufzeit relativ zum Rollentext
     (getBoundingClientRect), wandern also automatisch mit -- keine
     JS-Anpassung nötig. */
  .hero__inner {
    height: 100%;
    padding-top: 3vh;
  }
}

/* Zeilenumbrüche in den Zeitleisten-Zeilen sind nur ab Mobile fest vorgegeben
   (Desktop/Tablet umbrechen weiterhin natürlich per Zeilenbreite). */
.hero__timeline-break {
  display: none;
}

/* Ab hier (<=560) weicht Figma von Tablet ab: doppelter Content-Gap (Platz für
   den Cursor zwischen Rolle und Zeitleiste), feste Zeilenumbrüche und
   größerer/anders getrackter Fließtext bei den Zeitleisten-Zeilen. Name und
   Rolle werden von hero-cursor.js per JS exakt auf 100% Breite skaliert --
   die Werte hier sind nur der Startwert, bevor JS greift bzw. falls es fehlschlägt. */
@media (max-width: 560px) {
  .hero__content {
    width: 100%;
    gap: 72px;
  }

  /* .hero__intro schrumpft sonst (align-items:flex-start) auf die Breite
     seines Inhalts -- fürs exakte Breite-Fitten per JS braucht Name/Rolle
     eine verlässliche 100%-Breite zum Messen. */
  .hero__intro {
    align-self: stretch;
  }

  .hero__timeline-break {
    display: inline;
  }

  .hero__logo-box {
    width: 36px;
    height: 36px;
  }

  .hero__timeline-item p {
    font-size: 17px;
    line-height: 23px;
    letter-spacing: -0.34px;
  }

  .hero__name {
    font-size: 48px;
  }

  .hero__role {
    font-size: 22px;
  }
}

/* ---------- Project slider ---------- */

.project-slider {
  position: relative;
  width: 100%;
  height: 100vh;
  /* dvh statt vh: auf Mobile weicht 100vh von der tatsächlich sichtbaren
     Höhe ab, sobald sich die Adressleiste ein-/ausblendet -- der Pin-Start
     ("top top") landet dann nicht exakt auf der vertikal zentrierten
     Position. dvh folgt der echten sichtbaren Höhe, vh bleibt als Fallback
     für Browser ohne dvh-Support. */
  height: 100dvh;
  margin-bottom: var(--space-1200);
  overflow: hidden;
  display: flex;
  align-items: center;
  /* Leichter Versatz nach unten (Versatz = padding-top / 2), analog zum
     Mobile-Trick weiter unten -- dort per @media überschrieben. */
  padding-top: 4vh;
}

.project-slider__inner {
  width: 100%;
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-400);
}

/* Unterhalb der Content-Breite ist der Container noch nicht auf max-width
   gedeckelt (er läuft voll bis zum Screen-Rand) -- dort braucht es einen
   Sicherheitsabstand, damit Karten nicht an der Kante kleben. */
@media (max-width: 767px) {
  .project-slider {
    margin-bottom: var(--space-1000);
    /* Menü sitzt auf Mobile wieder oben -- padding-top ohne Gegenstück
       unten verschiebt die zentrierte Kartenreihe nach unten (Versatz =
       padding-top / 2), analog zum selben Trick im Hero. Ohne den alten
       Aufwärts-Versatz startet der horizontale Scroll (ScrollTrigger,
       "top top") wieder an der richtigen vertikalen Position. 8.9dvh
       ergibt (bezogen auf ~812px Referenzhöhe) einen Versatz von ca.
       36px -- 12px Basis-Zentrierung + 24px zusätzlich, wie gewünscht,
       in dvh statt px, damit es auf Mobile proportional bleibt. */
    padding-top: 8.9dvh;
  }

  .project-slider__inner {
    padding: 0 var(--space-600);
  }
}

.project-slider__track {
  display: flex;
  align-items: flex-start;
  gap: var(--space-400);
  will-change: transform;
}

.project-slider__track .project-card {
  flex-shrink: 0;
}

/* ---------- Project card ---------- */

.project-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 309px;
}

.project-card--1 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/vertrauen-durch-XAI-hero.jpg"); }
.project-card--2 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/multimodale-fahrzeugsteuerung-hero.jpg"); }
.project-card--3 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/chat-app-hero.jpg"); }
.project-card--4 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/navira-hero.jpg"); }
.project-card--5 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/automotive-voice-ux-hero.jpg"); }
.project-card--6 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/midori-hero.jpg"); }
.project-card--7 .project-card__image::before { background: center / cover no-repeat url("../assets/photos/sport1-hero.jpg"); }

.project-card__link {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-500);
  width: 100%;
  text-decoration: none;
  color: inherit;
}

.project-card__image {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: var(--space-400);
  overflow: hidden;
}

.project-card__image::before {
  content: "";
  position: absolute;
  inset: 0;
  background: #d9d9d9;
  transition: transform 0.25s ease;
}

.project-card:hover .project-card__image::before {
  transform: scale(1.05);
}

.project-card__header {
  display: flex;
  align-items: center;
  gap: var(--space-300);
}

.project-card__title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 24px;
}

.project-card__tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-300);
  width: 100%;
  margin-top: var(--space-300);
}

.project-card__tag,
.project-tag {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: -0.24px;
  color: var(--color-black);
  background: transparent;
  border: 1px solid var(--color-black);
  border-radius: var(--radius-full);
  padding: 4px 10px;
  white-space: nowrap;
}

.project-card__tag {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}

.project-card:hover .project-card__tag {
  opacity: 1;
  transform: translateY(0);
}

.project-card:hover .project-card__tag:nth-child(1) { transition-delay: 0s; }
.project-card:hover .project-card__tag:nth-child(2) { transition-delay: 0.05s; }
.project-card:hover .project-card__tag:nth-child(3) { transition-delay: 0.1s; }
.project-card:hover .project-card__tag:nth-child(4) { transition-delay: 0.15s; }
.project-card:hover .project-card__tag:nth-child(5) { transition-delay: 0.2s; }
.project-card:hover .project-card__tag:nth-child(6) { transition-delay: 0.25s; }

/* ---------- Project card: Tablet (M) ---------- */

@media (max-width: 770px) {
  .project-card {
    width: 345px;
  }

  .project-card__tags {
    margin-top: var(--space-400);
  }

  /* Kein Hover auf Touch -- die Tags/Buttons sind ab Tablet abwärts immer
     sichtbar (entspricht dem Figma-State "Hover" der L-Variante). */
  .project-card__tag {
    opacity: 1;
    transform: none;
  }
}

/* Gleiche "immer sichtbar"-Regel wie oben, aber für den Rest der
   Tablet-Breite (771-1024px, siehe Tablet-Definition weiter unten im File):
   die M-Variante oben deckt nur bis 770px ab, während Touch/kein-Hover bis
   1024px gilt. */
@media (min-width: 771px) and (max-width: 1024px) {
  .project-card__tag {
    opacity: 1;
    transform: none;
  }
}

/* ---------- Project card: Mobile (nutzt die L-Variante -- sowohl die
   S- (411x548) als auch die M-Größe (345x460) waren auf Mobile noch zu
   groß für eine gut zentrierte, gepinnte Section). ---------- */

@media (max-width: 560px) {
  .project-card {
    width: 309px;
  }

  .project-card__title {
    font-family: var(--font-body);
    font-size: 24px;
  }

  .project-card__tags {
    margin-top: var(--space-300);
  }
}

/* ---------- Über mich (Bild, Persönliches, Ausbildung & Beruf, Publikationen) ---------- */

.about {
  margin-bottom: var(--space-1000);
  /* Anker-Ziel für die Nav (#ueber-mich, siehe page-transition.js): identisch
     zu .about__photo-wrap top, damit der Anker exakt dort landet, wo das
     sticky Foto aufhört sich zu bewegen, statt weiter unten. */
  scroll-margin-top: var(--space-1000);
}

.about__inner {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}

@media (max-width: 767px) {
  .about {
    margin-bottom: var(--space-1000);
  }

  .about__inner {
    padding: 0 var(--space-600);
  }
}

@media (max-width: 560px) {
  .about__inner {
    flex-direction: column;
    gap: var(--space-700);
  }
}

/* Bild bleibt beim Scrollen durch die deutlich längere Textspalte stehen --
   gleiches Muster wie .project-content__main auf der Projektdetailseite.
   Border wie beim Floating-Header (dort #f4f5f3 = var(--color-light-grey),
   siehe .site-header.is-floating) und Hintergrund-Verlauf wie bei der Page
   Transition (siehe .page-transition, hier auf 50% Deckkraft reduziert) --
   sitzen auf dem Wrapper, damit sie durch die echte Transparenz von
   portrait.png (statt des vorherigen blickdichten JPEGs) im Bildhintergrund
   durchscheinen. */
.about__photo-wrap {
  flex-shrink: 0;
  width: 299px;
  position: sticky;
  top: var(--space-1000);
  border-radius: var(--space-400);
  border: 1px solid var(--color-light-grey);
  background: linear-gradient(180deg, rgba(188, 214, 232, 0.5) 0%, rgba(255, 255, 255, 0.5) 100%);
}

@media (max-width: 767px) {
  /* Bleibt bei 225px, solange daneben genug Platz für den Content (max.
     345px, siehe .about__content) plus mind. 24px Abstand ist. Wird der
     Platz knapp (~561-767px, bevor ab 768px das Spalten-Grid greift),
     schrumpft stattdessen das Bild -- Content-Breite und Abstand zum Bild
     bleiben dabei unangetastet. */
  .about__photo-wrap {
    width: min(225px, calc(100% - 345px - 24px));
  }
}

@media (max-width: 560px) {
  .about__photo-wrap {
    position: static;
    width: 100%;
    max-width: 325px;
  }
}

/* Border + Verlauf sitzen auf .about__photo-wrap (siehe dort). portrait.png
   hat echte Transparenz, deshalb genügt normales Stapeln -- kein
   mix-blend-mode mehr nötig (sah mit dem alten blickdichten JPEG sichtbar
   an den Kanten/im Motiv verfälscht aus). */
.about__photo {
  display: block;
  width: 100%;
  aspect-ratio: 299 / 416;
  object-fit: cover;
  object-position: center;
  border-radius: var(--space-400);
}

.about__content {
  flex: 1 1 0;
  min-width: 0;
  max-width: 464px;
  display: flex;
  flex-direction: column;
  gap: var(--space-700);
}

@media (max-width: 767px) {
  .about__content {
    max-width: 345px;
  }
}

@media (max-width: 560px) {
  .about__content {
    max-width: none;
    width: 100%;
  }
}

.about__block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-400);
}

.about__block-title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 24px;
}

/* Mobile: Body Bold statt eigener Überschriften-Größe (Inter 500, 20px,
   wie .about-list__value etc. -- kein Letter-Spacing-Trim nötig, der Titel
   hat auf Desktop/Tablet ohnehin keinen). */
@media (max-width: 560px) {
  .about__block-title {
    font-size: 20px;
  }
}

/* ---------- Über mich: Listen (Persönliches / Ausbildung & Beruf) ---------- */

.about-list {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.about-list__row,
.about-edu {
  width: 100%;
  border-bottom: 1px solid #d9d9d9;
  padding: 12px 0;
}

.about-edu {
  cursor: pointer;
  transition: transform 0.2s ease;
}

@media (hover: hover) and (pointer: fine) {
  .about-edu:hover {
    transform: translateX(6px);
  }
}

.about-list > *:first-child {
  border-top: 1px solid #d9d9d9;
}

.about-list__row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-400);
}

.about-list__label {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.36px;
  color: var(--color-black);
}

.about-list__value {
  flex: 1 1 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 18px;
  letter-spacing: -0.36px;
  color: var(--color-black);
  text-align: right;
}

/* <strong> ist hier nur semantische Hervorhebung, keine eigene Typo-Stufe --
   ohne diese Regel synthetisiert der Browser ein fetteres Gewicht als das
   geladene Inter Regular (400), das für den Rest von .about-list__value gilt. */
.about-list__value strong {
  font-weight: 400;
}

.about-list__value--lines {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.about-list__sub {
  font-weight: 400;
  font-size: 14px;
  letter-spacing: -0.28px;
}

/* Ausbildung & Beruf: Kopf (Zeitraum als eigene Zeile oben, darunter Logo +
   Titel/Institution + Chevron-Button). Nur der aktive Eintrag (State
   "Education_Active") zeigt sein Logo -- alle anderen sind "Education"
   (kein Logo). Der Chevron-Button ist immer sichtbar, hat aber laut Figma in
   keinem State einen eigenen Hintergrund. Hover (State Education_Hover bzw.
   Education_Active_Hover, oeffnet dabei nichts von selbst -- das passiert
   nur noch per Klick, siehe about.js) fuegt stattdessen dem gesamten Eintrag
   links 6px Padding hinzu (.about-edu:hover). Der Chevron dreht sich beim
   Oeffnen um 180° (Education_Active). */
.about-edu__head {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-300);
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  /* Ohne das legt Mobile Safari/Chrome beim Antippen kurz einen grau/
     schwarzen Highlight-Rahmen über den Button (siehe .menu-toggle). */
  -webkit-tap-highlight-color: transparent;
}

.about-edu__date {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  color: var(--color-black);
}

.about-edu__row {
  width: 100%;
  display: flex;
  align-items: center;
}

/* Logo + Ueberschrift bilden eine eigene Gruppe mit align-items:flex-start
   (oben buendig), unabhaengig von der Row-Ausrichtung, deren align-items:
   center nur [Gruppe] gegen den Chevron zentriert. */
.about-edu__group {
  display: flex;
  flex: 1 1 0;
  min-width: 0;
  align-items: flex-start;
}

/* Layout-Platz (Spaltenbreite) und optischer Einblend-Effekt sind bewusst
   getrennt: der Wrap klappt nur die Breite auf (0px -> 36px, damit Institut/
   Description Platz machen), das Logo selbst bleibt dabei fix 36x36 und
   faded/scaled unabhaengig davon von seinem eigenen Zentrum aus ein -- so
   "waechst" es sichtbar aus seiner eigenen Mitte statt aus der Boxkante.
   margin-right laeuft dabei synchron mit der Breite mit (0 -> 8px) statt
   pauschal per Row-Gap: sonst wuerde der Abstand zur Ueberschrift auch im
   geschlossenen Zustand (Breite 0) bestehen bleiben und Titel/Organisation
   sichtbar nach rechts aus der Flucht der Zeitraum-Zeile darueber schieben.
   margin-top schiebt das 36px-Logo zusaetzlich 4px nach unten aus der
   gemeinsamen Oberkante mit der Ueberschrift -- kein zentrierter, sondern
   ein oben ausgerichteter Blank-Space-Versatz laut Figma. */
.about-edu__logo-wrap {
  display: grid;
  grid-template-columns: 0px;
  flex-shrink: 0;
  overflow: hidden;
  margin-right: 0;
  margin-top: 0;
  transition: grid-template-columns 0.3s ease, margin-right 0.3s ease, margin-top 0.3s ease;
}

.about-edu.is-open .about-edu__logo-wrap {
  grid-template-columns: 36px;
  margin-right: var(--space-300);
  margin-top: 4px;
}

.about-edu__logo {
  width: 36px;
  height: 36px;
  object-fit: contain;
  opacity: 0;
  transform: translateX(-6px) scale(0.4);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.about-edu__logo--rounded {
  border-radius: 4px;
}

.about-edu.is-open .about-edu__logo {
  opacity: 1;
  transform: translateX(0) scale(1);
}

.about-edu__heading {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-300);
  margin-right: var(--space-400);
}

.about-edu__title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.36px;
  color: var(--color-black);
}

.about-edu__org {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  color: var(--color-black);
}

.about-edu__chevron {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: transform 0.5s ease;
}

/* Rotation folgt exakt derselben Transition wie der Sprachumschalter-Pfeil
   im Header (.lang-switch__icon, 0.5s ease) -- bewusst dieselbe Animation
   wiederverwendet statt einer eigenen Geschwindigkeit. */
.about-edu__chevron-icon {
  display: block;
  width: 12px;
  height: 12px;
  color: var(--color-black);
}

.about-edu.is-open .about-edu__chevron {
  transform: rotate(180deg);
}

/* Aufklappbarer Bereich: grid-template-rows 0fr->1fr statt max-height, damit die
   Höhe nicht geschätzt werden muss und Inhalt mit beliebiger Zeilenzahl funktioniert. */
.about-edu__body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.about-edu.is-open .about-edu__body {
  grid-template-rows: 1fr;
}

.about-edu__body-inner {
  overflow: hidden;
}

.about-edu__desc {
  margin: var(--space-400) 0 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
  color: var(--color-black);
}

.about-edu__skills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-300);
  margin-top: var(--space-400);
}

.about-edu__skills .project-tag {
  white-space: normal;
}

/* Module, die noch nicht belegt wurden: gleiche reduzierte Opacity wie
   .about-pub__view--disabled ("Bald verfügbar"), damit unbelegte und
   belegte Module inhaltlich gleichwertig, aber optisch unterscheidbar sind. */
.project-tag--upcoming {
  opacity: 0.15;
}

/* Persönliches (.about-list__*) UND Ausbildung & Beruf (.about-edu__*)
   bleiben auf Mobile bei Figma-Größe L -- exakt dieselben Schriftgrößen/
   Abstände wie auf Tablet/Desktop, kein eigenes Mobile-Layout (Figma-Größe S
   entfällt: auf Mobile gibt es ohnehin keinen Maus-Hover, der die dortigen
   Hover-States nötig hätte). */

/* "Sprachkenntnisse" -> "Sprachen" und "Vereinsfußball" -> "Fußball" (siehe
   index.html) sind bewusst kürzere Label-/Werttexte statt eines eigenen
   Mobile-Layouts: dadurch bleibt die Zeile Label/Wert nebeneinander wie auf
   Desktop/Tablet, und Zeilen wie "Spanisch Grundkenntnisse" bzw. "Sport
   Fußball & Abteilungsleiter" passen jetzt auch auf Mobile in die
   Value-Spalte, ohne dass Layout oder Ausrichtung sich unterscheiden. */

/* ---------- Über mich: Publikationen ---------- */

.about-pub-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-400);
  width: 100%;
}

.about-pub {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-400);
  padding: var(--space-500);
  border: 1px solid transparent;
  border-radius: var(--space-300);
  background-color: #f7f7f7;
  transition: background-color 0.25s ease, border-color 0.25s ease;
}

/* Neuer Hover-State: heller Fill wird weiß, Border wird schwarz -- gleiche
   Behandlung wie bei den Clifton-Strengths-Karten. */
.about-pub:hover {
  background-color: var(--color-white);
  border-color: var(--color-black);
}

.about-pub__head {
  display: flex;
  align-items: center;
  gap: var(--space-300);
  width: 100%;
}

.about-pub__logo {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  object-fit: contain;
}

.about-pub__logo--framed {
  border-radius: var(--space-300);
  background-color: var(--color-white);
}

.about-pub__event {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.about-pub__event-name,
.about-pub__event-meta,
.about-pub__title,
.about-pub__authors {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-black);
}

.about-pub__event-name {
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
}

.about-pub__event-meta {
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
}

.about-pub__title {
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.36px;
}

.about-pub__authors {
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
}

/* Titel + Autor:innen bilden laut Komponente eine eigene Gruppe mit 8px
   Abstand -- Kopfzeile/Textgruppe/Button darüber liegen jeweils 16px
   auseinander (.about-pub gap), nur innerhalb der Textgruppe sind es 8px. */
.about-pub__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-300);
  width: 100%;
}

/* "View"-Button/Badge, gemeinsames Element für beide Zustände: bei bereits
   veröffentlichten Publikationen (z.B. Auto UI) als klickbarer Link, der
   laut Komponente durchgehend im Typ "Border" bleibt (kein Farbwechsel mehr
   beim Hovern) -- einzig das Pfeil-Icon animiert beim Hovern des Buttons
   ein. Noch nicht veröffentlichte Publikationen (z.B. IIKI) nutzen dasselbe
   Element mit .about-pub__view--disabled: gleiche Position/Abstände, nur
   anderer Text und reduzierte Opacity, ohne Interaktion -- das ist die
   einzige Ausnahme von der Komponenten-Vorgabe. */
.about-pub__view {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  letter-spacing: -0.32px;
  color: var(--color-black);
  text-decoration: none;
  background-color: transparent;
  border: 1px solid var(--color-black);
  border-radius: var(--radius-full);
  padding: 6px 14px;
}

.about-pub__view-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 0;
  margin-left: 0;
  overflow: hidden;
  opacity: 0;
  transition: width 0.25s ease, margin-left 0.25s ease, opacity 0.25s ease;
}

.about-pub__view:hover .about-pub__view-icon {
  width: 10px;
  margin-left: 6px;
  opacity: 1;
}

.about-pub__view-icon-img {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  transform: rotate(-90deg);
}

.about-pub__view--disabled {
  opacity: 0.15;
  pointer-events: none;
  white-space: nowrap;
}

/* Publikationen bleiben auf Mobile bei Figma-Größe L -- exakt dieselben
   Schriftgrößen wie auf Tablet/Desktop (Figma-Größe S entfällt), analog zu
   Persönliches/Ausbildung & Beruf weiter oben. */

/* ---------- Clifton Strengths ---------- */

.clifton {
  margin-bottom: var(--space-1000);
}

.clifton__inner {
  width: 100%;
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-600);
}

@media (max-width: 767px) {
  .clifton {
    margin-bottom: var(--space-1000);
  }

  .clifton__inner {
    padding: 0 var(--space-600);
  }
}

/* Mobile (<=560): .clifton__row wird zum Karussell wie .project-carousel
   (assets/js/clifton-slider.js) -- die Reihe läuft randlos, nur die Titelzeile
   behält den Seitenabstand. */
@media (max-width: 560px) {
  .clifton__inner {
    width: 100%;
    padding: 0;
  }

  .clifton__head {
    padding: 0 var(--space-600);
  }
}

.clifton__head {
  width: 100%;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-400);
}

.clifton__head-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-300);
}

/* Pfeile (Optik = .project-carousel__arrow) nur im Mobile-Karussell. */
.clifton__head .clifton__arrows {
  display: none;
}

/* Mobile: Pfeile stehen neben dem Titel, der Subtext läuft darunter über die
   volle Breite (Textblock per display:contents ins Head-Grid aufgelöst). */
@media (max-width: 560px) {
  .clifton__head {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: var(--space-300) var(--space-400);
  }

  .clifton__head-text {
    display: contents;
  }

  .clifton__subtitle {
    grid-column: 1 / -1;
  }

  .clifton__head .clifton__arrows {
    display: flex;
    grid-column: 2;
    grid-row: 1;
  }
}

.clifton__title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 36px;
  white-space: nowrap;
}

/* Subtext-Stil wie .clifton__card-desc (Figma "Subtext"): 18px, -0.36px. */
.clifton__subtitle {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
  color: var(--color-black);
}

/* Gap zwischen den Karten: 32px auf Desktop, 16px ab Tablet (Figma-Update).
   Karten sind bis Tablet gleich breit (flex, füllen die Zeile exakt); ab
   Mobile werden sie fix breit, und assets/js/clifton-slider.js scrollt die
   Reihe horizontal mit, während die Sektion gepinnt ist -- wie beim
   Project-Slider (assets/js/project-slider.js). */
.clifton__row {
  display: flex;
  align-items: stretch;
  gap: var(--space-600);
  width: 100%;
}

@media (max-width: 767px) {
  .clifton__row {
    gap: var(--space-400);
  }
}

/* Mobile-Karussell: alle Karten liegen in derselben Grid-Zelle übereinander
   (die Zeile ist so automatisch so hoch wie die höchste Karte, alle Karten
   gleich hoch -- auch wenn eine zum Spielfeld wird); clifton-slider.js
   verschiebt sie per transform. Kartenbreite = Breite im Projekt-Karussell. */
@media (max-width: 560px) {
  .clifton__row {
    display: grid;
    gap: 0;
    overflow: hidden;
    touch-action: pan-y;
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
  }

  .clifton__row.is-dragging {
    cursor: grabbing;
  }

  .clifton__row.is-dragging .clifton__card {
    pointer-events: none;
  }
}

.clifton__card {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-400);
  padding: var(--space-500);
  border: 1px solid transparent;
  border-radius: var(--space-300);
  background-color: #f7f7f7;
  transition: background-color 0.25s ease, border-color 0.25s ease;
}

/* Default_Hover / Locked Hover: heller Fill wird weiß, Border wird schwarz.
   Nur ab Tablet aufwärts -- auf Mobile (Figma "Size=S") gibt es laut Design
   gar keinen Hover-Zustand mehr, nur noch Default oder Locked (siehe unten). */
@media (min-width: 561px) {
  .clifton__card:hover {
    background-color: var(--color-white);
    border-color: var(--color-black);
  }
}

@media (max-width: 560px) {
  .clifton__card {
    grid-area: 1 / 1;
    width: var(--cc-card-w, 345px);
    will-change: transform;
  }
}

.clifton__card-logo {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  object-fit: contain;
}

.clifton__card-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-300);
  width: 100%;
}

.clifton__card-title,
.clifton__card-desc {
  margin: 0;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
  color: var(--color-black);
}

.clifton__card-title {
  font-weight: 500;
  white-space: nowrap;
}

/* Figma "Size=S": Titel wechselt auf den größeren "Body bold"-Textstil
   (20px, ohne Letter-Spacing) statt "Subtext bold" (18px, -0.36px). */
@media (max-width: 560px) {
  .clifton__card-title {
    font-size: 20px;
    letter-spacing: normal;
  }
}

.clifton__card-desc {
  font-weight: 400;
}

/* Locked-Karte: standardmäßig (ab Tablet aufwärts, Figma "Size=L") nur das
   Schloss-Icon, exakt mittig -- der "Freischalten"-Text nimmt ohne Hover
   keinerlei Raum ein (siehe .clifton__card-unlock-wrap), sonst würde er das
   Icon aus der Mitte drücken. "Freischalten" ist dort die Figma-Variante
   "Locked Hover" -- rein visueller Hover-Reveal, kein dauerhafter Zustand.
   Kein Gap auf der Karte selbst: der Abstand zum Text steckt im Wrap (siehe
   unten), damit er beim Einklappen mitverschwindet statt eine Lücke zu
   lassen. Auf Mobile (Figma "Size=S") ist das Icon+Text-Paar dagegen IMMER
   sichtbar (siehe @media weiter unten) -- kein Hover-Zustand mehr. */
.clifton__card--locked {
  align-items: center;
  justify-content: center;
  gap: 0;
  text-align: center;
  cursor: pointer;
}

/* Während auf der jeweils anderen Karte gespielt wird oder die
   Abweis-Nachricht läuft (siehe setCardsInert() in clifton-game.js): kein
   Hover (also auch kein Schloss-Morph aus clifton-lock.js) und kein Klick,
   bis der Cursor diese Karte wieder ganz verlassen hat. */
.clifton__card--inert {
  pointer-events: none;
}

.clifton__card-lock {
  position: relative;
  width: 48px;
  height: 48px;
  flex-shrink: 0;
}

/* Das Morphen zwischen offenem und geschlossenem Schloss läuft per JS
   (assets/js/clifton-lock.js), nicht über eine CSS-"d"-Transition: Safari
   (macOS wie iPadOS) animiert die CSS-Eigenschaft "d" auf SVG-Pfaden nicht
   zuverlässig (der Icon-Switch blieb dort schlicht aus), während Chromium
   das unterstützt -- GSAP liefert denselben Ablauf in jedem Browser. Die
   Füllfarbe steckt daher direkt im inline-SVG (siehe HTML). */

/* Grid-Rows-Trick (0fr -> 1fr): animiert von "kein Platz" auf "volle Höhe",
   ohne die Zielhöhe zu kennen. So bleibt das Icon im Ruhezustand exakt
   zentriert und die ganze Gruppe (Icon + Text) rückt beim Hover gemeinsam
   und weich in die neue Mitte. */
.clifton__card-unlock-wrap {
  min-width: 0;
  display: grid;
  grid-template-rows: 0fr;
  width: 100%;
  transition: grid-template-rows 0.25s ease;
}

.clifton__card-unlock {
  margin: 0;
  margin-top: var(--space-300);
  overflow: hidden;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
  color: var(--color-black);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}

/* Hover-Reveal nur ab Tablet aufwärts (Figma "Size=L"). */
@media (min-width: 561px) {
  .clifton__card--locked:hover .clifton__card-unlock-wrap,
  .clifton__card--locked:focus-visible .clifton__card-unlock-wrap {
    grid-template-rows: 1fr;
  }

  .clifton__card--locked:hover .clifton__card-unlock,
  .clifton__card--locked:focus-visible .clifton__card-unlock {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Mobile (Figma "Size=S", aktualisiert): kein Hover-Zustand mehr -- Icon
   (dauerhaft offen, siehe clifton-lock.js) und "Freischalten"-Text sind
   hier permanent sichtbar, mit dem dort vorgesehenen 16px-Gap zwischen
   beiden statt des Hover-Collapse-Tricks. */
@media (max-width: 560px) {
  .clifton__card--locked {
    gap: var(--space-400);
  }

  .clifton__card-unlock-wrap {
    grid-template-rows: 1fr;
    transition: none;
  }

  .clifton__card-unlock {
    margin-top: 0;
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---------- Programme & Coding ---------- */

/* margin-bottom bewusst auf 2/3 des sonst hier üblichen Sections-Abstands
   reduziert (statt var(--space-1200)): das ist die letzte Section vor dem
   Footer, dessen Farbverlauf-Scrub schon losläuft, sobald der Footer am
   unteren Bildschirmrand auftaucht (siehe footer-gradient.js) -- weniger
   Leerraum davor heißt, man erreicht den Footer (und damit den Verlaufsstart)
   schneller. */
.programs {
  margin-bottom: calc(var(--space-1200) * 2 / 3);
}

.programs__inner {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0;
  display: flex;
  align-items: flex-start;
  gap: var(--space-600);
}

@media (max-width: 767px) {
  .programs__inner {
    flex-direction: column;
    padding: 0 var(--space-600);
  }
}

.programs__col {
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-400);
}

/* align-self:stretch auf Head/Filters, weil .programs__col sonst
   (align-items:flex-start) seine Kinder nur auf Inhaltsbreite schrumpft --
   erst dadurch greift overflow-x bei den Kategorien statt die Box zu
   verbreitern. */
.programs__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  align-self: stretch;
  gap: var(--space-400);
}

.programs__title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 36px;
  white-space: nowrap;
}

/* Kategorien scrollen horizontal statt umzubrechen -- vor allem auf Mobile
   darf das über den Seitenrand hinaus scrollen (siehe Media-Query unten). */
.programs__filters {
  display: flex;
  align-items: center;
  align-self: stretch;
  min-width: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: var(--space-300);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.programs__filters::-webkit-scrollbar {
  display: none;
}

/* States der Kategorie-Buttons (Figma-Komponente "Buttons", Size M): Default
   = Status "Inaktiv" (kein Fill/Border), aktiver Filter = Status "Border"
   (schwarze Outline), Hover = Status "Primary" (helles Grau) -- unabhängig
   vom Active-Status. */
.programs__filter {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  letter-spacing: -0.32px;
  color: var(--color-black);
  background-color: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  padding: 6px 14px;
  flex-shrink: 0;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.3s ease, border-color 0.3s ease;
}

.programs__filter.is-active {
  border-color: var(--color-black);
}

.programs__filter:hover {
  background-color: var(--color-light-grey);
  border-color: transparent;
}

/* Nutzt immer die komplette Breite: so viele Icons wie möglich pro Reihe,
   mit mind. 12px Abstand -- wird der unterschritten, bricht die nächste
   Spalte in die neue Zeile (repeat(auto-fill) + minmax errechnet das
   automatisch; der Rest wird gleichmäßig als Gap verteilt statt am
   Zeilenende ungenutzt zu bleiben, wie es reines flex-wrap tun würde). Gilt
   für alle Breakpoints, die Icon-Größe wechselt bei ≤560px (siehe unten). */
.programs__icons {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  justify-items: start;
  gap: 12px;
  width: 100%;
}

/* Default = helles Grau ohne Border. Hover/Active (per Klick oder Hover
   gesetzt, siehe programs.js) = weißer Fill mit schwarzer Border -- bleibt so
   stehen, bis ein anderes Icon in derselben Gruppe gehovert/angeklickt wird. */
.programs__icon {
  flex-shrink: 0;
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--space-300);
  background-color: #f7f7f7;
  cursor: pointer;
  transition: background-color 0.3s ease, border-color 0.3s ease;
}

.programs__icon.is-active {
  background-color: var(--color-white);
  border-color: var(--color-black);
}

.programs__icon[hidden] {
  display: none;
}

.programs__icon-logo {
  width: 36px;
  height: 36px;
  object-fit: contain;
}

.programs__card {
  position: relative;
  overflow: hidden;
  width: 100%;
  border: 1px solid var(--color-black);
  border-radius: var(--space-300);
  padding: var(--space-500);
}

.programs__card-text {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-400);
  transition: opacity 0.2s ease;
}

.programs__card-text.is-fading {
  opacity: 0;
}

.programs__card-title {
  margin: 0;
  font-family: var(--font-body);
  font-size: 18px;
  letter-spacing: -0.36px;
  color: var(--color-black);
  font-weight: 500;
  white-space: nowrap;
}

/* Skill- und Nutzung-Zeile enger untereinander als der Abstand zum Titel
   (der kommt weiterhin über den gap auf .programs__card-text). */
.programs__card-levels {
  display: flex;
  flex-direction: column;
  gap: var(--space-300);
}

.programs__card-skill {
  display: flex;
  align-items: center;
  gap: var(--space-400);
}

/* Feste Breite, damit die Quadrat-Reihen von "Skill" und "Nutzung" trotz
   unterschiedlich langer Labels exakt untereinander starten. */
.programs__card-skill-label {
  margin: 0;
  min-width: 64px;
  flex-shrink: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  color: var(--color-black);
}

/* Kompetenz-Level (1-5, per Icon in data-level hinterlegt): gefüllte Quadrate
   = Skill-Level, ungefüllte = Rest bis 5. Wird von programs.js beim
   Icon-Wechsel zusammen mit Titel/Beschreibung/Logo aktualisiert. */
.programs__card-level {
  display: flex;
  align-items: center;
  gap: var(--space-300);
}

.programs__card-level-dot {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: 2px;
  background-color: var(--color-black);
}

.programs__card-level-dot.is-filled {
  background-color: #0166b1;
}

/* ---------- Programme & Coding: Tablet/Desktop (768px+) ---------- */

@media (min-width: 768px) {
  .programs__card {
    width: 308px;
  }
}

/* ---------- Programme & Coding: Mobile (≤767px) ---------- */

@media (max-width: 767px) {
  .programs__filters {
    margin: 0 calc(-1 * var(--space-600));
    padding: 0 var(--space-600);
  }

  /* Überschrift darf zweizeilig umbrechen, wenn "Programme & Coding" nicht
     mehr in eine Zeile passt. */
  .programs__title {
    white-space: normal;
  }
}

/* ---------- Programme & Coding: Mobile (S, ≤560px) ---------- */

@media (max-width: 560px) {
  .programs__icon {
    width: 69px;
    height: 69px;
  }

  .programs__icons {
    grid-template-columns: repeat(auto-fill, minmax(69px, 1fr));
  }
}

/* ---------- Project detail page ---------- */

.project-page-wrap {
  position: relative;
  z-index: 0;
  width: 100%;
}

.project-hero-bg {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  width: 100%;
  height: 780px;
  background: linear-gradient(180deg, #bcd6e8 0%, #ffffff 100%);
  pointer-events: none;
}

@media (max-width: 560px) {
  .project-hero-bg {
    height: 1080px;
  }
}

.project-page,
.legal-page {
  align-items: flex-start;
  padding-top: var(--space-1000);
  margin-bottom: var(--space-1000);
}

.legal-page {
  padding-top: var(--space-1200);
}

.project-hero {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-700);
  width: 100%;
}

.project-title {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 50px;
}

/* Desktop: große Überschrift (50px). Tablet: ausgeblendet (Platz ist dort
   knapper). Mobile: wieder sichtbar, mit 32px. */
@media (min-width: 561px) and (max-width: 1024px) {
  .project-title {
    display: none;
  }
}

@media (max-width: 560px) {
  .project-title {
    font-size: 32px;
  }
}

.project-cover-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  background: var(--color-light-grey);
}

.project-cover-image--1 {
  aspect-ratio: 1920 / 1080;
  background: center / cover no-repeat url("../assets/photos/vertrauen-durch-XAI-hero-project.jpg");
}

.project-content {
  display: flex;
  align-items: flex-start;
  gap: var(--space-600);
  width: 100%;
}

.project-content__main {
  flex: 1 1 0;
  min-width: 0;
  max-width: 760px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-500);
  position: sticky;
  top: var(--space-1000);
}

.project-subtitle {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 36px;
}

.project-content__tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  width: 100%;
}

.project-content__text {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
  width: 100%;
}

/* Redaktioneller Rhythmus der rechten Spalte (Zeitungs-Look): enge, gleichmäßige
   Abstände zwischen Blöcken, Bild→Text und Titel→Text. Gilt für alle Projekte. */
.project-content__media {
  --media-stack-gap: 32px;
  --media-block-gap: 12px;
  --media-caption-gap: 4px;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--media-stack-gap);
  width: 100%;
}

.project-media-block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--media-block-gap);
  width: 100%;
}

.project-media-block__image,
.project-media-standalone {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 16px;
  background: var(--color-light-grey);
}

.project-media-block__image--szenario {
  background: center / cover no-repeat url("../assets/photos/xai-fahren-szenario-utility.jpg");
}

.project-media-block__image--hmi-mockup {
  aspect-ratio: 16 / 9;
  background: center / cover no-repeat url("../assets/photos/xai-fahren-hmi-mockup.jpg");
}

/* ---------- Projekt: Chat-App Entwicklung ---------- */

.project-cover-image--chatapp {
  aspect-ratio: 1920 / 1080;
  background: center / cover no-repeat url("../assets/photos/chat-app-hero-project.jpg");
}

.project-media-block__image--chatapp-html-vs-bootstrap {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/chat-app-0102.jpg");
}

.project-media-block__image--chatapp-login-friends {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/chat-app-03-04.jpg");
}

.project-media-block__caption {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--media-caption-gap);
  width: 100%;
}

.project-media-block__caption-title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
}

.project-media-block__caption-text {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
}

/* <strong> ist hier nur semantische Hervorhebung, keine eigene Typo-Stufe --
   ohne diese Regel synthetisiert der Browser ein fetteres Gewicht als das
   geladene Inter Medium (500), siehe .about-list__value strong. */
.project-media-block__caption-text strong {
  font-weight: 500;
}


.project-hypothesis {
  display: block;
  margin-top: 20px;
}

.project-hypothesis__label {
  display: block;
  font-weight: 600;
}

.project-image-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
}

.project-image-placeholder-note {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  color: rgba(0, 0, 0, 0.5);
}

/* ---------- Project detail page: Mobile (Small, stacked, no sticky) ---------- */

@media (max-width: 560px) {
  .project-hero {
    gap: var(--space-600);
  }

  .page-container.project-page {
    gap: var(--space-600);
  }

  .project-content {
    flex-direction: column;
    gap: var(--space-800);
  }

  .project-content__main,
  .project-content__media {
    max-width: none;
  }

  .project-content__media {
    --media-stack-gap: 24px;
    --media-block-gap: 10px;
  }

  .project-content__main {
    position: static;
    top: auto;
  }

  .project-subtitle {
    display: none;
  }

  .project-content__text,
  .project-media-block__caption-text {
    font-size: 17px;
    line-height: 23px;
    letter-spacing: normal;
  }

  .project-media-block__caption-title {
    font-size: 17px;
    line-height: 23px;
    letter-spacing: normal;
  }

  /* Zwischenüberschrift der Slider-Karten ("Projekt N"): war H3 Span (Open
     Sans Medium 24px), soll auf Mobile Body Bold (Inter Medium 20px) sein.
     Über .project-page-wrap gescoped, damit der gleiche Slider auf der
     Homepage unangetastet bleibt. */
  .project-page-wrap .project-card__title {
    font-family: var(--font-body);
    font-size: 20px;
  }
}

/* ---------- Projekt: Navira (autonome Fahrzeuge im ÖPNV) ---------- */

.project-cover-image--oepnv {
  aspect-ratio: 1600 / 900;
  background: center / cover no-repeat url("../assets/photos/navira-hero-project.jpg");
}

.project-media-block__image--oepnv-persona {
  aspect-ratio: 800 / 446;
  background: center / cover no-repeat url("../assets/photos/navira-persona.jpg");
}

.project-media-block__image--oepnv-journey {
  aspect-ratio: 800 / 425;
  background: center / cover no-repeat url("../assets/photos/navira-journey.jpg");
}

.project-media-block__image--oepnv-prototyp {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/navira-prototyp.jpg");
}

/* ---------- Projekt: Empathic AI Car (Stimme, Geschlecht & Vertrauen) ---------- */

.project-cover-image--eac {
  aspect-ratio: 1920 / 1080;
  background: center / cover no-repeat url("../assets/photos/automotive-voice-ux-hero-project.jpg");
}

.project-media-block__image--eac-setup {
  aspect-ratio: 4 / 3;
  background: center / cover no-repeat url("../assets/photos/automotive-voice-ux-simulator.jpg");
}

/* ---------- Projekt: Midori (VR-Handrehabilitation nach Schlaganfall) ---------- */

.project-cover-image--midori {
  aspect-ratio: 1819 / 1100;
  background: center / cover no-repeat url("../assets/photos/midori-hero-project.jpg");
}

.project-media-block__image--midori-moodboard {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/midori-moodboard.jpg");
}

.project-media-block__image--midori-personas {
  aspect-ratio: 800 / 532;
  background: center / cover no-repeat url("../assets/photos/midori-personas.jpg");
}

.project-media-block__image--midori-paper {
  aspect-ratio: 800 / 621;
  background: center / cover no-repeat url("../assets/photos/midori-paper-prototyping.jpg");
}

.project-media-block__image--midori-screendesign {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/midori-screendesign.jpg");
}

/* ---------- Projekt: Sport1 (Komponenten, Konzepte, Fanforschung) ---------- */

.project-cover-image--sport1 {
  aspect-ratio: 1920 / 1080;
  background: center / cover no-repeat url("../assets/photos/sport1-hero-project.jpg");
}

.project-media-block__image--sport1-komponente {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/sport1-komponente-noch-da.jpg");
}

.project-media-block__image--sport1-navigationskonzept {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/sport1-navigationskonzept-sportarten.jpg");
}

.project-media-block__image--sport1-umfrage-football {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/sport1-umfrage-american-football.jpg");
}

.project-media-block__image--sport1-liveticker-konzept {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/sport1-liveticker-konzept.jpg");
}

.project-media-block__image--sport1-landingpage {
  aspect-ratio: 1 / 1;
  background: center / cover no-repeat url("../assets/photos/sport1-landingpage-exatlon-mystylerocks.jpg");
}

/* ---------- Projekt: Multimodale Fahrzeugsteuerung (Sprache + EMG-Gesten) ---------- */

.project-cover-image--gesten {
  aspect-ratio: 1920 / 1080;
  background: center / cover no-repeat url("../assets/photos/multimodale-fahrzeugsteuerung-hero-project.jpg");
}

.project-media-block__image--gesten-simulator {
  aspect-ratio: 800 / 505;
  background: center / cover no-repeat url("../assets/photos/multimodale-fahrzeugsteuerung-simulator.jpg");
}

.project-media-block__image--gesten-calibration {
  aspect-ratio: 800 / 535;
  background: center / cover no-repeat url("../assets/photos/multimodale-fahrzeugsteuerung-calibration.jpg");
}

.project-media-block__image--gesten-live-demo {
  aspect-ratio: 800 / 439;
  background: center / cover no-repeat url("../assets/photos/multimodale-fahrzeugsteuerung-live-demo.jpg");
}

.project-media-block__video {
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  overflow: hidden;
  background: #000;
}

.project-media-block__video video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-media-block__gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-400);
  width: 100%;
}

.project-media-block__gallery img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 16px;
}

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

/* ---------- Legal page (Impressum / Datenschutz) ---------- */

.legal-block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-600);
  width: 100%;
}

.legal-title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 32px;
}

.legal-columns {
  display: flex;
  align-items: flex-start;
  gap: var(--space-400);
  width: 100%;
}

.legal-column {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-400);
}

.legal-heading {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 24px;
}

.legal-text {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
}

.legal-richtext {
  display: flex;
  flex-direction: column;
  gap: var(--space-400);
  width: 100%;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
  line-height: normal;
  color: var(--color-black);
}

.legal-richtext h2 {
  margin: 0;
  font-weight: 500;
  font-size: 24px;
}

.legal-richtext h3 {
  margin: 0;
  font-weight: 500;
  font-size: 20px;
}

.legal-richtext p {
  margin: 0;
  font-weight: 400;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
}

.legal-richtext a {
  color: inherit;
  text-decoration: underline;
}

.legal-richtext ul {
  margin: 0;
  padding-left: 1.5em;
  display: flex;
  flex-direction: column;
  gap: var(--space-300);
}

.legal-richtext li {
  margin: 0;
  font-size: 17px;
  line-height: 23px;
  letter-spacing: -0.34px;
}

@media (max-width: 560px) {
  .legal-columns {
    flex-direction: column;
    gap: var(--space-600);
  }
}

/* ---------- Footer ---------- */

/* Statischer Block am Seitenende (kein Pin/Scroll-Effekt) mit festem
   Weiß->Blau-Verlauf. .footer ist bewusst das letzte Element der Seite ohne
   eigenes margin-bottom, damit danach kein zusätzlicher Abstand folgt. */
.footer {
  position: relative;
  width: 100%;
  /* Der Footer-Cursor fährt von rechts außerhalb ein -- horizontal abschneiden,
     vertikal darf er über den Footer hinausragen (Bildschirmmitte). */
  overflow-x: clip;
}

/* --footer-gap = Abstand zwischen "Programme & Coding" und der ersten
   Footer-Zeile; Basis ist der Abstand zwischen den Sections je Breakpoint:
   256px (Desktop) / 128px (Tablet, Mobile) -- auf Tablet und Desktop um 1/3
   vergrößert (Mobile unverändert, dort per JS an Clifton gekoppelt). */
.footer {
  --footer-gap: calc(var(--space-1200) * 4 / 3);
  height: auto;
  margin-top: 0;
}

@media (min-width: 768px) and (max-width: 1024px) {
  .footer {
    --footer-gap: calc(var(--space-1000) * 4 / 3);
  }
}

@media (max-width: 767px) {
  .footer {
    --footer-gap: var(--space-1000);
  }
}

/* Die Section direkt vor dem Footer (auf der Startseite .programs, auf den
   Projektseiten die Weitere-Projekte-Section) liegt über dem überlappenden
   Pin und liefert selbst keinen Abstand mehr -- den macht --footer-gap. */
main > :has(+ .footer) {
  position: relative;
  z-index: 1;
  margin-bottom: 0;
}

/* Der Pin ragt um (100vh - Footer-Inhalt - Footer-Gap) nach oben -- bei
   hohem Viewport weiter als die Programme-Section hoch ist, also auch in
   die Clifton-Stärken hinein. Dort liegt er sonst als weiße Fläche über den
   Karten und schneidet sie ab. */
.clifton {
  position: relative;
  z-index: 1;
}

/* Gleiches gilt für "Über mich": auf hohen Tablets (z. B. iPad Pro Hochformat)
   ragt der Pin bis in die Publikationen hinein und würde die unterste
   (IIKI) sonst mit seinem weißen Hintergrund abschneiden. */
.about {
  position: relative;
  z-index: 1;
}

.footer__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  background-image: linear-gradient(180deg, #ffffff 0%, #ffffff 28%, #faf6e4 50%, #bcd6e8 72%, #bcd6e8 100%);
  background-repeat: no-repeat;
  background-size: 100% 300%;
  background-position: 0% 0%;
}

.footer__inner {
  width: 100%;
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: var(--space-800) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-600);
}

.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-400);
}

/* Bewusst OHNE min-width:0: die Spalte darf nie schmaler werden als ihr
   eigener (nowrap) Inhalt -- sonst würde "kontakt@tobiasseidl.com" auf
   schmalen Mobile-Breiten (z. B. 375px) in die Nachbarspalte hineinlaufen.
   flex-wrap oben fängt den (seltenen) Fall ab, dass für beide Spalten
   nebeneinander schlicht nicht genug Platz ist -- dann rutscht die zweite
   Spalte einfach eine Zeile tiefer statt zu überlappen. */
.footer__col {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-300);
}

.footer__link {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
  text-decoration: none;
  color: var(--color-black);
  white-space: nowrap;
}

.footer__legal {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  width: 100%;
}

.footer__legal-link {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  text-decoration: none;
  color: var(--color-black);
  white-space: nowrap;
}

/* Ab Desktop (>1024px) hat der Footer laut aktuellem Figma-Stand (node-id
   42-3253) einen eigenen Aufbau statt nur andere Spaltenbreiten: keine
   Nav-Spalte (Home/Projekte/Über mich) mehr, und Kontakt-/Legal-Links werden
   zu echten Pill-Buttons -- exakt dasselbe "Buttons"-Component/Hover wie
   .nav-link im Header (Primary/Active = hellgrauer Fill). Analog zum
   Header/Mobile-Flyout-Muster (zwei komplett getrennte Markups statt eines
   einzigen, responsive überladenen) gibt es dafür ein zweites .footer__inner
   -- .footer__inner--responsive (bisheriges Tablet/Mobile-Markup) und
   .footer__inner--desktop wechseln sich per Media Query ab, nie beide
   gleichzeitig sichtbar. */
.footer__inner--desktop {
  display: none;
}

.footer__contact {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-300);
}

/* Gleiches Muster wie .nav-link (Header): transparent per default, Hover
   füllt mit --color-light-grey ("Primary/Active"-Look). */
.footer__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 400;
  text-decoration: none;
  white-space: nowrap;
  color: var(--color-black);
  background-color: transparent;
  border-radius: var(--radius-full);
  transition: background-color 0.5s ease;
}

.footer__btn:hover {
  background-color: var(--color-light-grey);
}

.footer__btn--lg {
  padding: 6px 14px;
  font-size: 18px;
  letter-spacing: -0.36px;
}

.footer__btn--sm {
  padding: 4px 10px;
  font-size: 12px;
  letter-spacing: -0.24px;
}

@media (min-width: 1025px) {
  .footer__inner--responsive {
    display: none;
  }

  .footer__inner--desktop {
    display: flex;
  }
}

/* Alle Breakpoints: kein Pin/Scroll-Effekt, sondern ein normaler Block mit
   statischem Weiß->Blau-Verlauf in einem Durchgang. Oberes Padding =
   --footer-gap (Abstand zu "Programme & Coding"). */
.footer__pin {
  position: static;
  height: auto;
  background-image: linear-gradient(180deg, #ffffff 0%, #bcd6e8 100%);
  background-size: 100% 100%;
  background-position: 0% 0%;
}

.footer__inner {
  padding-top: var(--footer-gap);
}

/* Mobile (<768px): wie beim Hero (siehe dort) kein Pin/Scroll-Effekt, sondern
   ein ganz normaler Block mit statischem Weiß->Blau-Verlauf in einem Durchgang.
   Kein negativer Rückzug: --footer-gap (von footer-gradient.js aus dem
   sichtbaren Abstand Clifton-Inhalt -> Programme & Coding berechnet, die
   Clifton-Section ist dort eine 100dvh-Bühne mit zentriertem Inhalt) wird
   hier direkt als oberes Padding genutzt. */
@media (max-width: 767px) {
  .footer {
    height: auto;
    margin-top: 0;
  }

  .footer__pin {
    position: static;
    height: auto;
    background-image: linear-gradient(180deg, #ffffff 0%, #bcd6e8 100%);
    background-size: 100% 100%;
    background-position: 0% 0%;
  }

  .footer__inner {
    padding: var(--footer-gap) var(--space-600) var(--space-700);
  }

  /* Gleiche Größe wie die Kontakt-/Legal-Links im Mobile-Flyout (15px). */
  .footer__link {
    font-size: 15px;
    letter-spacing: -0.3px;
  }

  /* Kontakt und Seiten-Links untereinander, Abstände wie im Mobile-Flyout:
     32px zwischen den drei Bereichen, 8px zwischen den Links innerhalb. */
  .footer__links {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: var(--space-600);
  }

  .footer__col {
    flex: none;
  }
}

/* ---------- Tablet-Seitenabstand (768-1024px) ---------- */

/* Dieselben Container sind unterhalb von content-max-width (960px) noch
   nicht auf max-width gedeckelt, laufen also randlos bis zur Bildschirm-
   kante -- ohne dieses Padding fehlt hier der 32px-Sicherheitsabstand, den
   Mobile oben schon hat und Desktop ab 1024px automatisch durchs
   Zentrieren (margin:auto) bekommt. Bewusst als eigener Block ganz am
   Dateiende (statt direkt neben den jeweiligen Basis-Regeln): bei gleicher
   Selektor-Spezifität entscheidet in CSS die Reihenfolge im Dokument, und
   die Basis-Regeln (z. B. .about__inner mit padding:0) stehen an vielen
   Stellen weiter unten im File als ihre jeweilige Mobile-Media-Query. */
@media (min-width: 768px) and (max-width: 1024px) {
  .page-container,
  .about__inner,
  .clifton__inner,
  .programs__inner,
  .project-slider__inner,
  .footer__inner {
    /* Longhand statt "padding: 0 32px" -- die Shorthand würde sonst
       padding-top auf 0 zurücksetzen und damit das padding-top: 128px von
       .project-page/.legal-page (gleiches Element, .page-container davor
       im File) auf Tablet überschreiben. */
    padding-left: var(--space-600);
    padding-right: var(--space-600);
  }
}

/* ---------- Über mich: Spalten-Layout ab Tablet (768px+) ---------- */

/* Gilt ab Tablet UND Desktop (kein max-width mehr) -- die alte Desktop-Regel
   (Bild fix 299px, Inhalt flex:1 bis max-width:464px, justify-content:
   space-between) folgt keinem Spalten-Raster und lässt auf schmaleren
   Breiten (Tablet) keinen Platz für space-between übrig, wodurch Bild und
   Inhalt ohne Abstand aneinanderkleben. Stattdessen wie in Figma ein
   6-Spalten-Grid mit 16px Gutter: Bild nimmt Spalte 1-2 (fluid statt fester
   Breite), Inhalt Spalte 4-6 -- Spalte 3 bleibt bewusst leer und bildet so
   eine ganze freie Spalte (nicht nur den normalen 16px-Gutter) als Abstand
   zwischen Bild und Inhalt. */
@media (min-width: 768px) {
  .about__inner {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    align-items: start;
    gap: var(--space-400);
  }

  .about__photo-wrap {
    grid-column: span 2;
    width: auto;
  }

  .about__content {
    grid-column: 4 / -1;
    max-width: none;
  }
}

/* ---------- Project detail page: Tablet (768-1024px) ---------- */

@media (min-width: 768px) and (max-width: 1024px) {
  .project-content {
    gap: var(--space-400);
  }

  .project-content__media {
    --media-stack-gap: 28px;
  }
}

/* ---------- Tic-Tac-Toe ("Freischalten" bei Clifton Strengths) ----------
   Kein Popup/Overlay: Ein Klick auf eine der beiden Locked-Karten verwandelt
   GENAU DIESE Karte in-place in das Spielfeld (assets/js/clifton-game.js baut
   nur noch das Raster per innerHTML in die Karte, .clifton__card-lock/
   -unlock-wrap werden dafür entfernt). Die Karte behält dabei exakt ihre
   normale Größe -- .clifton__row nutzt align-items:stretch, die Zeilenhöhe
   richtet sich also weiter nach der unlocked "Ideation"-Karte, das Raster
   muss in deren Höhenbudget passen (siehe Kommentar unten). "Beenden"/
   "Wiederholen" sitzen bewusst NICHT mehr im Karten-Hintergrund, sondern als
   eigene, fixed-positionierte Leiste darunter (siehe .ttt-actions weiter
   unten) -- Cursor/Tag/Bubble ebenso ein einziges, global positioniertes
   Overlay (gleiches Muster wie .hero__cursor), dessen Zielpositionen live
   von der jeweils aktiven Karte berechnet werden statt von einer festen
   Bildschirmmitte. */
/* Überschreibt gezielt das vertikale Karten-Padding (sonst 24px, siehe
   .clifton__card) auf die gewünschten 12px -- das Raster soll fast die
   ganze Kartenbox ausfüllen, nicht nur ihren ohnehin schon gepolsterten
   Innenraum. Horizontal bleibt das normale Padding, sizeGrid() in
   clifton-game.js liest beide Werte live aus und begrenzt das Quadrat
   zusätzlich auf die verfügbare Breite. */
.clifton__card--playing {
  justify-content: center;
  padding-top: 16px;
  padding-bottom: 16px;
}

.clifton__game {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

/* Größe wird von clifton-game.js (sizeGrid()) inline gesetzt -- aus der
   tatsächlichen Kartenhöhe abgeleitet, minus 16px Luft oben+unten, und
   zusätzlich auf die Kartenbreite begrenzt. 108px hier ist nur der Fallback
   für den Sekundenbruchteil vor dem ersten JS-Zugriff (bzw. falls JS aus
   irgendeinem Grund nicht läuft). */
.clifton__game-grid {
  position: relative;
  width: 108px;
  height: 108px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
}

.clifton__game-line {
  position: absolute;
  background: rgba(0, 0, 0, 0.15);
}

.clifton__game-line--v1 {
  left: 33.3333%;
  top: 0;
  width: 1px;
  height: 100%;
}

.clifton__game-line--v2 {
  left: 66.6666%;
  top: 0;
  width: 1px;
  height: 100%;
}

.clifton__game-line--h1 {
  top: 33.3333%;
  left: 0;
  height: 1px;
  width: 100%;
}

.clifton__game-line--h2 {
  top: 66.6666%;
  left: 0;
  height: 1px;
  width: 100%;
}

.clifton__game-cell {
  position: relative;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.clifton__game-cell:disabled {
  cursor: inherit;
}

/* Marker-Farben: Gast (Kreis) schwarz, Cursor (Kreuz) im Marken-Blau der
   Seite -- als Custom Properties, damit sie sich (z. B. nach Vorlage) an
   einer Stelle ändern lassen. Gezeichnet per data-mark + Pseudo-Elementen
   statt Icon-Datei, damit das Setzen ein reiner Attribut-Toggle in
   clifton-game.js bleibt. */
.clifton__game {
  --ttt-o: var(--color-black);
  --ttt-x: #0072F7;
}

/* Marker-Größe: max. 32x32px (Vorgabe), sonst 70% der (jetzt deutlich
   größeren, höhengesteuerten) Zelle -- min() statt eines festen inset/einer
   festen Balkenlänge, damit die Marker bei kleinen Zellen weiter mitskalieren,
   aber bei großen Zellen nicht zu Klumpen aufblähen.
   box-sizing:border-box explizit hier statt sich auf die globale "* { }"-Regel
   zu verlassen -- die greift bei Pseudo-Elementen (::before/::after) in
   diesem Browser nachweislich NICHT (gemessen: computed box-sizing dort
   "content-box"). Ohne das kam zum 32px-width/height noch die 3px-Border on
   top drauf (-> 38px sichtbar, genau der gemeldete Wert), statt sie
   einzuschließen. */
.clifton__game-cell[data-mark="o"]::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  box-sizing: border-box;
  width: min(32px, 70%);
  height: min(32px, 70%);
  transform: translate(-50%, -50%);
  border: 3px solid var(--ttt-o);
  border-radius: 50%;
}

/* Balkenlänge, nicht die sichtbare Kreuz-Breite -- ein um 45° gedrehter
   Balken der Länge L hat eine Bounding-Box von nur L*cos(45°) ≈ L*0.707,
   war vorher also trotz gleichem min(32px, 70%) wie beim Kreis optisch
   spürbar kleiner. Faktor 1.41421 (1/cos 45°) gleicht das aus, damit X und
   O bei gleichem Parameter gleich groß WIRKEN (~32px Bounding-Box am Cap). */
.clifton__game-cell[data-mark="x"]::before,
.clifton__game-cell[data-mark="x"]::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 3px;
  height: min(45.25px, 99%);
  background: var(--ttt-x);
  border-radius: 2px;
}

.clifton__game-cell[data-mark="x"]::before {
  transform: translate(-50%, -50%) rotate(45deg);
}

.clifton__game-cell[data-mark="x"]::after {
  transform: translate(-50%, -50%) rotate(-45deg);
}

.clifton__game-cell.is-popping {
  animation: ttt-pop 0.25s ease-out;
}

@keyframes ttt-pop {
  0% {
    transform: scale(0.4);
    opacity: 0;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* Farbe wird per JS inline gesetzt (Gewinnerfarbe: --ttt-o oder --ttt-x). */
.clifton__game-winline {
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  border-radius: 2px;
  transform-origin: center;
  opacity: 0;
  pointer-events: none;
}

/* "Beenden"/"Wiederholen" leben außerhalb der Karte (siehe #tttActions in
   index.html) -- eine fixed-positionierte Leiste, die clifton-game.js
   (positionActions()) mittig unter die jeweils aktive Karte setzt, analog
   zu .ttt-cursor. Default unsichtbar/nicht interaktiv, bis ein Spiel läuft. */
.ttt-actions {
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-500);
  flex-wrap: wrap;
  opacity: 0;
  pointer-events: none;
  z-index: 60;
  will-change: transform;
}

.ttt-actions.is-active {
  pointer-events: auto;
}

/* Button-States nach Figma-Vorlage (node-id 202-7739): "Wiederholen" und
   "Beenden" tauschen beim Hover ihren Füllzustand -- Wiederholen ist normal
   umrandet/transparent und wird bei Hover zur gefüllten Pille (+Replay-Icon),
   Beenden ist normal gefüllt und wird bei Hover transparent/umrandungslos
   (+X-Icon). Die Border ist beim Hover-Wechsel nie wirklich weg, sondern nur
   auf transparent gesetzt -- sonst würde sich die Button-Höhe beim Hovern um
   die Border-Breite verschieben. */
.clifton__game-btn {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 6px 14px;
  background: transparent;
  border: 1px solid var(--color-black);
  border-radius: var(--radius-full);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  letter-spacing: -0.32px;
  color: var(--color-black);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.clifton__game-btn:hover,
.clifton__game-btn:focus-visible {
  background: var(--color-light-grey);
  border-color: transparent;
}

.clifton__game-btn--end {
  background: var(--color-light-grey);
  border-color: transparent;
}

.clifton__game-btn--end:hover,
.clifton__game-btn--end:focus-visible {
  background: transparent;
  border-color: transparent;
}

/* Gemeinsame Icon-Klasse für beide Buttons (Replay bei Wiederholen, X bei
   Beenden) -- beide erscheinen erst bei Hover/Focus. */
/* width/margin-left animieren (nicht nur opacity/scale) -- sonst reserviert
   das Icon per Flex-Gap/eigener Breite schon im Ruhezustand Platz, und der
   Button ist von Anfang an genauso breit wie im Hover-Zustand, statt beim
   Hover sichtbar zu wachsen (siehe Figma node-id 202-7739: die Hover-Pillen
   sind spürbar breiter als die Normal-Pillen). overflow:hidden verhindert,
   dass das Icon bei width:0 seitlich rausschaut. */
.clifton__game-btn-icon {
  width: 0;
  height: 10px;
  margin-left: 0;
  opacity: 0;
  transform: scale(0.4);
  overflow: hidden;
  flex-shrink: 0;
  transition: width 0.2s ease, margin-left 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
}

.clifton__game-btn:hover .clifton__game-btn-icon,
.clifton__game-btn:focus-visible .clifton__game-btn-icon {
  width: 10px;
  margin-left: 6px;
  opacity: 1;
  transform: scale(1);
}

.ttt-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------- Cursor (Tic-Tac-Toe-Gegner) ----------
   Eigenständige, global fixed-positionierte Kopie von .hero__cursor
   (gleiches Markup-/Timing-Muster) -- wird für alle drei Fälle wiederverwendet:
   die kurze lokale Vorstellung beim Spielstart, die Zielzüge während des
   Spiels und die große Von-rechts-Einfahrt für die "andere Karte schon
   gesperrt"-Abweis-Nachricht (siehe openDenyMessage() in clifton-game.js). */
/* z-index unter .site-header (100, siehe dort) -- der Cursor läuft beim
   Ein-/Rausfahren bzw. beim Scrollen mit der Karte gelegentlich unter dem
   Menü hindurch, das Menü soll dabei immer sichtbar obenauf bleiben statt
   vom Cursor verdeckt zu werden. */
.ttt-cursor {
  position: fixed;
  top: 0;
  left: 0;
  opacity: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  pointer-events: none;
  z-index: 60;
  will-change: transform;
}

.ttt-cursor__arrow {
  width: 28px;
  height: 28px;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.25));
}

.ttt-cursor__row {
  display: flex;
  align-items: flex-start;
  gap: 4px;
}

.ttt-cursor__spacer {
  width: 17px;
  height: 23px;
  flex-shrink: 0;
}

/* Absichtlich KEIN position:relative + absolute Kinder (anders als beim
   sonst identischen .hero__cursor-morph): Tag/Bubble würden dann ohne
   eigene In-Flow-Breite als Containing Block auf 0 kollabieren, wodurch die
   Bubble ihre Breite nicht mehr gegen max-width, sondern nur noch gegen das
   längste Wort berechnet -- bei .hero__cursor fällt das nicht auf, weil die
   Willkommensnachricht kurz und nowrap ist; unsere Spielnachrichten sind
   ganze, umbrechende Sätze und brauchen echten verfügbaren Platz. Tag und
   Bubble schließen sich per display:none/flex ohnehin gegenseitig aus, ein
   normaler Fluss-Wechsel reicht für den GSAP-Crossfade in morphTo(). */
.ttt-cursor__morph {
  display: flex;
}

.ttt-cursor__tag {
  transform-origin: left bottom;
  background: #0072F7;
  color: var(--color-white);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
  padding: 2px 4px;
  border-radius: 4px;
  white-space: nowrap;
}

.ttt-cursor__bubble {
  transform-origin: left bottom;
  display: none;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  max-width: min(85vw, 256px);
  background: #0072F7;
  padding: 8px 16px;
  border-radius: 4px 4px 4px 24px;
}

.ttt-cursor__bubble-name {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: -0.3px;
  color: var(--color-white);
  white-space: nowrap;
}

/* Anders als .hero__cursor-bubble-message: hier KEIN white-space:nowrap --
   die Spielnachrichten sind ganze Sätze, nicht Kurzfloskeln wie "Herzlich
   willkommen!", und würden auf schmalen Screens sonst weit über den
   Viewport hinausragen. */
.ttt-cursor__bubble-message {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  letter-spacing: -0.34px;
  color: var(--color-white);
  white-space: normal;
}


/* ---------- Projekt-Karussell (Home) ----------
   Ersetzt den gepinnten Horizontal-Scroll-Slider. Layout/Animation macht
   assets/js/project-carousel.js: Karten sind absolut positioniert, JS setzt
   --pc-card-w und die Höhe des Viewports. */
.project-carousel {
  margin-bottom: var(--space-1000);
  scroll-margin-top: var(--space-1000);
}

.project-carousel__inner {
  width: 100%;
  max-width: var(--content-max-width);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}

.project-carousel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-400);
  margin-bottom: var(--space-600);
}

.project-carousel__title {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 36px;
  white-space: nowrap;
}

.project-carousel__arrows {
  display: flex;
  gap: var(--space-300);
}

.project-carousel__arrow {
  width: 44px;
  height: 44px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-black);
  background: transparent;
  border: 1px solid #f4f5f3;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.project-carousel__arrow svg {
  width: 20px;
  height: 20px;
}

@media (hover: hover) {
  .project-carousel__arrow:hover {
    background: #f4f5f3;
  }
}

/* Volle Breite: die Karten füllen den ganzen Screen, das JS setzt bei Bedarf
   Duplikate von vorn an, damit rechts/links nie Lücken entstehen. */
.project-carousel__viewport {
  position: relative;
  width: 100%;
  overflow: hidden;
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}

.project-carousel__viewport.is-dragging {
  cursor: grabbing;
}

.project-carousel__viewport.is-dragging a {
  pointer-events: none;
}

.project-carousel__track {
  position: relative;
  width: 100%;
  height: 100%;
}

.project-carousel .project-card {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--pc-card-w, 309px);
  will-change: transform, opacity;
}

.project-carousel .project-card img {
  -webkit-user-drag: none;
}

@media (max-width: 767px) {
  .project-carousel {
    margin-bottom: var(--space-1000);
  }

  .project-carousel__inner {
    padding: 0 var(--space-600);
  }

  .project-carousel__title {
    font-size: 28px;
  }
}

@media (min-width: 768px) and (max-width: 1024px) {
  .project-carousel__inner {
    padding-left: var(--space-600);
    padding-right: var(--space-600);
  }
}
