:root {
  --bg: #f7f6f2;
  --text: #191918;
  --muted: rgba(25,25,24,.36);
  --axis-left: 5.65vw;
  --axis-right: 5.65vw;
  --axis-top: 9.15vh;
  --axis-bottom: 6.9vh;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
  font-weight: 300;
  font-kerning: normal;
  font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

body.is-leaving { opacity: 0; transition: opacity 220ms ease; }

/* GLOBAL NAV FINAL v0.2
   Bottom-right discovery navigation.
   Desktop now uses the same geometry as the mobile navigation. */
.menu-toggle {
  position: fixed;
  top: auto;
  bottom: calc(var(--axis-bottom) + 4px);
  right: calc(var(--axis-right) + 1.15rem);
  z-index: 50;
  width: 22px;
  height: 18px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 6px;
  overflow: visible;

  /* Die Masse der Navigation als Zahlen, damit der Weg des oberen Strichs
     gerechnet werden kann statt abgezählt – siehe DER OBERE STRICH weiter
     unten. Jeder Bruchpunkt setzt hier nur die Werte neu, die er ändert.
       --menu-unten    Unterkante des Menüs über der unteren Achse
       --menu-zeile    Höhe eines Eintrags (Schriftgrad x line-height 1.14)
       --menu-abstand  Abstand zwischen zwei Einträgen (gap des Menüs)
       --strich-hoehe  Oberkante des oberen Strichs über der unteren Achse
                       (4px Achsabstand + 1px + 6px gap + 1px)
       --menu-luft     Luft zwischen Strich und oberstem Eintrag
       --menu-anzahl   Anzahl der Einträge, gezählt von den Regeln darunter */
  --menu-unten: 2.55rem;
  --menu-zeile: calc(24px * 1.14);
  --menu-abstand: .20rem;
  --strich-hoehe: 12px;
  --menu-luft: 20.72px;
  --menu-anzahl: 3;
}

/* Die Anzahl der Einträge – gezählt, nicht angenommen. `nth-child(n)` und
   `:last-child` zusammen treffen genau dann, wenn es n Einträge sind; es
   greift also immer nur eine dieser Regeln. Reicht bis neun, und wer einen
   zehnten anlegt, bekommt den Grundwert 3 – dann ist hier eine Zeile
   nachzutragen, sichtbar an einem Strich, der zu tief liegt. */
.menu-toggle:has(+ .site-menu > a:nth-child(1):last-child) { --menu-anzahl: 1; }
.menu-toggle:has(+ .site-menu > a:nth-child(2):last-child) { --menu-anzahl: 2; }
.menu-toggle:has(+ .site-menu > a:nth-child(3):last-child) { --menu-anzahl: 3; }
.menu-toggle:has(+ .site-menu > a:nth-child(4):last-child) { --menu-anzahl: 4; }
.menu-toggle:has(+ .site-menu > a:nth-child(5):last-child) { --menu-anzahl: 5; }
.menu-toggle:has(+ .site-menu > a:nth-child(6):last-child) { --menu-anzahl: 6; }
.menu-toggle:has(+ .site-menu > a:nth-child(7):last-child) { --menu-anzahl: 7; }
.menu-toggle:has(+ .site-menu > a:nth-child(8):last-child) { --menu-anzahl: 8; }
.menu-toggle:has(+ .site-menu > a:nth-child(9):last-child) { --menu-anzahl: 9; }

/* Der Griff, nicht der Strich. Gemessen am 02.10.2026 auf dem Telefon war
   die anfassbare Flaeche 22 x 18 Bildpunkte gross - unter dem Daumenmass
   44 x 44, und das bei der einzigen Navigation der Seite. Die Flaeche waechst
   unsichtbar nach unten und zu beiden Seiten; die beiden Striche bleiben auf
   den Bildpunkt, wo sie sind. Nach oben nur 10px, damit der Griff nicht an
   den untersten Menueeintrag stoesst (dessen Unterkante liegt auf dem
   Telefon bei 744, der Griff beginnt bei 753). Nach links 8px, damit er den
   Mail-Verweis nicht beruehrt (dessen rechtes Ende liegt bei 310, der Griff
   beginnt bei 318). Ausserhalb des Flusses, also ohne Wirkung auf den
   flex-gap zwischen den Strichen. */
.menu-toggle::after {
  content: "";
  position: absolute;
  inset: -10px -12px -16px -8px;
}

.menu-toggle span {
  display: block;
  width: 24px;
  height: 1px;
  margin: 0;
  background: var(--text);
  transition:
    transform 360ms ease,
    width 320ms ease,
    filter .55s ease;
}

.site-menu {
  position: fixed;
  top: auto;
  bottom: calc(var(--axis-bottom) + 2.55rem);
  right: calc(var(--axis-right) + 1.15rem);
  z-index: 49;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .20rem;
  opacity: 0;
  pointer-events: none;
  transform: translateY(12px);
  transition:
    opacity .35s ease,
    transform .32s ease,
    filter .55s ease;
  text-align: right;
  filter: blur(6px);

  /* Der Takt der Einblendung: Grundwert plus ein Schritt je Eintrag.
     Steht hier und nicht am Knopf, weil die Einträge Kinder des Menüs
     sind – eine Eigenschaft am Knopf erreicht sie nicht. */
  --menu-versatz: 0ms;
  --menu-takt: 40ms;
}

.site-menu.is-open {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  filter: blur(0);
}

/* DER SCHLEIER HINTER DEM MENUE (30.09.2026, D1H)
   Simon, an einem Telefon, mit Bild: „Auf simon-haener.ch/kontakt ueberlagert
   die Navigation den Inhalt." Auf dem Bild greifen „impressum" und
   „kontakt@simon-haener.ch" buchstabenweise ineinander.

   Der Fehler ist nicht die Lage des Menues – es liegt auf der rechten Achse,
   und das soll so sein. Der Fehler ist, dass zwei Textebenen ohne Trennung
   uebereinanderliegen. Auf einem schmalen Schirm laeuft die Mailzeile ueber
   die ganze Breite und damit zwangslaeufig unter die Eintraege.

   Statt das Menue zu verschieben bekommt es einen Grund: solange es offen
   ist, tritt alles dahinter zurueck. Rein sichtbar, `pointer-events: none` –
   der Schleier faengt keinen Griff ab und aendert nichts am Bedienweg.

   Bewusst OHNE Farbe, nur Weichzeichner. Die Seite wechselt ihre Tinte nach
   dem gemessenen Hintergrund des Wetterraums; ein fester Farbwert waere in
   der einen Lage richtig und in der anderen falsch. Ein Weichzeichner nimmt
   dem Text dahinter die Lesbarkeit, ohne ueber hell oder dunkel zu
   entscheiden. Wo `backdrop-filter` fehlt, bleibt es beim alten Bild – das
   ist kein neuer Fehler, nur eine ungeloeste alte Lage.

   UND ER HAENGT AM KOERPER, NICHT AM MENUE. Der erste Wurf (D1H) setzte ihn
   als `.site-menu::before` – gemessen wirkungslos: `.site-menu` traegt selbst
   `filter: blur(...)`, und ein Filter macht aus seinem Element eine
   Hintergrundwurzel. Ein `backdrop-filter` darin sieht nichts mehr, was
   dahinter liegt, weil fuer ihn nichts mehr dahinter LIEGT. Der Schirmschuss
   im Handyformat zeigte den Text hinter dem offenen Menue gestochen scharf,
   waehrend `getComputedStyle` brav `blur(10px)` meldete – die Eigenschaft
   stand, die Wirkung nicht. Am Koerper gibt es keinen Filter darueber. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 48;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s ease;
  backdrop-filter: blur(10px) saturate(.85);
  -webkit-backdrop-filter: blur(10px) saturate(.85);
}

body:has(.site-menu.is-open)::before { opacity: 1; }

.site-menu a {
  color: var(--text);
  text-decoration: none;
  font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
  font-weight: 200;
  font-size: 24px;
  line-height: 1.14;
  letter-spacing: 0.012em;
  text-align: right;
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity 260ms ease,
    transform 300ms ease;
}

/* Der Takt von oben nach unten – gerechnet, damit auch ein vierter und
   fünfter Eintrag in der Reihe erscheint statt gleichzeitig mit dem
   ersten. Vorher standen hier nur nth-child(2) und (3). */
.site-menu a:nth-child(1) { --menu-index: 1; }
.site-menu a:nth-child(2) { --menu-index: 2; }
.site-menu a:nth-child(3) { --menu-index: 3; }
.site-menu a:nth-child(4) { --menu-index: 4; }
.site-menu a:nth-child(5) { --menu-index: 5; }
.site-menu a:nth-child(6) { --menu-index: 6; }
.site-menu a:nth-child(7) { --menu-index: 7; }
.site-menu a:nth-child(8) { --menu-index: 8; }
.site-menu a:nth-child(9) { --menu-index: 9; }

.site-menu a {
  transition-delay: calc(var(--menu-versatz, 0ms)
                         + (var(--menu-index, 1) - 1) * var(--menu-takt, 40ms));
}

.site-menu.is-open a {
  opacity: 1;
  transform: translateY(0);
}

/* ══════════════════════════════════════════════════════════════════
   DER OBERE STRICH — 28.09.2026

   Bei offenem Menü wandert der obere Strich des Menüzeichens über den
   obersten Eintrag. Vorher stand dafür an fünf Stellen ein fester
   Pixelwert, und jeder war für DREI Einträge gerechnet. Seit
   »impressum« dazukam, sind es vier – der Strich landete mitten im Wort
   »haupt« und strich es durch. Gemessen im echten Chromium an der
   lebenden Seite, 28.09.2026: Schreibtisch 1440x900 Strich y=665 in
   »haupt« 652–684; 412x892 index 708 in 684–709; 390x844 index 663 in
   639–664; 375x812 index 634 in 609–634. Alle Grössen, alle Seiten.

   Deshalb wird der Weg jetzt EINMAL gerechnet, aus den Massen und der
   gezählten Anzahl. Jeder Bruchpunkt setzt nur noch seine Masse; die
   Luft, die er bei drei Einträgen hatte, bleibt dabei erhalten – so
   ändert sich nur das, was falsch war. Der untere Strich bleibt
   unberührt, und weil nur die Zielgrösse der `transform` gerechnet
   wird, läuft der Übergang (360ms ease) weiter wie zuvor.
   ══════════════════════════════════════════════════════════════════ */
.menu-toggle:has(+ .site-menu.is-open) span:first-child {
  transform: translateY(calc(-1 * (var(--menu-unten)
                                   + var(--menu-anzahl) * var(--menu-zeile)
                                   + (var(--menu-anzahl) - 1) * var(--menu-abstand)
                                   + var(--menu-luft)
                                   - var(--strich-hoehe))));
}

.menu-toggle:has(+ .site-menu.is-open) span:last-child {
  transform: translateY(0);
}


/* HOME */

.home-scroll { min-height: 230vh; }

.home-stage {
  position: fixed;
  inset: 0;
  height: 100vh;
  overflow: hidden;
  background: #f7f6f2;
}

.home-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .28;
  transform: scaleX(-1) scale(1.08);
  filter: blur(26px) grayscale(1) brightness(1.25) contrast(.70);
  z-index: 0;
}

