/* ============================================================================
   Editorial STILL  ·  Pull-Quote + Editorial-Liste
   ----------------------------------------------------------------------------
   Ersetzt die ehemalige Mission-Timeline ("Von Null auf Live in 72 Stunden")
   und die Mission-Manifest-Section ("Das ist erst der Anfang.").

   Tonalitaet: ruhig, editorial, premium durch Zurueckhaltung. Kein Mission-
   Control-Vokabular, kein Glow, keine Sci-Fi-Telemetry. Typografie traegt.

   Eingesetzt auf: voice-agent.html, index.html.
   ============================================================================ */


/* ---- 1. Editorial Pull-Quote ----------------------------------------------
   Vertikales Padding bewusst grosszuegig — der Quote braucht Atemraum.
   Hintergrund bleibt transparent, damit der Page-Background (schwarz) sich
   ungestoert durchzieht. Keine Halos, keine Lichtspuren. */

.editorial-quote-section {
  position: relative;
  padding: clamp(96px, 14vw, 180px) 24px;
  background: transparent;
  overflow: hidden;
}

/* Sticky-Scroll-Variant — wenn die Section [data-sticky] traegt, wird das
   Padding entfernt und ein 100vh-Container uebernimmt die Buehne. Der innere
   Container wird per GSAP ScrollTrigger.pin gepinned, die Stages cross-faden
   ueber den Scroll-Bereich. */
.editorial-quote-section[data-sticky] {
  padding: 0;
  overflow: visible;
}

.editorial-quote-inner {
  max-width: 880px;
  margin-inline: auto;
  text-align: center;
}

.editorial-quote-kicker {
  display: inline-block;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, 'Menlo', monospace;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.32);
  margin-bottom: clamp(28px, 3.6vw, 48px);
}

.editorial-quote-text {
  font-family: Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(22px, 3.4vw, 40px);
  line-height: 1.32;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.95);
  max-width: 760px;
  margin-inline: auto;
  /* Visuell offene Anfuehrungsstriche, Zeilenumbrueche durch <br> im HTML */
  text-wrap: balance;
}

.editorial-quote-text em {
  /* Sub-Italic-Akzent falls innerhalb eines kursiven Quotes ein Wort
     hervorgehoben werden soll (Doppel-Italic kippt zurueck auf upright). */
  font-style: normal;
  font-weight: 500;
}

.editorial-quote-attribution {
  margin-top: clamp(28px, 3.4vw, 44px);
  font-family: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, 'Menlo', monospace;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.38);
  position: relative;
  display: inline-flex;
  align-items: center;
}


/* ---- 2. Editorial-Liste (Naechste Missionen) -------------------------------
   Vertikale Liste mit Hairline-Trennern. Eintraege sind komplette Links —
   die ganze Zeile ist klickbar. Mono-Nummer links, Inhalt mittig, Pfeil
   rechts. Auf Hover wandert der Pfeil leicht nach rechts und bekommt mehr
   Sichtbarkeit, wie in einer guten Editorial-Inhaltsangabe. */

.editorial-list-section {
  position: relative;
  padding: clamp(80px, 10vw, 140px) 24px clamp(96px, 12vw, 168px);
  background: transparent;
  overflow: hidden;
}

.editorial-list-inner {
  max-width: 960px;
  margin-inline: auto;
}

.editorial-list-header {
  max-width: 720px;
  margin: 0 0 clamp(40px, 5vw, 64px);
}

.editorial-list-header .bracket-label {
  display: inline-block;
}

.editorial-list-header p {
  /* Tailwind-Utilities setzen Groesse, Farbe und Zeilenhoehe direkt im
     Markup — hier nur die maximale Breite, damit der Sub-Text nicht ueber
     die Liste hinaus fliesst. */
  max-width: 560px;
}

.editorial-list {
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}

.editorial-list-item {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  gap: 32px;
  align-items: baseline;
  padding: clamp(20px, 2.4vw, 28px) 4px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
  text-decoration: none;
  color: inherit;
  position: relative;
  transition: background 320ms ease;
}

.editorial-list-item:hover,
.editorial-list-item:focus-visible {
  background: rgba(255, 255, 255, 0.018);
  outline: none;
}

