/*
 * Leistungen-Übersicht (Hub-Seite) — portiert aus dem Mockup
 * ~/nonoise-mockups/leistungen-uebersicht-v1.html.
 *
 * Datei heißt bewusst leistungen-hub.css, NICHT leistungen.css: Letzteres existiert
 * bereits als komplett eigenständige Datei für den Leistungen-Abschnitt der Startseite
 * (patterns/leistungen.php) und wird hier nicht angefasst.
 *
 * ABWEICHUNG von der Projektkonvention „ein Pattern = eine CSS-Datei" (Abschnitt 8 der
 * CLAUDE.md): genau wie bei SEO/GEO, Tracking & Analytics und CRO bedient diese eine Datei
 * alle Patterns der Seite, weil sie sich ein durchgehendes Gerüst teilen (.section, .wrap,
 * .section-head, .kicker, .btn, die Farb-Tokens).
 *
 * ALLE Regeln sind unter .nn-hub gescopt. Klassennamen aus dem Mockup (.section, .wrap,
 * .hub …) sind bewusst generisch geblieben, damit ein Abgleich mit dem Mockup möglich
 * bleibt — sie gelten aber ausschließlich innerhalb von .nn-hub und können deshalb nicht in
 * andere Patterns lecken.
 *
 * Light/Dark folgt der Theme-Konvention: Hell unbedingt, Dunkel als einzige bewachte
 * Variante (siehe Kommentarblock in assets/css/nav.css).
 *
 * IDs, die von assets/js/leistungen-hub.js angesprochen werden, sind nnHub-präfigiert
 * (nnHubCanvasColor, nnHubPanel etc.): alle JS-Dateien laden site-weit auf jeder Seite,
 * gleichlautende IDs auf einer anderen Leistungsseite würden sonst von zwei Skripten
 * gleichzeitig bedient (siehe Stolperstein in CLAUDE.md Abschnitt 8).
 *
 * Nav-, Footer- und Theme-Switcher-CSS des Mockups wurde NICHT übernommen — dafür sorgen
 * die echten Template Parts (parts/header.html, parts/footer.html). Die .cta-Sektion des
 * Mockups wurde ebenfalls nicht übernommen — die Seite nutzt stattdessen das echte,
 * theme-weite .nn-cta-band (siehe patterns/leistungen-hub-cta.php).
 *
 * Große Screens (ab ca. 1900px, z. B. 2560×1440): alle clamp()-Obergrenzen und die
 * .wrap-Maximalbreite wurden von Beginn an auf demselben, bereits austarierten Niveau wie
 * bei der CRO-Seite gebaut (--wrap 1760px→2200px, H1 5.75rem→7.25rem usw. — dieselben
 * Grundwerte, weil Wrap/H1/H2/H3/Kicker/Lede/Section/Section-Head/Hero/Hero-Row/Hero-Box
 * strukturell identisch zu den anderen Leistungsseiten sind). Der vw/vh-Koeffizient blieb
 * dabei jeweils unverändert. Für das sechsteilige Hub-Grid, das kein direktes Vorbild in
 * den übrigen Leistungsseiten hat, wurde dieselbe Technik selbst angewendet: nur die
 * Obergrenze der jeweiligen clamp()-Werte um ca. 30 % angehoben, min-Wert und
 * vw/vh-Koeffizient unangetastet. Mobile Media Queries wurden UNVERÄNDERT aus dem Mockup
 * übernommen — die Skalierung für große Screens darf das kleine Verhalten nie berühren.
 */


/* ==========================================================================
   TOKENS
   ========================================================================== */
.nn-hub {
  --jade-light:#b7e5ba;
  --jade-mid:#5ca87c;
  --jade:#288760;
  --jade-dark:#1a5140;

  --bg:#ffffff;
  --paper:#f7f9f7;
  --ink:#12211a;
  --ink-muted:#5c6d64;
  --line:#dfe8e2;
  --card:#ffffff;
  --grain-blend:overlay;

  --jade-accent:#288760;

  --serp-chip:#eef3ef;
  --shadow:0 1px 2px rgba(18,33,26,.04), 0 8px 24px rgba(18,33,26,.06);

  /* Fallback-Höhe der fixierten Theme-Nav (.nn-nav), an der die Ankerleiste
     klebt. assets/js/leistungen-hub.js misst die echte Höhe und überschreibt den Wert. */
  --nav-h:97px;
}
@media (prefers-color-scheme:dark) {

  html:not([data-theme="light"]) .nn-hub {
    --bg:#0b0c0c;
    --paper:#0f1512;
    --ink:#e7e9ec;
    --ink-muted:#b0b5bf;
    --line:#22362c;
    --card:#111815;
    --jade-accent:#5ca87c;
    --serp-chip:#16211c;
    --grain-blend:soft-light;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
  }

}
html[data-theme="dark"] .nn-hub {
  --bg:#0b0c0c;
  --paper:#0f1512;
  --ink:#e7e9ec;
  --ink-muted:#b0b5bf;
  --line:#22362c;
  --card:#111815;
  --jade-accent:#5ca87c;
  --serp-chip:#16211c;
  --grain-blend:soft-light;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
}

.nn-hub, .nn-hub *, .nn-hub *::before, .nn-hub *::after {box-sizing:border-box}

/* WP-Core setzt via theme.json global blockGap (md, 2rem) eine margin-block-start-Regel
   auf jedes direkte Kind von .wp-site-blocks (siehe ausführliche Begründung in
   assets/css/seo-geo.css bei derselben Regel für .nn-seo). .nn-hub ist DOM-Nachbar des
   fixierten Headers (aus dem Fluss genommen) und dadurch das optisch erste Element im
   Fluss — sein 32px-margin-top würde sonst nach oben kollabieren und den Hero zusätzlich
   zum bereits auf die Nav-Höhe abgestimmten padding-top nach unten schieben. */
