/* ==========================================================================
   Sichtweite — design layer on top of the Counselor (Colorlib) template.
   Loaded AFTER css/style.css, so these rules win.
   Calm sage palette, generous whitespace, modern components.
   ========================================================================== */

:root {
  --sage:        #6f9c8f;
  --sage-dark:   #4f7a6e;
  --sage-deep:   #365a51;
  --sage-tint:   #eef3f1;
  --sage-tint-2: #dfeae5;
  --ink:         #26302d;
  --body:        #5d6b66;
  --line:        #e6e5df;
  --cream:       #faf8f5;

  /* Warme Beige-Familie (Rueckmeldung Carolin 21.09.2026: die Seite war ihr
     zu dunkel). --sand ist der Seitengrund, --sand-2 die tieferen Baender
     (Footer), --sand-3 Haarlinien und Fussleiste. --muted ersetzt ueberall
     dort, wo frueher rgba(255,255,255,.6) stand, den gedaempften Text.

     Die Werte sind gemessen, nicht gegriffen: --ink kommt auf --sand auf
     11.60, --muted auf 6.11, --body auf 4.76. Auf --sand-2 faellt --body auf
     4.36 und damit unter AA - dort steht deshalb --muted (5.60).
     Gegenprobe: _check_contrast.py. */
  --sand:        #f3ece1;
  --sand-2:      #ece2d4;
  --sand-3:      #dfd3bf;
  --muted:       #4f5a56;

  /* Anzeigehoehe der Kontakt-Bilder (Adresse/Mail/Telefon aus about/utils.py).
     MUSS NOMINAL_HEIGHT dort entsprechen - die PNGs werden auf genau diese
     Hoehe gerechnet, und die Icons richten ihre line-height danach aus.
     _check_images.py vergleicht beide Seiten. Eine Zahl, eine Stelle.

     Von 18px auf 16px: bei 18px setzte der Generator die Schrift auf 45px
     und damit auf umgerechnet 15,0px - genauso gross wie der Text daneben
     (0.93rem = 14,9px). Zusammen mit dem zu satten Schwarz (siehe
     about/utils.py) wirkte die Zeile dadurch schwerer als ihre Umgebung.
     Jetzt rund 13,6px: eine Spur leiser als der Text, was fuer Kontaktdaten
     im Footer richtig ist. */
  --sw-contact-img-h: 16px;
  --shadow-sm:   0 4px 16px rgba(38, 48, 45, .05);
  --shadow:      0 12px 34px rgba(38, 48, 45, .08);
  --shadow-lg:   0 26px 60px rgba(38, 48, 45, .14);
  --r:           18px;
}

/* ----------------------------------------------------------- foundations -- */
body {
  font-family: 'Roboto', -apple-system, 'Segoe UI', sans-serif;
  color: var(--body);
  background: var(--sand);
  font-size: 16px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5 { color: var(--ink); font-weight: 700; letter-spacing: -.02em; line-height: 1.2; }
h1 { font-size: clamp(2.1rem, 4.6vw, 3.5rem); }
h2 { font-size: clamp(1.8rem, 3.4vw, 2.6rem); }
h3 { font-size: 1.3rem; }

p { color: var(--body); }
a { color: var(--sage-dark); }
a:hover { color: var(--sage); text-decoration: none; }

.ftco-section { padding: 7em 0; }
@media (max-width: 767px) { .ftco-section { padding: 4em 0; } }

/* section headings */
.heading-section .subheading {
  display: block;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--sage) !important;
  margin-bottom: .6rem;
}
.heading-section h2 { margin-bottom: .75rem; }
.heading-section.text-center h2::after {
  content: "";
  display: block;
  width: 54px;
  height: 3px;
  border-radius: 3px;
  background: var(--sage);
  margin: 1.1rem auto 0;
}

/* ---------------------------------------------------------------- buttons -- */
.btn {
  /* Roboto ships 300/400/500/700 - never 600. Asking for 600 makes the browser
     fake a bold by smearing the 400 weight, which reads as distorted text. */
  font-weight: 500;
  letter-spacing: .01em;
  border-radius: 999px !important;
  padding: .85rem 2.1rem;
  border-width: 2px;
  transition: all .25s ease;
}
.btn-primary,
.btn-primary:not(:disabled):not(.disabled) {
  background: var(--sage) !important;
  border-color: var(--sage) !important;
  color: #fff !important;
  box-shadow: 0 8px 22px rgba(111, 156, 143, .32);
}
.btn-primary:hover {
  background: var(--sage-dark) !important;
  border-color: var(--sage-dark) !important;
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(111, 156, 143, .42);
}
/* Steht nur im Hero, also auf dem hellen Schleier: weisser Rahmen und weisse
   Schrift waeren dort unsichtbar. */
.btn-ghost {
  background: rgba(243, 236, 225, .45);
  border: 2px solid rgba(38, 48, 45, .45);
  color: var(--ink) !important;
}
.btn-ghost:hover {
  background: var(--ink);
  color: var(--sand) !important;
  border-color: var(--ink);
}
.btn-sm-pill { padding: .5rem 1.15rem; font-size: .85rem; }

/* -------------------------------------------------------------- navigation -- */
/* Header sits transparently on the hero, then turns into a light glass bar.
   Only background/shadow/colour animate - never margin or height, so there is
   no layout jump and no flicker when scrolling up and down. */
.ftco_navbar, #ftco-navbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1030;
  padding: .85rem 0 !important;
  margin: 0 !important;
  background: transparent !important;
  border-bottom: 1px solid transparent;
  box-shadow: none;
  transition: background-color .35s ease, box-shadow .35s ease,
              border-color .35s ease, backdrop-filter .35s ease;
  will-change: background-color;
}

/* accent hairline, fades in with the glass state */
.ftco_navbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: linear-gradient(90deg, var(--sage), var(--sage-dark), transparent 85%);
  opacity: 0;
  transition: opacity .35s ease;
}
#ftco-navbar.scrolled::after { opacity: .9; }

#ftco-navbar.scrolled {
  background: rgba(255, 255, 255, .82) !important;
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid rgba(230, 229, 223, .8);
  box-shadow: 0 8px 30px rgba(38, 48, 45, .09);
}

/* kill the template's slide-out animation entirely (source of the flicker) */
.ftco-navbar-light.scrolled.awake,
.ftco-navbar-light.scrolled.sleep,
.ftco_navbar.awake, .ftco_navbar.sleep {
  margin-top: 0 !important;
  transform: none !important;
}

/* ---- brand: logo + wordmark ---- */
.sw-brand { display: flex !important; align-items: center; gap: .7rem; padding: 0 !important; }
.sw-brand__mark {
  max-height: 42px; width: auto; display: block;
  transition: max-height .35s ease, filter .35s ease;
}
.sw-brand__text {
  display: flex; flex-direction: column; line-height: 1.05;
  font-weight: 700; font-size: 1.35rem; letter-spacing: -.01em;
  color: var(--ink); transition: color .35s ease;
}
.sw-brand__text small {
  font-size: .62rem; font-weight: 500; letter-spacing: .16em;
  text-transform: uppercase; color: var(--sage-dark); opacity: 1; margin-top: .18rem;
}
/* Der Hero ist jetzt hell: die Marke bleibt in ihrer echten Farbe, der
   Stanzfilter entfaellt. Damit sieht die Navbar in beiden Zustaenden gleich
   aus - der Unterschied ist nur noch der Glas-Hintergrund. */
#ftco-navbar .sw-brand__mark { filter: none; }
#ftco-navbar.scrolled .sw-brand__mark { filter: none; max-height: 38px; }
#ftco-navbar.scrolled .sw-brand__text { color: var(--ink); }
#ftco-navbar.scrolled .sw-brand__text small { color: var(--sage-dark); opacity: 1; }

.ftco-navbar-light .navbar-brand,
#ftco-navbar .navbar-brand { color: var(--ink) !important; font-weight: 700; }
#ftco-navbar.scrolled .navbar-brand { color: var(--ink) !important; }