.editorial-list-item::after {
  /* Subtile Akzentkante links beim Hover — verraet dem Auge, dass die
     Zeile aktiv ist, ohne ein klotziges Background-Highlight zu bauen. */
  content: '';
  position: absolute;
  left: -2px;
  top: 14px;
  bottom: 14px;
  width: 2px;
  background: var(--accent-primary, #3355FF);
  opacity: 0;
  transform: scaleY(0.4);
  transform-origin: center;
  transition: opacity 280ms ease, transform 280ms ease;
}

.editorial-list-item:hover::after,
.editorial-list-item:focus-visible::after {
  opacity: 0.65;
  transform: scaleY(1);
}

.editorial-list-number {
  font-family: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, 'Menlo', monospace;
  font-size: 12px;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.4);
  align-self: start;
  padding-top: 6px;
  transition: color 280ms ease;
}

.editorial-list-item:hover .editorial-list-number,
.editorial-list-item:focus-visible .editorial-list-number {
  color: rgba(255, 255, 255, 0.7);
}

.editorial-list-body {
  min-width: 0;
}

.editorial-list-name {
  font-family: 'Montserrat', sans-serif;
  font-size: clamp(18px, 2vw, 22px);
  font-weight: 500;
  letter-spacing: -0.018em;
  color: rgba(255, 255, 255, 0.95);
  margin: 0 0 6px;
  transition: color 240ms ease;
}

.editorial-list-item:hover .editorial-list-name,
.editorial-list-item:focus-visible .editorial-list-name {
  color: #ffffff;
}

.editorial-list-lead {
  font-family: 'Montserrat', sans-serif;
  font-size: clamp(13px, 1.3vw, 14.5px);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.5);
  max-width: 520px;
  margin: 0;
}

.editorial-list-arrow {
  align-self: center;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, monospace;
  font-size: 18px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.4);
  transition: transform 280ms cubic-bezier(0.22, 1, 0.36, 1), color 240ms ease;
  user-select: none;
}

.editorial-list-item:hover .editorial-list-arrow,
.editorial-list-item:focus-visible .editorial-list-arrow {
  transform: translateX(8px);
  color: rgba(255, 255, 255, 0.92);
}


/* ---- 3. Scroll-Reveal -----------------------------------------------------
   Items nutzen `.animate-on-scroll` plus `.stagger-1/2/3` aus dem globalen
   Reveal-System (siehe initScrollReveals in index.html). Hier nur die
   Override, die sicherstellt, dass das hover-Background-Tween sauber neben
   dem opacity-Tween laeuft. */


/* ---- 4. Mobile -----------------------------------------------------------
   Auf engen Viewports geht die Liste auf zwei Spalten (Nummer kollabiert,
   Pfeil bleibt). Quote schrumpft, behaelt aber Atemraum. */

@media (max-width: 640px) {
  .editorial-quote-section {
    padding: clamp(72px, 18vw, 120px) 20px;
  }

  .editorial-quote-text {
    font-size: clamp(20px, 5.6vw, 28px);
    line-height: 1.36;
  }

  .editorial-list-section {
    padding: clamp(64px, 14vw, 100px) 20px clamp(80px, 16vw, 120px);
  }

  .editorial-list-item {
    grid-template-columns: 36px 1fr auto;
    gap: 16px;
    padding: 20px 4px;
  }

  .editorial-list-number {
    font-size: 11px;
    padding-top: 4px;
  }

  .editorial-list-name {
    font-size: 17px;
    margin-bottom: 4px;
  }

  .editorial-list-lead {
    font-size: 13px;
  }

  .editorial-list-arrow {
    font-size: 15px;
  }
}


/* ---- 5. Reduced motion ---------------------------------------------------
   Kein Stagger, kein Translate. Nur Opacity, sofort. */

@media (prefers-reduced-motion: reduce) {
  .editorial-list-item {
    opacity: 1 !important;
    transform: none !important;
    transition: background 200ms ease !important;
  }

  .editorial-list-item .editorial-list-arrow {
    transition: color 200ms ease !important;
  }

  .editorial-list-item:hover .editorial-list-arrow,
  .editorial-list-item:focus-visible .editorial-list-arrow {
    transform: none;
  }
}