.intro,
.home-name,
.page-kicker,
.page-name {
  position: absolute;
  left: var(--axis-left);
  margin: 0;
}

.intro {
  top: var(--axis-top);
  font-size: clamp(22px, 2.65vw, 45px);
  line-height: 1.12;
  letter-spacing: .004em;
  z-index: 2;
}

.focus-words {
  position: absolute;
  left: var(--axis-left);
  top: 52.8vh;
  transform: translateY(-50%);
  z-index: 2;
}

.focus-words p {
  margin: 0;
  font-size: clamp(54px, 9.2vw, 138px);
  line-height: 0.99;
  letter-spacing: 0.003em;
  opacity: .06;
  filter: blur(18px);
  transform: translateY(18px);
}

.home-name {
  bottom: calc(var(--axis-bottom) - 0.4vh);
  font-size: clamp(22px, 2.65vw, 45px);
  line-height: 1.12;
  letter-spacing: .004em;
  font-weight: 300;
  opacity: 0;
  filter: blur(16px);
  z-index: 2;
}

/* SUBPAGE ROOM ATMOSPHERE v1.0
   Vita and Kontakt share the same visual language as Home:
   a soft, scroll-reactive glimpse of the room behind the text.
   Purely additive — no existing subpage rule is touched. */
.room-stage {
  position: fixed;
  inset: 0;
  height: 100vh;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}

.room-atmosphere {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .16;
  transform: scale(1.05);
  filter: blur(30px) grayscale(1) brightness(1.22) contrast(.74);
}

/* SUBPAGES */

.sub-page {
  min-height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
}


.page-kicker {
  top: var(--axis-top);

  font-size: clamp(19px, 1.45vw, 26px);
  line-height: 1.12;
  letter-spacing: .006em;
  color: var(--muted);
}

.page-name {
  top: calc(var(--axis-top) + 1.05em);
  font-size: clamp(22px, 2.65vw, 45px);
  line-height: 1.12;
  letter-spacing: .004em;
  font-weight: 300;
}

.vita-main {
  margin: 0;

  max-width: 650px;
  font-size: clamp(42px, 5.35vw, 84px);
  line-height: 1.00;
  letter-spacing: 0.005em;
}

.vita-facts {
  max-width: 430px;
  display: grid;
  gap: 3.1vh;
  padding-bottom: .65vh;
}

.vita-facts div { margin: 0; }

.vita-facts span {
  display: block;
  margin-bottom: .42rem;
  color: var(--muted);
  font-size: clamp(18px, 1.35vw, 24px);
  line-height: 1.2;
  letter-spacing: .006em;
}

.vita-facts p {
  margin: 0;
  color: rgba(25,25,24,.52);
  font-size: clamp(24px, 1.9vw, 34px);
  line-height: 1.42;
  letter-spacing: .001em;
}

@media (max-width: 820px) {

  :root {
    --axis-left: 6vw;
    --axis-right: 6vw;
    --axis-top: 8vh;
    --axis-bottom: 7vh;
  }

  .vita-main {
    font-size: clamp(40px, 11vw, 72px);
  }
}


/* v1.5 — contact reference alignment fix */

.contact-room {
  position: relative;
  min-height: 100vh;
  display: block;
  padding: var(--axis-top) var(--axis-right) var(--axis-bottom) var(--axis-left);
}

.contact-room .page-kicker {
  position: absolute;
  top: var(--axis-top);
  left: var(--axis-left);
  margin: 0;
}

/* v1.8 — Adobe Fonts / Neue Haas Grotesk */

.intro,
.home-name,
.focus-words p,
.vita-main {
  font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
}

.page-kicker,
.page-name,
.site-menu a,
.vita-facts {
  font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
}

/* v1.9 — display light weight refinement */

.intro,
.home-name,
.focus-words p,
.vita-main {
  font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
  font-weight: 300;
}

.page-name {
  font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
  font-weight: 300;
}

.page-kicker,
.site-menu a,
.vita-facts {
  font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
}

/* v2.0 — ultra thin display test */

.focus-words p,
.vita-main {
  font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
  font-weight: 100;
}

.intro,
.home-name,
.page-name {
  font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
  font-weight: 300;
}

/* v2.2 — home text axis shifted into calmer room area */


.home-page .intro,
.home-page .focus-words,
.home-page .home-name {
  left: 11vw;
}