/* sub-pages have no hero behind the header -> start in glass state */
body.has-solid-nav #ftco-navbar {
  background: rgba(255, 255, 255, .92) !important;
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid rgba(230, 229, 223, .8);
}
body.has-solid-nav #ftco-navbar .sw-brand__mark { filter: none; }
body.has-solid-nav #ftco-navbar .sw-brand__text { color: var(--ink); }
body.has-solid-nav #ftco-navbar .sw-brand__text small { color: var(--sage-dark); opacity: 1; }
body.has-solid-nav #ftco-navbar .navbar-brand { color: var(--ink) !important; }
/* fixed header would overlap the page hero on sub-pages */
body.has-solid-nav .hero-wrap { padding-top: 80px; }

/* mobile: the open menu needs a readable background */
@media (max-width: 991.98px) {
  #ftco-navbar .navbar-collapse.show,
  #ftco-navbar .navbar-collapse.collapsing {
    background: rgba(255, 255, 255, .97);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border-radius: 14px;
    margin-top: .75rem;
    padding: .5rem .25rem;
    box-shadow: var(--shadow);
  }
  #ftco-navbar .navbar-collapse.show .nav-link,
  #ftco-navbar .navbar-collapse.collapsing .nav-link {
    color: var(--ink) !important;
    text-shadow: none;
  }
  .sw-brand__text { font-size: 1.15rem; }
  .sw-brand__mark { max-height: 34px; }
  .sw-brand__text small { display: none; }
  #ftco-navbar .navbar-toggler { color: var(--ink); border-color: rgba(38,48,45,.35); }
  #ftco-navbar.scrolled .navbar-toggler,
  body.has-solid-nav #ftco-navbar .navbar-toggler {
    color: var(--ink); border-color: var(--line);
  }

  /* Logo und Burger-Button klebten am Bildschirmrand. Zwei Regeln trafen
     sich unglücklich: Bootstrap nimmt `.container` UNTERHALB des
     Expand-Breakpoints das seitliche Padding weg (style.css:3609), weil
     normalerweise die Navbar selbst den Abstand stellt - und unsere
     Navbar-Regel setzt `padding: .85rem 0`, um die senkrechte Rhythmik zu
     halten. Ergebnis: beide Seiten null.

     Die 15px sind bewusst genau der Bootstrap-Gutter, den auch der
     Seiteninhalt benutzt. Damit steht das Logo auf derselben Kante wie der
     Text darunter statt auf einer eigenen. */
  #ftco-navbar > .container {
    padding-right: 15px;
    padding-left: 15px;
  }
}

/* Links sind in beiden Zustaenden dunkel - ueber dem hellen Hero traegt sie
   ein heller Schimmer, im Glaszustand brauchen sie gar nichts. */
.ftco-navbar-light .navbar-nav > .nav-item > .nav-link {
  color: var(--ink) !important;
  text-shadow: 0 1px 10px rgba(243, 236, 225, .9);
  font-weight: 500;
  font-size: .97rem;
  padding: .5rem .95rem !important;
  opacity: 1;
  position: relative;
  transition: color .3s ease, text-shadow .3s ease;
}
#ftco-navbar.scrolled .navbar-nav > .nav-item > .nav-link,
body.has-solid-nav #ftco-navbar .navbar-nav > .nav-item > .nav-link {
  color: var(--ink) !important;
  text-shadow: none;
}
.ftco-navbar-light .navbar-nav > .nav-item > .nav-link::after {
  content: "";
  position: absolute;
  left: .95rem; right: .95rem; bottom: .15rem;
  height: 2px;
  background: var(--sage);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .25s ease;
}
.ftco-navbar-light .navbar-nav > .nav-item > .nav-link:hover::after,
.ftco-navbar-light .navbar-nav > .nav-item.active > .nav-link::after { transform: scaleX(1); }
.ftco-navbar-light .navbar-nav > .nav-item > .nav-link:hover,
.ftco-navbar-light .navbar-nav > .nav-item.active > .nav-link { color: var(--sage-dark) !important; }

/* --- social links in the navbar: present, never competing with the menu ---
   Deliberately NOT .nav-link/.navbar-nav, so they inherit neither the link
   padding nor the sage underline animation. */
.sw-nav-social {
  display: flex;
  align-items: center;
  gap: .15rem;
  list-style: none;
  margin: 0 0 0 .9rem;
  padding: 0 0 0 .9rem;
  border-left: 1px solid rgba(255, 255, 255, .22);
}
.sw-nav-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  color: var(--muted);
  font-size: 1rem;
  text-shadow: 0 1px 10px rgba(0, 0, 0, .28);
  transition: color .25s ease, background-color .25s ease;
}
.sw-nav-social a:hover { color: var(--ink); background: rgba(38, 48, 45, .08); }
/* the label is for screen readers and the collapsed mobile menu */
.sw-nav-social__label { display: none; }

/* glass / solid navbar: same restraint, dark palette */
#ftco-navbar.scrolled .sw-nav-social,
body.has-solid-nav #ftco-navbar .sw-nav-social { border-left-color: var(--line); }
#ftco-navbar.scrolled .sw-nav-social a,
body.has-solid-nav #ftco-navbar .sw-nav-social a {
  color: var(--body);
  text-shadow: none;
}
#ftco-navbar.scrolled .sw-nav-social a:hover,
body.has-solid-nav #ftco-navbar .sw-nav-social a:hover {
  color: var(--sage-dark);
  background: var(--sage-tint);
}

@media (max-width: 991.98px) {
  /* inside the collapsed panel a bare icon is an unlabelled tap target,
     so it becomes a normal menu row with its name spelled out */
  .sw-nav-social {
    display: block;
    margin: .35rem .25rem .25rem;
    padding: .5rem 0 0;
    border-left: none;
    border-top: 1px solid var(--line);
  }
  .sw-nav-social a {
    width: auto; height: auto;
    justify-content: flex-start;
    gap: .7rem;
    padding: .5rem .95rem;
    border-radius: 10px;
    color: var(--body) !important;
    text-shadow: none;
    font-size: .95rem;
  }
  .sw-nav-social a:hover { background: var(--sage-tint); color: var(--sage-dark) !important; }
  .sw-nav-social__label { display: inline; font-weight: 500; }
}

#ftco-navbar .navbar-toggler {
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  font-size: .8rem;
  padding: .5rem .8rem;
}
.dropdown-menu {
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: .4rem;
  margin-top: .5rem;
}
.dropdown-menu .dropdown-item { border-radius: 8px; padding: .55rem .9rem; color: var(--ink); }
.dropdown-menu .dropdown-item:hover { background: var(--sage-tint); color: var(--sage-dark); }

/* -------------------------------------------------------------------- hero -- */
.sw-hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  /* --sw-hero / --sw-hero-sm are set inline by the template (DB-driven path) */
  background-image: var(--sw-hero);
  background-size: cover !important;
  background-position: center !important;
  padding: 8rem 0 6rem;
}
/* phones get the 1280px variant - a quarter of the desktop file */
@media (max-width: 767.98px) {
  .sw-hero { background-image: var(--sw-hero-sm, var(--sw-hero)); }
}
/* Heller Schleier statt des frueheren dunklen Filters.

   --sw-hero-veil ist die EINE Stellschraube: wie stark das Beige ueber dem
   Foto liegt. Klein = Bild kraeftig, Text knapp; gross = Bild blass, Text
   sicher. Dieselbe Idee wie --sw-ball-run bei der Wortkugel: aendern, neu
   laden, fertig - ohne die Verlaeufe anzufassen.

   Der Wert ist nicht geschaetzt. Das Waldfoto enthaelt echte schwarze Pixel
   (rgb(0,0,0)), und gegen den schlechtesten Pixel unter dem Textblock
   gerechnet kommt --ink bei .70 auf 4.69, bei .68 nur noch auf 4.41 - also
   unter AA. Gedaempfter Text funktioniert hier bei KEINER vertretbaren
   Staerke, deshalb laufen Claim und Unterzeile beide auf --ink.

   .76 war Carolin zu blass ("das Titelbild ist ein bisschen zu hell"), also
   runter auf .70: das Foto tritt sichtbar hervor, und es bleibt ein kleiner
   Abstand zur Grenze. Weiter runter geht nicht, ohne den Text unter AA zu
   drucken - ab .69 wird es knapp, ab .68 schlaegt _check_contrast.py fehl. */