/* ---- 6. Hover-Modal Variante (Cnippet-Pattern) ---------------------------
   Wird aktiv, wenn die Liste das Attribut [data-hover-modal] traegt. Die Item-
   Names werden editorial-bold, dazu kommt ein Cursor-folgendes Preview-Pane
   plus eine kreisrunde "Mehr"-Cursor-Markierung. Logik in
   assets/js/editorial-hover-modal.js. */

.editorial-list[data-hover-modal] .editorial-list-item {
  padding: clamp(28px, 3.6vw, 48px) 4px;
  align-items: center;
  grid-template-columns: 72px 1fr auto;
  gap: clamp(20px, 2.6vw, 36px);
}

.editorial-list[data-hover-modal] .editorial-list-number {
  font-size: 12px;
  padding-top: 0;
  align-self: center;
}

.editorial-list[data-hover-modal] .editorial-list-name {
  font-family: 'Montserrat', sans-serif;
  font-size: clamp(30px, 5vw, 60px);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.04;
  margin: 0 0 10px;
  text-transform: none;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1), color 240ms ease;
}

.editorial-list[data-hover-modal] .editorial-list-item:hover .editorial-list-name,
.editorial-list[data-hover-modal] .editorial-list-item:focus-visible .editorial-list-name {
  transform: translateX(10px);
  color: #ffffff;
}

.editorial-list[data-hover-modal] .editorial-list-lead {
  font-size: clamp(13px, 1.3vw, 15px);
  max-width: 520px;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.editorial-list[data-hover-modal] .editorial-list-item:hover .editorial-list-lead,
.editorial-list[data-hover-modal] .editorial-list-item:focus-visible .editorial-list-lead {
  transform: translateX(10px);
}

.editorial-list[data-hover-modal] .editorial-list-arrow {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.32);
}

/* Auf Hover wird die ganze Zeile leicht heller, statt das Background-Tint
   aus der Standard-Liste zu nutzen. Der Cursor-Modal traegt jetzt den
   visuellen Fokus, die Zeile selbst bleibt ruhig. */
.editorial-list[data-hover-modal] .editorial-list-item:hover,
.editorial-list[data-hover-modal] .editorial-list-item:focus-visible {
  background: transparent;
  opacity: 0.78;
}

.editorial-list[data-hover-modal] .editorial-list-item:hover::after,
.editorial-list[data-hover-modal] .editorial-list-item:focus-visible::after {
  /* Akzentkante deaktiviert in der Hover-Modal-Variante — der Modal selbst
     ist der Hover-Beweis. */
  opacity: 0;
}


/* ---- 7. Editorial Hover-Modal Pane + Cursor ------------------------------
   Folgen dem Cursor (siehe JS). Vor erstem Hover via scale(0) unsichtbar,
   bei .is-active scale(1). Position via inline left/top von GSAP gesetzt. */

.editorial-hover-modal,
.editorial-hover-cursor {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0);
  transition: transform 320ms cubic-bezier(0.76, 0, 0.24, 1),
              opacity 220ms ease;
  will-change: transform, top, left;
  opacity: 0;
}

.editorial-hover-modal.is-active,
.editorial-hover-cursor.is-active {
  transform: translate(-50%, -50%) scale(1);
  opacity: 1;
}

.editorial-hover-modal {
  z-index: 90;
  width: clamp(280px, 28vw, 400px);
  height: clamp(220px, 22vw, 320px);
  border-radius: 6px;
  overflow: hidden;
  background: #08080c;
  box-shadow:
    0 30px 80px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(255, 255, 255, 0.05),
    0 0 60px rgba(var(--accent-rgb, 51, 85, 255), 0.12);
}

.editorial-hover-modal-track {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transition: top 500ms cubic-bezier(0.76, 0, 0.24, 1);
}

.editorial-hover-modal-slide {
  display: flex;
  width: 100%;
  height: 100%;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.editorial-hover-modal-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Auralex-Treatment: Bilder etwas entsaettigt + leicht gedimmt, damit sie
     nicht aus dem Premium-Schwarz herausfallen. */
  filter: brightness(0.78) saturate(0.7) contrast(1.05);
}

