/* ─── Custom Cursor ──────────────────────────────────────────────────────── */
/* Ein DOM-Element statt nativem Cursor: pixelscharf (Vektor-Maske) und in Weiß.
   Default = Pixel-X, Hover über Klickbarem = Pixel-+. */

body.custom-cursor,
body.custom-cursor * {
  cursor: none !important; /* schlägt auch ID-Regeln wie #loader { cursor: pointer } */
}

#cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 16px;
  height: 16px;
  background: #fff;
  -webkit-mask: url("assets/cursor-2.svg") center / contain no-repeat;
  mask: url("assets/cursor-2.svg") center / contain no-repeat;
  pointer-events: none;
  z-index: 10000; /* über allem, auch Loader/Lightbox */
  opacity: 0; /* erst nach erster Mausbewegung sichtbar */
}

#cursor.hover {
  -webkit-mask-image: url("assets/cursor-1.svg");
  mask-image: url("assets/cursor-1.svg");
}

/* ─── Dither-Hintergrund ─────────────────────────────────────────────────── */

#dither {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none; /* Klicks/Ripples laufen über window-Listener */
}

/* ─── Landing / Morph-Loader ─────────────────────────────────────────────── */

#loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: opacity 0.4s ease;
}

#loader.exiting {
  pointer-events: none;
}

#loader.fade-out {
  opacity: 0;
}

#logo-wrap {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  will-change: transform;
}

#morphSvg {
  display: block;
}

/* Füllung nur über CSS (kein fill-Attribut im HTML) */
#morphSvg .shape {
  fill: #fff;
}

/* Wort-Slot: bottom-aligned mit der Logo-Unterkante (Logo 182px, mittig →
   Unterkante liegt 91px unter der Mitte), rechts neben dem Logo.
   Feste Zeilenhöhe + overflow:hidden, damit die Wörter "aus dem Off" kommen. */
#shape-text {
  position: fixed;
  left: calc(50% + 56px);
  bottom: calc(50% - 91px);
  color: white;
  font-family: "IBM Plex Mono", monospace;
  font-size: 1rem;
  line-height: 1.6;
  letter-spacing: 0.04em;
  opacity: 0;
  transition: opacity 0.6s ease;
  pointer-events: none;
  z-index: 101;
  height: 1.6em; /* genau eine Zeile */
  overflow: hidden;
  /* Grid statt absoluter Positionierung: die Wörter stapeln in derselben Zelle,
     bestimmen aber weiterhin die Breite des Slots. (Absolut positioniert wäre
     der Container 0 breit und overflow:hidden würde alles wegschneiden.) */
  display: grid;
  align-items: end;
  justify-items: start;
}

#shape-text.visible {
  opacity: 1;
}

#shape-text .word {
  grid-area: 1 / 1;
  white-space: nowrap;
  transition:
    transform 0.5s cubic-bezier(0.76, 0, 0.24, 1),
    opacity 0.5s ease;
}

/* wartet unterhalb des Slots … */
#shape-text .word.enter {
  transform: translateY(110%);
  opacity: 0;
}

/* … fährt an seinen Platz … */
#shape-text .word.current {
  transform: translateY(0);
  opacity: 1;
}

/* … und verlässt ihn nach oben */
#shape-text .word.exit {
  transform: translateY(-110%);
  opacity: 0;
}

/* ─── Site-Reveal: Header + Content erst nach dem Intro sichtbar ─────────── */

header,
.content,
#mobile-nav {
  opacity: 0;
  transition: opacity 0.5s ease;
}

body.revealed header,
body.revealed .content,
body.revealed #mobile-nav {
  opacity: 1;
}

/* ─── Root ────────────────────────────────────────────────────────────────── */

:root {
  /* Inhalt füllt die Resthöhe, statt fix 70vh zu sein — so endet die untere
     Kante mit gleichmässigem Abstand zum Seitenrand:
     100vh − Header (5vh + 2×10px) − oberer Abstand (80px) − unterer (40px) */
  --content-height: calc(95vh - 140px);

  /* Bildbox (4:3): so hoch wie der Inhalt, aber nie breiter als 60vw.
     Einmal definiert — Breite folgt aus der Höhe, damit die Toggle-Ausrichtung
     dieselbe Rechnung nicht nochmal fest einkodiert. */
  --image-height: min(var(--content-height), calc(60vw * 3 / 4));
  --image-width: calc(var(--image-height) * 4 / 3);

  /* exakt das Rot des Shaders: gl_FragColor = vec4(1.0 - dithered, 0, 0, 1) */
  --dither-red: #ff0000;
}