.sw-hero__veil {
  --sw-hero-veil: .70;
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg,
      rgba(243,236,225, calc(var(--sw-hero-veil) - .06)) 0%,
      rgba(243,236,225, var(--sw-hero-veil)) 55%,
      rgba(236,226,212, calc(var(--sw-hero-veil) + .06)) 100%),
    linear-gradient(120deg,
      rgba(238,243,241, .34) 0%,
      rgba(243,236,225, .22) 45%,
      rgba(236,226,212, .30) 100%);
}
.sw-hero .container { position: relative; z-index: 2; }
.sw-hero .slider-text { height: auto !important; }

/* single focal point: the logo. No competing watermark. */
.sw-hero__mark {
  max-height: 240px;
  max-width: min(280px, 68vw);
  width: auto;
  margin-bottom: 2rem;
  /* Kein brightness(0) invert(1) mehr: auf hellem Grund darf die Marke in
     ihrer echten Farbe stehen (Navy #234663). Der Schatten ist jetzt ein
     heller Hauch, der die Kanten vom Foto abhebt - ein dunkler Schlagschatten
     wuerde auf Beige schmutzig wirken. */
  filter: drop-shadow(0 4px 18px rgba(243, 236, 225, .9));
}

/* Hero-Text ist jetzt dunkel. Bewusst --ink und nicht --body: ueber einem
   Foto mit schwarzen Stellen kommt ein gedaempfter Ton nicht auf AA. */
.sw-hero .text,
.sw-hero .text p,
.sw-hero .text h1,
.sw-hero .text a:not(.btn) { color: var(--ink); }

/* quiet hierarchy: logo -> claim -> subline -> action */
.sw-hero__title {
  color: var(--ink) !important;
  font-size: clamp(1.2rem, 2.1vw, 1.7rem) !important;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: .01em;
  max-width: 30ch;
  margin: 0 auto 1.1rem;
  text-shadow: 0 1px 18px rgba(243, 236, 225, .95);
}
.sw-hero__role {
  /* frueher rgba(255,255,255,.8); ein gedaempftes Gegenstueck scheitert hier
     am Kontrast (3.45 statt 4.5), deshalb voll --ink mit etwas weniger
     Gewicht ueber die Schriftgroesse. */
  color: var(--ink) !important;
  font-size: .88rem;
  font-weight: 400;
  max-width: 46ch;
  margin: 0 auto 2.2rem;
  text-shadow: 0 1px 14px rgba(243, 236, 225, .95);
}
.sw-hero__cta .btn { font-size: .9rem; padding: .7rem 1.7rem; }
.sw-hero__cta { display: flex; gap: .8rem; justify-content: center; flex-wrap: wrap; }
.sw-hero__cta .btn { margin: 0; }

.sw-hero__scroll {
  position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%);
  width: 26px; height: 42px; border: 2px solid rgba(38,48,45,.45); border-radius: 14px;
  z-index: 2;
}
.sw-hero__scroll span {
  position: absolute; top: 8px; left: 50%; margin-left: -2px;
  width: 4px; height: 8px; border-radius: 2px; background: var(--ink);
  animation: swScroll 1.8s infinite;
}
@keyframes swScroll { 0%{opacity:0;transform:translateY(0)} 40%{opacity:1} 80%{opacity:0;transform:translateY(14px)} 100%{opacity:0} }

/* --------------------------------------------------------------- wordball -- */
/* Die acht "-sicht"-Woerter fliegen beim Scrollen von aussen ein und setzen
   sich zur Kugel zusammen - eine Explosionszeichnung rueckwaerts. Gross
   zuerst: der Kern steht frueh, die kleinen Woerter fuellen ihn danach auf.

   KEIN sticky, kein Pinning: die Sektion ist eine ganz normale Sektion und
   scrollt mit. Die Kugel baut sich auf, WAEHREND sie durchs Fenster wandert.
   Eine festgepinnte Buehne hatte den Scroll-Fluss angehalten - man scrollte
   und die Seite stand. Der Aufbau war dadurch vom Lesefluss abgekoppelt.

   Zwei Stellschrauben, beide hier oben:

     --sw-ball-run     Anteil der Reise durch das Fenster, den der Aufbau
                       braucht. Klein = die Kugel steht frueh, gross = sie
                       baut sich laenger auf. Ueber ~.75 wird es knapp, dann
                       scrollt die Kugel oben schon wieder heraus.
     --sw-ball-spread  Wie weit draussen die Woerter starten (Faktor auf die
                       im SVG hinterlegten Startversaetze).

   Die Zeitfenster der einzelnen Woerter (--a/--b im erzeugten SVG) sind
   ANTEILE von --sw-ball-run, keine festen Prozente. Deshalb bleibt die
   Choreografie stimmig, egal wie der Lauf eingestellt wird - sonst muesste
   bei jeder Aenderung das halbe SVG neu erzeugt werden. */
.sw-ball {
  --sw-ball-run: .58;
  --sw-ball-spread: 1;
  position: relative;
  padding: 7rem 0 6rem;
  background: var(--sand);
  /* clip beschneidet nur waagerecht - senkrecht bleibt sichtbar, damit die
     Woerter von ausserhalb der Sektion anfliegen duerfen. Mit `hidden` ginge
     das nicht: das erzwingt Beschneidung auf beiden Achsen. */
  overflow-x: clip;
  view-timeline-name: --swball;
  view-timeline-axis: block;
}
.sw-ball__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2.75rem;
}
.sw-ball__svg {
  width: min(600px, 84vw);
  height: auto;
  /* Die Woerter starten weit ausserhalb der viewBox - ohne das hier wuerden
     sie am Rand der Kugel abgeschnitten statt hereinzufliegen. */
  overflow: visible;
  font-family: 'SW Kugel', 'Roboto', sans-serif;
  /* PIL kernt beim Ausmessen nicht, der Browser sonst schon: ohne das hier
     sitzt jedes Wort ein paar Pixel neben seinem Platz im Packlauf. */
  font-kerning: none;
}
/* Die Zeile erscheint ERST, wenn die Kugel steht. Waehrend des Aufbaus ist
   sie unsichtbar - und gibt damit den Raum unter der Kugel frei, aus dem die
   Woerter anfliegen. Ein dauerhaft sichtbarer Absatz dort unten wirkt wie
   eine Wand, gegen die die Choreografie laeuft. */