/* HOME MOBILE FINAL v1.0 */
@media (max-width: 480px) and (orientation: portrait) {
  .home-page .home-image {
    object-position: 42% center;
  }

  .home-page .menu-toggle {
    top: auto;
    bottom: calc(var(--axis-bottom) + 4px);
    right: calc(var(--axis-right) + 1.15rem);
    left: auto;
    transform: none;
    width: 22px;
    height: 18px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: flex-end;
    gap: 6px;
    /* Masse dieses Bruchpunkts – Menü 1.62rem über der Achse, Schrift 18px.
       Er hatte vorher keinen eigenen Wert für den Strich und erbte die
       138px des Grundfalls; das waren dort rund 56px Luft statt 20. */
    --menu-unten: 1.62rem;
    --menu-zeile: calc(18px * 1.14);
    opacity: 0;
    pointer-events: none;
    filter: blur(6px);
    overflow: visible;
    transition:
      opacity .35s ease,
      transform .32s ease,
      filter .55s ease;
  }

  .home-page.nav-visible .menu-toggle {
    opacity: 1;
    pointer-events: auto;
    filter: blur(0);
  }

  .home-page .menu-toggle span {
    display: block;
    width: 24px;
    height: 1px;
    margin: 0;
    background: currentColor;
  }

  .home-page .menu-toggle span:first-child,
  .home-page .menu-toggle span:last-child {
    transform: translateY(0);
    transition:
      transform 360ms ease,
      width 320ms ease,
      filter .55s ease;
  }

  .home-page .site-menu {
    top: auto;
    bottom: calc(var(--axis-bottom) + 1.62rem);
    right: calc(var(--axis-right) + 1.15rem);
    left: auto;
    transform: translateY(12px);
    align-items: flex-end;
    text-align: right;
    gap: .20rem;
    --menu-versatz: 160ms;
    opacity: 0;
    pointer-events: none;
    filter: blur(6px);
    transition:
      opacity .35s ease,
      transform .32s ease,
      filter .55s ease;
  }

  .home-page.nav-visible .site-menu.is-open {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
    filter: blur(0);
  }

  .home-page .site-menu a {
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 200;
    font-size: 18px;
    line-height: 1.14;
    letter-spacing: 0.015em;
    color: #111 !important;
    text-align: right;
    opacity: 0;
    transform: translateY(8px);
    transition:
      opacity 260ms ease,
      transform 300ms ease;
  }

  .home-page .site-menu.is-open a {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (min-width: 360px) and (max-width: 480px) and (orientation: portrait) {
  .home-page .intro,
  .home-page .home-name {
    font-size: 26px;
  }

  .home-page .focus-words p {
    font-size: 70px;
    line-height: 1.14;
  }

  .home-page .site-menu {
    bottom: calc(var(--axis-bottom) + 2.25rem);
  }

  .home-page .site-menu a {
    font-size: 22px;
  }

  /* D1C setzte hier die 4.36px Luft ein, die aus dem alten festen Wert
     (-110px) zurückgerechnet waren. Beim Dreierstand war das richtig; bei
     vier Einträgen steht der Strich aber weit weiter oben, und 4px sehen
     dort aus wie ein Versehen statt wie eine Entscheidung – gemessen
     412x892 und 390x844 gegen 20px auf den Unterseiten und 23px auf dem
     Schreibtisch. Seit D1D (28.09.2026) dieselben 20.72px wie überall
     sonst: eine Luft, nicht drei. */
  .home-page .menu-toggle {
    --menu-unten: 2.25rem;
    --menu-zeile: calc(22px * 1.14);
    --menu-luft: 20.72px;
  }
}

@media (min-width: 421px) and (max-width: 480px) and (orientation: portrait) {
  .home-page .intro,
  .home-page .home-name {
    font-size: 28px;
  }

  .home-page .focus-words p {
    font-size: 80px;
    line-height: 1.18;
  }

  .home-page .site-menu {
    bottom: calc(var(--axis-bottom) + 2.55rem);
  }

  .home-page .site-menu a {
    font-size: 24px;
  }

  /* Statt fester -138px: 2.55rem + 3*27.36 + 2*3.2 + 20.72 - 12 = 138. */
  .home-page .menu-toggle {
    --menu-unten: 2.55rem;
    --menu-zeile: calc(24px * 1.14);
    --menu-luft: 20.72px;
  }
}


/* SUBPAGE USES HOME MOBILE NAVIGATION v1.0
   Home navigation geometry copied for Vita/Kontakt.
   Home rules remain untouched. */
@media (max-width: 480px) and (orientation: portrait) {
  .sub-page .menu-toggle {
    top: auto;
    bottom: calc(var(--axis-bottom) + 4px);
    right: calc(var(--axis-right) + 1.15rem);
    left: auto;
    transform: none;
    width: 22px;
    height: 18px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: flex-end;
    gap: 6px;
    opacity: 0;
    pointer-events: none;
    filter: blur(6px);
    overflow: visible;
    /* Dieselben Masse wie im Grundfall – hier ausgeschrieben, damit eine
       spätere Änderung der Unterseiten-Navigation neben ihren Zahlen
       steht: 2.55rem + 3*27.36 + 2*3.2 + 20.72 - 12 = 138 (vorher fest). */
    --menu-unten: 2.55rem;
    --menu-zeile: calc(24px * 1.14);
    --menu-luft: 20.72px;
  }

  .sub-page.nav-visible .menu-toggle {
    opacity: 1;
    pointer-events: auto;
    filter: blur(0);
  }

  .sub-page .menu-toggle span {
    display: block;
    width: 24px;
    height: 1px;
    margin: 0;
    background: currentColor;
    transition:
      transform 360ms ease,
      width 320ms ease,
      filter .55s ease;
  }

  .sub-page .site-menu {
    top: auto;
    bottom: calc(var(--axis-bottom) + 2.55rem);
    right: calc(var(--axis-right) + 1.15rem);
    left: auto;
    transform: translateY(12px);
    align-items: flex-end;
    text-align: right;
    gap: .20rem;
    --menu-versatz: 160ms;
    opacity: 0;
    pointer-events: none;
    filter: blur(6px);
    transition:
      opacity .35s ease,
      transform .32s ease,
      filter .55s ease;
  }

  .sub-page.nav-visible .site-menu.is-open {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
    filter: blur(0);
  }

  .sub-page .site-menu a {
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 200;
    font-size: 24px;
    line-height: 1.14;
    letter-spacing: 0.012em;
    color: #111 !important;
    text-align: right;
    opacity: 0;
    transform: translateY(8px);
    transition:
      opacity 260ms ease,
      transform 300ms ease;
  }

  .sub-page .site-menu.is-open a {
    opacity: 1;
    transform: translateY(0);
  }

  .sub-page .menu-toggle:has(+ .site-menu.is-open) span:last-child {
    transform: translateY(0);
  }
}
/* VITA MOBILE ADAPTIVE SCROLL LAYOUT v1.1
   First viewport: identity + short statement.
   Scroll area: facts and expandable Vita content.
   Home remains untouched. */
@media (max-width: 480px) and (orientation: portrait) {
  .sub-page.vita-page,
  .vita-room {
    min-height: 180svh;
  }

  .vita-room {
    display: block;
    padding: 0;
  }

  .vita-intro {
    min-height: 100svh;
    padding-left: 11vw;
    padding-right: 9vw;
    padding-top: var(--axis-top);
    display: flex;
    flex-direction: column;
  }

  .vita-intro .page-kicker,
  .vita-intro .page-name {
    position: static;
    left: auto;
    margin: 0;
  }

  .vita-intro .page-name {
    font-size: clamp(22px, 2.65vw, 45px);
    line-height: 1.12;
    letter-spacing: .004em;
  }

  .vita-intro .page-kicker,
  .vita-facts span {
    font-size: 18px;
    line-height: 1.2;
    letter-spacing: .006em;
  }

  .vita-main {
    width: 100%;
    max-width: 310px;
    font-size: 43px;
    line-height: 1.14;
    letter-spacing: 0.014em;
    margin: 24vh 0 0;
  }

  .vita-facts {
    width: 100%;
    max-width: 100%;
    display: grid;
    gap: 3.2vh;
    padding-left: 11vw;
    padding-right: 9vw;
    padding-bottom: 18vh;
  }

  .vita-facts p {
    margin: 0;
    font-size: clamp(22px, 2.65vw, 45px);
    line-height: 1.12;
    letter-spacing: .004em;
  }

  .vita-facts span {
    display: block;
    margin-bottom: .32rem;
  }
}



/* VITA MOBILE TRANSFORM EXPERIMENT v0.1
   Vita verwandelt sich: Intro bleibt ruhig, Facts erscheinen als Zustände. */
@media (max-width: 480px) and (orientation: portrait) {
  .vita-room {
    min-height: 430svh;
  }

  .vita-intro {
    position: sticky;
    top: 0;
    min-height: 100svh;
    z-index: 1;
  }

  .vita-main {
    transition:
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .vita-facts {
    position: relative;
    z-index: 2;
    margin-top: -72svh;
    padding-top: 72svh;
    padding-bottom: 76vh;
  }

  .vita-facts > div {
    min-height: 72svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    opacity: 0;
    filter: blur(18px);
    transform: translateY(28px);
    transition:
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .vita-facts > div.is-visible {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }
}

/* VITA MOBILE TRANSFORM EXPERIMENT v0.3
   Stage model: facts transform in place, final state accumulates all facts. */
@media (max-width: 480px) and (orientation: portrait) {
  .vita-room {
    min-height: 460svh;
  }

  .vita-intro {
    position: sticky;
    top: 0;
    min-height: 100svh;
    z-index: 1;
  }

  .vita-main {
    transition:
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .vita-facts {
    position: sticky;
    top: 0;
    min-height: 100svh;
    margin-top: -100svh;
    padding-left: 11vw;
    padding-right: 9vw;
    padding-bottom: 0;
    display: block;
    z-index: 2;
    pointer-events: none;
  }

  .vita-facts > div {
    position: absolute;
    left: 11vw;
    right: 9vw;
    top: 34vh;
    min-height: auto;
    opacity: 0;
    filter: blur(18px);
    transform: translateY(28px);
    transition:
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .vita-facts > div.is-visible {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }

  .vita-facts.is-final > div {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }

  .vita-facts.is-final > div:nth-child(1) { top: 24vh; }
  .vita-facts.is-final > div:nth-child(2) { top: 41vh; }
  .vita-facts.is-final > div:nth-child(3) { top: 61vh; }
}

/* CONTACT MOBILE TRANSFORM EXPERIMENT v0.1
   Contact uses the Vita transformation principle: staged reveal, final accumulation. */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-room {
    min-height: 430svh;
    display: block;
    padding: 0;
  }

  .contact-intro {
    position: sticky;
    top: 0;
    min-height: 100svh;
    padding-left: 11vw;
    padding-right: 9vw;
    padding-top: var(--axis-top);
    display: flex;
    flex-direction: column;
    z-index: 1;
  }

  .contact-intro .page-kicker,
  .contact-intro .page-name {
    position: static;
    left: auto;
    margin: 0;
  }

  .contact-intro .page-name,
  .contact-states p,
  .contact-states a {
    font-size: clamp(22px, 2.65vw, 45px);
    line-height: 1.12;
    letter-spacing: .004em;
  }

  .contact-intro .page-kicker,
  .contact-states span {
    font-size: 18px;
    line-height: 1.2;
    letter-spacing: .006em;
  }

  .contact-main {
    width: 100%;
    max-width: 310px;
    font-size: 43px;
    line-height: 1.14;
    letter-spacing: 0.014em;
    margin: 24vh 0 0;
    color: var(--text);
    transition:
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .contact-states {
    position: sticky;
    top: 0;
    min-height: 100svh;
    margin-top: -100svh;
    padding-left: 11vw;
    padding-right: 9vw;
    padding-bottom: 0;
    display: block;
    z-index: 2;
    pointer-events: none;
  }

  .contact-states > div {
    position: absolute;
    left: 11vw;
    right: 9vw;
    top: 36vh;
    opacity: 0;
    filter: blur(18px);
    transform: translateY(28px);
    transition:
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .contact-states > div.is-visible,
  .contact-states.is-final > div {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }

  .contact-states.is-final > div:nth-child(1) { top: 26vh; }
  .contact-states.is-final > div:nth-child(2) { top: 45vh; }
  .contact-states.is-final > div:nth-child(3) { top: 67vh; }

  .contact-states span {
    display: block;
    margin-bottom: .32rem;
    color: var(--muted);
  }

  .contact-states p {
    margin: 0;
    color: rgba(25,25,24,.52);
  }

  .contact-states a {
    color: var(--text);
    text-decoration: none;
  }

  .sub-page.nav-visible .menu-toggle {
    opacity: 1;
    pointer-events: auto;
    filter: blur(0);
  }

  .sub-page.nav-visible .site-menu.is-open {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
    filter: blur(0);
  }
}


/* CONTACT MAIN TYPE ALIGNMENT v0.1 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-main {
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 100;
  }
}

/* CONTACT MAIL FINAL CHOREOGRAPHY v0.1 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states a {
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 100;
  }

  .contact-states.is-final > div:nth-child(3) {
    transition:
      top 680ms cubic-bezier(.16,1,.3,1),
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }
}

/* CONTACT MAIL FINAL ALIGNMENT v0.2 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states a {
    font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
    font-weight: 400;
    font-size: clamp(22px, 2.65vw, 45px);
    line-height: 1.12;
    letter-spacing: .004em;
  }

  .contact-states.is-final > div:nth-child(3) {
    top: auto;
    bottom: calc(var(--axis-bottom) + 0.05rem);
  }
}

/* CONTACT FINAL TYPE CONSISTENCY v0.3 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-intro .page-name,
  .contact-states p,
  .contact-states a {
    font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
    font-weight: 400;
    font-size: clamp(22px, 2.65vw, 45px);
    line-height: 1.12;
    letter-spacing: .004em;
    color: rgba(25,25,24,.52);
  }

  .contact-states.is-final > div:nth-child(3) {
    top: auto;
    bottom: calc(var(--axis-bottom) - 0.28rem);
  }
}

/* CONTACT MAIL FINAL DRIFT v0.4 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states > div:nth-child(3) {
    transition:
      top 720ms cubic-bezier(.16,1,.3,1),
      bottom 720ms cubic-bezier(.16,1,.3,1),
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }
}

/* CONTACT MAIL FINAL DRIFT FIX v0.5 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states > div:nth-child(3) {
    bottom: calc(var(--axis-bottom) - 0.28rem);
    transform: translateY(-22vh);
  }

  .contact-states.is-final > div:nth-child(3) {
    top: auto;
    bottom: calc(var(--axis-bottom) - 0.28rem);
    transform: translateY(0);
  }
}

/* CONTACT MAIL DRIFT START POSITION FIX v0.6 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states > div:nth-child(3) {
    top: 36vh;
    bottom: auto;
    transform: translateY(0);
  }

  .contact-states.is-final > div:nth-child(3) {
    top: 36vh;
    bottom: auto;
    transform: translateY(45vh);
  }
}

/* CONTACT MAIL FINAL POSITION TUNE v0.7 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states.is-final > div:nth-child(3) {
    transform: translateY(47vh);
  }
}

/* CONTACT MAIL TOP-AXIS FINAL v0.8 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states > div:nth-child(3) {
    top: 36vh;
    bottom: auto;
    transform: none;
    transition:
      top 720ms cubic-bezier(.16,1,.3,1),
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .contact-states.is-final > div:nth-child(3) {
    top: calc(100svh - var(--axis-bottom) - 4.9rem);
    bottom: auto;
    transform: none;
  }
}

/* CONTACT MAIL TOP-AXIS FINAL TUNE v0.9 */
@media (max-width: 480px) and (orientation: portrait) {
  .contact-states.is-final > div:nth-child(3) {
    top: calc(100svh - var(--axis-bottom) - 2.9rem);
  }
}

/* VITA FINAL BLOCK DRIFT v0.1 */
@media (max-width: 480px) and (orientation: portrait) {
  .vita-facts > div {
    transition:
      top 720ms cubic-bezier(.16,1,.3,1),
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }
}








/* DESKTOP NAV MOBILE SCALE v1.0
   Direct scale derivation from mobile navigation.
   No reweighting, no opacity change, no separate desktop typography logic. */
@media (min-width: 821px) {
  .menu-toggle {
    bottom: calc(var(--axis-bottom) + 5px);
    right: calc(var(--axis-right) + 1.32rem);
    width: 25px;
    height: 21px;
    gap: 7px;
    /* Statt fester -159px: 2.93rem + 3*31.92 + 2*3.68 + 23 - 14 = 159.
       --strich-hoehe ist hier 14px, weil der Knopf 5px über der Achse
       sitzt und sein Inhalt 1+7+1 hoch ist. */
    --menu-unten: 2.93rem;
    --menu-zeile: calc(28px * 1.14);
    --menu-abstand: .23rem;
    --strich-hoehe: 14px;
    --menu-luft: 23px;
  }

  .menu-toggle span {
    width: 28px;
    height: 1px;
  }

  .site-menu {
    bottom: calc(var(--axis-bottom) + 2.93rem);
    right: calc(var(--axis-right) + 1.32rem);
    gap: .23rem;
  }

  .site-menu a {
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 200;
    font-size: 28px;
    line-height: 1.14;
    letter-spacing: 0.010em;
    opacity: 0;
  }

  .site-menu.is-open a {
    opacity: 1;
  }

  .menu-toggle:has(+ .site-menu.is-open) span:last-child {
    transform: translateY(0);
  }
}


/* DESKTOP SUBPAGE ATMOSPHERIC SCROLL v1.0
   Home untouched. Nav untouched. Subpages inherit Home-like staged atmosphere. */
@media (min-width: 821px) {
  .sub-page .sub-room {
    --sub-axis-left: 11vw;
    position: relative;
    min-height: 430svh;
    padding: 0;
    overflow: visible;
  }

  .sub-page .vita-intro,
  .sub-page .contact-intro {
    position: sticky;
    top: 0;
    min-height: 100svh;
    padding-left: var(--sub-axis-left);
    padding-right: calc(var(--axis-right) + 13rem);
    padding-top: var(--axis-top);
    display: flex;
    flex-direction: column;
    z-index: 1;
  }

  .sub-page .page-kicker,
  .sub-page .page-name {
    position: static;
    left: auto;
    margin: 0;
  }

  /* Grad und Abstand hingen allein an der BREITE – die Höhe kam in keiner
     der beiden Zahlen vor. Darum lief der Satz aus jedem flachen Fenster
     unten hinaus, und zwar nicht als Ausnahme: über ein Gitter aus zwölf
     Breiten und zwölf Höhen, im echten Chrome am 30.09.2026 gemessen,
     fielen mit `28vh` und `clamp(48px, 5vw, 86px)` **60 von 144**
     Fenstermassen durch. Simons eigenes Fenster war eines davon
     (1909x851: Grad 86 px, sechs Zeilen à 89,44 px, Oberkante 395,6 px,
     Unterkante 932,2 px – 81,2 px unter der Tafelkante); 1440x900 hielt
     mit 20 px Luft, 1440x800 lief schon über.

     Jetzt begrenzt die Höhe beides, und die drei Zahlen sind gerechnet,
     nicht geraten:

       24svh   der Abstand nach oben, als Anteil DERSELBEN Tafel, die ihn
               aufnehmen muss (`.vita-intro` ist 100svh hoch) – nicht in
               vh, denn auf dem Telefon ist vh grösser als die Tafel.
       70svh   was darunter für den Satz bleibt: 100 minus 24 Abstand
               minus 6 Reserve.
       170px   die Überschrift darüber (Achsenrand, „vita", der Name),
               gemessen an ihrer grössten Ausprägung 1909x994: 170,5 px.
       6,3     sechs Zeilen à 1,04 Zeilenhöhe, aufgerundet – so viele
               braucht der längste Satz der drei Unterseiten.

     Nachgemessen über dasselbe Gitter: 0 von 144 laufen über, die engste
     Luft beträgt 64 px. */
  .sub-page .vita-main,
  .sub-page .contact-main {
    position: static;
    width: min(72vw, 920px);
    margin: 24svh 0 0 0;
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 100;
    font-size: clamp(40px, min(5vw, calc((70svh - 170px) / 6.3)), 86px);
    line-height: 1.04;
    letter-spacing: 0.005em;
    color: var(--text);
    transition: opacity 420ms ease, filter 520ms ease, transform 520ms ease;
  }

  .sub-page .vita-facts,
  .sub-page .contact-states {
    position: sticky;
    top: 0;
    min-height: 100svh;
    margin-top: -100svh;
    padding: 0;
    display: block;
    z-index: 2;
    pointer-events: none;
  }

  /* Im Fluss (siehe ZEILENBLOCK am Dateiende) stehen die Kinder gewöhnlich,
     `left` gilt für sie nicht mehr – also trägt der Behälter die Achse.
     Ohne diese Zeilen klebte der Text am Fensterrand: gemessen 28.09.2026
     an der lebenden Seite, impressum 1440x900, Titel „Simon Haener"
     left=158 (11vw), die fünf Einträge alle left=0. Von Simon fotografiert.
     Dieselben Werte wie am Kind darüber, damit die Achse eine einzige
     bleibt. */
  .sub-page .sub-room.ist-fluss .vita-facts,
  .sub-page .sub-room.ist-fluss .contact-states {
    padding-left: var(--sub-axis-left);
    padding-right: calc(var(--axis-right) + 13rem);
  }

  .sub-page .vita-facts > div,
  .sub-page .contact-states > div {
    position: absolute;
    left: var(--sub-axis-left);
    right: calc(var(--axis-right) + 13rem);
    top: 44vh;
    width: min(64vw, 820px);
    opacity: 0;
    filter: blur(18px);
    transform: translateY(28px);
    transition:
      top 720ms cubic-bezier(.16,1,.3,1),
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .sub-page .vita-facts > div.is-visible,
  .sub-page .contact-states > div.is-visible,
  .sub-page .vita-facts.is-final > div,
  .sub-page .contact-states.is-final > div {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }

  .sub-page .vita-facts.is-final > div:nth-child(1),
  .sub-page .contact-states.is-final > div:nth-child(1) { top: 34vh; }

  .sub-page .vita-facts.is-final > div:nth-child(2),
  .sub-page .contact-states.is-final > div:nth-child(2) { top: 52vh; }

  .sub-page .vita-facts.is-final > div:nth-child(3),
  .sub-page .contact-states.is-final > div:nth-child(3) { top: 70vh; }

  .sub-page .vita-facts span,
  .sub-page .contact-states span {
    display: block;
    margin-bottom: .42rem;
    color: var(--muted);
    font-size: clamp(17px, 1.25vw, 22px);
    line-height: 1.2;
  }

  .sub-page .vita-facts p,
  .sub-page .contact-states p,
  .sub-page .contact-states a {
    margin: 0;
    color: rgba(25,25,24,.52);
    font-size: clamp(23px, 1.9vw, 34px);
    line-height: 1.34;
    text-decoration: none;
  }
}


/* CONTACT MAIL ↔ NAV ALIGNMENT v0.1
   Mail block visually ends on the lower navigation axis. */
@media (min-width: 821px) {

  .contact-states {
    padding-bottom: 7.5rem;
  }

}

/* SUBPAGE MEDIUM VIEWPORT ATMOSPHERE v1.0
   Schliesst die Luecke zwischen Mobile-Portrait (<=480px Hochformat) und
   Desktop (>=821px): Tablets, schmale Browserfenster, Handys im
   Querformat. Vita/Kontakt bekommen hier dieselbe linke Achse (11vw,
   identisch zu Home) und dieselbe sticky Scroll-Choreographie wie
   Desktop - nur mit reduzierten Abstaenden, damit nichts ueberlaeuft.
   Home-, Mobile- und Desktop-Regeln bleiben unveraendert. */
@media (min-width: 481px) and (max-width: 820px),
       (max-width: 480px) and (orientation: landscape) {

  .sub-page .sub-room {
    --sub-axis-left: 11vw;
    position: relative;
    min-height: 430svh;
    padding: 0;
    overflow: visible;
  }

  .sub-page .vita-intro,
  .sub-page .contact-intro {
    position: sticky;
    top: 0;
    min-height: 100svh;
    padding-left: var(--sub-axis-left);
    padding-right: calc(var(--axis-right) + 2rem);
    padding-top: var(--axis-top);
    display: flex;
    flex-direction: column;
    z-index: 1;
  }

  .sub-page .page-kicker,
  .sub-page .page-name {
    position: static;
    left: auto;
    margin: 0;
  }

  .sub-page .vita-main,
  .sub-page .contact-main {
    position: static;
    width: min(80vw, 640px);
    margin: 20vh 0 0 0;
    font-family: "neue-haas-grotesk-display", "Helvetica Neue", Arial, sans-serif;
    font-weight: 100;
    font-size: clamp(34px, 6vw, 62px);
    line-height: 1.08;
    letter-spacing: 0.008em;
    color: var(--text);
    transition: opacity 420ms ease, filter 520ms ease, transform 520ms ease;
  }

  .sub-page .vita-facts,
  .sub-page .contact-states {
    position: sticky;
    top: 0;
    min-height: 100svh;
    margin-top: -100svh;
    padding: 0;
    display: block;
    z-index: 2;
    pointer-events: none;
  }

  /* Dieselbe Achse für den Fluss wie oben auf dem Schreibtisch. */
  .sub-page .sub-room.ist-fluss .vita-facts,
  .sub-page .sub-room.ist-fluss .contact-states {
    padding-left: var(--sub-axis-left);
    padding-right: calc(var(--axis-right) + 2rem);
  }

  .sub-page .vita-facts > div,
  .sub-page .contact-states > div {
    position: absolute;
    left: var(--sub-axis-left);
    right: calc(var(--axis-right) + 2rem);
    top: 44vh;
    opacity: 0;
    filter: blur(18px);
    transform: translateY(28px);
    transition:
      top 720ms cubic-bezier(.16,1,.3,1),
      opacity 420ms ease,
      filter 520ms ease,
      transform 520ms ease;
  }

  .sub-page .vita-facts > div.is-visible,
  .sub-page .contact-states > div.is-visible,
  .sub-page .vita-facts.is-final > div,
  .sub-page .contact-states.is-final > div {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }

  .sub-page .vita-facts.is-final > div:nth-child(1),
  .sub-page .contact-states.is-final > div:nth-child(1) { top: 32vh; }

  .sub-page .vita-facts.is-final > div:nth-child(2),
  .sub-page .contact-states.is-final > div:nth-child(2) { top: 50vh; }

  .sub-page .vita-facts.is-final > div:nth-child(3),
  .sub-page .contact-states.is-final > div:nth-child(3) { top: 68vh; }

  .sub-page .vita-facts span,
  .sub-page .contact-states span {
    display: block;
    margin-bottom: .38rem;
    color: var(--muted);
    font-size: clamp(16px, 2vw, 20px);
    line-height: 1.2;
  }

  .sub-page .vita-facts p,
  .sub-page .contact-states p,
  .sub-page .contact-states a {
    margin: 0;
    color: rgba(25,25,24,.52);
    font-size: clamp(20px, 2.6vw, 28px);
    line-height: 1.34;
    text-decoration: none;
  }

  .sub-page .contact-states {
    padding-bottom: 5rem;
  }
}

/* SUBPAGE SHORT-LANDSCAPE STATIC FALLBACK v1.0
   Phones in landscape orientation have very little vertical height
   (~320-430px). The sticky/absolute vh-based fact choreography above
   assumes enough height for each fact's multi-line text to fit between
   its neighbours - on short landscape screens the facts overlap each
   other and the surrounding intro text (reported by Gruender as
   "mobile quer sind vita und kontakt unbrauchbar", verifiziert per
   Scroll-Screenshot-Test bei 568x320/667x375/812x375).
   Fix: below max-height:500px in landscape orientation, drop the
   scroll-triggered reveal entirely and show Vita/Kontakt as a normal,
   simply scrollable page with all facts visible at once, stacked in
   document flow. No overlap possible regardless of text length, since
   nothing is absolutely positioned on top of anything else anymore.
   Desktop, taller tablet-landscape and mobile-portrait remain fully
   untouched (gated by both orientation AND max-height, not by width -
   this is independent of the 481-820px width bucket above and always
   wins on ties because it is declared later in the file). */
@media (orientation: landscape) and (max-height: 500px) {

  .sub-page .sub-room {
    position: static;
    min-height: 0;
    height: auto;
    overflow: visible;
  }

  .sub-page .vita-intro,
  .sub-page .contact-intro {
    position: static;
    min-height: 0;
    height: auto;
    padding: 5vh calc(var(--axis-right) + 2rem) 3vh 8vw;
    display: block;
  }

  .sub-page .page-kicker,
  .sub-page .page-name {
    position: static;
    left: auto;
    margin: 0;
  }

  .sub-page .vita-main,
  .sub-page .contact-main {
    position: static !important;
    width: auto;
    max-width: 640px;
    margin: 2vh 0 0 0 !important;
    font-size: clamp(22px, 3.6vw, 32px) !important;
    line-height: 1.22 !important;
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }

  .sub-page .vita-facts,
  .sub-page .contact-states {
    position: static;
    min-height: 0;
    height: auto;
    margin-top: 0;
    padding: 1vh 8vw 5vh 8vw;
    display: grid;
    gap: 2.2vh;
    pointer-events: auto;
  }

  .sub-page .vita-facts > div,
  .sub-page .contact-states > div {
    position: static !important;
    left: auto;
    right: auto;
    top: auto !important;
    width: auto;
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }

  .sub-page .vita-facts span,
  .sub-page .contact-states span {
    font-size: 13px;
    margin-bottom: .2rem;
  }

  .sub-page .vita-facts p,
  .sub-page .contact-states p,
  .sub-page .contact-states a {
    font-size: clamp(16px, 2.6vw, 20px);
    line-height: 1.3;
  }

  .sub-page .contact-states {
    padding-bottom: 4vh;
  }
}

/* RAUM UND TINTE — Wetterraum seit D1E wieder da (28.09.2026)

   Eine WebGL2-Leinwand (`static/wetterraum/`) zeichnet den Hintergrund
   nach dem echten Wetter am Ort des Besuchers, `static/wetterraum_seite.js`
   setzt danach die Tinte. D1C hatte das für zwanzig Minuten abgebaut –
   Simon, 22:45: *„Nimm diese bescheuerten Wetter Daten raus!"* – meinte
   aber nur die drei Namensnennungs-Zeilen im Impressum. Simon, 22:50:
   *„Nicht der Wetterraum im Hintergrund … NICHT DER HINTERGRUNDEFFEKT!"*
   Der Generator (build_presence_site.py) bekam die Leinwand mit D1E
   zurück, diese Regeln waren dabei liegengeblieben – ohne sie hatte die
   Leinwand keine Grösse, keinen Rand und keine Überblendung, obwohl
   Skript und Markup längst wieder da waren.

   Die Tintenwerte bleiben unverändert stehen: 25,25,24 / .52 / .36 sind
   genau `--text`, `rgba(25,25,24,.52)` und `--muted` der früheren
   Fassung, ob mit oder ohne Leinwand. */
:root {
  --tinte: 25,25,24;
  --tinte-halb: .52;
  --tinte-leise: .36;
}

.raum-buehne {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}

.raum-buehne-unterseite { position: fixed; }

/* Die Leinwand ragt über den Rand: der Weichzeichner franst sonst an den
   Kanten ins Helle aus. */
.raum-leinwand {
  position: absolute;
  left: -48px;
  top: -48px;
  width: calc(100% + 96px);
  height: calc(100% + 96px);
  display: block;
  opacity: 0;
  transition: opacity 1.2s ease;
}

.raum-schleier {
  position: absolute;
  inset: 0;
}

.wetterraum-an .raum-leinwand { opacity: 1; }
.wetterraum-an .raum-bild { opacity: 0 !important; transition: opacity 1.2s ease; }

.sub-page .sub-room {
  position: relative;
  z-index: 1;
}

.intro,
.home-name,
.focus-words p,
.page-name,
.vita-main,
.contact-main,
.sub-page .vita-main,
.sub-page .contact-main,
.site-menu a,
.home-page .site-menu a,
.sub-page .site-menu a,
.contact-field input,
.contact-field textarea,
.contact-form button,
.sub-page .contact-field input,
.sub-page .contact-field textarea,
.sub-page .contact-form button {
  color: rgb(var(--tinte)) !important;
}

.menu-toggle span,
.home-page .menu-toggle span,
.sub-page .menu-toggle span {
  background: rgb(var(--tinte)) !important;
}

.page-kicker,
.vita-facts span,
.contact-states span,
.sub-page .vita-facts span,
.sub-page .contact-states span,
.contact-field label,
.sub-page .contact-field label {
  color: rgba(var(--tinte), var(--tinte-leise)) !important;
}

.vita-facts p,
.contact-states p,
.contact-states a,
.sub-page .vita-facts p,
.sub-page .contact-states p,
.sub-page .contact-states a {
  color: rgba(var(--tinte), var(--tinte-halb)) !important;
}

@media (max-width: 480px) and (orientation: portrait) {
  .contact-intro .page-name {
    color: rgba(var(--tinte), var(--tinte-halb)) !important;
  }
}


/* ══════════════════════════════════════════════════════════════════
   ZEILENBLOCK — JEDE UNTERSEITE LÄUFT IM FLUSS
   28.09.2026 angelegt · 30.09.2026 auf ALLE Unterseiten ausgedehnt

   Die Choreografie darunter setzt `.contact-states > div` und
   `.vita-facts > div` absolut und gibt nur nth-child 1, 2 und 3 eine
   eigene Höhe. Am 28.09.2026 fiel auf, dass sie bei einer ANDEREN Anzahl
   bricht: das Impressum hat fünf Einträge, „fassung“ lag auf „daten“,
   „D1B“ auf „Der Hintergrund…“. Der Rückfall in den Fluss galt darum
   zunächst nur für Seiten mit einer anderen Anzahl als drei.

   DAS WAR DIE FALSCHE BEDINGUNG. Die Anzahl war nie das Problem – die
   Choreografie parkt Einträge bei festen vh-Höhen (34/52/70), also
   proportional zur FENSTERHÖHE, während ihr Text mit
   `clamp(23px, 1.9vw, 34px)` an der FENSTERBREITE hängt und nach unten
   wächst, sobald er umbricht. Beide Grössen haben nichts miteinander zu
   tun. Ein breites, flaches Fenster bringt sie darum zur Deckung, ganz
   gleich wie viele Einträge dastehen. Genau das ist auf vita.html
   passiert, bei GENAU drei Einträgen – Simon, 30.09.2026: „Bei Vita
   schreibt es Text ineinander und der Text wird unten abgeschnitten.“

   Gemessen im echten Chrome an der lebenden Seite, Unterkante „felder“
   gegen Oberkante „be pure“:

     1909x764 (Simons Fenster)  -32,2 px      1920x800   -25,8 px
     1909x851                   -16,6 px      1440x900   +37,0 px

   Der Fehler wächst also mit der Breite und schrumpft mit der Höhe; wer
   nur auf 1440x900 nachsieht, findet ihn nie. Im selben Zug hing der
   letzte Eintrag 142 bis 168 px unter der Fensterkante.

   Deshalb gilt ab hier: ALLE Unterseiten laufen im Fluss, der jede
   Anzahl und jede Textlänge trägt, und alle scrollen gleich – was Simon
   im selben Satz verlangt hat („Das Scrollverhalten auf der Kontaktseite
   ist nicht identisch mit den anderen Seiten“). Die Ein- und
   Ausblendung (opacity, blur) bleibt unberührt, nur die Ortsangabe
   entfällt; die Atmosphäre bleibt, das Pixelparken geht. Die Startseite
   (`body.home-page`, eigene Vorlage „buehne“) ist davon nicht berührt
   und behält ihren inszenierten Lauf – sie ist die Bühne, die
   Unterseiten sind die Räume.

   DIE SCHALTUNG LIEST JETZT EINE KLASSE STATT ZU ZÄHLEN. Vorher stand
   hier `:not(:has(> div:nth-child(1):nth-last-child(3)))` – ein Rätsel
   im Stilblatt über etwas, das der Generator längst weiss. Er setzt
   `.ist-fluss` und tut es ohne Bedingung. Damit ist der Zählzweig weg,
   und mit ihm die Falle: keine Seite kann mehr durch blosses Hinzufügen
   oder Weglassen eines Eintrags in die vh-Parkerei zurückfallen.

   SPEZIFITÄT. Das alte `:not(:has(…))` wog 0-3-1 bzw. 0-3-2, weil
   `:not()` und `:has()` die Spezifität ihres Arguments erben; die Kinder
   gewannen gegen `.sub-page …is-final > div:nth-child(3)` (ebenfalls
   0-3-2) nur über die Quellreihenfolge. `.sub-page .sub-room.ist-fluss …`
   wiegt 0-4-0 bzw. 0-4-1 und gewinnt ausdrücklich, nicht durch Zufall
   der Position. Nachgemessen an der GEBAUTEN Datei, nicht am Quelltext.
   ══════════════════════════════════════════════════════════════════ */

.sub-page .sub-room.ist-fluss .contact-states,
.sub-page .sub-room.ist-fluss .vita-facts {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(1.1rem, 3.4vh, 2.6rem);
  padding-top: 16vh;
  padding-bottom: 12vh;
}

.sub-page .sub-room.ist-fluss .contact-states > div,
.sub-page .sub-room.ist-fluss .vita-facts > div {
  position: static;
  top: auto;
  right: auto;
  bottom: auto;
  left: auto;
}

/* Die Klebrigkeit endet mit der Choreographie.

   Die Anschrift („kontakt / Simon Haener") klebt bei `position: sticky` am
   oberen Rand, und die Liste zieht sich mit `margin-top: -100svh` über sie –
   beides gehört zur Dreier-Choreographie, wo die Einträge bei 34vh, 52vh und
   70vh weit unterhalb der Anschrift stehen. Im Fluss stehen sie oben, und
   dann schreibt die klebende Anschrift quer darüber: gemessen am 30.09.2026
   auf kontakt.html bei 1512x945 – Unterkante „Simon Haener" 155 px, Oberkante
   „themen" 113 px. Von Simon gesehen und gemeldet.

   `.ist-fluss` setzt der Generator seit dem 30.09.2026 an JEDER Unterseite,
   ohne Bedingung (vorher nur bei einer anderen Anzahl als drei – warum das
   die falsche Bedingung war, steht im ZEILENBLOCK oben). Die Liste folgt der
   Anschrift damit überall schlicht nach unten. */
.sub-room.ist-fluss .contact-intro,
.sub-room.ist-fluss .vita-intro {
  position: static;
}

.sub-room.ist-fluss .contact-states,
.sub-room.ist-fluss .vita-facts {
  position: static;
  margin-top: 0;
  min-height: 0;
}

/* Die 430svh sind die Anlaufstrecke, an der die Dreier-Choreographie ihre
   Einträge nacheinander einblendet. Ohne sie bleibt nur leerer Weg: im Fluss
   endete der letzte Eintrag am 30.09.2026 bei 1843 px, die Seite aber erst
   bei 4064. Der Fluss braucht die Höhe seines Inhalts, sonst nichts. */
.sub-page .sub-room.ist-fluss {
  min-height: 100svh;
}


/* ══════════════════════════════════════════════════════════════════
   DIE LETZTE ZEILE SITZT AUF DER UNTEREN ACHSE — 30.09.2026

   Simon: „Auf Kontakt ist die Mailzeile nicht unten bündig mit der
   Navigation Unterlinie." Gemessen an der lebenden Seite bei 1440x757:
   Grundlinie der Mailzeile 650,7 px, oberer Rand des unteren Strichs
   698,8 px – 48 px zu hoch. Bei 1920x1080 waren es 59 px, bei 1280x657
   noch 42. Der Fehler wuchs also mit dem Fenster, weil das Polster
   `padding-bottom: 12vh` aus dem Fluss-Block oben mit der Fensterhöhe
   mitwächst, die Navigation aber auf einer festen Achse hängt.

   ZWEI GRÖSSEN, NICHT EINE. Das Polster allein reicht nicht: es legt
   die Unterkante des Textkastens an die Achse, und die liegt um
   Unterlänge und halben Durchschuss tiefer als die Grundlinie. Bündig
   ist eine Zeile aber dann, wenn ihre GRUNDLINIE auf dem Strich sitzt –
   wie ein Wort auf einer Unterstreichung. Darum hebt die zweite Regel
   genau diesen Abstand wieder auf.

   Der Ausgleich ist gemessen, nicht geschätzt. Drei Breiten ergaben
   15,3 px bei 27,36 px Schriftgrad, 14,3 bei 24,32 und 17,3 bei 34 –
   das ist keine reine em-Grösse, sondern `0,329em + 6,3px`; die Gerade
   durch die ersten zwei Punkte trifft den dritten auf 0,19 px. Ein
   fester px-Wert hätte nur bei einer Fensterbreite gestimmt, ein reiner
   em-Wert bei 1920 um 1,5 px danebengelegen.

   Gegenprobe im echten Chrome, Versatz Grundlinie gegen Strich:
     1440x900  -0,13 px    1512x945  -0,58 px    2560x1440  +0,27 px
     1280x800  +0,20 px    1920x1080 +0,56 px
   Alle fünf unter einem Pixel.

   DIE SPEZIFITÄT IST HIER KEIN SCHMUCK. Die erste Fassung dieser Regel
   stand auf 0-3-0, wurde vom Fluss-Block oben still überstimmt und
   bewegte gar nichts – gemessen an der gebauten Datei, Versatz
   unverändert 33 bis 60 px. Beide Blöcke wiegen heute 0-4-0; es
   entscheidet die Quellreihenfolge, und dieser steht weiter unten. Ein
   `!important` bleibt der Seite damit erspart.

   UND DIE VITA BEKOMMT DIESELBE BÜNDIGKEIT (30.09.2026). Sie läuft seit
   heute im selben Fluss (siehe ZEILENBLOCK), und ihr letzter Absatz
   trägt dieselbe Schrift wie der Mailblock: `.sub-page .vita-facts p`
   und `.sub-page .contact-states p` stehen in EINER Deklaration, gleicher
   Grad, gleicher Durchschuss 1,34. Der oben gerechnete Ausgleich gilt
   darum unverändert. Es ist eine Gestaltungsentscheidung und keine
   Notwendigkeit: zwei Räume desselben Hauses sollen unten auf derselben
   Linie enden, sonst wirkt der eine abgebrochen und der andere gesetzt. */
.sub-page .sub-room.ist-fluss .contact-states,
.sub-page .sub-room.ist-fluss .vita-facts {
  padding-bottom: calc(var(--axis-bottom) + 6px);
}

.sub-page .sub-room.ist-fluss .contact-states > div:last-child p,
.sub-page .sub-room.ist-fluss .vita-facts > div:last-child p {
  margin-bottom: calc(-0.329em - 6.3px);
}


/* ══════════════════════════════════════════════════════════════════
   KONTAKTFORMULAR — 29.09.2026

   Tritt an die Stelle der blossen Mailadresse (Simon, 29.09.2026). Liegt
   als vierter Block in derselben Liste wie „themen", „antwort" und
   „mail" und läuft damit über den ZEILENBLOCK-Fluss oben statt über die
   auf eine einzeilige Mailadresse eingestellte Pixelchoreographie.

   Keine Rahmenkästen: Felder tragen nur eine Unterstreichung, keinen
   Kasten. Dieselbe Schrift und dieselben Farbvariablen wie die übrigen
   Blöcke – die Beschriftung in derselben leisen Art wie die vorhandenen
   span-Überschriften (.contact-states span, weiter oben und in „RAUM UND
   TINTE" gesetzt). Keine mittige Zentrierung: das Formular hängt an der
   bestehenden linken Achse des jeweiligen Bruchpunkts, dieselbe, an der
   auch „themen" und „antwort" hängen. */

/* Die Zeigersperre des Behälters endet am Formular. `.contact-states` trägt
   `pointer-events: none`, damit die durchscheinende Choreographie keine
   Klicks abfängt – und `pointer-events` vererbt sich. Damit war das Formular
   zwar zu sehen, aber tot: gemessen am 30.09.2026 an der lebenden Seite,
   `elementsFromPoint` auf der Mitte von `#cf-name` lieferte `P.contact-main`,
   also den Einleitungssatz dahinter. Simon sah genau das: „Das Formular
   funktioniert nicht, anscheinend liegt es hinter dem Text." */
.contact-form-block {
  max-width: 380px;
  pointer-events: auto;
}

/* Dieselbe Zeigersperre traf den Mail-Verweis, nur eine Kachel weiter: am
   30.09.2026 wurde sie fuer das Formular geloest, der Verweis darunter blieb
   stehen. Gemessen am 02.10.2026 an der lebenden Seite, ueber den ganzen
   Rollweg und in allen drei Breiten: `elementsFromPoint` auf der Mitte von
   „kontakt@simon-haener.ch" lieferte `MAIN.sub-room`, Selenium meldete
   ElementClickInterceptedException. Auf impressum.html ist dieser Verweis
   der einzige Weg zu Simon. */
.contact-states a[href^="mailto:"],
.sub-page .contact-states a[href^="mailto:"] {
  pointer-events: auto;
}

.contact-form {
  display: grid;
  gap: 1.05rem;
  margin-top: .5rem;
}

.contact-field {
  display: grid;
  gap: .3rem;
}

.contact-field label {
  font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
  font-size: clamp(13px, 1vw, 15px);
  letter-spacing: .006em;
}

.contact-field input,
.contact-field textarea {
  font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
  /* Untergrenze 16px, nicht 15: Safari auf dem iPhone vergroessert jedes
     fokussierte Feld unter 16px von selbst. Gemessen am 02.10.2026 bei
     390 px: der Satz stand ruhig, scrollWidth 390 = clientWidth 390 - erst
     das Antippen von „Name" haette die Seite verzoomt und waagrecht
     verschiebbar gemacht. Auf 1440 px aendert sich nichts (dort gilt 17px),
     auf dem Telefon ist es ein Bildpunkt. */
  font-size: clamp(16px, 1.2vw, 17px);
  line-height: 1.3;
  background: transparent;
  border: 0;
  /* Über `--tinte`, nicht fest verdrahtet: im Wetterraum ist die Tinte hell
     (239,238,234), auf hellem Papier dunkel (25,25,24). Fest eingetragenes
     Dunkel ergab am 30.09.2026 eine Unterstreichung von rgba(25,25,24,.22)
     auf dunklem Grund – das Feld war nicht zu erkennen. */
  border-bottom: 1px solid rgba(var(--tinte), .34);
  border-radius: 0;
  padding: .3rem 0;
  resize: vertical;
}

.contact-field input:focus,
.contact-field textarea:focus {
  outline: none;
  border-bottom-color: rgb(var(--tinte));
}

.contact-field textarea { min-height: 3.6em; }

/* Honigtopf: für Menschen unsichtbar, für einen Automaten weiterhin
   ausfüllbar – siehe kontakt/kontakt.php. Kein display:none, das
   überspringen manche Automaten gezielt; stattdessen aus dem sichtbaren
   Bereich geschoben und per aria-hidden aus der Vorlesereihenfolge. */
.contact-field-versteckt {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact-form button {
  justify-self: start;
  position: relative;
  font-family: "neue-haas-grotesk-text", "Helvetica Neue", Arial, sans-serif;
  /* Untergrenze 16px wie bei den Feldern - ein Knopf unter 16px loest beim
     Fokus denselben Zoom aus. */
  font-size: clamp(16px, 1.2vw, 17px);
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgb(var(--tinte));
  border-radius: 0;
  padding: .2rem 0;
  cursor: pointer;
}

/* Der Griff, nicht der Strich: gemessen am 02.10.2026 war „senden" auf dem
   Telefon 51 x 26 Bildpunkte gross, also unter dem Daumenmass 44 x 44. Die
   Flaeche waechst unsichtbar nach aussen, die Unterstreichung bleibt, wo sie
   ist - sonst waere es eine Gestaltungsaenderung statt einer Behebung. */
.contact-form button::after {
  content: "";
  position: absolute;
  inset: -9px -10px -9px -10px;
}

.contact-form button:hover,
.contact-form button:focus-visible {
  opacity: .6;
}

.contact-form p.contact-status {
  margin: 0;
  min-height: 1.2em;
  font-size: clamp(13px, 1vw, 15px);
}

.contact-form p.contact-status.ist-ok { color: rgb(var(--tinte)) !important; }
.contact-form p.contact-status.ist-fehler { color: rgba(163,52,33,.85) !important; }

/* BESTAETIGUNG NACH DEM SENDEN (30.09.2026, D1H)
   Simon, an einem Telefon: „Nach dem erfolgreichen Versenden aus dem
   Kontaktformular muss das visuell bestaetigt werden." Bis hierher stand
   dort ein Satz in Fliesstextgroesse unter dem Knopf – gemessen ununter-
   scheidbar von der Beschriftung daneben, und nach der Weiterleitung
   ausserhalb des Bildes. Die Felder treten ab, die Bestaetigung nimmt
   ihren Platz ein und traegt einen Strich ueber sich. Kein Kasten, keine
   Farbe ausser der Tinte des Hauses, kein Zeichen – die Aenderung ist die
   Groesse und der leere Raum, den sie bekommt. */

.contact-form.ist-gesendet .contact-field,
.contact-form.ist-gesendet button {
  display: none;
}

/* Und die Beschriftung des Blocks tritt mit ab. „schreiben" ueber einer
   Bestaetigung fordert zu etwas auf, das gerade getan ist – gesehen im
   ersten Wurf von D1H, zusammen mit einem Nachsatz, der den Satz aus dem
   Block „antwort" zwei Zeilen darueber wortwoertlich wiederholte. Beides
   weg; die Bestaetigung steht fuer sich. */
.contact-form-block:has(.contact-form.ist-gesendet) > span {
  opacity: 0;
  transition: opacity .35s ease;
}

.contact-form.ist-gesendet p.contact-status.ist-ok {
  margin: .2rem 0 .45rem;
  padding-top: 1.15rem;
  border-top: 1px solid rgba(var(--tinte), .28);
  font-size: clamp(19px, 2.4vw, 27px);
  line-height: 1.25;
  animation: contact-bestaetigt .5s ease both;
}

@keyframes contact-bestaetigt {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .contact-form.ist-gesendet p.contact-status.ist-ok { animation: none; }
}