/* Custom Text-Select statt Systemfarbe: Text wird rot statt blau markiert.
   Hintergrund bleibt transparent, sonst überdeckt die Markierung selbst den
   roten Text. */
::selection {
  color: var(--dither-red);
  background: transparent;
}

html {
  background-color: black; /* Fallback, bevor der Canvas zeichnet */
}

/* Nirgends auf der Seite eine Scrollbar — gescrollt wird trotzdem normal */
* {
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* alte Edge/IE */
}

*::-webkit-scrollbar {
  display: none; /* Chrome/Safari */
}

body {
  font-family: "IBM Plex Mono", monospace;
  font-weight: 300;
  font-style: normal;
  line-height: 1.5; /* noch unsicher */
  background-color: transparent; /* Dither-Canvas scheint durch */
  color: white;
}

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 5vh;
  padding: 10px 20px;
  /* border-bottom: 1px solid white; */
  position: sticky;
  top: 0;
  z-index: 2; /* über dem Dither-Canvas */
  /* fluid: skaliert mit der Viewport-Breite, damit die Schrift auf schmalen
     Screens nicht zu gross wirkt */
  font-size: clamp(0.8rem, 1.5vw, 1.5rem);
}

.header-left {
  display: flex;
  align-items: center;
  gap: 0.5em;
  font-size: inherit;
  cursor: pointer;
}

/* Alle klickbaren Header: normal weiss + dünn, Hover bold + Shader-Rot.
   IBM Plex Mono ist monospaced → der Gewichtswechsel verschiebt nichts. */
.header-left,
.nav-link {
  color: #fff;
  font-weight: 300;
  transition:
    color 0.2s ease,
    font-weight 0.2s ease;
}

.header-left:hover,
.nav-link:hover {
  color: var(--dither-red);
  font-weight: 600;
}

.header-left .logo-mark {
  flex-shrink: 0;
  display: block;
  width: 0.85em; /* skaliert mit der Header-Schrift */
  height: 0.85em;
}

.header-right {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.6vw, 40px);
  font-size: inherit;
}

.nav-link {
  cursor: pointer;
}

/* Auf der About-Seite ist ABOUT ausgegraut — auch beim Hover (nicht mehr
   anklickbar, da man schon dort ist). Die anderen Header zeigen nie einen
   aktuellen Zustand, brauchen also keine Aktiv-Kennzeichnung. */
body[data-view="about"] #about-link,
body[data-view="about"] #about-link:hover {
  color: gray;
  font-weight: 300;
  cursor: default;
}

/* Desktop: "GRID VIEW / DETAIL VIEW" rechtsbündig an die rechte Kante der
   Bildspalte (Mitte). Die ist mittig im Viewport → rechte Kante bei
   50vw + Breite/2. Breite kommt aus --image-width, bleibt so automatisch
   korrekt, wenn sich --content-height ändert. */
@media (min-width: 1101px) {
  #view-toggle {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: calc(50vw - var(--image-width) / 2);
  }

  /* spiegelbildlich: linke Kante der Bildspalte */
  #lang-toggle {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: calc(50vw - var(--image-width) / 2);
  }
}

/* ─── View-Umschaltung ───────────────────────────────────────────────────── */
/* Sichtbarkeit über .is-hidden (JS), damit die Media-Query-Displays der
   jeweils sichtbaren View erhalten bleiben. */

.is-hidden {
  display: none !important;
}

#grid-view {
  position: relative;
  z-index: 1;
}

#about-view {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 20px;
  height: var(--content-height);
  margin: 80px 20px 40px;
  position: relative;
  z-index: 1;
}

/* Content Layout */
.content {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: auto;
  height: var(--content-height); /* Verwende die Variable für die Höhe */
  gap: 20px;
  margin: 80px 20px 40px;
  position: relative;
  z-index: 1; /* über dem Dither-Canvas */
}

.column {
  display: flex;
  flex-direction: column;
  gap: 20px;
}


/* Left Column */
.column.left .textbox ul {
  list-style: none;
  padding: 0;
}