.sw-ball__caption {
  margin: 0;
  max-width: 32rem;
  text-align: center;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--sage-dark);
  opacity: 0;
}
@keyframes swBallCaption {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.sw-ball__w {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}

@keyframes swBallIn {
  from {
    opacity: 0;
    transform: translate(calc(var(--dx) * var(--sw-ball-spread) * 1px),
                         calc(var(--dy) * var(--sw-ball-spread) * 1px))
               rotate(calc(var(--dr) * 1deg))
               scale(var(--ds));
  }
  to { opacity: 1; transform: none; }
}

/* Weg 1 - Chrome, Edge, Safari: der Scroll-Fortschritt TREIBT die Animation,
   ganz ohne JavaScript. `cover` ist die gesamte Reise der Sektion durchs
   Fenster (0% = tritt unten ein, 100% = oben heraus); --sw-ball-run schneidet
   daraus das Stueck heraus, in dem sich die Kugel zusammensetzt. */
@supports (animation-timeline: view()) {
  .sw-ball__w {
    animation: swBallIn cubic-bezier(.22, .68, .3, 1) both;
    animation-timeline: --swball;
    animation-range: cover calc(var(--a) * var(--sw-ball-run) * 100%)
                     cover calc(var(--b) * var(--sw-ball-run) * 100%);
  }
  .sw-ball__caption {
    animation: swBallCaption ease-out both;
    animation-timeline: --swball;
    animation-range: cover calc(var(--sw-ball-run) * 100%)
                     cover calc((var(--sw-ball-run) + .1) * 100%);
  }
}

/* Weg 2 - Firefox: sw-wordball.js schreibt denselben Fortschritt als
   --sw-ball-p (1 = Kugel steht), gerechnet wird weiter hier. */
.sw-ball-js .sw-ball__w {
  --t: clamp(0, calc((var(--sw-ball-p, 0) - var(--a)) / (var(--b) - var(--a))), 1);
  opacity: var(--t);
  transform:
    translate(calc((1 - var(--t)) * var(--dx) * var(--sw-ball-spread) * 1px),
              calc((1 - var(--t)) * var(--dy) * var(--sw-ball-spread) * 1px))
    rotate(calc((1 - var(--t)) * var(--dr) * 1deg))
    scale(calc(var(--ds) + (1 - var(--ds)) * var(--t)));
}
.sw-ball-js .sw-ball__caption {
  --t: clamp(0, calc((var(--sw-ball-p, 0) - 1) / .17), 1);
  opacity: var(--t);
  transform: translateY(calc((1 - var(--t)) * 10px));
}

@media (max-width: 767.98px) {
  .sw-ball {
    /* Auf dem Handy ist die Reise durchs Fenster kuerzer - der Aufbau darf
       einen groesseren Anteil davon belegen, sonst huscht er vorbei. */
    --sw-ball-run: .66;
    --sw-ball-spread: .75;
    padding: 4.5rem 0 3.5rem;
  }
  .sw-ball__stage { gap: 2rem; }
  .sw-ball__svg { width: 88vw; }
  .sw-ball__caption { font-size: .95rem; padding: 0 1.25rem; }
}

/* Ohne Bewegung steht die fertige Kugel einfach da, Zeile inklusive. */
@media (prefers-reduced-motion: reduce) {
  .sw-ball__w,
  .sw-ball__caption {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* Die Kugel misst ihre Wortpositionen gegen genau diese Schriftdateien.
   font-display: block - lieber kurz nichts zeigen als die Kugel mit den
   falschen Metriken aufbauen und dann umspringen lassen. */
@font-face {
  font-family: 'SW Kugel';
  src: url('../fonts/roboto.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'SW Kugel';
  src: url('../fonts/robotob.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

/* ------------------------------------------------------------------ values -- */
.sw-values { padding: 4.5rem 0 1rem; background: var(--sand); }
.sw-value {
  text-align: center;
  padding: 2.25rem 1.5rem;
  border-radius: var(--r);
  height: 100%;
  transition: transform .28s ease, box-shadow .28s ease;
}
.sw-value:hover { transform: translateY(-5px); box-shadow: var(--shadow); }
.sw-value__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 68px; height: 68px; border-radius: 50%;
  background: var(--sage-tint); color: var(--sage-dark);
  font-size: 1.7rem; margin-bottom: 1.15rem;
}
.sw-value h3 { font-size: 1.12rem; margin-bottom: .55rem; }
.sw-value p { font-size: .95rem; margin: 0; }

/* ---------------------------------------------------------------- beratung -- */
/* the flying columns start outside the grid; clip (not hidden) keeps sticky alive */
#beratung { background: var(--cream); overflow-x: clip; }

/* Base = plain stacked flow. Works everywhere, no JS, no sticky.
   The scroll choreography is layered on top further down. */
.sw-scene { position: relative; }
.sw-scene + .sw-scene { margin-top: 3.5rem; }

.sw-scene__media {
  width: 100%;
  height: clamp(190px, 40vh, 420px);
  background-size: cover;
  background-position: center 35%;
  border-radius: var(--r);
  box-shadow: var(--shadow);
  margin-bottom: 2.1rem;
}
.sw-scene__text { max-width: 62ch; margin: 0 auto; }
.sw-scene__bar {
  display: block;
  width: 46px; height: 3px;
  border-radius: 2px;
  background: var(--sage);
  margin: 0 auto 1.3rem;
}
.sw-scene__text p {
  font-size: 1.04rem;
  line-height: 1.82;
  margin: 0;
}

/* sub-headline of a scene ("Paartherapie", "Meine Arbeitsweise") */
.sw-scene__title {
  font-size: 1.55rem;
  line-height: 1.25;
  margin: 0 0 .9rem;
  color: var(--ink);
}

/* a scene that carries a quotation instead of prose */
.sw-scene__quote { margin: 0; }
.sw-scene__quote p {
  font-size: 1.24rem;
  line-height: 1.7;
  font-style: italic;
  color: var(--sage-deep);
}
.sw-scene__quote cite {
  display: block;
  margin-top: 1.1rem;
  font-style: normal;
  font-size: .86rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--sage-dark);
}
@media (max-width: 767.98px) {
  .sw-scene__title { font-size: 1.32rem; }
  .sw-scene__quote p { font-size: 1.1rem; }
}

/* ---- side by side once there is room for two columns ---- */
@media (min-width: 768px) {
  .sw-scene__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    column-gap: 4.5rem;
  }
  .sw-scene__media { grid-column: 1; grid-row: 1; margin-bottom: 0; height: auto; aspect-ratio: 4 / 3; }
  .sw-scene__text  { grid-column: 2; grid-row: 1; max-width: 46ch; margin: 0; }

  .sw-scene--reverse .sw-scene__media { grid-column: 2; }
  .sw-scene--reverse .sw-scene__text  { grid-column: 1; }

  .sw-scene__bar { margin-left: 0; margin-right: 0; }

  /* a block without an image keeps the centred single-column look */
  .sw-scene--solo .sw-scene__grid { grid-template-columns: 1fr; }
  .sw-scene--solo .sw-scene__text {
    grid-column: 1; max-width: 62ch; margin: 0 auto; text-align: center;
  }
  .sw-scene--solo .sw-scene__bar { margin-left: auto; margin-right: auto; }
}

@media (min-width: 992px) {
  .sw-scene__grid { column-gap: 5.5rem; }
}

/* ---- sticky stacking: each scene pins, the next one slides over it ---- */
@media (min-width: 768px) and (min-height: 620px) {
  /* the last pinned scene already provides the closing whitespace */
  #beratung { padding-bottom: 0; }
  /* --sw-runway = extra scroll distance per scene on top of its own 100vh.
     This is the "nothing moves" part - raise it for a slower, more
     deliberate stack, lower it for a snappier one. */
  .sw-stack { position: relative; --sw-runway: 0vh; }

  .sw-scene {
    /* 100vh of this is the scene itself - only the surplus is pinned time,
       i.e. scrolling where nothing moves. Tune the dead scroll here. */
    height: calc(100vh + var(--sw-runway, 10vh));
    margin-top: 0;
    view-timeline-name: --sw-scene;
    view-timeline-axis: block;
  }
  .sw-scene + .sw-scene { margin-top: 0; }
  /* nothing follows the last scene, so it needs no extra scroll runway */
  .sw-scene:last-child { height: 100vh; }

  /* NOTE: never transform this element. A sticky box that is also animated
     forces the compositor to reconcile the sticky offset with the transform
     on every frame, which shows up as sub-pixel jitter. It only pins. */
  .sw-scene__inner {
    position: sticky;
    top: 0;
    min-height: 100vh;
    display: flex;
    align-items: center;
    background: var(--cream);
    /* clears the fixed navbar so centred content never hides behind it */
    padding: 92px 0 2rem;
  }
  /* the pinned viewport is the frame now — let the image use its height */
  .sw-scene__media { aspect-ratio: auto; height: min(62vh, 520px); }

  /* one stable compositor layer per animated element, rasterised once */
  .sw-scene__grid,
  .sw-scene__media,
  .sw-scene__text {
    will-change: transform, opacity;
    backface-visibility: hidden;
    transform-style: flat;
  }
}

/* ---- native scroll-driven animation (Chrome/Edge 115+, Safari 26+) ---- */
@supports (animation-timeline: view()) {
  @media (min-width: 768px) and (min-height: 620px) and (prefers-reduced-motion: no-preference) {
    /* the content lifts, not the sticky box itself (see note above) */
    .sw-scene__grid {
      animation-name: sw-scene-lift;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --sw-scene;
      animation-range: exit 0% exit 70%;
    }
    /* the last scene must never fade out — nothing follows it */
    .sw-scene:last-child .sw-scene__grid { animation-name: none; }

    .sw-scene__media,
    .sw-scene__text {
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --sw-scene;
    }
    /* the columns fly in over the whole entry phase, so the short runway is
       spent on motion instead of standing still.
       The image may scale/rotate (it is one raster layer); the text only
       translates — scaling live text re-rasterises glyphs every frame and
       that is what reads as shimmer. */
    .sw-scene__media { animation-name: sw-fly-left;   animation-range: entry 5% entry 88%; }
    .sw-scene__text  { animation-name: sw-slide-right; animation-range: entry 15% entry 100%; }
    .sw-scene--reverse .sw-scene__media { animation-name: sw-fly-right; }
    .sw-scene--reverse .sw-scene__text  { animation-name: sw-slide-left; }
    .sw-scene--solo .sw-scene__text     { animation-name: sw-slide-up; }
  }
}

/* Motion is the loud part, opacity the quiet one: everything is fully
   opaque at ~45% and keeps travelling after that. */

/* image: one raster layer, so scale + rotate are free */
@keyframes sw-fly-left {
  from { opacity: 0; transform: translate3d(-130px, 64px, 0) scale(.9) rotate(-2deg); }
  45%  { opacity: 1; }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1) rotate(0deg); }
}
@keyframes sw-fly-right {
  from { opacity: 0; transform: translate3d(130px, 64px, 0) scale(.9) rotate(2deg); }
  45%  { opacity: 1; }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1) rotate(0deg); }
}