.nn-hub {margin-top:0}

/* 2026-09-02 (Nutzer-Wunsch, Seitenangleichung): 2550px statt 2200px — 1:1 der SEO/GEO-Wert,
   damit alle Leistungsseiten auf großen Screens (2560×1440) gleich viel Fläche nutzen. */
.nn-hub .wrap {width:min(2550px,100% - clamp(2rem,10vw,13rem));margin-inline:auto}

.nn-hub h1, .nn-hub h2, .nn-hub h3 {font-weight:900;letter-spacing:-.03em;line-height:1.03;margin:0}
.nn-hub h1 {font-size:clamp(2.4rem,6.2vw,7.25rem)}
/* Zweizeilige H1 mit <br> plus die flache clamp()-Untergrenze (2.4rem) sind auf sehr
   schmalen Screens (~375px) zusammen zu breit — der zweite <br>-Textblock bricht dann
   selbst nochmal um, macht 4 statt 2 Zeilen. Nur hier verkleinert, bleibt bei max. 3
   Zeilen (siehe Analogregel in cro.css/ai-automatisierungen.css). */
@media (max-width:700px) {
  .nn-hub h1 {font-size:1.75rem}
}
.nn-hub h2 {font-size:clamp(2rem,4.5vw,4.75rem);text-transform:uppercase}
.nn-hub h3 {font-size:clamp(1.15rem,2vw,2.1rem);letter-spacing:-.02em}
.nn-hub p {margin:0}

.nn-hub .kicker {
  font-size:clamp(.8rem,.45vw,1rem);font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--jade-accent);margin:0;
}
.nn-hub .lede {font-size:clamp(1rem,1.35vw,1.5rem);color:var(--ink-muted);max-width:62ch;text-wrap:pretty}
.nn-hub .lede--wide {max-width:70ch}

.nn-hub .section {padding:clamp(4.5rem,11vh,12rem) 0;position:relative}
.nn-hub .section--paper {background:var(--paper)}
.nn-hub .section-head {margin-bottom:clamp(2.25rem,5vh,5rem)}
.nn-hub .section-head .kicker {margin-bottom:.75rem}
.nn-hub .section-head p.lede {margin-top:1.25rem}

/* 2026-09-02 (Nutzer-Wunsch, Seitenangleichung): justify-content:center + text-align:center
   ergänzt — fehlten bisher als einzige Leistungsseite (alle anderen haben beides). Ohne sie
   stand der Text bei zweizeilig umbrechenden Button-Labels (z. B. auf schmalen Screens)
   linksbündig statt zentriert unter der ersten Zeile. */