.editorial-hover-cursor {
  z-index: 91;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  background: var(--accent-primary, #3355FF);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    0 0 30px rgba(var(--accent-rgb, 51, 85, 255), 0.55),
    0 0 60px rgba(var(--accent-rgb, 51, 85, 255), 0.25);
}

.editorial-hover-cursor span {
  font-family: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.96);
  font-weight: 500;
}

/* Touch / no-hover-Geraete: Modal + Cursor-Stack komplett ausblenden,
   sie haben ohne Cursor-Tracking keinen Sinn. */
@media (hover: none) {
  .editorial-hover-modal,
  .editorial-hover-cursor {
    display: none;
  }
}

/* Reduced Motion: Track schaltet hart, kein Slide-Tween. */
@media (prefers-reduced-motion: reduce) {
  .editorial-hover-modal-track {
    transition: none !important;
  }
  .editorial-hover-modal,
  .editorial-hover-cursor {
    transition: opacity 180ms ease !important;
  }
}


/* ---- 8. Sticky-Scroll-Narrative fuer den Pull-Quote ----------------------
   Aktiv wenn .editorial-quote-section data-sticky traegt. Die Section ist
   100vh hoch im natuerlichen Flow, GSAP pinned den .editorial-quote-stage
   Container fuer ~240% extra Scroll. Ein vertikales Hairline waechst, dann
   Kicker, Quote-Text und Attribution faden gestaffelt ein.

   Layer-Stack innerhalb .editorial-quote-stage (von hinten nach vorn):
     - .editorial-quote-ambient  : sehr weicher Brand-Glow im Zentrum, damit
       die schwarze Buehne nicht stumm wirkt.
     - .editorial-quote-hairline : 1px-Brand-Linie, vertikal, von oben nach
       unten gestreckt; JS animiert die scaleY-Eigenschaft.
     - .editorial-quote-kicker / -text / -attribution : Inhalt, opacity 0
       initial, von JS auf 1 getweent. */

.editorial-quote-section[data-sticky] {
  position: relative;
  /* Section ist 520vh hoch — 100vh Sticky-Buehne + 420vh Scroll-Distanz, damit
     der User merklich "ins Dunkle scrollt", bevor und nachdem die Quote
     erscheint. Die obere Glow-Linie waechst beim Eintritt, die Quote selbst
     liegt im Mittelband ohne Linie, und die untere Linie waechst nach der
     Attribution wieder raus. */
  height: 520vh;
  min-height: calc(640px + 420vh);
}