/* text: translation only - no scale, no rotate, no glyph re-rasterising */
@keyframes sw-slide-left {
  from { opacity: 0; transform: translate3d(-130px, 64px, 0); }
  45%  { opacity: 1; }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes sw-slide-right {
  from { opacity: 0; transform: translate3d(130px, 64px, 0); }
  45%  { opacity: 1; }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes sw-slide-up {
  from { opacity: 0; transform: translate3d(0, 90px, 0); }
  45%  { opacity: 1; }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* whole scene leaves: translation only, it carries the text along */
@keyframes sw-scene-lift {
  from { opacity: 1; transform: translate3d(0, 0, 0); }
  55%  { opacity: .5; }
  to   { opacity: 0; transform: translate3d(0, -150px, 0); }
}

/* ---- JS fallback (Firefox): same look, IntersectionObserver-driven ---- */
.sw-js .sw-scene__media,
.sw-js .sw-scene__text {
  opacity: 0;
  transform: translate3d(0, 90px, 0);
  transition: opacity .55s cubic-bezier(.22,.61,.36,1),
              transform 1.05s cubic-bezier(.16,.84,.3,1);
}
.sw-js .sw-scene__media { transform: translate3d(0, 90px, 0) scale(.94); }
.sw-js .sw-scene__text { transition-delay: .1s; }
@media (min-width: 768px) {
  .sw-js .sw-scene__media { transform: translate3d(-130px, 64px, 0) scale(.9) rotate(-2deg); }
  .sw-js .sw-scene__text  { transform: translate3d(130px, 64px, 0); }
  .sw-js .sw-scene--reverse .sw-scene__media { transform: translate3d(130px, 64px, 0) scale(.9) rotate(2deg); }
  .sw-js .sw-scene--reverse .sw-scene__text  { transform: translate3d(-130px, 64px, 0); }
  .sw-js .sw-scene--solo .sw-scene__text     { transform: translate3d(0, 90px, 0); }
}
.sw-js .sw-scene.is-in .sw-scene__media,
.sw-js .sw-scene.is-in .sw-scene__text { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .sw-js .sw-scene__media,
  .sw-js .sw-scene__text { opacity: 1; transform: none; transition: none; }
}

@media (max-width: 767.98px) {
  .sw-scene__media { height: clamp(180px, 34vh, 260px); margin-bottom: 1.6rem; }
  .sw-scene__text p { font-size: .99rem; line-height: 1.78; }
  .sw-scene + .sw-scene { margin-top: 3rem; }
}

/* ---------------------------------------------------------------- angebote -- */
#angebote { background: var(--sand); }

/* Own grid instead of Bootstrap columns: `row`/`col-*` provide horizontal
   gutters but no vertical ones, so stacked cards touched on phones. `gap`
   handles both axes and scales with the breakpoint. */
.offer-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2rem;
  align-items: stretch;
}
.offer-grid__cell { display: flex; }

.offer-card {
  width: 100%;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-sm);
  transition: transform .3s ease, box-shadow .3s ease;
}
.offer-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); }
.offer-card__img {
  height: 190px;
  background-size: cover;
  background-position: center;
  position: relative;
}

/* --- bundle tile ---

   Frueher trug diese Karte einen multiply-Blend plus einen dunkelgruenen
   Verlauf. Der Grund stand im alten Kommentar: das 4er-Paket benutzte
   DASSELBE Foto wie die Einzelsitzung, und die Einfaerbung war das Mittel,
   damit die beiden Karten nicht identisch aussehen.

   Seit dem 05.09. hat das Paket sein eigenes Motiv (paar.jpg). Der Grund ist
   damit entfallen, die Verdunkelung blieb - und genau die hat Carolin am
   20.09. gemeldet ("wieder zu dunkel, wegen dem spare-buttom wahrscheinlich").
   Ihre Vermutung traf zu: Verdunkelung und Ribbon hingen beide an
   .offer-card--featured.

   Die Karte hebt sich jetzt ueber Rahmen, Schatten und Ribbon ab. Das reicht;
   ein Foto muss dafuer nicht leiden. --- */
.offer-card--featured {
  border-color: var(--sage);
  box-shadow: 0 14px 38px rgba(79, 122, 110, .16);
}
/* Das Ribbon war weiss. Auf dem hellen Paar-Foto kam es damit auf 1.17
   Kantenkontrast - praktisch unsichtbar, sobald die Verdunkelung weg ist.
   Als Salbei-Plakette sind es 6.57, der weisse Text darauf 7.67. */
.offer-card__ribbon {
  position: absolute;
  z-index: 1;
  top: .9rem; left: .9rem;
  background: var(--sage-deep);
  color: #fff;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: .3rem .8rem;
  border-radius: 999px;
  box-shadow: 0 4px 14px rgba(38, 48, 45, .28);
}
.offer-card--featured .offer-price strong { color: var(--sage-deep); }
.offer-card__body { padding: 1.6rem 1.5rem 1.4rem; display: flex; flex-direction: column; flex: 1; }
.offer-card__body h3 { font-size: 1.25rem; margin-bottom: .6rem; }
.offer-quote {
  font-style: italic;
  color: var(--sage-dark);
  font-size: .92rem;
  line-height: 1.6;
  margin-bottom: .8rem;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.offer-teaser {
  font-size: .93rem;
  margin-bottom: 1.2rem;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.offer-card__foot {
  margin-top: auto;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
}
/* full detail list inside the offer card */
.offer-details {
  list-style: none;
  padding: 0;
  margin: 0 0 1.1rem;
}
.offer-details li {
  position: relative;
  padding-left: 1.35rem;
  margin-bottom: .4rem;
  font-size: .88rem;
  line-height: 1.5;
  color: var(--body);
}
.offer-details li::before {
  content: "\f00c";
  font-family: FontAwesome;
  position: absolute;
  left: 0; top: .12rem;
  color: var(--sage);
  font-size: .7rem;
}
.offer-agb {
  max-width: 60rem;
  margin: 2.5rem auto 0;
  font-size: .78rem;
  line-height: 1.6;
  color: #7b857f;
  text-align: justify;
  hyphens: auto;
}

/* --- free first talk: an invitation, not a fourth product card --- */
.sw-invite {
  margin-top: 3.5rem;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2.5rem;
  padding: 2.5rem 2.75rem;
  border-radius: var(--r);
  background:
    linear-gradient(135deg, var(--sage-tint) 0%, var(--sage-tint-2) 100%);
  border: 1px solid var(--sage-tint-2);
  position: relative;
  overflow: hidden;
}
/* soft halo so the banner reads as a surface, not a box */
.sw-invite::after {
  content: "";
  position: absolute;
  right: -90px; top: -110px;
  width: 320px; height: 320px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .45);
  pointer-events: none;
}
.sw-invite__main { position: relative; z-index: 1; }

.sw-invite__badge {
  display: inline-block;
  background: var(--sage);
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: .3rem .85rem;
  border-radius: 999px;
  margin-bottom: .9rem;
}
.sw-invite h3 { font-size: 1.5rem; margin-bottom: .5rem; }
.sw-invite__lead {
  font-size: 1rem;
  color: var(--sage-deep);
  margin-bottom: 1.1rem;
  max-width: 52ch;
}
.sw-invite__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .4rem 1.75rem;
}
.sw-invite__list li {
  position: relative;
  padding-left: 1.35rem;
  font-size: .88rem;
  line-height: 1.5;
  color: var(--body);
}
.sw-invite__list li::before {
  content: "\f00c";
  font-family: FontAwesome;
  position: absolute;
  left: 0; top: .12rem;
  color: var(--sage);
  font-size: .7rem;
}