.nn-hub .btn {
  display:inline-flex;align-items:center;justify-content:center;gap:.6rem;
  background:var(--jade);color:#06110c;
  font-weight:700;font-size:1.1rem;text-align:center;
  padding:1.05rem 2.1rem;border-radius:999px;
  text-decoration:none;border:0;cursor:pointer;
  font-family:inherit;
}
.nn-hub .btn:hover, .nn-hub .btn:focus-visible {background:#2f9c6f;transition:background .2s ease}
.nn-hub .btn--ghost {background:transparent;color:var(--ink);border:1px solid var(--line)}
.nn-hub .btn--ghost:hover, .nn-hub .btn--ghost:focus-visible {background:transparent;border-color:var(--jade-accent);color:var(--jade-accent);transition:color .2s ease,border-color .2s ease}
.nn-hub .btn--sm {padding:.6rem 1.3rem;font-size:.92rem}

/* ==========================================================================
   BREADCRUMB
   ========================================================================== */
/* Abstand Breadcrumb→H1 als clamp() statt fixer 1.5rem: die vier vertikalen Abstaende im
   First View (Breadcrumb→H1→Lede→Buttons→Button-Notiz) waren durchgehend feste rem-Werte,
   waehrend H1/Lede selbst per clamp() mitwuchsen — auf 2560px stand dadurch sehr grosse
   Schrift in unveraendert engen Abstaenden. Alle vier wachsen jetzt mit, mit Koeffizienten,
   die ihren Minimalwert erst ab ca. 1300-1400px ueberschreiten (darunter aendert sich also
   praktisch nichts, siehe Koeffizienten-Stolperstein CLAUDE.md Abschnitt 8). */
.nn-hub .crumbs {
  display:flex;flex-wrap:wrap;align-items:center;gap:.45rem;
  list-style:none;margin:0 0 clamp(1.5rem,1.9vw,3.5rem);padding:0;
  font-size:.85rem;color:var(--ink-muted);
}
.nn-hub .crumbs li {display:flex;align-items:center;gap:.45rem}
.nn-hub .crumbs a {color:var(--ink-muted);text-decoration:none}
.nn-hub .crumbs a:hover, .nn-hub .crumbs a:focus-visible {color:var(--jade-accent);transition:color .2s ease}
.nn-hub .crumbs [aria-current="page"] {color:var(--ink);font-weight:600}
.nn-hub .crumbs__sep {opacity:.45}

/* ==========================================================================
   HERO — TYPOGRAFISCHES POSTER
   Architektur D des Awwwards-Hero-Skills, 1:1 aus assets/css/seo-geo.css portiert (siehe
   dortiger Kommentarblock für die volle Begründung von Layout/Atmosphäre/Badge/Animation).
   Einziger struktureller Unterschied: .hero__bottom-main enthält hier ZWEI CTAs statt
   einem (siehe Kopf von patterns/leistungen-hub-hero.php) — dafür ein eigener
   .hero__actions-Flex-Wrapper statt eines einzelnen .btn mit margin-top.
   ========================================================================== */
/* Große Screens (analog zu seo-geo.css, gleiche Ursache): der sichtbare Weißraum
   zwischen Nav-Unterkante und Text soll genauso groß sein wie zwischen Text und
   Ankernavigation. Zwei Ursachen behoben: (1) padding-top hatte jenseits von --nav-h einen
   anderen Clamp als padding-bottom — jetzt derselbe; (2) justify-content:flex-start ließ
   bei Inhalt, der die 100svh-Mindesthöhe nicht ausfüllt, den gesamten Leerraum unten vor
   der Ankernavigation liegen — justify-content:center verteilt ihn gleichmäßig. */
.nn-hub .hero {
  position:relative;min-height:100svh;display:flex;flex-direction:column;
  justify-content:center;overflow:hidden;isolation:isolate;
  padding:calc(var(--nav-h) + clamp(2rem,4vh,4.64rem)) 0 clamp(2rem,4vh,4.64rem);
}
/* 2026-09-02 (Nutzer-Wunsch, Seitenangleichung): kurze Browserfenster (z. B. ein nicht-
   maximiertes Fenster auf einem 13"-MacBook) — dieselbe Regel wie in assets/css/cro.css. */
@media (max-height:820px) {
  .nn-hub .hero {padding:calc(var(--nav-h) + 1.25rem) 0 1.5rem}
}

/* Atmosphäre statt Canvas-Farbfeld — siehe Begründung in seo-geo.css/seo-geo-hero.php. */
.nn-hub .hero__atmo {
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(52% 48% at 80% 40%, color-mix(in srgb,var(--jade) 34%,transparent) 0%, transparent 70%),
    radial-gradient(46% 40% at 16% 84%, color-mix(in srgb,var(--jade-mid) 30%,transparent) 0%, transparent 70%),
    radial-gradient(36% 32% at 12% 8%, color-mix(in srgb,var(--jade-light) 28%,transparent) 0%, transparent 72%),
    var(--bg);
}
.nn-hub .hero__grain {position:absolute;inset:0;width:100%;height:100%;z-index:1;opacity:.18;mix-blend-mode:var(--grain-blend);image-rendering:pixelated;pointer-events:none}
/* margin-top:0: .wrap ist drittes Kind im Flow von .hero (nach .hero__atmo und
   .hero__grain), WP-Core haengt seinem "is-layout-flow"-Default-Blockgap-CSS deshalb
   32px margin-top an — hier unerwuenscht, .hero steuert den Abstand zur Nav bereits
   selbst ueber sein eigenes padding-top (siehe auch .nn-hub weiter oben in dieser Datei). */
.nn-hub .hero .wrap {position:relative;z-index:3;margin-top:0}

/* Masthead-Zeile: Brotkrümel links, rotierendes Marken-Badge rechts. */
.nn-hub .hero__top {
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:.75rem 1.5rem;
}
.nn-hub .hero__top .crumbs {margin-bottom:0}

/* 2026-09-03 (Nutzer-Wunsch, Nachschärfung): Koeffizient/Deckel nochmal von 7.7vw/8.8rem auf
   5.62vw/6.42rem gesenkt (Faktor ~0,73) — bei 7.7vw/8.8rem brach "SEO, Ads, CRO, Tracking,
   KI." auf einem 13"-MacBook-Air (1440px) TATSÄCHLICH auf zwei Zeilen um (live per
   getBoundingClientRect gemessen: die Zeile war dort bereits ca. 9,6 % BREITER als der
   verfügbare Wrap, nicht nur knapp dran — die frühere "ab ca. 800px einzeilig"-Kalibrierung
   war für diese Breite schlicht nie zutreffend). Jetzt ca. 20 % Puffer bei 1440px. */
.nn-hub .hero__poster-heading {
  margin:clamp(1rem,2.5vh,2.5rem) 0 0;
  font-size:clamp(2.5rem,5.62vw,6.42rem);font-weight:900;
  letter-spacing:-.045em;line-height:.94;
}
.nn-hub .hero__line {display:block;max-width:100%}
.nn-hub .hero__line--a {text-align:left;color:var(--ink)}
/* Zeile 2 rechtsbündig UND in --jade-accent: visualisiert die Breite-vs-Einheit-Dualität
   dieser Seite (viele Bereiche vs. ein System), siehe Kopf von leistungen-hub-hero.php. */
.nn-hub .hero__line--b {
  text-align:right;color:var(--jade-accent);
  margin-top:clamp(.2rem,1vw,.75rem);
}
/* 2026-09-03 (Nutzer-Wunsch): Mobile-Stufe ergänzt (fehlte bisher als einzige Leistungsseite)
   — "Ein System. Ein Team." (Zeile 2) brach auf schmalen Screens selbst um UND blieb dabei
   rechtsbündig statt wie bei allen übrigen Leistungsseiten links. Zeile 2 bleibt hier bewusst
   Teil derselben Formel wie Zeile 1 (kein eigener Deckel), 1.75rem hält den String bei
   375px mit ausreichend Puffer einzeilig (live gemessen). */
@media (max-width:640px) {
  .nn-hub .hero__poster-heading {font-size:clamp(1.6rem,7vw,1.75rem)}
  .nn-hub .hero__line--b {text-align:left}
}
/* Großbildschirm-Stufe: Breakpoint bei 1900px (statt 1600px), weil die neue, sicherere
   Basisformel (5.62vw/6.42rem) ihren Deckel erst bei ca. 1827px erreicht — bei 1600px läge
   sie noch darunter, ein Breakpoint dort würde also einen sichtbaren Sprung erzeugen. Bei
   1900px liegt sie sicher am 6.42rem-Deckel, die Stufe übernimmt dort nahtlos und wächst
   bis 8.5rem weiter. */
@media (min-width:1900px) {
  .nn-hub .hero__poster-heading {font-size:clamp(6.42rem,5.4vw,8.5rem)}
}

/* Untere Zeile: Lede+CTAs links, Bereiche-Vorschau rechts. */
.nn-hub .hero__bottom {
  display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;
  gap:clamp(1.5rem,4vw,3rem);
  margin-top:clamp(1.5rem,3.5vh,3.5rem);
}
.nn-hub .hero__bottom-main {max-width:50ch}
.nn-hub .hero__lede {margin:0;font-size:clamp(1rem,1.35vw,1.3rem);color:var(--ink)}
.nn-hub .hero__actions {display:flex;flex-wrap:wrap;gap:.85rem;margin-top:clamp(1.5rem,2.5vh,2.25rem)}

/* Ruhige Fließtext-Zeile statt Pill-Chips — hier eine Vorschau auf die sechs
   Leistungsbereiche direkt darunter statt einer Plattform-Liste. Etwas breiter als bei
   SEO/GEO (32ch statt 26ch), weil sechs Bereichsnamen in Summe deutlich länger sind als
   die dortige Plattform-Aufzählung. */
.nn-hub .hero__platforms {
  margin:0;max-width:32ch;text-align:right;
  font-size:.85rem;line-height:1.6;color:var(--ink-muted);
}
/* min-height:0 statt der 100svh-Untergrenze aus der Basis-Regel: die volle
   Viewport-Höhe war als UNTERGRENZE für große/leere Viewports gedacht (siehe
   Kommentar bei .hero weiter oben), auf Mobile kippt das aber ins Gegenteil —
   diese Seite hat mit zwei CTAs und der langen Bereichs-Vorschau spürbar mehr
   Content als SEO/GEO, wodurch bei erzwungener 100svh-Höhe ein auffälliger
   Leerraum zwischen der Bereichs-Zeile und dem Hero-Ende entstand (Nutzer-
   Feedback). Der Hero umschließt seinen Inhalt hier stattdessen einfach eng,
   mit demselben Innenabstand wie auf breiteren Screens. */
@media (max-width:640px) {
  .nn-hub .hero {min-height:0}
  .nn-hub .hero__bottom {align-items:flex-start}
  .nn-hub .hero__platforms {text-align:left;max-width:none}
}
@media (max-height:820px) {
  .nn-hub .hero {min-height:0}
}

.nn-hub .btn--hero {
  font-size:.98rem;letter-spacing:.04em;text-transform:uppercase;
}
/* 2026-09-02 (Nutzer-Wunsch, Seitenangleichung): identisch zu allen übrigen Leistungsseiten. */
@media (max-width:640px) {
  .nn-hub .btn--hero {font-size:.85rem;padding:.85rem 1.5rem}
}

/* ==========================================================================
   HERO — ROTIERENDES MARKEN-BADGE
   1:1 aus seo-geo.css portiert, nur der Text im Ring geändert (DIGITALAGENTUR statt
   SEO/GEO AGENTUR — diese Seite ist die Übersicht, kein Einzelleistungs-Label). Eigener
   Keyframe-Name (nn-hub-badge-spin statt nn-seo-badge-spin): @keyframes sind global, nicht
   auf .nn-hub gescopt, und alle Leistungsseiten-CSS-Dateien laden site-weit — ein
   gleichlautender Name würde mit seo-geo.css kollidieren (derselbe Grund wie bei
   JS-IDs, siehe CLAUDE.md Abschnitt 8).
   ========================================================================== */
.nn-hub .hero__badge {
  flex:none;position:relative;
  width:clamp(4.5rem,6.5vw,7.5rem);aspect-ratio:1;
  display:grid;place-items:center;color:var(--jade-accent);
}
.nn-hub .hero__badge-ring {
  width:100%;height:100%;animation:nn-hub-badge-spin 16s linear infinite;
}
.nn-hub .hero__badge-ring text {
  font-size:8.6px;fill:currentColor;opacity:.75;
  font-weight:700;text-transform:uppercase;
}
.nn-hub .hero__badge-spark {
  position:absolute;font-size:1.15rem;line-height:1;
  filter:drop-shadow(0 0 8px color-mix(in srgb,var(--jade) 55%,transparent));
}
@keyframes nn-hub-badge-spin {to {transform:rotate(360deg)}}
@media (max-width:480px) {
  .nn-hub .hero__badge {width:clamp(3.5rem,11vw,4.5rem)}
}
@media (prefers-reduced-motion:reduce) {
  .nn-hub .hero__badge-ring {animation:none}
}

/* ==========================================================================
   HERO — EINTRITTS-ANIMATION
   1:1 aus seo-geo.css portiert, eigener Keyframe-Name aus demselben Grund wie beim Badge.
   ========================================================================== */
@keyframes nn-hub-hero-in {
  from {opacity:0;transform:translateY(18px);filter:blur(6px)}
  to {opacity:1;transform:none;filter:blur(0)}
}
.nn-hub .hero .wrap > * {
  animation:nn-hub-hero-in .8s cubic-bezier(.16,1,.3,1) both;
}
.nn-hub .hero .wrap > *:nth-child(1) {animation-delay:0s}
.nn-hub .hero .wrap > *:nth-child(2) {animation-delay:.16s}
.nn-hub .hero .wrap > *:nth-child(3) {animation-delay:.34s}
@media (prefers-reduced-motion:reduce) {
  .nn-hub .hero .wrap > * {animation:none}
}

/* ==========================================================================
   SPINE — "Sechs Bereiche" als scroll-gesteuerte horizontale Reihe, verankert
   an einem fixen Claim-Hintergrund. Löst die frühere Netzwerk-Graph-Version ab
   (siehe Kopfkommentar in patterns/leistungen-hub-bereiche.php) — Feedback:
   Kreise+Verbindungslinien+Seiten-Panel lasen wie ein generisches SmartArt-
   Diagramm. Der neue Ansatz verzichtet komplett auf Icons/Kreise/Linien:
   "less noise." (blass) und "more impact." (voll Jade, fix) stehen unbewegt
   im Hintergrund, sechs Bereiche laufen davor als horizontale Reihe durch —
   die Verbindung zum Claim ist die geteilte Bühne, kein gezeichneter Pfeil.

   Zwei Ebenen, bewusst NICHT über :has() gelöst (siehe Stolperstein weiter
   oben in der Git-Historie dieser Datei: :has()-ausgelöste Invalidierung war
   für Opacity/Radius-Änderungen auf SVG-Elementen in Tests nachweislich
   unzuverlässig — hier gibt es gar kein SVG mehr, aber die Lehre "einfache,
   von JS gesetzte Klassen statt exotischer Selektor-Ketten" bleibt):
   1. Basis, IMMER aktiv, kein JS nötig: .spine__track ist eine normale
      horizontal scrollende Reihe (native scroll-snap) unterhalb des fixen
      Claims — funktioniert auf jedem Gerät, auch ganz ohne JavaScript.
   2. Progressive Verbesserung ab 900px + ohne reduced-motion (leistungen-
      hub.js hängt dafür die Klasse .is-pinned an): die Sektion wird für die
      Dauer des Durchscrollens fixiert (position:sticky), vertikales Scrollen
      steuert die horizontale Position der Reihe sowie die Rail-Füllung
      darunter — dieselbe Fortschritts-Berechnung (Scroll-Distanz / Element-
      Höhe), die der Fortschrittsbalken der Ankerleiste in dieser Datei schon
      lange nutzt, nur auf ein größeres Element angewendet. Ohne .is-pinned
      bleibt die Basis-Reihe unverändert nutzbar — kein Blocker für die
      Kernfunktion, nur der Scroll-Scrub-Effekt entfällt. */
.nn-hub .spine {position:relative}
.nn-hub .spine__pin {
  display:flex;flex-direction:column;justify-content:center;gap:clamp(1.75rem,4vh,3rem);
  padding:clamp(2rem,5vh,3.5rem) 0;
}

/* ---- Claim: fixer Hintergrund, ändert sich nie mit dem Scroll-Fortschritt.
   "less noise." als blasse, zum Hintergrund hin ausgeblendete Variante von
   --ink statt eines echten Kontur-Effekts (-webkit-text-stroke erzeugte bei
   Inter Black 900 auf engen Rundungen wie "e" sichtbare Doppelkanten/Klumpen
   — ein bekannter Schwachpunkt der Eigenschaft bei fetten Schriftschnitten,
   siehe Konzept-Diskussion). "more impact." bleibt dauerhaft volles Jade —
   das ist die einzige Verbindung zum Claim, die diese Sektion braucht. ---- */
.nn-hub .spine__claim {
  text-align:center;font-weight:900;letter-spacing:-.03em;line-height:1.04;
  font-size:clamp(2.2rem,6vw,5.5rem);pointer-events:none;user-select:none;
}
.nn-hub .spine__ghost {display:block;color:color-mix(in srgb,var(--ink) 14%,var(--bg))}
.nn-hub .spine__solid {display:block;color:var(--jade)}

/* ---- Rail: Fortschrittsleiste im "Rauschen -> Signal"-Stil (gestrichelt
   im Ruhezustand, löst sich in eine durchgezogene Jade-Linie auf), dieselbe
   visuelle Sprache wie das Korn im Hero. Pips darüber sind reguläre Buttons,
   nicht nur Deko: Klick springt direkt zum jeweiligen Bereich (siehe JS). ---- */
.nn-hub .spine__rail {
  position:relative;width:min(640px,88%);margin:0 auto;height:3px;border-radius:3px;
  background:repeating-linear-gradient(90deg,var(--line) 0 2px,transparent 2px 6px);
}
.nn-hub .spine__rail-fill {
  position:absolute;inset:0;width:8.33%;border-radius:3px;background:var(--jade);
  transition:width .2s linear;
}
.nn-hub .spine__pips {
  position:absolute;inset:0;display:flex;justify-content:space-between;align-items:center;
  transform:translateY(-50%);top:50%;
}
.nn-hub .spine__pip {
  width:1.6rem;height:1.6rem;margin:0;padding:0;border:0;background:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
/* Größere Tippfläche auf Touch-Geräten (1.6rem = 25.6px liegt unter der
   empfohlenen 44px-Mindestgröße) — nur die Klickfläche wächst, der sichtbare
   Punkt (::before) bleibt in beiden Fällen 7px, damit die Rail optisch nicht
   überladen wirkt. */
@media (hover:none) {
  .nn-hub .spine__pip {width:2.75rem;height:2.75rem}
}
.nn-hub .spine__pip::before {
  content:'';width:7px;height:7px;border-radius:50%;background:var(--card);
  border:1.5px solid var(--ink-muted);transition:background .2s ease,border-color .2s ease,transform .2s ease;
}
.nn-hub .spine__pip:hover::before, .nn-hub .spine__pip:focus-visible::before {border-color:var(--jade-accent)}
.nn-hub .spine__pip[aria-current="true"]::before {background:var(--jade);border-color:var(--jade);transform:scale(1.3)}

/* ---- Reihe: Basis ist eine native, per Touch/Trackpad scrollende Reihe
   (funktioniert ohne jedes JS) — greift nur noch, wenn JS fehlt oder
   prefers-reduced-motion aktiv ist (siehe .spine.is-pinned weiter unten,
   das inzwischen auf allen Bildschirmbreiten aktiviert wird). Karten mit
   leichtem Nachbar-Einblick statt randlos voller Breite, damit auch in
   diesem Fallback klar ist, dass man weiterscrollen kann. ---- */
.nn-hub .spine__track {
  display:flex;gap:1.25rem;overflow-x:auto;scroll-snap-type:x mandatory;
  padding:.5rem clamp(1rem,8vw,20%) 1rem;margin:0 clamp(-1rem,-8vw,-20%);
  scrollbar-width:none;-ms-overflow-style:none;
}
.nn-hub .spine__track::-webkit-scrollbar {display:none}
.nn-hub .spine__item {
  flex:0 0 min(560px,82%);scroll-snap-align:center;
  border:1px solid var(--line);border-radius:24px;background:var(--card);
  box-shadow:var(--shadow);padding:clamp(1.5rem,3vw,2.5rem);
}
.nn-hub .spine__item-num {font-size:.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--jade-accent);font-variant-numeric:tabular-nums}
.nn-hub .spine__item-kicker {margin-top:.6rem;font-size:clamp(1rem,1.35vw,1.5rem);font-weight:700;color:var(--ink-muted)}
.nn-hub .spine__item-name {margin-top:.35rem;font-weight:900;font-size:clamp(1.4rem,2.6vw,2.2rem);letter-spacing:-.02em}
.nn-hub .spine__item-desc {margin-top:1rem;color:var(--ink-muted);text-wrap:pretty;line-height:1.65;font-size:clamp(1rem,1.35vw,1.5rem)}
.nn-hub .spine__item-link {margin-top:1.4rem}

/* ---- Ab hier NUR aktiv, wenn leistungen-hub.js die Sektion erfolgreich
   verstärkt hat (JS aktiviert, kein prefers-reduced-motion — bewusst OHNE
   Mindestbreite, siehe leistungen-hub.js: läuft auf jeder Bildschirmgröße,
   damit auch Mobile per vertikalem Scroll statt per Wisch-Geste durch die
   Bereiche kommt) — .is-pinned kommt ausschließlich per JS dazu, nie per
   CSS-Default. ---- */
/* svh statt vh: jetzt auch auf Mobile aktiv, wo die Adressleiste beim Scrollen
   ein-/ausblendet und normales vh dadurch instabil ist (dieselbe Lehre wie
   beim Hero, siehe hero.css min(100svh,80rem)). window.innerHeight in
   leistungen-hub.js folgt derselben dynamischen Höhe, beide bleiben also
   konsistent. */
.nn-hub .spine.is-pinned {height:420svh}
.nn-hub .spine.is-pinned .spine__pin {
  position:sticky;top:0;height:100svh;overflow:hidden;
}
/* .spine__pin klebt bei top:0, also am tatsächlichen Viewport-Rand — die fixierte
   Theme-Nav (.nn-nav) liegt aber DARÜBER (position:fixed, ca. 75-97px hoch) und schneidet
   dadurch den oberen Rand von "more impact." ab (live gemessen: Claim-Text beginnt bei
   nur ca. 20-33px Innenabstand, die Nav deckt aber bis zu 75px vom oberen Rand ab). Eigenes
   padding-top statt des generischen .spine__pin-Innenabstands weiter oben schafft die nötige
   Nav-Freifläche, unabhängig von Bildschirmhöhe — dieselbe --nav-h-Variable, die der Hero
   dafür schon nutzt (siehe .hero padding-top weiter oben in dieser Datei). */
.nn-hub .spine.is-pinned .spine__pin {
  padding-top:calc(var(--nav-h) + clamp(.5rem,2vh,1.5rem));
}
/* Claim + Rail behalten immer ihre volle, ungeschrumpfte Höhe (flex-shrink:0)
   — auf knapper Vertikalfläche (kurze Mobile-Landscape-Viewports, große
   Screens mit wenig Bildschirmhöhe wie ein 21:9-Monitor) wächst/schrumpft
   stattdessen ausschließlich .spine__track. Vorher konnten Claim + Rail +
   Karte zusammen die 100svh überschreiten; justify-content:center hat den
   Überschuss dann oben UND unten gleichmäßig weggeschnitten — getroffen hat
   das zuverlässig "more impact." (zweite, näher an der Mitte liegende Zeile
   des Claims). Jetzt frisst höchstens die Karte etwas Höhe ein, nie der
   Claim — deutlich unauffälliger, die Karte bleibt beim Weiterscrollen
   ohnehin nur einen Wimpernschlag im Bild. */
.nn-hub .spine.is-pinned .spine__claim,
.nn-hub .spine.is-pinned .spine__rail {
  flex:0 0 auto;
}
.nn-hub .spine.is-pinned .spine__track {
  flex:1 1 auto;min-height:0;
}
/* Padding = (Bühnenbreite - Kartenbreite) / 2 auf beiden Seiten, exakt wie bei
   einem klassischen "center"-Scroll-Snap-Karussell — nur dass hier JS statt
   des Browsers die Position setzt. Wichtig: leistungen-hub.js verschiebt jede
   Karte um genau die Distanz zur ERSTEN Karte, damit sie an deren Ausgangs-
   position landet (siehe measure()/offsets in leistungen-hub.js) — die erste
   Karte muss also von Anfang an in der Bühnenmitte sitzen, sonst würde JEDE
   aktive Karte an einer falschen (zu weit linken) Position statt zentriert
   landen. Ursprünglich stand hier ein schmales Padding für einen "Nachbar-
   Einblick"-Look, wodurch die jeweils fokussierte Karte sichtbar links statt
   mittig auftauchte. */
.nn-hub .spine.is-pinned .spine__track {
  overflow:visible;scroll-snap-type:none;margin:0;
  padding:0 calc(50% - min(680px,72vw) / 2);
  will-change:transform;
}
.nn-hub .spine.is-pinned .spine__item {flex:0 0 min(680px,72vw)}
/* Sicherheitsnetz NUR für Desktop/breite Screens mit knapper Fensterhöhe (z. B. ein
   schlicht nicht maximiertes Browserfenster) — .spine__track darf laut Regel oben
   schrumpfen (flex:1 1 auto;min-height:0), die Karte bekommt dadurch per
   align-items:stretch (Default) exakt dessen Höhe zugewiesen. Reicht der Inhalt nicht
   hinein, bekommt die Karte hier einen eigenen Scrollbereich statt den Button hart
   abzuschneiden. Auf Mobile ausdrücklich NICHT erwünscht (Nutzer-Wunsch: dort lieber
   kleinere Schrift als Scrollen) — die max-width:600px- und max-height:480px-Regeln
   weiter unten schalten es dort gezielt wieder ab, weil der Inhalt sich dort per
   verkleinerter Schrift/Abstände ohnehin ausgeht. */
.nn-hub .spine.is-pinned .spine__item {overflow-y:auto;-webkit-overflow-scrolling:touch}
@media (max-height:820px) {
  .nn-hub .spine__pin {gap:clamp(1rem,3vh,2rem);padding:clamp(1rem,3vh,2rem) 0}
  .nn-hub .spine.is-pinned .spine__item {padding:clamp(1rem,2vw,1.75rem)}
}
/* 1920×1080 (verbreitetste Desktop-Auflösung überhaupt) ist mit 1080px Höhe knapp
   unter dem 820px-Schwellenwert oben NICHT erfasst, obwohl Kicker und Fließtext ab
   ca. 1780px Fensterbreite bereits ihren clamp()-Deckel (1.5rem) erreicht haben —
   in Kombination mit der bei so breiten Screens vergleichsweise knappen Höhe reicht
   das bei den längsten Kartentexten (z. B. "SEO & GEO") nicht mehr aus (live
   gemessen: ca. 25px Überhang). Eigener, deutlich sanfterer Zwischenschritt: nur der
   Deckel des Fließtexts sinkt hier leicht (1.5rem -> 1.35rem), alles andere bleibt
   unverändert — betrifft ausschließlich Screens, die ohnehin schon am Deckel hängen,
   nicht die Mehrheit der (schmaleren oder höheren) Desktop-Größen. */
@media (max-height:1100px) {
  .nn-hub .spine__item-desc {font-size:clamp(1rem,1.35vw,1.35rem)}
}
/* [inert] entfernt nicht-aktive Karten aus Tab-Reihenfolge/Accessibility-Baum
   (leistungen-hub.js setzt/entfernt es beim Fortschritt) — rein funktional,
   OHNE eigenen visuellen Effekt. Die Opazität selbst kommt bewusst NICHT von
   hier (keine CSS-Transition an ein Boolean-Attribut gekoppelt), sondern wird
   jeden Frame direkt per style.opacity aus der stetigen Scroll-Position
   berechnet (siehe onScroll() in leistungen-hub.js) — sonst wirkt das Ein-/
   Ausblenden wie ein Schnappen neben der sonst durchgehend am Scroll
   hängenden Kartenposition. */

/* Der Scroll-Scrub-Effekt lief anfangs nur ab 900px, mit der nativen
   Touch-Scroll-Reihe als "Basis-Erlebnis" für Mobile. Auf ausdrücklichen
   Wunsch aufgegeben: Nutzer haben die horizontale Wisch-Geste nicht
   zuverlässig entdeckt, und die Rail-Füllung aktualisierte sich in diesem
   Modus nie (siehe onScroll() in leistungen-hub.js), weil dort niemand sie
   antrieb. Jetzt läuft überall dieselbe Mechanik — vertikales Scrollen
   steuert die Karten, kein Wischen nötig. Nur die Kartenbreite wird auf
   sehr schmalen Screens etwas großzügiger, damit der Fließtext nicht zu
   schmal umbricht. */
@media (max-width:600px) {
  .nn-hub .spine.is-pinned .spine__track {padding:0 calc(50% - min(680px,88vw) / 2)}
  .nn-hub .spine.is-pinned .spine__item {flex:0 0 min(680px,88vw)}
  /* Nutzer-Wunsch: statt eines Scrollbereichs in der Karte (siehe Kommentar bei
     .spine.is-pinned .spine__item weiter oben) wird hier stattdessen nur der
     Fließtext selbst verkleinert — fixer Wert statt des sonst clamp()-basierten
     Fließtexts, NUR innerhalb der Sechs-Bereiche-Karten, sonst nirgends auf der
     Seite (.spine__item-desc kommt ausschließlich in diesen Karten vor). */
  .nn-hub .spine__item-desc {font-size:.8rem}
  /* Schaltet das Desktop-Sicherheitsnetz (siehe Kommentar bei .spine.is-pinned
     .spine__item weiter oben) auf Mobile gezielt wieder ab — die kleinere Schrift
     hier lässt den Inhalt ohnehin passen, ein Scrollbereich ist hier unerwünscht. */
  .nn-hub .spine.is-pinned .spine__item {overflow-y:hidden}
}
/* Mobile-QUERFORMAT (z. B. iPhone SE/8/12-16 gedreht: 667-932px breit, aber nur
   ~375-430px hoch) fällt durch die max-width:600px-Regel oben durch (die Breite liegt
   über 600px), UND die generelle max-height:820px-Kürzung weiter oben reicht bei so
   wenig Höhe bei Weitem nicht — live gemessen bei 667×375: für die Karte blieben nur
   ~95px Höhe, der Karteninhalt brauchte aber über 400px. Eigener, deutlich härterer
   Breakpoint direkt an der Höhe statt der Breite: Claim + Kartenabstände + Fließtext
   werden hier zusätzlich zur max-height:820px-Regel nochmal spürbar kleiner, damit
   auch dieser Fall ohne Scrollen in der Karte auskommt (kein :has(), reine Zahlen). */
@media (max-height:480px) {
  .nn-hub .spine__pin {gap:.25rem;padding-bottom:.25rem}
  .nn-hub .spine.is-pinned .spine__pin {padding-top:var(--nav-h)}
  .nn-hub .spine__claim {font-size:clamp(1rem,3vw,1.3rem);line-height:1}
  .nn-hub .spine.is-pinned .spine__item {padding:.5rem .7rem}
  .nn-hub .spine__item-num {display:none}
  .nn-hub .spine__item-kicker {margin-top:0;font-size:.75rem}
  .nn-hub .spine__item-name {margin-top:.1rem;font-size:1rem}
  .nn-hub .spine__item-desc {margin-top:.3rem;font-size:.7rem;line-height:1.25}
  .nn-hub .spine__item-link {margin-top:.35rem}
  .nn-hub .spine__item-link .btn--sm {padding:.3rem .75rem;font-size:.75rem}
  .nn-hub .spine.is-pinned .spine__item {overflow-y:hidden}
}
/* Sehr schmale/alte Geräte (≤340px, z. B. iPhone SE 1. Generation) wickeln den
   Fließtext trotz der Verkleinerung oben in so vielen Zeilen um, dass selbst noch
   kleinere Schrift kaum mehr lesbar wäre (live gemessen bei 320×568: Karteninhalt
   bräuchte dort bis zu 125px mehr, als selbst die 480px-Regel oben hergibt). Für
   diesen seltenen Rand fall bleibt der Button lieber über einen kurzen Scroll in
   der Karte erreichbar, statt ihn komplett unerreichbar zu machen — auf allen
   gängigen Mobilgrößen (ab 375px Breite, hoch wie quer, siehe Tests) bleibt es
   wie gewünscht beim reinen Verkleinern ohne jedes Scrollen. */
@media (max-width:340px) {
  .nn-hub .spine.is-pinned .spine__item {overflow-y:auto;-webkit-overflow-scrolling:touch}
}

/* ==========================================================================
   WARUM ZUSAMMEN
   ========================================================================== */
.nn-hub .proof-band {
  display:grid;grid-template-columns:repeat(3,1fr);
  border:1px solid var(--line);border-radius:24px;
  background:var(--card);box-shadow:var(--shadow);
  margin:clamp(2rem,5vh,4.5rem) 0;overflow:hidden;
}
.nn-hub .proof {
  padding:clamp(1.5rem,3vw,3.4rem);
  display:flex;flex-direction:column;gap:.5rem;
  border-right:1px solid var(--line);
}
.nn-hub .proof:last-child {border-right:0}
.nn-hub .proof__num {font-size:clamp(2.25rem,5vw,4.75rem);font-weight:900;letter-spacing:-.04em;line-height:1;color:var(--ink)}
.nn-hub .proof__label {color:var(--ink-muted);font-size:clamp(1rem,1.35vw,1.5rem);text-wrap:pretty}
@media (max-width:760px) {
  .nn-hub .proof-band {grid-template-columns:1fr}
  .nn-hub .proof {border-right:0;border-bottom:1px solid var(--line)}
  .nn-hub .proof:last-child {border-bottom:0}
}

.nn-hub .pullquote {
  margin-top:clamp(2rem,5vh,4.5rem);
  padding-left:1.4rem;border-left:3px solid var(--jade);
  font-size:clamp(1.15rem,2.2vw,2.1rem);font-weight:700;letter-spacing:-.01em;
  color:var(--ink);max-width:56ch;text-wrap:pretty;
}
.nn-hub .pullquote--center {margin-inline:auto}

@media (prefers-reduced-motion:reduce) {

  .nn-hub, .nn-hub * {animation-duration:.001ms !important;transition-duration:.001ms !important}

}

/* Ersetzt inline style-Attribute aus dem Mockup. Als Klassen statt inline, damit sie eine
   Bearbeitung im Editor überstehen und hier an einer Stelle änderbar bleiben. */
.nn-hub .section-head--center { text-align: center; }
.nn-hub .warum__h2 { max-width: 26ch; margin-inline: auto; }
.nn-hub .warum__lede--center { margin-inline: auto; text-align: center; }
.nn-hub .warum__outro { margin: 2rem auto 0; text-align: center; }
/* Interne Verlinkung (SEO/GEO, Ads, CRO, Tracking) im Fließtext — gleiche Konvention
   wie .why__link auf Tracking & Analytics bzw. .warum__link auf Google Ads: eigene
   Klasse pro Section, überschreibt nur die Farbe mit dem Akzent-Grün, Rest
   (Unterstrich) bleibt der Browser-Default für <a>. */
.nn-hub .warum__link { color: var(--jade-accent); }

/* CTA-Band: siehe Begründung in assets/css/cro.css — der Kicker der wiederverwendeten
   .nn-cta-band braucht dieselbe Anhebung über den Canvas/Scrim. */
.nn-hub .nn-cta-band .kicker {
  position: relative; z-index: 2;
  margin-bottom: .75rem;
}
.nn-hub .nn-cta-band > p:not(.kicker):not(.wp-block-heading) {
  position: relative; z-index: 2;
  color: var(--hero-ink); opacity: .75;
  max-width: 52ch; margin: 0 auto 2.25rem;
}