.column.left .textbox ul li {
  margin-bottom: 2px; /* enger beieinander, normalerer Zeilenabstand */
  color: gray;
  cursor: pointer;
  padding-left: 0;
  transition:
    padding-left 0.25s ease,
    color 0.25s ease,
    font-weight 0.25s ease;
}

/* Einrücken + Weiss als Hover- und als Selected-Effekt, Abstand wie im CV */
.column.left .textbox ul li:hover,
.column.left .textbox ul li.active {
  padding-left: 1.6em;
  color: white;
}

/* zusätzlich nur beim Hover: bold */
.column.left .textbox ul li:hover {
  font-weight: 600;
}

.column.left .textbox.meta {
  margin-top: auto;
}

/* Middle Column — Snap Scroll */
.snap-scroll {
  height: var(--image-height);
  aspect-ratio: 4 / 3;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
}

/* height: 100% statt der Formel zu wiederholen — so ist die Item-Höhe exakt
   die tatsächliche Containerhöhe. Sonst runden beide minimal verschieden und
   der Fehler summiert sich (sichtbarer Streifen des nächsten Bildes).
   Zusätzlich 2px schwarzer Puffer oben/unten: falls durch Rundung doch eine
   Kante des Nachbarbilds durchblitzt, ist sie schwarz auf schwarz und damit
   unsichtbar. Robuster als exaktes Treffen der Rundung (v.a. mobil). */
.snap-item {
  height: 100%;
  box-sizing: border-box;
  padding: 2px 0;
  scroll-snap-align: start;
}

.snap-item img,
.snap-item video {
  height: 100%;
  width: 100%;
  object-fit: contain;
  object-position: top center;
}

/* Right Column */
.column.right .textbox {
  margin-bottom: 20px;
}

.column.right .textbox a {
  color: gray;
  text-decoration: none;
}

.column.right .textbox a:hover {
  color: white;
}

.column.right .textbox:last-child {
    margin-bottom: 0;
    margin-top: auto; /* Jahreszahl nach unten */
}

/* ─── Grid / Overview (Masonry) ──────────────────────────────────────────── */

#grid-view {
  padding: 40px 20px;
}

/* Echte Spalten-Container: Bilder werden per JS round-robin verteilt
   (Bild i → Spalte i mod n), damit die Reihenfolge zeilenweise liest. */
.masonry {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.masonry-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.masonry-item {
  position: relative;
  overflow: hidden; /* schneidet den Zoom sauber am Rand ab */
  cursor: pointer;
  line-height: 0;
}

.masonry-item img,
.masonry-item video {
  width: 100%;
  display: block;
  transition: transform 0.35s ease;
}

.masonry-item:hover img,
.masonry-item:hover video {
  transform: scale(1.18);
}

/* Im Grid-View ist der Hintergrund-Canvas nie sichtbar — er dient dort nur
   als Pixelquelle für das Hover-Overlay. So erscheint der Dither ausschliesslich
   im gehoverten Bild und nirgends sonst. */
body[data-view="grid"] #dither {
  opacity: 0;
}

/* Dither-Overlay: füllt bei Hover exakt das gehoverte Grid-Bild mit einem
   Live-Ausschnitt des Dither-Hintergrunds (siehe setupGridDitherPeek in
   script.js). Grösse/Position werden per JS auf das Item gesetzt. */
#dither-peek {
  position: fixed;
  pointer-events: none;
  z-index: 60;
  opacity: 0;
  transition: opacity 0.2s ease;
  image-rendering: pixelated;
  /* screen: Schwarz wirkt transparent → nur die Dither-Pixel legen sich über
     das Bild, das darunter sichtbar bleibt */
  mix-blend-mode: difference;
}

#dither-peek.visible {
  opacity: 0.6;
}

/* ─── About ──────────────────────────────────────────────────────────────── */