.editorial-quote-stage {
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  min-height: 640px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.editorial-quote-stage .editorial-quote-inner {
  position: relative;
  z-index: 2;
  max-width: 860px;
  width: 100%;
  padding: 0 24px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.editorial-quote-ambient {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(ellipse 56% 44% at 50% 50%,
      rgba(var(--accent-rgb, 51, 85, 255), 0.10) 0%,
      rgba(var(--accent-rgb, 51, 85, 255), 0.05) 35%,
      transparent 70%),
    radial-gradient(ellipse 26% 18% at 50% 50%,
      rgba(var(--accent-rgb, 51, 85, 255), 0.08) 0%,
      transparent 80%);
  filter: blur(8px);
  opacity: 0; /* JS faded ein, damit die Section nicht alleine im Schwarz liegt */
  will-change: opacity;
}

/* Glow-Linien-Pattern — uebernimmt die Optik der .ity-pain-thread (30%-Linie):
   3px breite Saeule, leuchtender Akzent-Gradient, dreifaches box-shadow plus
   filter drop-shadow als Halo. Aufgeteilt in zwei Segmente: -top oberhalb der
   Quote (waechst beim Eintritt), -bottom unterhalb der Attribution (waechst
   beim Austritt). Mittelband mit der Quote bleibt linienfrei. */

.editorial-quote-hairline-top,
.editorial-quote-hairline-bottom {
  position: absolute;
  left: 50%;
  width: 3px;
  height: 32vh;
  transform: translateX(-50%) scaleY(0);
  pointer-events: none;
  z-index: 1;
  background: rgba(255, 255, 255, 0.03);
  border-radius: 2px;
  overflow: hidden;
  will-change: transform;
  /* Halo um die Saeule (gleiche Werte wie .ity-pain-thread) */
  filter:
    drop-shadow(0 0 8px rgba(var(--accent-rgb, 51, 85, 255), 0.55))
    drop-shadow(0 0 22px rgba(var(--accent-rgb, 51, 85, 255), 0.32));
}

.editorial-quote-hairline-top {
  top: 0;
  transform-origin: top center;
}

.editorial-quote-hairline-bottom {
  bottom: 0;
  transform-origin: bottom center;
}

.editorial-quote-hairline-top::after,
.editorial-quote-hairline-bottom::after {
  /* Innerer Fill mit Akzent-Gradient + dreifachem Inner-Glow (gleiches
     Schema wie .ity-pain-thread-fill). */
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(140, 170, 255, 1) 0%,
    var(--accent-primary, #3355FF) 25%,
    var(--accent-primary, #3355FF) 100%
  );
  box-shadow:
    0 0 14px rgba(var(--accent-rgb, 51, 85, 255), 0.85),
    0 0 32px rgba(var(--accent-rgb, 51, 85, 255), 0.55),
    0 0 70px rgba(var(--accent-rgb, 51, 85, 255), 0.30);
}

.editorial-quote-hairline-bottom::after {
  /* Bottom-Linie laeuft optisch von oben weiss/blau in dunkleres Brand-Blau,
     damit sie wie eine Fortsetzung des Eintritts wirkt, nur in die Tiefe. */
  background: linear-gradient(
    180deg,
    var(--accent-primary, #3355FF) 0%,
    var(--accent-primary, #3355FF) 75%,
    rgba(140, 170, 255, 1) 100%
  );
}

/* Editorial-Rules (kleine horizontale Linien um Kicker/Attribution) wurden
   2026-05-08 entfernt — User wollte den Mittelblock ohne zusaetzliche
   graue Linien, nur Glow-Saeulen oben/unten. Die Span-Hooks bleiben im
   HTML als no-op fuer den Fall, dass wir spaeter wieder ein Trenner-Element
   einbauen wollen. */

.editorial-quote-section[data-sticky] .editorial-quote-kicker,
.editorial-quote-section[data-sticky] .editorial-quote-text,
.editorial-quote-section[data-sticky] .editorial-quote-attribution {
  opacity: 0;
  transform: translateY(14px);
  will-change: opacity, transform;
}

.editorial-quote-section[data-sticky] .editorial-quote-kicker {
  margin-bottom: clamp(28px, 3.6vw, 44px);
}

/* Static fallback (no GSAP, prefers-reduced-motion, mobile): zeigt alle
   Stages auf einmal und die Hairlines voll, ohne Choreografie. JS setzt
   diese Klasse, falls ein Bypass greift. */
.editorial-quote-section[data-sticky].is-static .editorial-quote-hairline-top,
.editorial-quote-section[data-sticky].is-static .editorial-quote-hairline-bottom {
  transform: translateX(-50%) scaleY(1);
}
.editorial-quote-section[data-sticky].is-static .editorial-quote-ambient {
  opacity: 1;
}
.editorial-quote-section[data-sticky].is-static .editorial-quote-kicker,
.editorial-quote-section[data-sticky].is-static .editorial-quote-text,
.editorial-quote-section[data-sticky].is-static .editorial-quote-attribution {
  opacity: 1;
  transform: none;
}

/* Mobile: kein Pin, kein Sticky-Choreografie. Sektion klappt auf normales
   Editorial-Layout zurueck. */
@media (max-width: 900px) {
  .editorial-quote-section[data-sticky] {
    height: auto;
    min-height: 0;
    padding: clamp(80px, 16vw, 140px) 24px;
  }

  .editorial-quote-stage {
    height: auto;
    min-height: 0;
  }

  /* Mobile Sticky-Reveal-Choreographie (User-Feedback 2026-05-13):
     Section wird auf 280vh hoch, .editorial-quote-stage pinnt via position:
     sticky am Viewport-Top. JS schreibt --qx-progress (0–1) auf die Section
     und mappt das in CSS auf die einzelnen Reveal-Phasen (hairline top fill,
     kicker, text, attribution, hairline bottom fill).

     Wichtig: damit sticky funktioniert, muss html/body auf overflow-x: clip
     stehen (nicht hidden) — sonst wird body zum Scroll-Container und sticky
     bricht. Siehe inline-Override in voice-agent.html. */
  .editorial-quote-section[data-sticky].qx-mobile-pin {
    min-height: 280vh;
    padding: 0 !important;
    position: relative;
    contain: layout paint;
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-stage {
    position: sticky;
    top: 0;
    height: 100vh;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(56px, 12vw, 96px) 24px;
    box-sizing: border-box;
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-stage > .editorial-quote-inner {
    width: 100%;
    max-width: 540px;
    margin: 0 auto;
    position: relative;
    text-align: center;
  }

  /* Hairlines: fill via CSS-var --qx-line-top / --qx-line-bottom (0–1). */
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-hairline-top,
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-hairline-bottom {
    position: absolute;
    left: 50%;
    width: 3px;
    height: 18vh;
    pointer-events: none;
    z-index: 1;
    background: rgba(255,255,255,0.03);
    border-radius: 2px;
    overflow: hidden;
    filter:
      drop-shadow(0 0 8px rgba(var(--accent-rgb, 51, 85, 255), 0.55))
      drop-shadow(0 0 22px rgba(var(--accent-rgb, 51, 85, 255), 0.32));
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-hairline-top {
    top: 0;
    transform-origin: top center;
    transform: translateX(-50%) scaleY(var(--qx-line-top, 0));
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-hairline-bottom {
    bottom: 0;
    transform-origin: bottom center;
    transform: translateX(-50%) scaleY(var(--qx-line-bot, 0));
  }

  /* Kicker / Text / Attribution Reveals via CSS-vars. */
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-kicker {
    opacity: var(--qx-kicker-op, 0);
    transform: translateY(calc((1 - var(--qx-kicker-op, 0)) * 14px));
    margin-bottom: clamp(20px, 4vw, 32px);
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-text {
    opacity: var(--qx-text-op, 0);
    transform: translateY(calc((1 - var(--qx-text-op, 0)) * 14px));
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-attribution {
    opacity: var(--qx-attr-op, 0);
    transform: translateY(calc((1 - var(--qx-attr-op, 0)) * 14px));
    margin-top: clamp(20px, 4vw, 32px);
  }
  .editorial-quote-section[data-sticky].qx-mobile-pin .editorial-quote-ambient {
    opacity: var(--qx-ambient-op, 0);
  }

  /* Fallback wenn JS nicht greift / kein .qx-mobile-pin: kompakter Static-Look
     mit beiden Linien sichtbar (was vorher auch da war). */
  .editorial-quote-section[data-sticky]:not(.qx-mobile-pin) .editorial-quote-hairline-top,
  .editorial-quote-section[data-sticky]:not(.qx-mobile-pin) .editorial-quote-hairline-bottom {
    height: 96px;
    transform: translateX(-50%) scaleY(1);
  }
  .editorial-quote-section[data-sticky]:not(.qx-mobile-pin) .editorial-quote-ambient {
    opacity: 0.7;
  }
  .editorial-quote-section[data-sticky]:not(.qx-mobile-pin) .editorial-quote-kicker,
  .editorial-quote-section[data-sticky]:not(.qx-mobile-pin) .editorial-quote-text,
  .editorial-quote-section[data-sticky]:not(.qx-mobile-pin) .editorial-quote-attribution {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .editorial-quote-section[data-sticky] .editorial-quote-hairline-top,
  .editorial-quote-section[data-sticky] .editorial-quote-hairline-bottom {
    transform: translateX(-50%) scaleY(1);
  }
  .editorial-quote-section[data-sticky] .editorial-quote-ambient {
    opacity: 1;
  }
  .editorial-quote-section[data-sticky] .editorial-quote-kicker,
  .editorial-quote-section[data-sticky] .editorial-quote-text,
  .editorial-quote-section[data-sticky] .editorial-quote-attribution {
    opacity: 1;
    transform: none;
  }
}