.sw-invite__aside {
  position: relative;
  z-index: 1;
  text-align: center;
  flex-shrink: 0;
}
.sw-invite__aside .btn { white-space: nowrap; }
.sw-invite__note {
  font-size: .78rem;
  color: #7b857f;
  margin: .7rem 0 0;
}

@media (max-width: 991.98px) {
  /* two up on tablets - three would squeeze the price row */
  .offer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2rem; }
  .sw-invite { grid-template-columns: 1fr; gap: 1.75rem; padding: 2rem; }
  .sw-invite__aside { text-align: left; }
  .sw-invite::after { display: none; }
}
@media (max-width: 767.98px) {
  /* single column: the gap is now the only thing separating two cards,
     so it has to read as a deliberate break, not as a hairline */
  .offer-grid { grid-template-columns: 1fr; gap: 2.75rem; }
  .sw-invite { margin-top: 2.75rem; }
}
@media (max-width: 575.98px) {
  .offer-grid { gap: 2.25rem; }
  .sw-invite__list { grid-template-columns: 1fr; }
  .offer-agb { text-align: left; }
}

/* Standalone legal pages (Impressum, Datenschutz). Reference documents, so
   they get a plain readable column rather than the marketing styling. */
.sw-legal { background: var(--sand); }
.sw-legal__body {
  max-width: 52rem;
  margin: 0 auto;
  padding: 2.75rem 3rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  font-size: .95rem;
  line-height: 1.75;
}
.sw-legal__body h3 {
  font-size: 1.02rem;
  margin: 2.25rem 0 .75rem;
  color: var(--sage-deep);
}
.sw-legal__body > h3:first-child { margin-top: 0; }
.sw-legal__body p,
.sw-legal__body li { color: var(--body); }
.sw-legal__body p { margin-bottom: 1rem; }
.sw-legal__body ul { padding-left: 1.15rem; margin-bottom: 1.25rem; }
.sw-legal__body li { margin-bottom: .5rem; }
.sw-legal__body a { word-break: break-word; }
.sw-legal__back {
  margin: 2.5rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-weight: 500;
}

@media (max-width: 767.98px) {
  .sw-legal__body { padding: 1.75rem 1.35rem; font-size: .92rem; }
}

/* footer legal navigation */
.sw-legal-links {
  margin-bottom: .5rem;
  font-size: .88rem;
  line-height: 1.9;
}
.sw-legal-links a { color: var(--sage-dark); }
.sw-legal-links a:hover { color: var(--ink); }
.sw-legal-links span { color: rgba(38,48,45,.35); margin: 0 .3rem; }

.offer-price { font-size: .95rem; color: var(--ink); line-height: 1.3; }
.offer-price strong { color: var(--sage-dark); font-size: 1.15rem; display: block; }
.offer-price--free { color: var(--sage) !important; }
.offer-duration { display: block; font-size: .78rem; color: #8a938f; }

/* ------------------------------------------------------------------- about --
   Editorial layout: the portrait sits inside the text column and the copy
   wraps around it, like a magazine feature. One text block, no side column. */
#about { background: var(--cream); }
.about-editorial {
  max-width: 58rem;
  margin: 0 auto;
}
.about-editorial::after { content: ""; display: table; clear: both; }

.about-figure {
  float: right;
  width: min(300px, 38%);
  margin: .4rem 0 1.5rem 2.75rem;
  /* let the text follow the rounded corners instead of a hard box */
  shape-outside: inset(0 round var(--r));
  shape-margin: 1.1rem;
}
.about-portrait {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--r);
  box-shadow: var(--shadow);
  display: block;
}
.about-signature {
  margin: 1rem 0 0;
  padding-top: .85rem;
  border-top: 2px solid var(--sage);
  line-height: 1.4;
}
.about-signature strong {
  display: block;
  color: var(--ink);
  font-size: 1.02rem;
}
.about-signature span {
  display: block;
  font-size: .82rem;
  color: var(--sage-dark);
}

.about-copy p {
  margin-bottom: 1.05rem;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}
.about-copy p:last-child { margin-bottom: 0; }

/* drop cap — the newspaper cue that opens the story */
.about-copy > p:first-child::first-letter {
  float: left;
  font-size: 3.65rem;
  line-height: .84;
  font-weight: 700;
  color: var(--sage-dark);
  margin: .28rem .55rem 0 0;
}

@media (max-width: 767.98px) {
  .about-figure {
    float: none;
    width: min(260px, 70%);
    margin: 0 auto 2rem;
    shape-outside: none;
  }
  .about-signature { text-align: center; }
  /* justified text needs width; on a phone it tears open ugly rivers */
  .about-copy p { text-align: left; }
  .about-copy > p:first-child::first-letter { font-size: 3.1rem; }
}

/* Qualifications are short static lists, the references are a dated career
   history. Three equal columns forced them into one shape - they get two
   tiers instead: lists side by side, the history as a full-width timeline. */
.credentials {
  max-width: 58rem;
  margin: 3.5rem auto 0;
  border-top: 1px solid var(--line);
  padding-top: 2.75rem;
}
.credentials h4 {
  font-size: .78rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--sage-dark); margin-bottom: 1rem;
}
.cred-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2.5rem 3.5rem;
}
.cred-block--wide { margin-top: 2.75rem; }

.list-check { list-style: none; padding: 0; margin: 0; }
.list-check li {
  position: relative; padding-left: 1.5rem; margin-bottom: .6rem;
  font-size: .93rem; color: var(--body); line-height: 1.55;
}
.list-check li::before {
  content: "\f00c"; font-family: FontAwesome;
  position: absolute; left: 0; top: .15rem; color: var(--sage); font-size: .75rem;
}

/* --- career history as a vertical timeline --- */
.cred-timeline { list-style: none; margin: 0; padding: 0; }
.cred-timeline li {
  position: relative;
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  column-gap: 2.25rem;
  padding-bottom: 1.35rem;
}
.cred-timeline li:last-child { padding-bottom: 0; }

/* connecting line, drawn per item so it stops at the last entry */
.cred-timeline li::before {
  content: "";
  position: absolute;
  left: calc(6.5rem + 1.125rem - 1px);
  top: .95rem;
  bottom: -.25rem;
  width: 2px;
  background: var(--sage-tint-2);
}
.cred-timeline li:last-child::before { content: none; }

/* node */
.cred-timeline li::after {
  content: "";
  position: absolute;
  left: calc(6.5rem + 1.125rem - 5px);
  top: .38rem;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--sage);
}
/* Only the most recent *open* entry is "current". Keying this off
   :first-child broke as soon as a second ongoing engagement was added -
   both rows then claimed to be the present one. */