.about-col {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.about-cv,
.about-bio {
  overflow-y: auto;
}

.about-cv {
  gap: 0;
  text-transform: uppercase;
  line-height: 1.4;
}

/* ganze Gruppe grau; nur die aktuelle (.current) steht in Weiss */
.cv-group {
  color: gray;
  margin-bottom: 18px;
}

.cv-group.current {
  color: white;
}

.about-cv .cv-year {
  display: block;
}

.about-cv .cv-entry {
  color: inherit; /* folgt der Gruppe (weiss bzw. grau) */
  padding-left: 1.6em; /* eingerückt unter der Jahreszahl */
  margin-bottom: 0;
  /* Zeilenumbrüche kommen als \n aus den data-de/data-en-Attributen */
  white-space: pre-line;
}

/* Exakt dieselbe Box wie die Bildspalte im Detail-View. Breite explizit statt
   über aspect-ratio: das auto-Grid-Track würde sonst die max-content-Breite der
   beiden Bilder nehmen und die Spalte breiter machen. Die Hochformate werden
   per object-fit: cover auf die Box zugeschnitten. */
.about-portrait {
  flex-direction: row;
  gap: 12px;
  width: var(--image-width);
  height: var(--image-height);
}

.about-portrait img {
  flex: 1;
  min-width: 0;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.about-bio .about-name {
  margin-bottom: 20px;
}

.about-bio .about-tagline {
  color: gray;
  letter-spacing: 0.1em;
  margin-bottom: 24px;
}

.about-bio .about-text {
  margin-bottom: 28px;
}

/* Socials sitzen unten rechts, am Ende der Bio-Spalte */
.about-contact {
  list-style: none;
  padding: 0;
  margin-top: auto;
  padding-top: 60px; /* Mindestabstand zum Text, auch wenn auto nicht greift */
}

.about-contact li {
  margin-bottom: 4px;
}

.about-contact a {
  color: gray;
  text-decoration: none;
}

.about-contact a:hover {
  color: white;
}

/* Lebenslauf-Download unten links, am Ende der CV-Spalte */
.cv-download {
  margin-top: auto;
  padding-top: 60px;
  display: flex;
  gap: 1em;
}

.cv-download-label {
  color: white;
}

/* wie die Socials unten rechts: grau mit weiß im Hover */
.cv-download a {
  color: gray;
  text-decoration: none;
}

.cv-download a:hover {
  color: white;
}

/* ─── Mobile Nav: global versteckt, Dropdown-Styles immer aktiv ─────────── */

#mobile-nav {
  display: none;
  background-color: black;
}

#mobile-dropdown {
  position: relative;
}

#mobile-project-btn {
  display: flex;
  align-items: center;
  gap: 0.5em;
  background: none;
  border: none;
  color: white;
  font-family: "IBM Plex Mono", monospace;
  font-weight: 300;
  font-size: 0.875rem;
  cursor: pointer;
  padding: 0;
  letter-spacing: 0.05em;
}

/* ein Glyph, der rotiert — kein Sprung durch unterschiedliche Glyph-Metriken */
.mobile-arrow {
  display: inline-block;
  line-height: 1;
  transition: transform 0.28s ease;
}

#mobile-dropdown.open .mobile-arrow {
  transform: rotate(180deg);
}

#mobile-project-list {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  background: black;
  padding: 8px 0;
  min-width: 220px;
  z-index: 100;

  /* animierbar statt display:none — sanft ein-/ausblenden */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition:
    opacity 0.28s ease,
    transform 0.28s ease,
    visibility 0s linear 0.28s;
}

#mobile-project-list.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition:
    opacity 0.28s ease,
    transform 0.28s ease,
    visibility 0s;
}

.mobile-project-item {
  padding: 6px 20px; /* Basis-Innenabstand; Einrückung kommt bei Hover/Active obendrauf */
  margin-bottom: 2px; /* enger, wie die Desktop-Nav */
  color: gray;
  cursor: pointer;
  font-size: 0.875rem;
  transition:
    color 0.2s ease,
    padding-left 0.25s ease;
}

.mobile-project-item:hover,
.mobile-project-item.active {
  color: white;
  padding-left: calc(20px + 1.6em); /* gleicher Einzug wie Desktop-Nav */
}

.mobile-project-item.active {
  font-weight: 600;
}

#mobile-counter {
  font-size: 0.875rem;
}

/* ─── Tablet (769–1100px) ─────────────────────────────────────────────────── */

@media (min-width: 769px) and (max-width: 1100px) {
  body {
    display: flex;
    flex-direction: column;
    height: 100dvh;
    overflow: hidden;
  }

  header { flex-shrink: 0; }

  /* "GRID VIEW / DETAIL VIEW" wie im Dreispaltenmodus an der rechten Kante der
     Bildspalte. Hier ist sie nicht zentriert, sondern linksbündig: bei
     grid-template-columns 3fr 2fr über die volle Breite (margin/gap 0) endet
     sie bei 60vw → Abstand von rechts = 40vw. */
  #view-toggle {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: 40vw;
  }

  #mobile-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 20px;
    height: 40px;
    flex-shrink: 0;
    position: relative;
    z-index: 10;
  }

  /* 2-spaltiges Grid: Bild links, Text+Meta rechts gestapelt */
  .content {
    display: grid;
    grid-template-columns: 3fr 2fr;
    grid-template-rows: 1fr auto;
    grid-template-areas:
      "middle right"
      "middle left";
    flex: 1;
    min-height: 0;
    margin: 0;
    gap: 0;
  }

  .column.middle {
    grid-area: middle;
    display: flex;
    flex-direction: column;
  }

  .column.right {
    grid-area: right;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
  }

  .column.left {
    grid-area: left;
  }

  .snap-scroll {
    flex: 1;
    width: 100%;
    height: auto;
    aspect-ratio: unset;
  }

  .column.right .textbox:nth-child(1),
  .column.right .textbox.year {
    display: none;
  }

  .column.right .textbox:nth-child(2) {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 20px;
    margin: 0;
  }

  .column.left .textbox.projects {
    display: none;
  }

  .column.left .textbox.meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 16px;
    padding: 16px 20px;
    font-size: 0.8rem;
    line-height: 1.5;
    margin: 0;
  }

  /* Grid + About füllen den Rest der Höhe und scrollen intern */
  #grid-view {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 20px;
  }

  #about-view {
    flex: 1;
    min-height: 0;
    height: auto;
    margin: 0;
    padding: 20px;
    gap: 16px;
  }

  .about-cv,
  .about-bio {
    overflow-y: auto;
  }

  /* Tablet: Box füllt die verfügbare Höhe statt fixer 4:3-Berechnung */
  .about-portrait {
    height: auto;
    aspect-ratio: auto;
    min-height: 0;
  }
}

/* ─── Mobile (≤768px) ────────────────────────────────────────────────────── */

@media (max-width: 768px) {
  body {
    display: flex;
    flex-direction: column;
    height: 100dvh;
    overflow: hidden;
  }

  header { flex-shrink: 0; }

  #mobile-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 20px;
    height: 40px;
    flex-shrink: 0;
    position: relative;
    z-index: 10;
  }

  .content {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    margin: 0;
    gap: 0;
  }

  .column.middle { order: 1; flex-shrink: 0; }
  .column.right  { order: 2; flex: 1; min-height: 0; display: flex; flex-direction: column; }
  .column.left   { order: 3; flex-shrink: 0; }

  .snap-scroll {
    width: 100%;
    height: 75vw;
    aspect-ratio: unset;
  }
  /* .snap-item erbt height: 100% aus der Basis-Regel → exakt = Containerhöhe */

  .column.right .textbox:nth-child(1),
  .column.right .textbox.year {
    display: none;
  }

  .column.right .textbox:nth-child(2) {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 16px 20px;
    margin: 0;
  }

  .column.left .textbox.projects {
    display: none;
  }

  .column.left .textbox.meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 16px;
    padding: 12px 20px;
    font-size: 0.8rem;
    line-height: 1.5;
    margin: 0;
  }

  /* Grid: 2 Spalten, intern scrollbar */
  #grid-view {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 16px 12px;
  }

  /* About: gestapelt (Portrait → Bio → CV), intern scrollbar */
  #about-view {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    height: auto;
    margin: 0;
    padding: 20px;
    gap: 24px;
    overflow-y: auto;
  }

  .about-portrait { order: 1; }
  .about-bio { order: 2; }
  .about-cv { order: 3; }

  /* Mobile: feste 4:3-Box aufheben, Bilder bleiben nebeneinander */
  .about-portrait {
    height: auto;
    aspect-ratio: auto;
    flex-shrink: 0;
  }

  .about-portrait img {
    height: auto;
    max-height: 40vh;
  }

  /* Textboxen in voller Höhe, kein internes Scrollen — stattdessen scrollt
     die gesamte About-Seite (#about-view) als Ganzes. */
  .about-cv,
  .about-bio {
    overflow-y: visible;
    height: auto;
    flex-shrink: 0;
  }
}