.cred-timeline li.is-current::after {
  background: var(--sage);
  box-shadow: 0 0 0 4px var(--sage-tint);
}

.cred-timeline__year {
  text-align: right;
  font-weight: 700;
  font-size: .8rem;
  color: var(--ink);
  line-height: 1.6;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.cred-timeline li.is-current .cred-timeline__year { color: var(--sage-dark); }
.cred-timeline__text {
  font-size: .93rem;
  color: var(--body);
  line-height: 1.55;
}

@media (max-width: 767.98px) {
  .cred-grid { grid-template-columns: 1fr; gap: 2rem; }
  .cred-block--wide { margin-top: 2rem; }
  .cred-timeline li {
    grid-template-columns: 1fr;
    row-gap: .15rem;
    padding-left: 1.4rem;
    padding-bottom: 1.2rem;
  }
  .cred-timeline li::before { left: 3px; top: 1.5rem; }
  .cred-timeline li::after  { left: -1px; top: .45rem; }
  .cred-timeline__year { text-align: left; }
}

/* ----------------------------------------------------------------- kontakt -- */
/* war --sage-tint (kuehles Gruen). Auf einer warmen Seite ist ein einzelnes
   kaltes Band ein Fremdkoerper - das Gruen bleibt als Akzent im Panel. */
.sw-contact { padding: 7em 0; background: var(--sand); }
.sw-contact__card {
  background: #fff;
  border-radius: 22px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
/* the card is one grid, so both panels are always exactly equal height */
.sw-contact__grid {
  display: grid;
  grid-template-columns: 5fr 7fr;
  align-items: stretch;
}
/* Frueher var(--sage-deep) mit weisser Schrift. Es war nicht als "schwarz"
   gemeldet, waere nach der Umstellung aber der einzige dunkle Block der
   Seite gewesen - das sieht nach Versehen aus, nicht nach Absicht.
   Jetzt der helle Salbei-Tint: das Panel bleibt farblich abgesetzt vom
   weissen Formular daneben, ohne dunkel zu sein. */
.sw-contact__aside {
  background: var(--sage-tint-2);
  color: var(--ink);
  padding: 3rem 2.5rem;
}
.sw-contact__aside h3 { color: var(--ink); margin-bottom: .9rem; }
.sw-contact__aside p { color: var(--muted); font-size: .95rem; }
.sw-contact__list { list-style: none; padding: 0; margin: 2rem 0 0; }
.sw-contact__list li {
  display: flex;
  align-items: center;
  gap: .85rem;
  margin-bottom: 1.1rem;
}
.sw-contact__list li:last-child { margin-bottom: 0; }
.sw-contact__list .fa {
  width: 36px; height: 36px; flex: 0 0 36px; border-radius: 50%;
  background: rgba(54,90,81,.12); color: var(--sage-deep);
  display: inline-flex; align-items: center; justify-content: center; font-size: .9rem;
}
/* min-width:0 stops the contact PNGs from forcing the panel wider than its column */
.sw-contact__value { min-width: 0; }
/* invert() entfaellt - schwarzer PNG-Text auf hellem Panel stimmt direkt. */
.sw-contact__value small {
  display: block;
  font-size: .74rem;
  color: var(--muted);
  margin-top: .25rem;
}

.footer-note {
  font-size: .74rem !important;
  color: var(--muted) !important;
  margin-top: .35rem;
  font-style: italic;
}

.sw-contact__form { padding: 3rem 2.5rem; }

/* own grid instead of a nested Bootstrap .row - no negative gutter margins
   leaking out of the padded panel, and one consistent gap everywhere */
.sw-form__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.15rem 1.25rem;
}
.sw-form__field--wide,
.sw-form__foot { grid-column: 1 / -1; }

.sw-form label {
  display: block;
  font-size: .88rem;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: .01em;
  margin-bottom: .45rem;
}
.sw-form input[type="text"],
.sw-form input[type="email"],
.sw-form textarea,
.sw-form .common-input,
.sw-form .common-textarea {
  display: block;
  width: 100%;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: var(--cream);
  padding: .8rem 1.05rem;
  /* explicit stack: form controls do not inherit the body font by default */
  font-family: 'Roboto', -apple-system, 'Segoe UI', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: normal;
  color: var(--ink);
  -webkit-font-smoothing: auto;
  transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
}
.sw-form textarea, .sw-form .common-textarea {
  min-height: 150px;
  height: 150px;
  resize: vertical;
}
.sw-form input:focus, .sw-form textarea:focus {
  outline: none;
  border-color: var(--sage);
  background: #fff;
  box-shadow: 0 0 0 4px rgba(111, 156, 143, .16);
}
/* #9aa5a1 on cream was ~2.4:1 - below the 4.5:1 minimum and hard to read */
.sw-form ::placeholder { color: #7c8783; opacity: 1; }

.sw-form__foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .85rem 1.5rem;
  margin-top: .35rem;
}
.sw-form__trap {
  /* Honigtopf des Kontaktformulars: fuer Menschen unsichtbar, fuer Bots ein
     Feld wie jedes andere. NICHT display:none - einfache Bots ueberspringen
     solche Felder. */
  position: absolute; left: -10000px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
.sw-form__note {
  flex: 1 1 17rem;
  margin: 0;
  font-size: .74rem;
  line-height: 1.5;
  color: #8a938f;
}
.sw-form__note a { color: var(--sage-dark); text-decoration: underline; }
.sw-form__foot .btn { flex: 0 0 auto; }
/* invisible reCAPTCHA: the badge is fixed-position, the inline node must not
   claim a flex slot or it tears a hole into the footer row */
.sw-form__captcha { flex: 0 0 0; width: 0; line-height: 0; }

/* Google renders the invisible-reCAPTCHA badge as a fixed panel in the bottom
   right corner, where it sat on top of the form's submit button. Hiding it is
   explicitly permitted as long as the reCAPTCHA branding is named in the form
   itself - that is what .sw-form__note does.
   https://developers.google.com/recaptcha/docs/faq */
.grecaptcha-badge {
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
}

@media (max-width: 991.98px) {
  .sw-contact__grid { grid-template-columns: 1fr; }
  .sw-contact__aside, .sw-contact__form { padding: 2.25rem 1.75rem; }
}
@media (max-width: 575.98px) {
  .sw-form__grid { grid-template-columns: 1fr; }
  .sw-form__foot .btn { width: 100%; }
}

/* ------------------------------------------------------------------ footer -- */
/* Frueher var(--ink) - das war die Flaeche, die Carolin als "schwarzer
   Hintergrund" gemeldet hat. Jetzt das tiefere Beige, abgesetzt vom
   Seitengrund durch eine Haarlinie statt durch einen Helligkeitssprung. */
.ftco-footer {
  background: var(--sand-2);
  border-top: 1px solid var(--sand-3);
  padding: 5em 0 0;
}
.ftco-footer .ftco-heading-2 {
  color: var(--ink);
  font-size: .82rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  margin-bottom: 1.1rem;
}
/* --muted statt --body: auf --sand-2 kommt --body nur auf 4.36 und damit
   unter AA, --muted auf 5.60. */
.ftco-footer p, .ftco-footer li { color: var(--muted); font-size: .93rem; }
.ftco-footer a { color: var(--sage-dark); }
.ftco-footer a:hover { color: var(--ink); }
/* Die Kontakt-PNGs sind schwarzer Text auf transparent. Auf dem dunklen
   Footer mussten sie aufgehellt werden - auf hellem Grund stimmen sie von
   selbst, der invert() muss also WEG und nicht etwa angepasst werden. */
.ftco-footer .fa { color: var(--sage); }

.sw-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 3rem;
  padding-bottom: 4rem;
}
/* every column shares one top edge - headings and logo start on the same line */
.sw-footer__col { min-width: 0; }

/* logo and claim sit side by side - the mark is square, so it costs little
   width and the sentence reads as its caption rather than a separate block */
.sw-footer__brand {
  display: flex;
  align-items: center;
  gap: 1.15rem;
  margin-bottom: 1.5rem;
}
.sw-footer__logo { flex: 0 0 auto; display: block; }
.sw-footer__logo img {
  height: 62px;
  width: auto;
  display: block;
}
.sw-footer__tagline {
  margin: 0;
  min-width: 0;
  max-width: 30ch;
  font-size: .9rem;
  line-height: 1.6;
}
/* the brand column is now logo + claim only, so it no longer needs to be
   wider than the two contact columns */
.sw-footer__col--brand .sw-footer__brand { margin-bottom: 0; }

.sw-footer__list { list-style: none; padding: 0; margin: 0; }
.sw-footer__list li {
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  margin-bottom: .85rem;
}
.sw-footer__list li:last-child { margin-bottom: 0; }
.sw-footer__list .fa {
  flex: 0 0 auto;
  width: 1.1em;
  line-height: var(--sw-contact-img-h);   /* damit Icon und Bild auf einer Linie sitzen */
  text-align: center;
}
.sw-footer__value { min-width: 0; }
.sw-footer__value small {
  display: block;
  margin-top: .3rem;
  font-size: .74rem;
  line-height: 1.5;
  color: var(--muted);
}

/* --- bottom bar: legal links, copyright, credits --- */
.sw-footer__bar {
  background: var(--sand-3);
  padding: 1.5rem 0;
}
.sw-footer__bar .container {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "legal   credits"
    "copy    credits";
  align-items: center;
  gap: .35rem 2.5rem;
}
.sw-legal-links {
  grid-area: legal;
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1.25rem;
  font-size: .88rem;
}
/* separators come from the gap, not from markup */
.sw-legal-links a { position: relative; }
.sw-legal-links a + a::before {
  content: "";
  position: absolute;
  left: -.65rem; top: 50%;
  width: 3px; height: 3px;
  margin-top: -1.5px;
  border-radius: 50%;
  background: rgba(38,48,45,.3);
}
.sw-footer__copy {
  grid-area: copy;
  margin: 0;
  font-size: .82rem;
  color: var(--muted) !important;
}
/* attribution is a legal obligation, not a design element - keep it quiet.
   No max-width: the old 26rem cap was measured against a shorter credit line
   and broke the current one at 1140px, where 648px are free and 576px needed.
   A width limit here would have to be re-tuned every time a credit changes;
   the grid already knows how much room there is. */
.sw-footer__credits {
  grid-area: credits;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: .2rem 1.1rem;
  font-size: .68rem !important;
  line-height: 1.6;
  color: var(--muted) !important;
}
/* each credit stays whole; a narrow viewport wraps between them, never
   inside "Michael Nussbaumer" */
.sw-footer__credits span { position: relative; white-space: nowrap; }
.sw-footer__credits span + span::before {
  content: "";
  position: absolute;
  left: -.6rem; top: 50%;
  width: 3px; height: 3px;
  margin-top: -1.5px;
  border-radius: 50%;
  background: rgba(38,48,45,.3);
}
.sw-footer__credits a { color: var(--muted); }

/* Rest der Counselor-Vorlage. Auf einer durchgehend hellen Seite darf hier
   nichts Dunkles mehr stehen. */
.bg-black { background: var(--sand-3) !important; }

@media (max-width: 991.98px) {
  .sw-footer__grid { grid-template-columns: 1fr 1fr; gap: 2.5rem; padding-bottom: 3rem; }
  .sw-footer__col--brand { grid-column: 1 / -1; }
}
@media (max-width: 767.98px) {
  .ftco-footer { padding-top: 3.5em; }
  .sw-footer__grid { grid-template-columns: 1fr; gap: 2.25rem; }
  .sw-footer__bar .container {
    grid-template-columns: 1fr;
    grid-template-areas: "legal" "copy" "credits";
    gap: .75rem;
  }
  .sw-footer__credits { justify-content: flex-start; }
}

/* ------------------------------------------------------- contact-detail PNGs --
   Address, mail and phone are rendered to images so crawlers can't harvest
   them (see about/utils.py). They are generated at 3x the display size for
   HiDPI screens and all share one canvas height, so one rule sizes them
   identically everywhere and they line up in a list. */
img[src*="4dr3ss"],
img[src*="m41l"],
img[src*="ph0n3"] {
  height: var(--sw-contact-img-h);
  width: auto;
  max-width: 100%;
  /* in a column too narrow for the full string the image scales down
     proportionally instead of being cropped or stretched */
  object-fit: contain;
  object-position: left center;
  vertical-align: middle;
}

/* --------------------------------------------------------------- utilities -- */
#beratung, #angebote, #about, #kontakt, [id^="offer-obj-"] { scroll-margin-top: 92px; }
/* standalone pages have no hero, so the fixed navbar would cover the heading */
.sw-legal { padding-top: 10em; }
html { scroll-behavior: smooth; }
:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

/* very narrow screens: mark only, no wordmark next to it */
@media (max-width: 400px) {
  .sw-brand__text { display: none; }
  .sw-brand__mark { max-height: 38px; }
}

/* hero logo scales down so the first screen stays intact */
@media (max-width: 991.98px) {
  .sw-hero__mark { max-height: 200px; max-width: min(235px, 64vw); margin-bottom: 1.6rem; }
}
@media (max-width: 575.98px) {
  .sw-hero__mark { max-height: 160px; max-width: min(185px, 58vw); margin-bottom: 1.3rem; }
  .sw-hero__role { margin-bottom: 1.8rem; }
}

/* ============================================================ large screens ==
   Bootstrap caps .container at 1140px, which on a 27" or 4K display leaves the
   content marooned in the middle of the screen. Widen it in measured steps -
   the text blocks keep their own ch-based max-widths, so lines never get long,
   only the layout breathes. */
@media (min-width: 1600px) {
  .container { max-width: 1320px; }
  .ftco-section { padding: 8.5em 0; }
  .sw-hero__mark { max-height: 270px; max-width: 320px; }
}
@media (min-width: 1920px) {
  .container { max-width: 1480px; }
  .ftco-section { padding: 9.5em 0; }
  .offer-grid { gap: 2.5rem; }
  .sw-footer__grid { gap: 4rem; }
  /* keep the form from drifting apart from its info panel */
  .sw-contact__grid { grid-template-columns: 1fr 1.35fr; }
}

/* ============================================================ small screens ==
   Target is the iPhone SE at 320 CSS px: the container's inner width is 290px,
   so every fixed padding is measured against that budget. */
@media (max-width: 400px) {
  .ftco-section { padding: 3.25em 0; }

  .offer-card__body { padding: 1.35rem 1.15rem 1.2rem; }
  .offer-card__foot { flex-wrap: wrap; gap: .6rem; }
  .offer-card__foot .btn { width: 100%; text-align: center; }

  .sw-invite { padding: 1.6rem 1.25rem; }
  .sw-invite h3 { font-size: 1.3rem; }
  .sw-invite__aside .btn { width: 100%; }

  /* the contact PNGs scale down proportionally, so a narrower icon column
     directly buys legibility */
  .sw-contact__aside,
  .sw-contact__form { padding: 1.75rem 1.25rem; }
  .sw-contact__list li { gap: .6rem; }
  .sw-contact__list .fa { width: 30px; height: 30px; flex: 0 0 30px; font-size: .8rem; }

  /* keep logo and claim side by side, just tighter */
  .sw-footer__brand { gap: .85rem; }
  .sw-footer__logo img { height: 50px; }
  .sw-footer__tagline { font-size: .85rem; }

  .sw-legal { padding-top: 7.5em; }
  .sw-legal__body { padding: 1.4rem 1.1rem; }
  .about-copy > p:first-child::first-letter { font-size: 2.7rem; }

  .sw-hero__cta { flex-direction: column; align-items: stretch; }
  .sw-hero__cta .btn { width: 100%; }
}

/* A single overflowing element makes every section scroll sideways on a phone.
   MUST be `clip`, never `hidden`: `hidden` turns the root into a scroll
   container, which silently disables `position: sticky` on the Beratung
   scenes. `clip` trims the overflow without creating a scroll port. */
html, body { overflow-x: clip; }
img, svg { max-width: 100%; }
