/* upstreem landing-hero.css — die Hero-Sektion der Landingpage. Prefix ulh-.
   Braucht core.css UND die CSS der Komponenten, die im Fenster stehen: sidebar, der
   Dashboard-Seitenkopf, visibility-chart, topcitations-dashboard. Der Lader in
   framer/landing_hero.html holt alles aus demselben Pin.

   Der Punkt dieser Datei: sie baut KEINE Produktoberflaeche nach. Im Fenster stehen die echten
   Komponenten der App mit ihrer echten CSS -- diese Datei liefert nur die Buehne: Ueberschrift,
   Unterzeile, den Fensterrahmen und die Verkleinerung. Was drin steht, entscheidet landing-hero.js
   mit Demodaten.

   Warum das wichtig ist: ein nachgezeichnetes Dashboard veraltet ab dem Tag, an dem sich die App
   aendert. Das echte nicht. Und es ist scharf in jeder Groesse, weil es DOM ist und kein Bild. */

/* Die Kante als registrierte Laenge (Begruendung an --ulh-kante unten). Kein Selektor und keine
   Regel fuer die Seite darum: @property macht nur, dass eine Eigenschaft mit genau diesem Namen
   als Laenge gerechnet wird -- es gibt sie ausserhalb dieser Sektion nicht. Ein Browser ohne
   @property rechnet die Spur trotzdem richtig (var() setzt den Ausdruck dann als Text ein), und
   landing-hero.js faellt fuer die Einpassung auf 64 zurueck. */
@property --ulh-kante {
  syntax: "<length>";
  inherits: true;
  initial-value: 64px;
}

.ulh-root {
  /* Die Landingpage traegt ihre Farben SELBST und nicht als --vc-*-Marken. Zwei Gruende, und der
     zweite ist der wichtigere:
     1. Die Marken stehen in core.css ausschliesslich an .up-root. Die Landing-Wurzel diese Klasse
        zu geben war der naheliegende Weg -- und hat den Renderer zum Stehen gebracht: eine .up-root,
        die weitere .up-root enthaelt, gibt es in der App nirgends, und core hat pro Wurzel eigene
        Beobachter und einen Heartbeat. Gemessen: die Seite antwortete danach auf keine Abfrage mehr.
     2. Selbst wenn es ginge, waere es falsch. Die Marken drehen sich mit dem Thema der APP, und wer
        die App einmal im Dunkeln benutzt hat, haette hier ein dunkles Fenster auf weisser Seite.
        Diese Sektion ist immer hell.
     Die Werte sind die hellen Werte aus core (--vc-bg, --vc-heading-bg, --vc-text, --vc-muted,
     --vc-sk). Aendert sich dort die helle Palette, gehoert sie hier nachgezogen. */
  /* REINWEISS (06.10. angefordert: "Background in #ffffff"). Bis dahin #FFFFFFF2, Weiss bei 95%
     Deckkraft, damit der Grund der Framer-Seite um fuenf Prozent durchschien. */
  /* VIER core-Marken, hier lokal. Sie stehen in vierzehn Regeln dieser Datei -- und waren an
     den Stellen, die NICHT unter .ulh-app liegen, unaufloesbar: gemessen an .ulh-btn-sec,
     .ulh-pk, .ulh-orb-chip und .ulh-vis-tab, alle vier "NICHT AUFLOESBAR". Eine Deklaration mit
     unaufloesbarem var() ist als Ganzes ungueltig, die Regeln taten dort also nichts. Unter
     .ulh-app loesen sie auf, weil dort echte .up-root-Bauteile stehen -- deshalb ist es nie
     aufgefallen.
     Die Werte sind die HELLEN aus core, wie es der Absatz darueber fuer alle anderen auch
     vorschreibt. Bauteile unter .ulh-app bleiben unberuehrt: ihr eigenes .up-root steht naeher
     und gewinnt. (22.09.) */
  --vc-bg: #ffffff; --vc-heading-bg: #f9f8f9; --vc-hover-border: #6f6f6f; --vc-third: #9e9e9e;
  --ulh-grund: #ffffff;
  /* Die Blende. Derselbe Ton wie die Leiste (#f5f5f5) machte den Rahmen dort unsichtbar, wo die
     Leiste anliegt; 15% dunkler (#d0d0d0 = 208) war zu viel und sah nach einem grauen Balken aus.
     Der Weg: #f5f5f5 unsichtbar, #d0d0d0 ein grauer Balken, #e8e8e8 zu dunkel, #e9edf1 mit Blau
     noch zu dunkel, #f2f5f8 richtig hell aber zu blau, #ebecee wieder zu dunkel. Jetzt #f1f2f4:
     derselbe kleine Blauabstand (b minus r = 3), nur wieder 2.5% heller. */
  --ulh-blende: #f1f2f4;
  /* DIE Spur der Seite. Alles richtet sich daran aus: die zwei Schienen, der Rahmen des Fensters
     und der Inhalt jeder Sektion darunter. Eine Zahl, damit nichts auseinanderlaeuft.
     1440 und nicht 1300: das Fenster oben ist 1440 breit (so gewuenscht), und ein Inhalt, der aus
     seiner eigenen Spur herausragt, ist kein Gitter. Die 48px Mindestrand halten die Schienen auf
     schmalen Seiten von der Kante weg.
     Ausgenommen sind die drei Nebenfenster: die ragen ueber die Spur hinaus -- aber seit dem
     28.09. nie mehr ueber --ulh-kante (siehe dort und einpassen() in landing-hero.js). */
  /* --ulh-rand: das Hauptfenster steht je Seite so weit INNERHALB der alten Spur. Es steckt in der
     Spur selbst und nicht in einem transform am Fenster: an dieser Spur haengen auch die beiden
     Schienen des Seitengitters und der Inhalt jeder Sektion. Wuerde nur das Fenster kleiner, stuenden
     die Schienen 40px daneben -- genau das war der Fehler beim ersten Versuch. */
  --ulh-rand: 40px;
  /* DER DECKEL IST WIEDER 1440 (28.09. angefordert: "im initialen, grossen Zustand Max Width
     1440px ... wichtig max width, also schoen mitskalieren bei kleinerer page width"). Bis dahin
     zog der Rand auch vom Deckel ab (1440 - 80 = 1360). Jetzt gilt er nur noch unterhalb des
     Deckels: bis 1568px Seitenbreite steht das Fenster wie bisher 64px von jeder Kante, darueber
     waechst es bis 1440 und bleibt dort.
     Der Deckel steht an der SPUR und nicht allein am Fenster, aus demselben Grund wie der Rand:
     Schienen, Leiste und Sektionen richten sich an ihr aus, und ein Fenster, das 40px ueber seine
     Schienen hinaussteht, ist genau der Versatz, der am 28.08. schon einmal zurueckgenommen wurde.
     --ulh-deckel-klein ist der Deckel der verkleinerten Fassung beim Scrollen. landing-hero.js
     liest BEIDE Zahlen (kleinFaktor) und rechnet daraus den Faktor -- zwei Orte fuer dieselbe Zahl
     waeren hier der Anfang davon, dass sie auseinanderlaufen. */
  --ulh-deckel: 1440px;
  --ulh-deckel-klein: 1300px;
  /* DIE KANTE: so weit bleibt das Hero-Fenster von jedem Seitenrand weg, 24 + 40 = 64px. Bis zum
     28.09. stand die Rechnung nur in der Spur (100% - 48px - 2 * Rand). Seitdem braucht sie noch
     jemand: die drei Nebenfenster duerfen nicht mehr aus der Seite laufen (gemeldet: "bei kleineren
     Bildschirmen verlassen die Fenster einfach den Bildschirmrand ... links und rechts soll auf
     Desktop immer ca. 64px Abstand bleiben"), und einpassen() in landing-hero.js rechnet mit
     genau diesem Abstand. Also steht die Zahl einmal hier, und Spur und Einpassung lesen sie --
     zwei Orte fuer dieselbe Zahl waeren wieder der Anfang vom Auseinanderlaufen.
     Registriert ist sie (@property oben), damit getComputedStyle "64px" liefert und nicht den
     Ausdruck "calc(24px + 40px)", den JavaScript nicht lesen kann. */
  --ulh-kante: calc(24px + var(--ulh-rand));
  --ulh-spur: min(100% - 2 * var(--ulh-kante), var(--ulh-deckel));
  /* Die Linie der Schienen. Heller als der Rahmen der Karten (#e7e9ec): sie laeuft ueber die ganze
     Hoehe der Seite und wird dadurch als Flaeche wahrgenommen, nicht als Linie. */
  --ulh-schiene: #ededf0;
  /* Der Grund der Seite ist seit dem 06.10. reinweiss (vorher 95%, siehe --ulh-grund).
     Off-white ist der KASTEN um die Karten: dort, und nur dort, braucht es
     einen dunkleren Grund, damit die Karten in reinem Weiss als Karten lesbar sind. */
  --ulh-seite: var(--ulh-grund);
  --ulh-kasten: #F6F6F7;
  --ulh-schirm: #FFFFFFF2;
  --ulh-text: #1f1f1b;
  --ulh-muted: #6f737c;
  /* Der Ton der Unterzeile und der drei Treiber: #6f737c um 5 Prozent in Richtung Weiss geschoben
     (111,115,124 -> 118,122,130). Eigener Wert und nicht --ulh-muted geaendert: die Schrift im Chip
     bleibt, wie sie ist -- sie steht auf einer fast weissen Pille und wuerde heller verschwinden. */
  --ulh-sub: #767a82;
  /* Die dritte Farbe der App (--vc-third im hellen Thema). Sie ist heller als die zweite, und
     genau darum steht die Ueberschrift der Quellen darin, bevor sie durchlaeuft: der Weg zur
     ersten Farbe ist damit weiter und der Effekt deutlicher zu sehen. */
  --ulh-third: #9e9e9e;
  --ulh-border: #e0e2e6;
  /* Der Funke -- seit dem 06.10. als Hover der zwei Knoepfe im Hero, vorher um den Chip. */
  --ulh-funke: #98CEF2;
  /* Die Buehne hat eine FESTE Breite und wird als Ganzes verkleinert. Nur so sieht man die App in
     ihrem Desktop-Layout: die Komponenten messen ihre eigene Breite (clientWidth) und stapeln
     darunter um -- eine transform-Verkleinerung aendert clientWidth nicht, ein kleinerer Kasten
     schon.
     Die Zahl ist ein Kompromiss zwischen zwei Messungen. Nach UNTEN: die Komponenten stapeln
     unter 880px EIGENER Breite auf ihre schmale Fassung um (NARROW_STACK in visibility-chart.js
     und topcitations-dashboard.js), und bei 1280 kuerzte die Top-Brands-Tabelle schon die
     Markennamen ab ("North..."). Nach OBEN: je breiter die Buehne, desto stauber die Verkleinerung
     und desto kleiner die Schrift im Fenster -- 1440 ergab 0.69, 1360 ergibt 0.73.
     1360 laesst dem Inhalt neben der 250px-Leiste und dem Polster 1066px: Namen vollstaendig,
     nichts gestapelt, Schrift noch lesbar. */
  --ulh-buehne: 1360px;
  --ulh-mass: 1;
  /* Das Seitenverhaeltnis des Fensters. 0.605 ist gemessen an der Vorlage (1058x640): flacher als
     die Buehne, der Inhalt wird also unten abgeschnitten -- genau das laesst es aussehen wie ein
     Blick in eine laufende App und nicht wie ein abfotografierter Bildschirm. */
  --ulh-verhaeltnis: 0.605;
  --ulh-t: 200ms;
  --ulh-ease: cubic-bezier(.4, 0, .2, 1);
  /* Der Verlauf fuer das Kleinerwerden, und zwar ein STARK ausschleichender: die Bewegung soll
     sofort losgehen und lange auslaufen. .22/1/.36/1 war die erste Fassung und noch zu gleichmaessig;
     .16/1/.3/1 liegt naeher an easeOutExpo -- nach einem Drittel der Zeit sind gut 80 Prozent des
     Weges getan. --ulh-ease waere hier falsch, das ist ein ein- UND ausschleichender Verlauf. */
  --ulh-aus: cubic-bezier(.1, 1, .2, 1);
  /* Weich an beiden Enden (easeInOutCubic). --ulh-aus ist ein reines Ausklingen: es beginnt mit
     voller Geschwindigkeit. Fuer etwas, das aus dem Stand losfaehrt und wieder anhaelt -- die
     gehobene Zeile in den Karten -- braucht es eine Kurve, die auch VORNE flach ist. */
  --ulh-weich: cubic-bezier(.65, 0, .35, 1);
  /* Schnell an, lang aus. Fuer Stuecke, die von unten hereinkommen: sie sollen sofort da sein und
     dann ausrollen -- eine symmetrische Kurve wirkt dabei zaeh. */
  --ulh-gleit: cubic-bezier(.16, 1, .3, 1);
  /* DIE KURVE VON MOBBIN (06.10. angefordert: "Animationen wirklich butter smooth, nimm dir
     mobbin.com als Beispiel"). Dort gemessen: opacity und transform, 1s, cubic-bezier(.19,1,.22,1)
     -- easeOutExpo. Sie setzt mit voller Geschwindigkeit ein und rollt sehr lange aus; nach einem
     Drittel der Zeit sind rund 90 Prozent des Weges getan. Das alte --ulh-ease (.4,0,.2,1) faehrt
     dagegen LANGSAM an -- auf einem Bildschirm, in dem gerade eine Seite wechselt, liest sich
     dieser zoegernde Beginn als Ruckler. */
  --ulh-expo: cubic-bezier(.19, 1, .22, 1);
  /* DIE FENSTERHOEHE ALS EINE MARKE (13.10.). --ulh-fh und nicht --ulh-vh: der Name ist schon die
     Hoehe der Vorschauen in den Karten (.ulh-vis, weiter unten). Alle Abstaende, die mit der Hoehe des Fensters
     wachsen (clamp(56px, 10vh, 120px) und 22 weitere), rechnen mit --ulh-fh statt mit vh.
     Warum: im Embed-Rahmen von Framer war "das Fenster" der RAHMEN, und der ist so hoch wie die
     ganze Seite (gemessen 11761px). 10vh waren dort 1176px -- jede dieser Angaben stand also auf
     ihrer Obergrenze, und genau so sieht upstreem.ai seit Wochen aus. In der Framer-Seite selbst
     (UpstreemLanding.tsx) ist vh das echte Fenster, und die Seite waere sichtbar enger geworden:
     gemessen bei 1440x900 stand der Hero 35px hoeher, der Fuss 48px niedriger. Dort setzt die
     Regel unten die Marke deshalb so hoch, dass wieder jede Obergrenze greift -- dasselbe Bild
     wie bisher. Ausserhalb (Pruefstand, Embed) bleibt sie 1vh, also wie geschrieben. */
  --ulh-fh: 1vh;
  position: relative;
  /* width: 100% ist hier PFLICHT und nicht Kosmetik. Ein Block-div fuellt seinen Eltern nur, wenn
     der ein normaler Block-Kontext ist. Steht die Sektion in einem FLEX-Eltern -- und genau so
     setzt Framer seine Elemente --, ist sie ein Flex-Item mit width: auto und schrumpft auf die
     Breite ihres Inhalts. Gemessen in _h_landing_flex.html bei einem 1440px breiten Flex-Eltern:
     ohne diese Zeile 618px, mit ihr 1440px. Das war die Meldung "das Embed steht auf fill, die
     Komponente ist trotzdem nur etwa 800px breit". */
  width: 100%;
  background: var(--ulh-grund);
  font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}
/* core.css setzt border-box an .up-root und deren Kinder -- diese Wurzel ist aber KEINE .up-root
   (siehe der Absatz oben), also galt hier die Vorgabe content-box. Folge: der Rahmen mit
   width: 100% plus 6px Polster links und rechts wurde 12px BREITER als sein Platz und stand ueber.
   Gemessen: 1340px in einem 1325px breiten Kasten. */
.ulh-root, .ulh-root * { box-sizing: border-box; }

/* ---------- Textblock ---------- */
/* Textblock und Fenster liegen UEBER dem Hintergrund. Ohne z-index wuerde der Hintergrund sie
   verdecken: er ist positioniert, sie sind normale Bloecke, und positionierte Elemente malen
   spaeter. Ein negativer z-index am Hintergrund waere der andere Weg, braucht aber einen eigenen
   Stapelkontext an der Wurzel -- so ist es weniger. */
.ulh-text, .ulh-stage { position: relative; z-index: 1; }

.ulh-text {
  max-width: 780px;
  margin: 0 auto;
  /* Unten das Doppelte: der Abstand von den Knoepfen zum Fensteranfang war zu knapp.
     06.10.: beides noch einmal groesser ("mehr Luft im Textblock und zur Hero hin"). */
  /* MEHR LUFT (14.10. abends: "wirkt noch etwas zu cramped"). Gemessen bei 1280x860 auf der
     eigenen Website: die Leiste liegt UEBER dem Textblock (63px), der Chip stand damit nur 57px
     unter ihr; Knoepfe zu Modellzeile 30, Modellzeile zum Fenster 120. Jetzt oben 14 statt 10
     Fensterhoehenprozent (Chip rund 90 unter der Leiste), unten zum Fenster 12 statt 10, und die
     Abstaende im Block je eine Stufe weiter (.ulh-sub, .ulh-cta, .ulh-modelle). */
  padding: clamp(88px, calc(14 * var(--ulh-fh)), 160px) 24px clamp(80px, calc(12 * var(--ulh-fh)), 136px);
  text-align: center;
}
/* Zeilenhoehe UNTER 1 und negative Laufweite: das ist der halbe Effekt. Bei 0.95 und -0.02em liest
   sich die Ueberschrift als Block und nicht als zwei Zeilen -- beides an der Vorlage gemessen
   (64px, Gewicht 600, Zeilenhoehe 60.8px = 0.95, Laufweite -1.28px = -0.02em).
   clamp statt fester 64px, weil die Sektion in Framer auch auf dem Telefon steht. */
.ulh-h1 {
  margin: 0;
  /* 06.10.: etwas groesser (66 -> 74 oben). Der Mindestwert bleibt 36 -- "AI Search Analytics"
     muss auf dem Telefon in eine Zeile passen. */
  font-size: clamp(36px, 6.2vw, 74px);
  font-weight: 600;
  line-height: .95;
  letter-spacing: -0.02em;
  color: var(--ulh-text);
}
.ulh-h1 span { display: block; }
.ulh-sub {
  /* 620 statt 560 (06.10.): mit 19px Schrift brauchte der Satz rund 590px, und "drive" fiel in
     eine eigene Zeile. */
  max-width: 620px;
  margin: clamp(24px, calc(3.4 * var(--ulh-fh)), 32px) auto 0;
  /* 06.10.: ganz leicht groesser (18 -> 19 oben) und 100 leichter (500 -> 400). */
  font-size: clamp(16px, 1.32vw, 19px);
  font-weight: 400;
  line-height: 1.45;
  color: var(--ulh-sub);
}
/* ---------- Der Chip ueber der Ueberschrift ----------
   Das ist Beispiel 2 deiner Vorlage, Zeile fuer Zeile, nur in unseren Farben und auf 32px Hoehe:

     .example-2      position: relative; overflow: hidden; display: flex; align-items: center;
                     justify-content: center
     .example-2 .inner  position: relative; z-index: 1; width: 100%; margin: 2px
     .example-2::before content:""; display:block; height:300px; width:100px;
                     background: linear-gradient(90deg, transparent 0%, GRAU 0.75 50%, transparent 100%);
                     position:absolute; top:50%; transform-origin: top center;
                     animation: rotate 5s linear forwards infinite

   Was ich in den zwei Versuchen davor falsch hatte, und beides zusammen ist der Grund, warum es
   nicht wie ein Rand aussah:
     1. Der AEUSSERE Kasten hatte keine Fuellung. Damit zeigte der Ring die weisse Seite, und zu
        sehen war kein leuchtender Rand, sondern ein blauer Fleck, der ueber Weiss wandert. In der
        Vorlage hat die Karte einen Grund -- der Ring ist eine sichtbare Linie, und das Licht zieht
        DURCH sie hindurch. Jetzt liegt die Randfarbe im aeusseren Kasten.
     2. Kein left. In der Vorlage ist der Behaelter ein zentrierendes Flexfeld, und die statische
        Lage eines absolut gesetzten Kindes ist darin die MITTE -- genau darum dreht der Streifen um
        den Mittelpunkt. Ich hatte das mit left: 50% und einem negativen Rand nachgebaut; das ist
        dasselbe, aber es verdeckt, woher es kommt. Jetzt steht es wie dort.
   300px Streifenlaenge reichen: die halbe Diagonale des Chips sind etwa 126px. */
.ulh-chip {
  position: relative;
  overflow: hidden;
  /* inline-flex und NICHT flex, obwohl in der Vorlage flex steht. Dort ist die Karte ein Kasten mit
     eigener Breite; hier steht der Chip in einem Absatz, und ein blockweites Flexfeld nimmt dessen
     ganze Breite. Gemessen war der Chip damit 732px breit -- ein riesiger grauer Balken, in dessen
     Mitte die Textpille schwamm und um den das Licht in Zeitlupe zog. DAS war der Grund, warum es
     immer noch falsch aussah, und nicht die Machart des Streifens. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  border-radius: 999px;
  /* Der Ring. Ohne diese Fuellung gibt es keinen Rand, durch den etwas ziehen koennte. */
  background: var(--ulh-chip-ring, var(--ulh-border));
}
/* DER FUNKE IST VOM CHIP AUF DIE KNOEPFE GEWANDERT (06.10. angefordert: "entferne den Spark, der
   da drum faehrt" und "fuege diesen Spark als Hover-Effekt auf Talk to Sales und Book a Demo
   hinzu"). Der Chip behaelt seinen Ring; der Streifen steht jetzt unten bei .ulh-cta. */
@keyframes ulhSchimmer { to { transform: rotate(360deg); } }
/* ALS BADGE (30.09. angefordert: "Icon vorne, dann der Text, Hintergrund komplett weiss, gerne
   ein bisschen invertiert, modern mit Schatten -- Abmessung gleich lassen").
   ZWEITE FASSUNG (30.09. spaet): das Zeichen ist Zap und steht OHNE den dunklen Kreis, der
   aeussere Schatten ist subtiler. Ein Schatten nach innen war kurz drin und ist auf Wunsch
   wieder weg ("doch kein invert shadow").
   Die Flaeche reinweiss statt --ulh-grund (95% Weiss), der Ring mit dem wandernden Licht bleibt
   aussen.
   ABMESSUNG: 32 hoch wie vorher, und die Breite bleibt dieselbe (bei 1024px gemessen: vorher
   224.3, jetzt 224 +/- 1). Das geht nur, weil der Satz normal geschrieben steht und nicht in
   gesperrten Versalien -- die brauchten fuer dieselben Worte rund 40px mehr. 13/500 in der
   Textfarbe, das Zeichen in derselben. */
.ulh-chip-in {
  position: relative;
  z-index: 1;
  margin: 1px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: calc(100% - 2px);
  padding: 0 19px 0 15px;
  border-radius: 999px;
  background: #ffffff;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .01em;
  color: var(--ulh-text);
  white-space: nowrap;
}
.ulh-chip-ic { display: inline-flex; flex: 0 0 auto; color: var(--ulh-text); }
.ulh-chip-ic svg { width: 14px; height: 14px; display: block; }
/* DER SCHATTEN: EINE LAGE, 1px TIEF (01.10., dritte Fassung -- "sieht richtig schlecht aus, mach
   ihn schoener, noch kleiner"). Nachgesehen, wie es gemacht wird: Vercel und Resend fuehren ihre
   kleinen Pillen mit einem Haarrand und hoechstens einem Schatten, der 1px tief faellt und 2-3px
   weich ist (Resend: Tailwinds shadow-sm, 0 1px 3px bei 10%). Nie mit einer weiten Lage -- genau
   die war hier das Problem: 12px Unschaerfe auf dem fast weissen Grund lagen als grauer Hof um den
   Chip, er sah verschmiert aus statt angehoben.
   Also die kleinste Stufe dieser Skala (Tailwinds shadow-xs): 1px tief, 2px weich, 5%. Die Kante
   macht der Ring, der Schatten sagt nur noch "liegt oben drauf". Am aeusseren Kasten -- overflow:
   hidden schneidet nur den Inhalt, nicht den eigenen Schatten. */
.ulh-chip { box-shadow: 0 1px 2px rgba(11, 13, 24, .05); }
/* Wer keine Bewegung will, bekommt keine. Mit .ulh-root davor: die Regel des Funkens weiter unten
   hat dieselbe Spezifitaet und steht spaeter -- ohne den Vorsatz drehte der Funke trotz der
   Einstellung (14.10., Audit). */
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-text .ulh-cta .ulh-btn::before { animation: none; }
}
.ulh-eyebrow { margin: 0 0 clamp(24px, calc(3.2 * var(--ulh-fh)), 34px); }

/* ---------- Die Unterzeile endet auf einem wechselnden Wort (14.10.) ----------
   Vorher standen hier drei lose Woerter in einer eigenen Zeile ("Qualified Traffic Leads
   Revenue"). Jetzt ein Satz; sein letztes Wort wechselt. Alle Woerter liegen im SELBEN Rasterfeld
   -- das Feld ist so breit wie das laengste, der Satz davor springt nie. Ihr Text ist CSS-Inhalt
   (attr): fuer Crawler und Vorleser steht der ganze Satz in .ulh-nurlesen. Das Wort in der
   Textfarbe und 500: es ist die Pointe des Satzes, nicht ein weiteres Wort darin.
   Bewegung nur ueber opacity und transform (easeOutExpo wie ueberall auf der Seite). */
/* Ausgeglichen umbrechen: sonst steht das wechselnde Wort allein in der zweiten Zeile (gemessen bei
   1440: "qualified traffic." als einziges Wort darin). So bricht der Satz nach "your brand". */
/* Zwei feste Zeilen (14.10. abends): jede ein eigener Block, mittig. Bricht eine auf sehr schmalen
   Schirmen doch um, dann innerhalb ihrer selbst -- die Teilung bleibt. */
.ulh-sub-z { display: block; }
.ulh-wechsel {
  display: inline-grid; vertical-align: bottom;
  /* FESTE Breite: die Mitte zwischen kuerzestem ("leads.") und laengstem Wort ("qualified
     traffic."), damit die zweite Zeile bei jedem Wort an derselben Stelle steht. In em und nicht vom
     Skript gemessen: so sprang die Zeile eine Sekunde nach dem Laden (14.10. gemeldet). In Geist 500
     sind die Woerter 2.78em, 3.96em und 7.28em breit (gemessen bei 16 und 16.9px Schrift, beide
     Male dieselben Anteile), die Mitte ist (2.78 + 7.28) / 2 = 5.03em. Wer die Woerter aendert
     (HERO.worte), misst neu.
     Eine Spur so breit wie das Feld -- ohne minmax(0, 1fr) wuechse sie auf das laengste Wort. */
  width: 5.03em;
  grid-template-columns: minmax(0, 1fr);
  /* Platz fuer Unterlaengen (g, q). */
  padding-bottom: .12em; margin-bottom: -.12em;
  /* Senkrecht schneiden (das Wort kommt von unten und geht nach oben), waagrecht NICHT: das
     laengste Wort ragt um die halbe Spanne ueber das Feld hinaus und muss ganz zu sehen sein.
     overflow kann das nicht (hidden in einer Richtung macht die andere zu auto), clip-path schon. */
  clip-path: inset(0 -100vw 0 0);
}
.ulh-wort {
  /* Links im Feld, direkt hinter "into" -- und justify-self: start statt dehnen, damit offsetWidth
     das Wort selbst misst. */
  grid-area: 1 / 1; justify-self: start; text-align: left; white-space: nowrap;
  font-weight: 500; color: var(--ulh-text);
  opacity: 0; transform: translateY(80%);
  transition: opacity 520ms var(--ulh-ease), transform 760ms var(--ulh-expo);
}
.ulh-wort::before { content: attr(data-wort); }
.ulh-wort.is-an  { opacity: 1; transform: none; }
.ulh-wort.is-weg { opacity: 0; transform: translateY(-80%); }
@media (prefers-reduced-motion: reduce) { .ulh-wort { transition: none; } }
/* Fuer das Auge unsichtbar, fuer alle anderen der Satz. */
.ulh-nurlesen {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---------- Die zweite Zeile der h1, zurueckgenommen (14.10.) ----------
   Erst die Sache, dann die Aufloesung im leisen Ton: ein Verlauf von --ulh-sub nach heller, als
   Textfuellung. Gleiches Gewicht und gleiche Groesse -- nur die Farbe tritt zurueck. */
.ulh-h1-leise {
  background: linear-gradient(90deg, #767a82 0%, #a9adb5 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* ---------- Der Chip als Ankuendigung (14.10.) ----------
   Derselbe Badge (32px, Ring, weisse Flaeche), jetzt ein Link: vorn das dunkle "New", hinten ein
   Pfeil, der beim Hover einen Schritt macht. Der Ring wird beim Hover dunkler -- dieselbe
   Rueckmeldung wie beim zweiten Knopf. */
.ulh-chip-link { text-decoration: none; cursor: pointer; transition: background 160ms ease; }
.ulh-chip-link:hover { --ulh-chip-ring: #c9ccd2; }
.ulh-chip-link .ulh-chip-in { gap: 8px; padding: 0 12px 0 4px; }
/* EIN NORMALER RAND (14.10. abends: "ganz normale Border"). Der Ring stand auf #f3f4f6, halb so
   kraeftig wie jeder andere Rahmen der Seite, und war auf dem hellen Grund kaum zu sehen -- der
   Chip wirkte wie eine weisse Flaeche ohne Kante. Jetzt dieselbe Rahmenfarbe wie Karten und
   Knoepfe. Am Chip selbst gesetzt: ein eigener Wert schlaegt den geerbten von .ulh-root. */
.ulh-chip-link { --ulh-chip-ring: var(--ulh-border); }
/* Und ein SCHAERFERER Schatten ("den Schatten bitte schaerfer"): zwei kurze Lagen statt einer
   weichen -- 1px ohne Unschaerfe-Hof fuer die Kante unten, 2px mit -1px Ausbreitung, damit er
   nicht seitlich ausfranst. Beide dunkler als die alten 5 Prozent, keine weiter als 3px. */
.ulh-chip.ulh-chip-link { box-shadow: 0 1px 1px rgba(11, 13, 24, .07), 0 2px 3px -1px rgba(11, 13, 24, .1); }
.ulh-chip-neu {
  display: inline-flex; align-items: center; height: 22px; padding: 0 8px;
  border-radius: 999px; background: var(--ulh-text); color: #ffffff;
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
}
.ulh-chip-pfeil { color: var(--ulh-sub); transition: transform 200ms var(--ulh-expo), color 160ms ease; }
.ulh-chip-pfeil svg { width: 13px; height: 13px; }
.ulh-chip-link:hover .ulh-chip-pfeil { transform: translateX(2px); color: var(--ulh-text); }
@media (prefers-reduced-motion: reduce) { .ulh-chip-pfeil { transition: none; } }

/* ---------- Worauf es laeuft (14.10.) ----------
   Eine leise Zeile unter den Knoepfen: die Antwortmaschinen mit ihren Zeichen. 13px in --ulh-sub,
   die Zeichen 16px -- ein Nachweis, kein zweiter Aufruf. */
.ulh-modelle {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 10px 18px; margin-top: clamp(32px, calc(4.8 * var(--ulh-fh)), 48px);
  font-size: 13px; color: var(--ulh-sub);
}
/* LEISER (14.10. abends: "subtiler, graue Logos, alles etwas dimmen"): Satz und Namen in der
   hellen Farben statt in zwei dunkleren, die Namen ohne 500, die Zeichen ohne Farbe und halb
   durchsichtig. Ein Nachweis am Rand -- er soll gefunden werden, nicht auffallen. */
/* Die Namen in --ulh-third wie die Zeichen daneben: sie sind hier Teil des Logos, nicht Text, der
   gelesen werden muss. Der Satz davor steht nur noch fuer Vorleser im Markup (.ulh-nurlesen). */
.ulh-modell { display: inline-flex; align-items: center; gap: 7px; font-weight: 400; color: var(--ulh-third); }
.ulh-modell img { width: 16px; height: 16px; border-radius: 4px; display: block;
  /* brightness hebt dunkle Kacheln (Perplexity ist ein dunkles Quadrat) an die hellen Zeichen der
     anderen heran -- ohne sie stand eines als dunkelgrauer Block in der Reihe. */
  filter: grayscale(1) brightness(1.2); opacity: .55; }
/* Schmal nur die Zeichen in einer Reihe: mit Namen brach die Zeile bei 375px in drei, und
   "Copilot" stand allein in der letzten. Die Namen bleiben fuer Vorleser und Crawler im Markup. */
@media (max-width: 560px) {
  .ulh-modelle { gap: 10px 12px; }
  .ulh-modell-n {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .ulh-modell img { width: 18px; height: 18px; }
}

/* ---------- Die zwei Knoepfe ----------
   Masse an attio.com abgelesen: 36px hoch, Radius 10, 14px/500, Polster 0 12px, und zwischen den
   beiden 10px (dort gemessen: der eine endet bei 714, der andere beginnt bei 724). */
.ulh-cta { display: flex; justify-content: center; gap: 12px; margin-top: clamp(36px, calc(5.2 * var(--ulh-fh)), 52px); }
.ulh-btn {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 12px;
  border-radius: 10px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  /* Der Knopf der Quellen-Sektion ist ein <a>, und der Browser unterstreicht ihn dann. Im Hero
     sind es <button>, dort fiel es nie auf. */
  text-decoration: none;
  transition: background 160ms var(--ulh-ease), border-color 160ms var(--ulh-ease);
}
.ulh-btn-sec { border: 1px solid var(--ulh-border); background: var(--ulh-grund); color: var(--ulh-text); }
/* Der Hover ist der aus der App (.up-btn-sec in core.css): kraeftigerer Rahmen und der Grund der
   Kopfzeilen. Die Werte stehen als Rueckfall dabei, weil --vc-* an .up-root haengt und dieser
   Knopf ausserhalb steht -- ohne Rueckfall waere die Regel hier ungueltig. */
.ulh-btn-sec:hover {
  background: var(--vc-heading-bg, #f5f5f5);
  border-color: var(--vc-hover-border, #6f6f6f);
}
.ulh-btn-pri { border: 1px solid var(--ulh-text); background: var(--ulh-text); color: #ffffff; }
.ulh-btn-pri:hover { background: #33332e; border-color: #33332e; }
/* DER FUNKE ALS HOVER der zwei Knoepfe im Hero (06.10. angefordert, vorher lief er um den Chip).
   Dieselbe Machart wie dort: der Knopf selbst ist der RING (seine Fuellung), innen liegt 1px
   eingerueckt die eigentliche Flaeche (::after), und zwischen beiden dreht der Lichtstreifen
   (::before) -- er ist nur auf dem 1px Ring zu sehen. isolation, damit die zwei Ebenen mit
   negativem z-index ueber dem Grund des Knopfs und unter seiner Schrift liegen.
   Streifen und Farben sind die des Chips (26px breit, --ulh-funke mit #4FA9E4 im Kern). Nur die
   Dauer ist kuerzer, 2.4s statt 5s: ein Knopf wird ein, zwei Sekunden lang ueberfahren, und in
   dieser Zeit soll das Licht einmal herumkommen. Ausserhalb des Hovers steht der Streifen still
   und unsichtbar; beim naechsten Hover laeuft er dort weiter, wo er stand.
   Der Rahmen war 1px border -- jetzt ist er der Ring, also border 0 und 1px mehr Polster, damit
   Breite und Schrift genau dort bleiben, wo sie waren. */
.ulh-text .ulh-cta .ulh-btn { position: relative; overflow: hidden; isolation: isolate; border: 0; padding: 0 13px; }
.ulh-text .ulh-cta .ulh-btn-sec, .ulh-text .ulh-cta .ulh-btn-sec:hover { background: var(--ulh-border); }
.ulh-text .ulh-cta .ulh-btn-pri, .ulh-text .ulh-cta .ulh-btn-pri:hover { background: var(--ulh-text); }
.ulh-text .ulh-cta .ulh-btn::after {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  border-radius: 9px;
  background: var(--ulh-grund);
  transition: background 160ms var(--ulh-ease);
}
.ulh-text .ulh-cta .ulh-btn-pri::after { background: var(--ulh-text); }
.ulh-text .ulh-cta .ulh-btn-sec:hover::after { background: var(--vc-heading-bg, #f5f5f5); }
.ulh-text .ulh-cta .ulh-btn-pri:hover::after { background: #33332e; }
.ulh-text .ulh-cta .ulh-btn::before {
  content: "";
  position: absolute;
  z-index: -2;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 300px;
  margin-left: -13px;
  transform-origin: top center;
  background: linear-gradient(90deg, rgba(152, 206, 242, 0) 0%,
              var(--ulh-funke) 32%, #4FA9E4 50%, var(--ulh-funke) 68%,
              rgba(152, 206, 242, 0) 100%);
  opacity: 0;
  transition: opacity 200ms ease;
  animation: ulhSchimmer 2.4s linear infinite;
  animation-play-state: paused;
}
.ulh-text .ulh-cta .ulh-btn:hover::before { opacity: 1; animation-play-state: running; }
/* Auf dem dunklen Knopf ist der Funke WEISS (06.10. abends angefordert): das Blau des Chips las sich
   auf Fast-Schwarz als Fremdfarbe. Derselbe Verlauf, nur die Farbe getauscht. */
.ulh-text .ulh-cta .ulh-btn-pri::before {
  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%,
              rgba(255, 255, 255, .7) 32%, #ffffff 50%, rgba(255, 255, 255, .7) 68%,
              rgba(255, 255, 255, 0) 100%);
}
/* Der Pfeil hinter "Book a Demo": 14px wie die Zeichen der Knoepfe der App, 6px Abstand. Die Spanne
   haelt ihre Breite auch leer -- das Zeichen kommt erst, wenn core da ist. Beim Ueberfahren ruckt
   er 2px nach rechts, auf der Kurve der Seite. */
.ulh-btn-pfeil {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 6px; flex: 0 0 14px;
  transition: translate 260ms var(--ulh-expo);
}
.ulh-btn-pfeil svg { width: 14px; height: 14px; display: block; }
.ulh-btn:hover .ulh-btn-pfeil { translate: 2px 0; }
@media (prefers-reduced-motion: reduce) { .ulh-btn-pfeil { transition: none; } }

/* ---------- Der Hintergrund ----------
   Zwei Ebenen, beide hinter allem:
     hinten   ein Verlauf in drei Blautoenen, nach oben heller und oben ganz durchsichtig
     davor    senkrechte weisse Linien, von unten her eingeblendet
   Bis zum 30.09. lag hinten ein Bild (ein blaues SVG auf Supabase, 12527x8769, per cover auf
   die Flaeche gezogen, Deckkraft .44). Ersetzt auf Wunsch durch reine Farbe -- dieselbe
   Wirkung, drei vorgegebene Toene, und keine Datei, die erst geladen werden muss. */
.ulh-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.ulh-bg-foto, .ulh-bg-grid {
  position: absolute;
  inset: 0;
}
/* DIE FARBE. Die drei Toene sind vorgegeben (30.09.): #8fb4e6 unten, der kraeftigste,
   #b9d1f1 in der Mitte, #eef4fc oben, fast weiss. Sie stehen hier so, wie man sie sieht --
   ohne Deckkraft und ohne Maske darueber. Das Bild brauchte beides, weil es selbst keinen
   Verlauf hatte; ein Verlauf kann seine Blende selbst tragen, und dann ist der Ton, der dasteht,
   der Ton, der verlangt war.
   Die Blende ist dieselbe wie vorher: bis 16% der Hoehe nichts -- dort steht die Ueberschrift --,
   bei 34% der hellste Ton (auf Hoehe der Knoepfe, kurz vor dem Fenster), die Mitte bei 60%, also
   etwa auf halber Hoehe des Fensters, und unten der kraeftigste. Der Anfang ist #eef4fc mit
   Deckkraft 0 und nicht "transparent": so blendet derselbe Ton ein, statt aus einem
   durchsichtigen Schwarz heraus grau zu werden. */
/* UNTEN MINIMAL SCHWAECHER (30.09. angefordert): #8fb4e6 -> #9cbde9, also 12 Prozent Weiss
   hineingemischt. Die Mitte bleibt #b9d1f1 -- verlangt war nur das untere Ende. */
/* GRAU STATT BLAU (06.10. angefordert: "ein schoenes, professionelles Grau, gern mit ganz
   leichtem Blau-Touch"). Dieselbe Blende und dieselben drei Marken wie das Blau davor, nur die
   Toene: ein kuehles Grau, dessen Blauanteil mit der Tiefe waechst (b minus r: 3, 8, 12) -- oben
   kaum von Weiss zu unterscheiden, unten erkennbar kuehl, nirgends blau. Die weissen Lichter
   (::after) und die senkrechten Linien bleiben, wie sie sind. */
/* WENIGER BLAU, EINEN HAUCH HELLER (07.10. angefordert, fuer Landingpage UND Anmeldeseite): der
   Blauanteil halbiert (b minus r: 3, 8, 12 -> 2, 4, 6), jede Stufe 2 bis 4 Helligkeitsstufen
   heller (#f4f5f7/#e3e6eb/#d4d9e0 -> #f6f7f8/#e8eaec/#dbdde1). auth-page.css (.uau-panel) traegt
   dieselben Werte -- beide zusammen aendern. */
.ulh-bg-foto {
  background: linear-gradient(to bottom,
              rgba(246, 247, 248, 0) 16%, #f6f7f8 34%, #e8eaec 60%, #dbdde1 100%);
}
/* MITTIG ETWAS HELLER (30.09. angefordert: "mittig ganz leicht weniger kraeftig als links und
   rechts"): ein Schleier aus Weiss darueber, der zu den Seiten auf 0 geht. Die quer laufende
   Glocke darin hat neun Stufen statt drei: mit nur Mitte und Raendern waere es ein Dach mit
   einem Grat, und den Grat sieht man als senkrechten Strich (Machsche Streifen).
   EIGENE EBENE MIT EIGENER BLENDE, nicht als zweite Lage im Hintergrund oben: dort lag der
   Schleier auch ueber dem durchsichtigen oberen Sechstel, und halbdurchsichtiges Weiss ueber
   Durchsichtigem kam gemessen als feine graue Laengsstreifen unter der Leiste heraus (zu sehen
   nur mit beiden Lagen zusammen, mit jeder allein nicht). Die Blende hier setzt den Schleier
   erst ein, wo das Blau schon deckt (34%), und ist ab 60% voll -- oben waere er ohnehin
   unsichtbar, Weiss auf fast Weiss. */
/* UNTEN IN DER MITTE HELLER, NICHT GANZ SYMMETRISCH (30.09. angefordert: "mittig unten minimal
   heller, nach links und rechts ein Fade von hellerem Blau zu dunklerem, ganz leicht
   randomisiert"). Drei Lagen statt der einen Glocke:
     - ein breites Oval, unten angesetzt, sein Kern leicht LINKS der Mitte (44%)
     - ein kleineres, schwaecheres rechts daneben (61%, etwas hoeher) -- zusammen ein Licht, das
       nicht auf einer Achse steht, sondern so aussieht, als fiele es zufaellig
     - darunter die flache Glocke von vorher, auf gut die Haelfte gesenkt, damit auch weiter oben
       zu den Seiten hin noch etwas Verlauf bleibt.
   Unregelmaessig ist es fest und nicht bei jedem Laden neu: ein Hintergrund, der sich von Besuch
   zu Besuch verschiebt, sieht im zweiten Blick nach Fehler aus. Die Ovale haben je vier Stufen,
   damit ihr Kern keinen Punkt bildet. */
.ulh-bg-foto::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(58% 72% at 44% 100%,
      rgba(255, 255, 255, .34) 0%, rgba(255, 255, 255, .24) 28%,
      rgba(255, 255, 255, .1) 62%, rgba(255, 255, 255, 0) 100%),
    radial-gradient(34% 52% at 61% 97%,
      rgba(255, 255, 255, .16) 0%, rgba(255, 255, 255, .09) 40%,
      rgba(255, 255, 255, .03) 72%, rgba(255, 255, 255, 0) 100%),
    linear-gradient(to right,
      rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, .02) 18%, rgba(255, 255, 255, .06) 32%,
      rgba(255, 255, 255, .09) 44%, rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, .09) 56%,
      rgba(255, 255, 255, .06) 68%, rgba(255, 255, 255, .02) 82%, rgba(255, 255, 255, 0) 100%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 34%, #000 60%);
  mask-image: linear-gradient(to bottom, transparent 34%, #000 60%);
}
/* Das Raster darueber: einfache SENKRECHTE Linien, 1px, alle 8px, weiss. Keine waagrechten.
   Vorher stand hier die Grafik aus dem Onboarding -- die bringt Bogen, Kreuzmarken und Lichter mit
   und war fuer diese Flaeche zu viel Bild. Ein Verlauf, der sich wiederholt, kostet ausserdem keine
   Datei, keine Maske und keine .up-root-Huelle.
   Weiss wirkt nur, wo darunter Blau liegt; ueber dem weissen Grund ist es unsichtbar -- genau
   richtig, denn oben soll ohnehin nichts zu sehen sein.
   Die Blende ist die, die frueher auch das Bild trug, und sie bleibt unveraendert: bis 16% nichts,
   dann langsam, ab 66% voll. Zwei Angaben, weil Safari die Kurzform noch mit Praefix braucht. */
.ulh-bg-grid {
  background: repeating-linear-gradient(to right,
              rgba(255, 255, 255, .55) 0, rgba(255, 255, 255, .55) 1px,
              transparent 1px, transparent 8px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 16%, rgba(0,0,0,.18) 32%,
                      rgba(0,0,0,.62) 50%, #000 66%);
  mask-image: linear-gradient(to bottom, transparent 16%, rgba(0,0,0,.18) 32%,
              rgba(0,0,0,.62) 50%, #000 66%);
}

/* ---------- Die Blende unten im Dashboardbereich ----------
   32px, in der Farbe des Rahmens, und erst rechts der Leiste: sie soll den Inhalt ausfaden lassen,
   nicht die Navigation. Der linke Versatz rechnet mit dem Mass, weil die Leiste im Fenster
   verkleinert dasteht -- 218px Layout ergeben bei 0.73 rund 159px auf dem Schirm.
   218 und nicht mehr 234: die Leiste ist seit der Umstellung auf 224px schmaler, und die Blende
   soll frueher anfangen als deren Kante -- so verlaeuft sie unter dem Inhalt und nicht erst
   daneben. Die 16px Unterschied sind die Ansage.
   z-index: 2, weil .ulh-app positioniert ist und sonst darueber laege. */
.ulh-view::after {
  content: "";
  position: absolute;
  left: calc(218px * var(--ulh-mass, 1));
  right: 0;
  bottom: 0;
  height: 32px;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(to top, var(--ulh-blende), rgba(240, 240, 240, 0));
}
/* Die Blende beginnt bei 218px mal Massstab, also kurz vor der Kante der ausgeklappten Leiste. Klappt die
   Leiste auf die Zeichenschiene zusammen (is-mini, von sidebar.js nach der Fensterbreite gesetzt),
   faellt die Blende ganz weg: sie soll den Uebergang zum Fensterrand weich machen, und auf einem
   schmalen Schirm ist von diesem Rand ohnehin nichts mehr zu sehen. */
.ulh-root:has(.usn-bar.is-mini) .ulh-view::after { display: none; }

/* ---------- Das Erscheinen ----------
   Dieselbe Machart wie im Onboarding (uob-rise): von unten heraufziehen und dabei aufblenden, in
   Stufen. Die Reihenfolge ist die gewuenschte -- erst der Rahmen samt Leiste, dann der Seitenkopf,
   dann Chart und Marken, dann der Zitatteil.
   backwards als fill: damit ist ein Teil VOR seiner Verzoegerung unsichtbar. Ohne das blitzten alle
   vier gleichzeitig auf und sprangen dann zurueck.
   Zwei Klassen und nicht eine: is-shown macht das Fenster ueberhaupt sichtbar und BLEIBT,
   is-entering traegt die Animationen und faellt nach dem letzten Durchlauf ab. Bliebe sie stehen,
   liefe jede spaetere Bewegung im Fenster gegen eine noch gesetzte animation -- genau davor warnt
   der Kommentar im Onboarding. */
/* Alle Aufstiege laufen seit dem 06.10. auf --ulh-expo und ein Drittel laenger als vorher (siehe
   dort: Mobbin). Die Uhren in landing-hero.js, die is-kommt wieder abnehmen, liegen dadurch zum Teil
   knapp vor dem Ende einer Bewegung -- bei dieser Kurve sind dort aber schon ueber 99 Prozent des
   Weges getan, das Abnehmen ist nicht zu sehen. */
@keyframes ulhRise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}
/* Das Ende aller Stufen mit is-entering steht als ERSCHEINEN_MS in landing-hero.js -- wer hier
   eine Verzoegerung oder Laenge aendert, zieht die Zahl dort mit (sonst faellt is-entering mitten
   in einer Stufe ab, und das Teil springt auf seinen Endzustand). */
.ulh-stage { opacity: 0; }
.ulh-root.is-shown .ulh-stage { opacity: 1; }
/* ---- STUFE 0: DER TEXTBLOCK UEBER DEM FENSTER (24.09. angefordert) --------------------------
   "Erst der GET-MENTIONED-Chip, dann die h1, usw. -- natuerlich vor dem Hero-Fenster darunter."
   backwards: vor der eigenen Verzoegerung ist ein Teil unsichtbar -- ohne das blitzen alle
   gleichzeitig auf und springen dann zurueck.

   LANGSAMER UND WEITER AUSEINANDER (01.10.: "etwas smoother und mit mehr Delay, die Sachen ueber
   dem Fenster sind noch fast instant -- erst Talk to Sales, dann Book a Demo").
   Vorher 560ms je Stufe im Abstand von 120: die vier lagen so dicht, dass sie als EIN Auftritt
   ankamen, und 560ms mit dem beidseitig weichen Verlauf sind vorbei, bevor das Auge folgt.
   Jetzt:
     - fuenf Stufen statt vier -- die zwei Knoepfe einzeln, in der Reihenfolge des Markups
     - 180ms Abstand, und die erste erst nach 150: die Seite steht einen Augenblick leer, bevor
       etwas kommt, sonst faellt der Anfang mit dem Laden zusammen
     - Deckkraft und Weg GETRENNT, wie beim Auftritt der Sektionen weiter unten (.ulh-auf):
       die Deckkraft blendet 900ms weich auf (--ulh-ease), der Weg rollt 1100ms aus (--ulh-gleit,
       schnell an, lang aus). In einer Kurve fuer beides stuende der Text nach einem Drittel der
       Zeit schon fast voll da -- genau das "fast instant".
   Das Ende liegt seit 14.10. bei 960 + 1100 = 2060ms (die Zeile der Modelle); ERSCHEINEN_MS in landing-hero.js ist darauf bezogen. */
@keyframes ulhAufblenden { from { opacity: 0; } to { opacity: 1; } }
@keyframes ulhHochrollen { from { transform: translateY(16px); } to { transform: none; } }
.ulh-root.is-entering .ulh-eyebrow { animation: ulhAufblenden 900ms var(--ulh-ease) 150ms backwards, ulhHochrollen 1100ms var(--ulh-gleit) 150ms backwards; }
.ulh-root.is-entering .ulh-h1      { animation: ulhAufblenden 900ms var(--ulh-ease) 330ms backwards, ulhHochrollen 1100ms var(--ulh-gleit) 330ms backwards; }
.ulh-root.is-entering .ulh-sub     { animation: ulhAufblenden 900ms var(--ulh-ease) 510ms backwards, ulhHochrollen 1100ms var(--ulh-gleit) 510ms backwards; }
/* Seit 14.10. steht der Hauptknopf links, also kommt er zuerst; danach die Zeile der Modelle. */
.ulh-root.is-entering .ulh-cta .ulh-btn-pri { animation: ulhAufblenden 900ms var(--ulh-ease) 690ms backwards, ulhHochrollen 1100ms var(--ulh-gleit) 690ms backwards; }
.ulh-root.is-entering .ulh-cta .ulh-btn-sec { animation: ulhAufblenden 900ms var(--ulh-ease) 780ms backwards, ulhHochrollen 1100ms var(--ulh-gleit) 780ms backwards; }
.ulh-root.is-entering .ulh-modelle          { animation: ulhAufblenden 900ms var(--ulh-ease) 960ms backwards, ulhHochrollen 1100ms var(--ulh-gleit) 960ms backwards; }
/* ---- STUFE 1: DAS FENSTER, ABER NOCH OHNE DEN INHALT DER DASHBOARD-SEITE --------------------
   Rahmen samt Leiste und Hintergruenden, dann der Seitenkopf. Der Inhalt kommt in Stufe 2 und
   haengt nicht mehr an einer Uhr -- siehe unten.
   Beide um 530ms spaeter (440 -> 970, 640 -> 1170), damit das Fenster wie bisher 100ms nach dem
   letzten Knopf anfaengt und nicht mitten in den Text faellt. Die Laengen bleiben. */
.ulh-root.is-entering .ulh-frame  { animation: ulhRise 900ms var(--ulh-expo) 970ms backwards; }
.ulh-root.is-entering .dph-root   { animation: ulhRise 810ms var(--ulh-expo) 1170ms backwards; }
/* NICHT MEHR ZWEI GROSSE RECHTECKE (21.09. gemeldet: "Bitte eine richtige Appear animation im
   Dashboard, das sieht aktuell sehr mau aus"). Vorher stiegen .vot-root und .tcd-root als je ein
   Block herauf -- zwei Bewegungen fuer vier Kaesten, und in einer Sektion, in der das Brett der
   Chancen vier Spalten einzeln aufbaut und die Prompts-Liste ihre Zeilen staffelt, war das
   Dashboard der duennste Auftritt von allen.
   Jetzt baut sich jeder der vier Kaesten selbst auf: erst seine Ueberschriftzeile, 70ms spaeter
   sein Panel -- die Geste, mit der eine Karte entsteht. Die vier laufen von links oben nach
   rechts unten durch.
   Die Kaesten und ihre Koepfe stehen FEST im Markup (.landing_markup.py zieht sie aus den
   Bubble-Vorlagen); nur ihr Inhalt wird spaeter gezeichnet. Deshalb laufen die Animationen hier
   und nicht an den Zeilen der Zitatliste: eine Zeile, die waehrend ihrer eigenen Verzoegerung
   neu gezeichnet wird, faengt die Verzoegerung von vorn an und springt, wenn is-entering
   inzwischen abgefallen ist.
   Die Kaesten haengen an is-inhalt und nicht an is-entering, ERSCHEINEN_MS in landing-hero.js
   betrifft sie also nicht. */
/* ---- STUFE 2: DER INHALT DER DASHBOARD-SEITE ------------------------------------------------
   NICHT MEHR AN EINER UHR, SONDERN AN is-inhalt (24.09. -- "die Appear-Animation beim ersten Load
   ist immer noch etwas hakelig, das muss butter smooth sein").
   Woher das Hakeln kam: die vier Kaesten liefen auf festen Verzoegerungen los, waehrend Chart.js
   im selben Moment seine zwei Charts AUFBAUTE. Eine Transformation, die ueber den Compositor
   laufen koennte, teilt sich dann den Hauptstrang mit Layout und Zeichnen -- und genau das sieht
   man als Stocken. Die Uhr wusste davon nichts; sie lief einfach ab.
   Jetzt setzt landing-hero.js die Klasse is-inhalt erst, wenn das Chart WIRKLICH steht (dieselbe
   Bedingung, auf die auch der Szenenwechsel wartet). Bis dahin ist der Inhalt unsichtbar -- das
   Fenster steht mit Rahmen, Leiste, Hintergruenden und Seitenkopf da, genau wie verlangt.
   Danach vier Stufen ohne Konkurrenz: Visibility links, Visibility rechts, Citations links,
   Citations rechts. */
.ulh-root:not(.is-inhalt) .vot-unit-left  .vot-head,
.ulh-root:not(.is-inhalt) .vot-unit-left  .vot-box,
.ulh-root:not(.is-inhalt) .vot-unit-right .vot-head,
.ulh-root:not(.is-inhalt) .vot-unit-right .vot-box,
.ulh-root:not(.is-inhalt) .tcd-unit-left  .tcd-head,
.ulh-root:not(.is-inhalt) .tcd-unit-left  .tcd-box,
.ulh-root:not(.is-inhalt) .tcd-unit-right .tcd-head,
.ulh-root:not(.is-inhalt) .tcd-unit-right .tcd-box { opacity: 0; }
/* ENGER GESTAFFELT (24.09.: "mach den Delay etwas kleiner, das soll von oben bis unten eine
   schoene smoothe Animation sein"). Die Abstaende waren 0/70/60/130/260/330/320/390 -- vor allem
   die 130ms Pause zwischen dem Visibility-Chart und den Zitaten lasen sich als zwei Auftritte
   statt als einer, der durchlaeuft. Jetzt laeuft er ohne Absatz von oben nach unten durch. */
.ulh-root.is-inhalt .vot-unit-left  .vot-head { animation: ulhRise 730ms var(--ulh-expo) backwards; }
.ulh-root.is-inhalt .vot-unit-left  .vot-box  { animation: ulhRise 810ms var(--ulh-expo) 45ms backwards; }
.ulh-root.is-inhalt .vot-unit-right .vot-head { animation: ulhRise 730ms var(--ulh-expo) 40ms backwards; }
.ulh-root.is-inhalt .vot-unit-right .vot-box  { animation: ulhRise 810ms var(--ulh-expo) 85ms backwards; }
.ulh-root.is-inhalt .tcd-unit-left  .tcd-head { animation: ulhRise 730ms var(--ulh-expo) 150ms backwards; }
.ulh-root.is-inhalt .tcd-unit-left  .tcd-box  { animation: ulhRise 810ms var(--ulh-expo) 195ms backwards; }
.ulh-root.is-inhalt .tcd-unit-right .tcd-head { animation: ulhRise 730ms var(--ulh-expo) 190ms backwards; }
.ulh-root.is-inhalt .tcd-unit-right .tcd-box  { animation: ulhRise 810ms var(--ulh-expo) 230ms backwards; }
@media (prefers-reduced-motion: reduce) {
  .ulh-root:not(.is-inhalt) .vot-unit-left  .vot-head,
  .ulh-root:not(.is-inhalt) .vot-unit-left  .vot-box,
  .ulh-root:not(.is-inhalt) .vot-unit-right .vot-head,
  .ulh-root:not(.is-inhalt) .vot-unit-right .vot-box,
  .ulh-root:not(.is-inhalt) .tcd-unit-left  .tcd-head,
  .ulh-root:not(.is-inhalt) .tcd-unit-left  .tcd-box,
  .ulh-root:not(.is-inhalt) .tcd-unit-right .tcd-head,
  .ulh-root:not(.is-inhalt) .tcd-unit-right .tcd-box { opacity: 1; }
  .ulh-root.is-entering .ulh-eyebrow,
  .ulh-root.is-entering .ulh-h1,
  .ulh-root.is-entering .ulh-sub,
  .ulh-root.is-entering .ulh-cta .ulh-btn-sec,
  .ulh-root.is-entering .ulh-cta .ulh-btn-pri,
  .ulh-root.is-entering .ulh-modelle,
  .ulh-root.is-inhalt .vot-unit-left  .vot-head,
  .ulh-root.is-inhalt .vot-unit-left  .vot-box,
  .ulh-root.is-inhalt .vot-unit-right .vot-head,
  .ulh-root.is-inhalt .vot-unit-right .vot-box,
  .ulh-root.is-inhalt .tcd-unit-left  .tcd-head,
  .ulh-root.is-inhalt .tcd-unit-left  .tcd-box,
  .ulh-root.is-inhalt .tcd-unit-right .tcd-head,
  .ulh-root.is-inhalt .tcd-unit-right .tcd-box,
  .ulh-quell-box.is-da .udd-card,
  .ulh-root.is-entering .ulh-frame,
  .ulh-root.is-entering .dph-root,
  .ulh-root.is-entering .vot-unit-left  .vot-head,
  .ulh-root.is-entering .vot-unit-left  .vot-box,
  .ulh-root.is-entering .vot-unit-right .vot-head,
  .ulh-root.is-entering .vot-unit-right .vot-box,
  .ulh-root.is-entering .tcd-unit-left  .tcd-head,
  .ulh-root.is-entering .tcd-unit-left  .tcd-box,
  .ulh-root.is-entering .tcd-unit-right .tcd-head,
  .ulh-root.is-entering .tcd-unit-right .tcd-box,
  .ulh-root .ulh-seite.is-kommt { animation: none; }
}

/* ---------- Kleiner, sobald die Seite bewegt wird ----------
   Das ganze Fenster geht auf seinen kleinen Zustand (seit 28.09. 1300 von 1440, also gut 90
   Prozent; der Faktor kommt aus kleinFaktor in landing-hero.js) -- WIRKLICH skaliert, nicht
   schmaler: Schriftgroessen, Abstaende und Polster darin bleiben im Verhaeltnis genau, wie sie
   sind. Ein transform kann das,
   eine Aenderung der Breite nicht (dort waeren die Schriften unveraendert gross und alles andere
   enger).
   transform-origin: center: der Kasten darum behaelt seine Layouthoehe, also waechst der Abstand
   nach oben, unten, links und rechts gleichmaessig. Genau dort kommen spaeter die Nachbarn hin.
   860ms, ein Viertel laenger als eine Stufe des Erscheinens (690), mit ausschleifendem Verlauf.
   :not(.is-entering): waehrend des Erscheinens laeuft auf demselben Element eine animation, und die
   schlaegt jede transition. Ohne diese Bedingung sprang das Fenster in der Sekunde, in der die
   Animation endete, auf 80 Prozent. So beginnt die Verkleinerung erst danach -- und dann weich. */
/* 1800ms. Der Reihe nach 860 -> 1290 -> 1800, und der Verlauf jedes Mal staerker ausschleifend
   (.22/1/.36/1 -> .16/1/.3/1 -> .1/1/.2/1): nach einem Viertel der Zeit sind rund 80 Prozent des
   Weges getan, der Rest laeuft lange aus. */
.ulh-buehne { transform-origin: center center; transition: transform 1800ms var(--ulh-aus); }
/* Der Faktor selbst steht NICHT hier, sondern als Inline-Stil aus landing-hero.js. Hier stand er,
   mit :not(.is-entering) davor, und er hat im Betrieb nicht gegriffen -- eine Regel mehr, deren
   Zutreffen von einer zweiten Klasse und von der Spezifitaet abhaengt. Der Inline-Stil kann von
   nichts geschlagen werden ausser einer laufenden animation, und die ist nach ERSCHEINEN_MS vorbei;
   der Takt in landing-hero.js schreibt ihn danach von sich aus nach. */
/* REDUZIERT HEISST KUERZER, NICHT SPRUNGHAFT (21.09. gemeldet: "auf einigen Windows-Rechnern
   ist das ein instantes Runterploppen auf den finalen Stand, ohne Animation dazwischen").
   Die Ursache ist keine kaputte Animation, sondern genau diese Regel: Windows schaltet unter
   Einstellungen > Barrierefreiheit > Visuelle Effekte die Animationen ab, der Browser meldet
   daraufhin prefers-reduced-motion: reduce -- und hier stand "transition: none". Das Fenster
   sprang also von scale(1) auf scale(.8), und die drei Nebenfenster standen im selben Bild da
   (auftritte() in landing-hero.js setzt sie dort ebenfalls sofort). Auf einem Mac ist die
   Einstellung selten an, deshalb "auf EINIGEN Rechnern".
   Ganz abschalten ist die falsche Antwort, aber 1800ms sind genau das, was die Einstellung
   meint: eine grosse, lange Bewegung. 260ms sind kurz genug, dass nichts mehr schwingt, und
   lang genug, dass die Groessenaenderung als Bewegung lesbar ist statt als Sprung. Die
   AUFTRITTE der Seitenstuecke bleiben dabei aus -- die sind viele kleine Bewegungen, und genau
   die sind gemeint. */
@media (prefers-reduced-motion: reduce) {
  .ulh-buehne { transition: transform 260ms var(--ulh-aus); }
}

/* ---------- Die zwei Seiten im Fenster ----------
   Dashboard und Mira liegen UEBEREINANDER, nicht nacheinander. Beide absolut im selben Kasten:
   sonst nimmt die ausgeblendete Seite weiter ihren Platz ein (opacity nimmt keinen Raum weg) und
   das Fenster waere doppelt so hoch wie sein Ausschnitt.
   Das Ausblenden ist DOPPELT so schnell wie das Erscheinen -- 690 / 2 -- so war die Ansage. Das
   Einblenden von Mira laeuft wieder mit der Dauer des Erscheinens und derselben Bewegung von unten,
   damit die zweite Szene sich anfuehlt wie die erste. */
.ulh-seiten {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}
.ulh-seite {
  position: absolute;
  inset: 0;
  /* Expo statt ease, 420 statt 345 (06.10., Mobbin): die alte Seite ist bei AUSBLENDEN_MS schon
     fast ganz weg, die neue steigt danach auf. will-change, damit der Wechsel auf dem Compositor
     laeuft: in genau diesem Augenblick baut die neue Seite ihre Komponente (bis 120ms am Stueck,
     gemessen) -- eine Ueberblendung, die auf den Hauptfaden wartet, stockt dann mit. */
  transition: opacity 420ms var(--ulh-expo);
  will-change: opacity;
}
/* Mira und die Prompts-Seite liegen von Anfang an im Markup, aber unsichtbar: die Komponenten
   brauchen ihre echte Groesse, um sich einzurichten (Chathoehe, is-compact, Scrollsperre, die
   Spaltenanpassung der Tabelle), und display: none haette ihnen eine Hoehe von 0 gemeldet. */
.ulh-mira, .ulh-prompts, .ulh-chancen, .ulh-perf, .ulh-shop { opacity: 0; }
.ulh-seite.is-da { opacity: 1; }
/* is-weg NACH is-da, und das ist kein Schoenheitsfehler in der Reihenfolge: eine Seite, die schon
   dran WAR und jetzt weg soll, traegt beide Klassen. Beide haben dieselbe Spezifitaet, also
   entscheidet die Reihenfolge -- und stand is-da hinten, blieb Mira mit Deckkraft 1 ueber der
   Prompts-Seite liegen. Genau so gemessen. */
.ulh-seite.is-weg { opacity: 0; }
.ulh-seite.is-kommt { animation: ulhRise 900ms var(--ulh-expo) backwards; }

/* Die Karte der Heatmap (performance-radar.css: .uhm-tip) haengt an <body> und bleibt deshalb
   UNVERKLEINERT -- sie steht in voller Groesse neben einer verkleinerten Oberflaeche.
   Hier stand ein Versuch, sie mit der unabhaengigen scale-Eigenschaft mitzuziehen. Er ist wieder
   heraus, und der Grund ist lehrreich: scale wird NACH dem transform der Komponente angewandt und
   skaliert damit auch deren Verschiebung. Die Karte stand danach bei 414,471 statt bei 670,763 --
   also genau die Werte mal 0.6176. Eine Karte an der falschen Stelle ist schlechter als eine, die
   eine Nummer zu gross ist. */

/* Die Detailkarte des Chancen-Bretts scrollt im Fenster NICHT. Sie ist ein Bild, und ein Bild,
   das man scrollen muesste, zeigt seinen Inhalt nicht -- man saehe die Haelfte.
   Weg faellt dafuer der Block "Mentioned competitors": er ist der laengste Teil der Karte, steht
   ganz unten und ist der einzige, der ohne den Rest fuer sich stehen kann. Seine Ueberschrift
   geht mit -- ueber :has(+ ...), weil sie im Markup VOR der Liste steht und CSS sonst keinen Weg
   rueckwaerts hat. */
.ulh-app .uo-m-body { overflow-y: hidden; }
/* Seit dem 13.10. ist der Block die Markenleiste .uo-mrow ("x brands mentioned", wie in Response
   Detail) und traegt keine eigene Ueberschrift mehr -- eine Regel statt zwei. */
.ulh-app .uo-mrow { display: none; }
/* Und der Themenblock. Ohne ihn blieben 31px zu viel -- gemessen 575 Inhalt auf 544 Platz.
   Er ist der Block, der am wenigsten fehlt: dieselben Themenchips stehen auf der Karte im Brett
   dahinter, und die Karte hier soll die Zahlen zeigen, die es dort NICHT gibt. */
.ulh-app .uo-tags { display: none; }
.ulh-app .uo-m-section-title:has(+ .uo-tags) { display: none; }

/* ---------- Der Auftritt der Performance-Seite ----------
   Die Matrix tritt AUF wie in der App: von unten herauf und dabei aufblendend, dieselbe Bewegung
   (ulhRise), die auch die Seiten und die Karten benutzen.
   Warum eine eigene Klasse und nicht is-kommt an der Seite: die Komponente zeichnet ihre Zellen
   ERST, wenn die Daten da sind -- also nach dem Auftritt der Seite. Eine Bewegung auf einer noch
   leeren Flaeche sieht aus wie gar keine, und genau so war es gemeldet. Jetzt wird gewartet, bis
   die Zellen stehen, und dann faehrt die fertige Matrix herauf.
   Auch der Seitenkopf zieht mit: er gehoert zum selben Bild. */
/* Der Auftritt kommt aus der Komponente selbst (performance-radar.js: runAppear -- die Zellen
   ploppen diagonal herein). Hier steht deshalb keine eigene Bewegung mehr; was hier stand, lief
   ueber die schon fertige Matrix und war doppelt.
   Der Trichter dieser Seite ist der Zeitpunkt: gefuellt wird erst, wenn die Seite steht. */
/* Der Abgang zwischen den zwei Ansichten: erst weg, dann kommt sie als Sentiment mit
   Gewichtsbalken zurueck -- neu aufgebaut, also mit dem Auftritt der Komponente. Kuerzer als der
   Auftritt: ein Abgang, der so lange braucht wie ein Auftritt, wirkt wie ein Fehler. */
.ulh-perf .uhm-root {
  transition: opacity 390ms var(--ulh-ease), translate 390ms var(--ulh-ease);
}
.ulh-perf .uhm-root.is-geht { opacity: 0; translate: 0 12px; }
/* Der Kasten der Matrix traegt keinen eigenen Rahmen mehr: er steht im Fenster schon in einem
   Container, und zwei Rahmen ineinander sind einer zu viel. */
/* Rahmen und Polster des Kastens: standen hier, stehen jetzt in performance-radar.css und
   gelten damit auch in der App -- genau das war der Auftrag. Hier bleibt nichts. */
/* Der Auftritt hier doppelt so lang wie in der App: dort ploppt die Matrix beim Seitenwechsel
   herein und soll aus dem Weg sein, hier ist sie die Vorfuehrung selbst und darf gesehen werden.
   Die Zahl steht als Faktor an EINER Stelle -- performance-radar liest sie fuer den Versatz der
   Zellen und die CSS fuer die Dauer, sonst laufen Versatz und Dauer auseinander. */
.ulh-perf .uhm-grid { --uhm-pop: 2; }
/* Die Themen links als Punkt mit Text, ohne Chip und ohne Emoji -- der Stil, den die Seite schon
   zeigt. Stand bis zum 08.10. in performance-radar.css fuer App UND Seite; die App zeigt seitdem
   den Topic-Chip (gewuenscht), die Seite bleibt, wie sie war. */
.ulh-perf .uhm-rowhead .up-topicchip {
  background: none; border: 0; padding: 0; gap: 8px; max-width: 100%;
}
.ulh-perf .uhm-rowhead .up-topicchip::before {
  content: ""; width: 8px; height: 8px; flex: 0 0 8px; border-radius: 999px;
  background: var(--ust-tag-color, var(--vc-muted));
}
.ulh-perf .uhm-rowhead .up-topicchip-e { display: none; }
.ulh-perf .uhm-rowhead .up-topicchip-lbl {
  color: var(--vc-text); font-weight: 500; font-size: 12.5px;
}
/* Keine Karten an den Zellen. Die Szene zeigt zwei Ansichten und braucht keine dritte Bewegung,
   und was hier ohnehin niemand anfassen kann, soll auch nicht auf eine Maus reagieren, die nur
   ueber das Fenster faehrt: ohne mouseover kein Tooltip -- genau darauf hoert die Komponente --
   und ohne hover kein Aufhellen einzelner Zellen. Nur das Raster, nicht der Kopf der Karte: der
   Umschalter darin gehoert zum Bild. */
.ulh-perf .uhm-scroll { pointer-events: none; }
/* Der Schimmer in den Zellen steht jetzt in performance-radar.css und gilt damit ueberall --
   hier stand er zuerst, aber er gehoert der Komponente, nicht dieser Seite. */
/* Die Themen links (Punkt + Name, kein Chip, kein Emoji): standen hier und stehen jetzt in
   performance-radar.css. Dieselben Zeilen an zwei Orten waren die naechste Stelle, die
   auseinanderlaeuft, und die App soll hier ausdruecklich so aussehen wie diese Seite. */

/* ---------- Der Auftritt der Prompts-Seite ----------
   Sie steigt NICHT als Ganzes herauf -- ihre Teile tun es nacheinander: erst der Seitenkopf, dann
   die Gruppenliste, dann der Kasten mit Kopf und Werkzeugleiste, dann die Zeilen eine nach der
   anderen. Zwei Bewegungen uebereinander (die ganze Seite UND ihre Teile) lesen sich als eine
   unruhige, deshalb die erste Zeile hier.
   backwards als fill, wie beim Erscheinen des Fensters: ohne das blitzt jeder Teil vor seiner
   Verzoegerung kurz auf und springt dann zurueck. */
.ulh-root .ulh-prompts.is-kommt { animation: none; }
.ulh-root .ulh-prompts.is-kommt .up-ph-root { animation: ulhRise 680ms var(--ulh-expo) backwards; }
.ulh-root .ulh-prompts.is-kommt .upt-grp-sidepanel { animation: ulhRise 680ms var(--ulh-expo) 140ms backwards; }
.ulh-root .ulh-prompts.is-kommt .up-box { animation: ulhRise 680ms var(--ulh-expo) 300ms backwards; }
/* Die Zeilen: eine nach der anderen, 55ms auseinander. Der Zaehler steht als --ulh-i an der Zeile
   und wird von landing-hero.js gesetzt, sobald die Zeilen wirklich da sind -- eine Regel mit
   nth-child fuer fuenfzehn Zeilen waere dasselbe in laenger und ginge bei einer anderen
   Seitengroesse schief.
   Der Koerper der Tabelle bleibt VERSTECKT, bis die Staffelung anfaengt. Das ist der Punkt, an dem
   es vorher nicht zu sehen war: der Kasten drumherum blendet selbst von 0 auf 1 auf, und ein
   Auftritt einzelner Zeilen INNERHALB einer Flaeche, die als Ganzes aufblendet, ist keiner --
   man sieht nur den Kasten kommen. Jetzt kommt der Kasten leer, und die Zeilen fallen danach
   einzeln ein. visibility und nicht opacity: der Platz bleibt stehen, die Tabelle springt nicht. */
.ulh-root .ulh-prompts.is-kommt .up-tbody { visibility: hidden; }
.ulh-root .ulh-prompts.is-zeilen .up-tbody { visibility: visible; }
.ulh-root .ulh-prompts.is-zeilen .up-tbody .up-row {
  animation: ulhRise 550ms var(--ulh-expo) backwards;
  animation-delay: calc(var(--ulh-i, 0) * 55ms);
}
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-prompts.is-kommt .up-ph-root,
  .ulh-root .ulh-prompts.is-kommt .upt-grp-sidepanel,
  .ulh-root .ulh-prompts.is-kommt .up-box,
  .ulh-root .ulh-prompts.is-zeilen .up-tbody .up-row { animation: none; }
}

/* Die Zeilen der Markentabelle wandern beim Filterwechsel an ihren neuen Platz. Die Bewegung setzt
   landing-hero.js als Inline-Stil (FLIP: erst messen, dann umsortieren, dann von der alten Lage
   zurueckfahren) -- hier steht, wie eine wandernde Zeile AUSSIEHT.

   Sie ist eine KACHEL: Linie oben, Linie unten, deckend gefuellt. Alle drei Teile sind noetig, und
   zwar aus einem gemessenen Grund: die Zeilen kreuzen sich unterwegs. Lumen und Verity tauschen
   Platz 5 und 6 und stehen auf halber Strecke exakt uebereinander, Kestrel ueberlappt Vantage und
   Halden um je 23 von 47 Pixeln. Ohne Fuellung schlagen dort zwei Zeilen durcheinander, und ohne
   Linie oben hat die Kachel nur einen Strich unten -- den der Zellen -- und sieht aus wie ein
   abgerissener Streifen.

   Beides in EINEM box-shadow und nicht als border-top plus background:
   - ein border-top machte die Zeile 1px hoeher und verschoebe das ganze Raster,
   - ein eigener background muesste ueberblendet werden, und dann faerbte sich auch das Ueberfahren
     mit der Maus weich, was es nicht soll.
   Der zweite Schatten mit 100px Spread fuellt die Kachel. Die Linie UNTEN gehoert den Zellen
   (.vt-td border-bottom) und liegt als Kind ohnehin ueber beiden Schatten -- sie bleibt stehen.

   Die Linie OBEN liegt AUSSERHALB der Zeile (0 -1px 0) und nicht innen (inset 0 1px 0), und das ist
   der Unterschied, der als "andere Farbe" aufgefallen ist. Innen sass sie in den ersten Pixeln der
   Zeile, waehrend der Nachbar darueber seine eigene Trennlinie in den letzten Pixeln vor der Zeile
   hat: zwei 1px-Linien direkt untereinander, also eine 2px starke Kante -- und die liest sich als
   dunkler, obwohl beide denselben Ton haben (gemessen rgb(231,233,236) auf beiden Seiten).
   Aussen liegt sie GENAU auf der Linie des Nachbarn. Die wandernde Zeile hat einen eigenen
   Stapelplatz und deckt sie dabei ab, es bleibt also eine einzige 1px-Linie -- und sobald die Zeile
   das Raster verlassen hat, ist es ihre eigene.
   Die Ueberblendung steht auf der Grundregel, damit sie in BEIDE Richtungen laeuft: beim Anlegen
   der Klasse und beim Abfallen. box-shadow und nichts sonst -- der Hover setzt background, davon
   bleibt er unberuehrt. */
.ulh-app .vt-row { transition: box-shadow 200ms ease; }
.ulh-app .vt-row.is-wandert {
  position: relative;
  z-index: 1;
  box-shadow: 0 -1px 0 var(--vc-border), inset 0 0 0 100px var(--vc-bg);
}

/* ---------- Das Fenster ---------- */
/* Die Buehne haengt in einem Raster mit EINER Zelle, damit der spaetere Ablauf mehrere Szenen
   uebereinander legen kann, ohne dass die Hoehe springt. */
.ulh-stage {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  /* Kein seitliches Polster mehr: der Rahmen nimmt 3/4 der Breite und steht mittig, der Rest ist
     ohnehin Luft. Zwei Begrenzungen uebereinander machten ihn nur schmaler als in der Vorlage. */
  padding: 0 0 clamp(40px, calc(8 * var(--ulh-fh)), 96px);
}
/* Der Rahmen ist ZWEI Kaesten, nicht einer -- so macht es die Vorlage, und daher kommt der Eindruck
   eines Geraets. Gemessen an attio.com bei 1440px Fensterbreite:
     aussen  1080x619  Radius 16  Polster 0 6px 6px  Fuellung fast-weiss bei 80% Deckkraft
     innen   1068x581  Radius 12  kein Polster       Fuellung weiss
   1080 - 1068 = 12, also 6px links und rechts: eine BLENDE. Oben sitzt in der Blende die Zeile mit
   den drei Punkten (die Hoehendifferenz von 38 sind 6px Polster unten plus 32px Zeile), und der
   Schirm darunter hat seinen eigenen, kleineren Radius. Ohne diese Blende sieht das Fenster aus wie
   ein Kasten mit einer Leiste obendrauf -- genau das war der Unterschied.
   Blende und Schirm nehmen --ulh-blende und --ulh-schirm: dasselbe Verhaeltnis wie in der Vorlage.
   Deckend und nicht mit Alpha, weil die Seite darunter weiss ist. */
/* ---------- Die Buehne ----------
   Sie ist genau das Hauptfenster gross und traegt die Verkleinerung beim Scrollen. Die drei
   Nebenfenster liegen absolut darin und stehen damit fest zu seinen Kanten -- in jeder
   Fensterbreite und in jeder Groesse. */
.ulh-buehne { position: relative; width: var(--ulh-spur); margin: 0 auto; }

.ulh-frame, .ulh-fen {
  /* Volle Breite des Platzes, hoechstens 1440 -- DERSELBE Deckel wie in der Vorlage
     (max-w-[1440px]). Ein PROZENTWERT ist hier der falsche Hebel: ich kenne die Breite des
     Framer-Abschnitts nicht, und 75% davon sind auf einer schmalen Seite winzig. Der Deckel ist
     davon unabhaengig -- schmaler Abschnitt wird ausgefuellt, breiter bei 1440 gedeckelt und
     mittig. Attios Fenster ist auf einer 1440er Seite 1080 breit und auf einer 1920er 1440; mit
     diesem Deckel kommt bei jeder Seite ab 1440 dasselbe heraus.
     Vorher standen hier 1200 -- zu schmal, so gemeldet.
     Breite und Mitte stehen jetzt an der BUEHNE: an ihr haengen auch die drei Nebenfenster, und
     die richten sich mit left/right an den Kanten des Hauptfensters aus -- das geht nur, wenn ihr
     gemeinsamer Bezugsrahmen genau so breit ist wie dieses Fenster. Die Breite steht deshalb
     unten allein an .ulh-frame -- ein Nebenfenster ist so breit wie sein Schirm und nicht so
     breit wie die Buehne (mit dem geteilten "width: 100%" waren alle drei 1440px breit,
     gemessen). */
  border-radius: 16px;
  padding: 0 6px 6px;
  background: var(--ulh-blende);
  /* Fuenf Ebenen statt einer, und die Deckkraft STEIGT nach aussen (.024 -> .035). Die erste Ebene
     hat keinen Weichzeichner: sie ist eine Haarlinie von 0.9px und ersetzt den Rahmen -- ein
     border wuerde bei jeder Verkleinerung mitskalieren und dabei ausfransen. Werte an der Vorlage
     abgelesen. Die Farbe ist ein kuehles Fast-Schwarz und nicht --vc-text: unser Textton ist warm
     (#1f1f1b), und ein warmer Schatten sieht unter einem weissen Fenster schmutzig aus. */
  box-shadow:
    0 0 0 .906px rgba(11, 13, 24, .055),
    0 .906px 1.812px rgba(11, 13, 24, .024),
    0 2.719px 5.437px rgba(11, 13, 24, .027),
    0 7.25px 12.687px rgba(11, 13, 24, .03),
    0 14.5px 25.374px rgba(11, 13, 24, .035);
}
/* Die Punktezeile liegt IN der Blende und hat keine eigene Fuellung und keine Trennlinie -- sie
   ist ein Teil des Rahmens, kein Balken auf dem Schirm. 32px wie in der Vorlage. */
.ulh-chrome {
  height: 32px;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 0 8px;
}
/* Die drei Punkte in den Farben, die jeder kennt -- so steht es auch in der Vorlage. Grau sahen sie
   aus wie deaktivierte Knoepfe statt wie ein Fensterrahmen. Feste Werte und keine Marken: das sind
   die Farben eines Betriebssystems und nicht die unserer App, sie drehen sich mit keinem Thema. */
.ulh-dot { width: 10px; height: 10px; border-radius: 999px; background: #ff5f57; }
.ulh-dot:nth-child(2) { background: #febc2e; }
.ulh-dot:nth-child(3) { background: #28c840; }

/* Der Ausschnitt: so hoch wie das Verhaeltnis vorgibt, und was die Buehne darunter noch traegt,
   ist abgeschnitten. */
.ulh-view {
  position: relative;
  width: 100%;
  height: 0;
  /* 50px flacher als das Verhaeltnis ergibt. Als Abzug in Pixeln und nicht als kleineres
     Verhaeltnis: 50px sollen 50px bleiben, egal wie breit das Fenster ist -- ein geaendertes
     Verhaeltnis waere bei 1600px Breite ein anderer Abzug als bei 1000.
     Was unten wegfaellt, faellt weg: das Dashboard ist ohnehin angeschnitten, und bei Mira ist
     darunter Platz.
     21.09.: von 50 auf 25 -- bei 50 waren die unteren Kanten einiger Fenster angeschnitten. */
  padding-bottom: calc(var(--ulh-verhaeltnis) * 100% - 25px);
  overflow: hidden;
  /* Kleiner als der Rahmen: 12 gegen 16, gemessen an der Vorlage. Der Unterschied ist genau die
     Blende -- ein Schirm mit demselben Radius wie sein Rahmen sieht aufgeklebt aus. */
  border-radius: 12px;
  background: var(--ulh-schirm);
}
/* Die Buehne selbst: die feste Breite aus --ulh-buehne, oben links verankert, als Ganzes
   verkleinert. transform und nicht zoom: zoom aendert die gemessene Breite der Kinder und haette
   genau die schmalen Fassungen ausgeloest, die wir nicht wollen. */
.ulh-app {
  position: absolute;
  top: 0;
  left: 0;
  /* --ulh-buehne-ist setzt landing-hero.js: es ist das Maximum aus der Basisbreite und der
     wirklichen Fensterbreite, damit nie hochskaliert wird. Der Rueckfall ist die Basis. */
  width: var(--ulh-buehne-ist, var(--ulh-buehne));
  transform: scale(var(--ulh-mass));
  transform-origin: top left;
  display: flex;
  align-items: stretch;
  /* KEINE eigene Fuellung. Sie stand hier und an .ulh-main, zusammen mit der am Ausschnitt waren es
     drei Ebenen -- bei 95% je Ebene kommt daraus praktisch deckendes Weiss, und von der
     Durchsichtigkeit bliebe nichts. Die Fuellung liegt jetzt einmal, am Ausschnitt. */
  /* Beschneiden muss HIER passieren, nicht nur am Ausschnitt. Die Seitenleiste ist position: fixed,
     und ein fixed positioniertes Element beschneidet nur der Vorfahre, der auch sein Bezugsrahmen
     ist -- das ist dieser hier (er traegt das transform), nicht .ulh-view mit seinem overflow.
     Deshalb lief die grau hinterlegte Leiste ueber die runden Ecken hinaus und die beiden LINKEN
     Ecken sahen eckig aus, waehrend die rechten rund waren.
     Der Radius muss durch das Mass geteilt werden: was hier gezeichnet wird, verkleinert das
     transform anschliessend, und ohne die Division waere die Rundung innen kleiner als aussen. */
  overflow: hidden;
  border-radius: calc(12px / var(--ulh-mass, 1));
}
/* Mit zoom (siehe landing-hero.js, verkleinerungSetzen) wird die Rundung schon im Layout
   verkleinert -- sie darf dann NICHT noch einmal hochgerechnet werden, sonst ist sie innen
   groesser als aussen.
   contain ist dort PFLICHT und keine Feinheit: ein transform macht sein Element zum Bezugsrahmen
   fuer alles, was darin position: fixed traegt -- zoom tut das nicht. Ohne contain sprang die
   Seitenleiste aus dem Fenster heraus und legte sich ueber dessen Titelzeile; gemessen 64,557
   statt 70,589, also 6px links und 32px ueber der Fensterkante. layout stellt den Bezugsrahmen
   wieder her, paint beschneidet wie vorher das transform. */
.ulh-root.is-zoom .ulh-app { border-radius: 12px; contain: layout paint; }
/* Die Leiste bringt ihre Breite selbst mit; rechts davon der Seiteninhalt.
   min-width: 0, sonst sprengt eine breite Tabelle die Spalte. */
.ulh-side { flex: 0 0 auto; }
.ulh-main, .ulh-prompts, .ulh-chancen, .ulh-perf, .ulh-shop {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* 16px links und rechts, und 16px zwischen den Bloecken -- eine Zahl fuer alles. Die Abstaende
     INNERHALB der Bloecke (zwischen Chart und Tabelle, zwischen Chart und Liste bei den Zitaten)
     sind in den Komponenten schon 16px, die mussten nicht angefasst werden. */
  gap: 16px;
  padding: 1px 16px 0;
}
/* Die Komponenten im Fenster sind Schaustuecke. Nichts darin ist anklickbar, und der Zeiger soll
   das auch nicht behaupten -- der Ablauf spaeter fuehrt die Maus selbst.
   Der Zeiger bleibt trotzdem stehen: pointer-events: none an der Buehne wuerde auch die
   Tooltips der spaeteren Szenen verhindern, die ueber echte Hover-Zustaende laufen.

   HIER STAND ".ulh-app * { cursor: default }", UND DAS HAT NICHT GEREICHT (21.09. gemeldet).
   Gemessen waren im Fenster ueber hundert Elemente trotzdem auf pointer: die Komponenten setzen
   ihn aus Regeln, die entweder eine eigene Kennung tragen (#ask-mira ...) oder zwei Klassen
   fuehren, und (0,1,0) kommt gegen beides nicht an.
   Drei Aenderungen daran:
     - die WURZEL traegt jetzt eine Kennung (landing-hero.js, start()), damit hier (1,2,0)
       moeglich ist -- !important ist verboten, und Ladereihenfolge ist kein Argument (§1),
     - die Klasse steht DOPPELT (.up-root.up-root). Das ist kein Tippfehler, sondern der
       uebliche Weg, eine Spezifitaet um eine Klasse zu heben, ohne einen Vorfahren zu erfinden,
       den es gar nicht gibt (dieselbe Machart wie .udr-menu.udr-menu in export-data.css),
     - und es sind DREI Bereiche statt einem: .up-root faengt jede echte Komponente (die tragen
       alle diese Klasse), .ulh-fen die Nebenfenster und .ulh-vis die Vorschaukarten -- die
       beiden letzten sind Markup MIT den Klassen der App, aber ohne deren Wurzel.
   Was ausdruecklich NICHT betroffen ist: die Bedienelemente der Seite selbst. Sie tragen kein
   .up-root -- Navigation, die zwei Knoepfe, der Preisumschalter und die Fusszeile behalten ihren
   Zeiger, weil man sie wirklich anklicken kann. */
#ulh .up-root.up-root,  #ulh .up-root.up-root *,
#ulh .ulh-fen.ulh-fen,  #ulh .ulh-fen.ulh-fen *,
#ulh .ulh-vis.ulh-vis,  #ulh .ulh-vis.ulh-vis * { cursor: default; }

/* ---------- Was im Fenster anders steht als in der App --------------------------------------
   Diese vier Regeln fassen fremde Komponenten an, und das braucht eine Begruendung. Sie aendern
   nichts an deren Bauteilen, sondern nur, was das SCHAUSTUECK zeigt -- gebunden an .ulh-app, also
   ausschliesslich hier und niemals in der App. */

/* Die Legende unter der Linie faellt weg: im Fenster steht sie doppelt, denn die Tabelle rechts
   nennt dieselben fuenf Marken mit demselben Zeichen. */
.ulh-app .up-legend { display: none; }

/* ---------- Die Prompts-Seite im Schaustueck ----------
   Der Kasten der Tabelle auf 12px Radius, wie die vier Kaesten des Dashboards. In der App sind es
   16 -- dort steht die Tabelle allein auf der Seite, hier neben Kaesten mit 12. */
.ulh-app .up-box { border-radius: 12px; }
/* Die Gruppenliste links auch auf 12px -- sie ist der zweite Kasten derselben Ansicht. Mit
   .up-root davor, weil die 16px aus .up-root.is-grouped .upt-grp-sidepanel kommen und eine Klasse
   mehr tragen als .ulh-app allein. */
.ulh-app .up-root .upt-grp-sidepanel,
.ulh-app .up-root .upt-grp-sidelist { border-radius: 12px; }

/* Und sie ist schmaler als in der App: max(256px, 20%) ergibt hier 256, und dann bleiben der
   Tabelle 898px -- zu wenig fuer Prompt, Visibility, Rank, Sentiment, Brand Mentions UND Market
   (zusammen 934 an ihren Mindestbreiten). Die Spaltenanpassung der Komponente laesst dann Market
   fallen. 208px reichen fuer die vier Gruppennamen und geben der Tabelle 946.
   Das ist kein Streit mit der Komponente, sondern der Ausgleich fuer die Buehne: sie ist 1428
   breit, ein echter Schreibtisch ist breiter, und dort passen alle Spalten ohne Zutun. */
.ulh-app .up-root.is-grouped.is-groups-wide .upt-grp-sidepanel { width: 208px; }

/* Und dieselbe Verkleinerung der Schriften wie im Dashboard: dort liegt jede Groesse 1px unter der
   App, und eine zweite Seite in derselben Sektion mit den Originalgroessen liest sich als ein
   Sprung. Gemessen und nicht geraten -- die Werte links sind die, die die Komponente wirklich
   zeigt (up-th 12, up-heading 14, upt-prompt-text 14, upt-grp-name 13.5 und so weiter).
   Die geteilten up-*-Klassen (up-num, up-pop-*, up-filter-*, up-trend ...) stehen schon in den
   Gruppen weiter oben und sind hier nicht zu wiederholen. */
.ulh-app .up-heading,
.ulh-app .up-head-label,
.ulh-app .up-head-count,
.ulh-app .upt-prompt-text,
.ulh-app .upt-grp-sidehead-lbl { font-size: 13px; }
.ulh-app .upt-grp-name { font-size: 12.5px; }
.ulh-app .upt-brand-toggle,
.ulh-app .upt-brand-toggle-lbl { font-size: 12.3px; }
.ulh-app .upt-grp-sidechip,
.ulh-app .upt-grp-sideitem { font-size: 12px; }
/* .up-td steht NICHT hier: es ist schon 12px und damit auf demselben Wert wie .vt-td im
   Dashboard -- eine 11 daraus zu machen haette die Zeilentexte der zwei Seiten auseinandergebracht,
   also genau das Gegenteil. Gemessen, nicht angenommen. */
.ulh-app .up-th,
.ulh-app .up-page,
.ulh-app .up-pager-info,
.ulh-app .up-pagesize-lbl,
.ulh-app .up-pagesize-btn,
.ulh-app .up-stack-more,
.ulh-app .upt-brand-label,
.ulh-app .upt-grp-sidecount,
.ulh-app .upt-selcount-n,
.ulh-app .upt-status-btn { font-size: 11px; }

/* Die Werkzeugleiste der Prompts-Tabelle steht OFFEN und faehrt nicht beim Ueberfahren heraus.
   Angepinnt wird sie ueber die Einstellung (upt_tools__..., siehe landing-hero.js) -- hier faellt
   nur der Einklapp-Knopf weg: er ist im Schaustueck ein Angebot, das niemand annehmen kann, und
   nimmt links 32px Platz. */
.ulh-app .up-tbcol { display: none; }

/* Die Gruppenliste: Lupe und Sortierung weg -- beides Werkzeuge, die hier niemand braucht. Plus
   und der Knopf zum Einklappen bleiben, in der dritten Textfarbe: sie sind Beiwerk und sollen
   nicht wie die Beschriftung daneben wirken. */
.ulh-app .upt-grp-sidesort,
.ulh-app .upt-grp-sidesearch-btn { display: none; }
.ulh-app .upt-grp-sideadd,
.ulh-app .upt-grp-widebtn { color: var(--vc-third); }

/* Die Liste rundet nur UNTEN. Oben sitzt der Kopf der Liste, und der Radius dort liess die grau
   hinterlegte erste Zeile (All Prompts) an ihren oberen Ecken angeschnitten aussehen. */
.ulh-app .up-root .upt-grp-sidelist { border-radius: 0 0 12px 12px; }

/* Das Ueberfahren einer ZEILE faerbt nicht mehr das Kaestchen mit. Das ist in der App richtig --
   dort will man wissen, dass man die Zeile auswaehlen kann --, hier ist nichts auswaehlbar, und
   ein mitleuchtendes Kaestchen sieht nach einem Angebot aus. Auf dem Kaestchen selbst bleibt es. */
.ulh-app .up-row:hover .upt-check:not(.is-checked) { border-color: var(--vc-border); }
.ulh-app .upt-check:hover:not(.is-checked) { border-color: var(--vc-third); }

/* Die Eintraege der Seitenleiste 2px flacher und die vier Gruppen enger beieinander -- im
   Fenster steht die Leiste ganz, und die zwei Pixel je Zeile sind am Ende eine Zeile mehr Platz.

   Die zweite Runde kam aus einer Messung: der Inhalt der Leiste war 594px hoch, sichtbar waren
   561 -- der letzte Pin (kestrel.example) lag bei 560 bis 594 und damit vollstaendig unter der
   Kante. Gescrollt wird hier nicht, also war er schlicht nicht da. Das war der Bericht "bei
   Pinned fehlt die Domain".
   Die Abstaende sind gegenueber der App veraendert, die Zeilenhoehe NICHT: der Abstand ueber der
   Liste (24 -> 24, also wie in der App), der ueber den Schnellaktionen (24 -> 16), die drei zwischen den vier Gruppen
   (32 -> 16) und die Unterkante der vier Gruppenbeschriftungen (6 -> 4).
   Gemessen wird an der Liste selbst: scrollHeight gegen clientHeight. Passt sie nicht mehr, fehlt
   unten die gepinnte Domain -- dann muss die Luft woanders herkommen oder das Fenster hoeher
   werden. Stillschweigend die Zeilen zu stauchen ist keine Loesung, das ist einmal passiert und
   wieder zurueckgenommen. */
/* Die Zeilenhoehe ist die der App -- 34px, unveraendert. Ich hatte sie einmal auf 31 gesetzt, um
   Platz zu schaffen; das war nicht gefragt und ist wieder zurueck. Die Liste ist damit hoeher als
   ihre Flaeche und scrollt am Ende; was das bedeutet, steht in der Uebergabe. */
/* Die Leiste ist 6px schmaler als in der App: 2px weniger Polster je Seite und 2px weniger
   Breite. Was sie abgibt, bekommt der Bereich rechts -- er richtet sich an ihrer Breite aus. */
.ulh-app .usn-item { height: 34px; }
.ulh-app .usn-block + .usn-block { margin-top: 16px; }
/* Der Vorhang der Chancen-Schublade blendet nur das BRETT ab, nicht die Seitenleiste. In der App
   ist er "position: fixed; inset: 0" und deckt das ganze Fenster -- dort ist das richtig, denn dort
   IST das Fenster die Seite. Hier steht die App in einem Bild, und eine weichgezeichnete Navigation
   sieht aus, als waere sie kaputt. 185px sind gemessen: die Leiste ist 180 breit und sitzt 5px
   innerhalb der Buehne, und die Buehne ist der Bezugsrahmen des Vorhangs (sie traegt das
   transform). */
.ulh-app .uo-scrim { left: 185px; }
/* Das Brett steht buendig mit allem anderen im Fenster. In der App traegt seine Wurzel 16px
   Polster links und rechts; der Seitenkopf darueber und die Prompts-Tabelle haben keines, und
   damit begann das Brett 16px weiter rechts als alles andere -- gemessen 319 gegen 303. */
.ulh-app .uo-root { padding-left: 0; padding-right: 0; }
/* 2px mehr zwischen der Gruppenbeschriftung und ihrer ersten Zeile. */
.ulh-app .usn-head { padding-bottom: 4px; }
/* Der Kasten der Schnellaktionen 30px hoch und 24px Luft darunter: er ist ein Knopf und keine
   Zeile der Liste, und mit demselben Abstand wie zwischen zwei Zeilen las er sich als eine. */
.ulh-app .usn-qa > * { height: 30px; }
/* Die Liste WAECHST hier nicht ueber ihren Inhalt hinaus (flex 0 1 statt 1 1). Sonst nimmt sie
   allen freien Platz, der Abstand darunter faellt auf null, und die Konto-Zeile klebt am letzten
   Menuepunkt. Schrumpfen darf sie weiterhin -- in einem zu niedrigen Fenster ist Scrollen besser
   als ein abgeschnittener Eintrag. */
.ulh-app .usn-nav { margin-top: 24px; flex: 0 1 auto; }
/* Die Konto-Zeile wird nach UNTEN geschoben, statt 64px Mindestabstand ueber sich zu verlangen.
   In der App ist die Leiste so hoch wie das Fenster, dort sind die 64 Luft. Hier ist sie 742px
   hoch, und genau diese 64 fehlten der Liste: sie braucht 554 und bekam 498 -- die gepinnte
   Domain fiel unten heraus, und der Kasten mit den Menuepunkten wurde scrollbar.
   Mit auto nimmt der Abstand, was uebrig ist: bleibt Platz, steht die Zeile trotzdem unten;
   bleibt keiner, faellt er auf null, bevor die Liste anfaengt zu scrollen. */
.ulh-app .usn-accwrap { margin-top: auto; }
.ulh-app .usn-qa:not(:empty) { margin-top: 16px; }

/* Die ausgewaehlte Zeile in der Seitenleiste etwas heller: 7 Prozent Textfarbe waren im
   Schaustueck neben dem fast weissen Grund ein deutlicher Kasten. */
.ulh-app .usn-bar { --usn-sel: color-mix(in srgb, var(--vc-text) 4.5%, transparent); }

/* ---------- Mira, Feinheiten ----------
   Die Knopfreihe unter einer Antwort (Export, Kopieren, Daumen) steht dauerhaft da statt beim
   Ueberfahren zu erscheinen: im Schaustueck fasst niemand die Nachricht an, und die Reihe gehoert
   zum Bild einer fertigen Antwort. pointer-events bleibt aus -- sie ist zum Ansehen.
   Waehrend des Tippens bleibt sie weg, das regelt ask-mira.css selbst. */
.ulh-app .am-msg-actions { opacity: 1; transform: none; pointer-events: none; }

/* Die Datenpunktzeile erscheint erst MIT der fertigen Antwort. Waehrend getippt wird stand sie
   schon da -- sie haengt in .am-msg-main neben der Blase und wird vom Tippen nicht mit versteckt --
   und las sich als Teil der Ladeanzeige: zwei farbige Chips ueber einer Antwort, die es noch nicht
   gibt. opacity und nicht display: das Tippen setzt display an ihren Teilen selbst, und ein
   Inline-Stil schlaegt jede Regel. */
.ulh-app .am-msg.am-typing .am-evidence { opacity: 0; }

/* Der Text in der Nutzerblase sass minimal zu hoch: bei 15px Schrift und 1.7 Zeilenhoehe traegt
   die Blase oben und unten 12px, und der optische Schwerpunkt der Schrift liegt darueber. 1px vom
   unteren Polster nach oben schiebt ihn auf die Mitte. */
.ulh-app .am-msg.is-user .am-bubble { padding: 13px 15px 11px; }

/* Der Fokusrahmen des Eingabefelds waehrend des Tippens. In der App entsteht er ueber
   :focus-within -- hier wird nie etwas fokussiert (die Buehne schluckt focusin), also setzt
   landing-hero.js die Klasse. Halb so kraeftig wie in der App: der Ring bei 50 Prozent seiner
   Deckkraft, die Kante zur Haelfte in Richtung des normalen Rahmens gemischt. */
.ulh-app .am-composer.is-tippt {
  border-color: color-mix(in srgb, var(--up-focus) 50%, var(--am-border));
  box-shadow: 0 0 0 var(--up-focus-w) color-mix(in srgb, var(--up-focus-ring) 50%, transparent),
              var(--am-shadow-sm);
}

/* Der Klick auf den Sendeknopf. Kein Zeiger im Bild, aber die Bewegung, die ein Klick macht:
   Hoverfarbe, kurz 2px kleiner, dann zurueck. .am-send:active gibt es schon (scale .95) -- das
   greift nur bei einem echten Zeigerdruck, den es hier nicht gibt. */
/* Langsamer und in beide Richtungen weich: 260ms mit ein- UND ausschleifendem Verlauf
   (cubic-bezier .4/0/.2/1). Die federnde Kurve davor war kurz und hart -- bei 120ms sieht man von
   einer Feder nichts ausser dem Ruck. */
.ulh-app .am-send { transition: transform 260ms cubic-bezier(.4, 0, .2, 1), background 260ms cubic-bezier(.4, 0, .2, 1); }
.ulh-app .am-send.is-klick { transform: scale(.9); background: #585855; }

/* Das Eingabefeld faehrt beim Abschicken nach unten -- die Fahrt selbst ist Miras eigene (FLIP,
   200ms, ask-mira.js: setHasMessages). Gemessen: die Strecke betraegt 289px, von 400 auf 689.
   Was hier steht, ist NUR das spaetere Ausblenden. Vorher lag es mit 95ms Verzoegerung mitten auf
   der Fahrt -- das Feld war bei 205ms weg, also bevor es unten ankam, und genau deshalb war die
   Bewegung nicht zu sehen ("ich sehe immernoch nicht, dass das Inputfeld nach unten slidet").
   Jetzt bleibt es die ganze Fahrt und die Ladezeit ueber stehen, wie in der App, und geht erst
   kurz vor der Antwort weg -- die braucht seine 125px. Den Zeitpunkt setzt landing-hero.js. */
.ulh-app .am-composer-area { transition: opacity 260ms ease; }

/* Die Zeilen in Miras Antworttabelle kommen einzeln herein, wie die der Prompts-Tabelle.
   --ulh-i setzt landing-hero.js an jeder Zeile, sobald die Antwort steht. */
/* ---- WENIGER LUFT ZWISCHEN KOPFZEILE UND CHAT (24.09. angefordert) --------------------------
   "Mach das Gap zwischen Topbar mit Titel und Start Mira-Chat-Content etwas kleiner, dass man die
   Nachricht + die Evidence-Chips + die Bottom-Buttons sieht."
   In der App hat der Chat oben 25px Polster -- richtig fuer ein Fenster, das so hoch ist wie der
   Bildschirm. Hier ist der Ausschnitt fest, und die 25px fehlen unten bei der Knopfreihe.
   8px statt 25: die Nachricht faengt naeher an der Zeile an, und die 17px stehen der Antwort zur
   Verfuegung. Nur in diesem Schaustueck -- die App bleibt bei 25. */
.ulh-root .ulh-mira .am-chat { padding-top: 8px; }

.ulh-root .ulh-mira.is-zeilen .am-msg table tbody tr {
  animation: ulhRise 420ms var(--ulh-expo) backwards;
  animation-delay: calc(var(--ulh-i, 0) * 60ms);
}
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-mira.is-zeilen .am-msg table tbody tr { animation: none; }
}

/* ---------- Der Auftritt des Dashboards, ab der zweiten Runde ----------
   Beim ersten Mal steigt das Dashboard mit dem Fenster herauf (is-entering am Root, siehe oben).
   Im Kreislauf danach kommt es wie jede andere Seite zurueck -- und dann soll es sich genauso
   aufbauen wie die drei Seiten nach ihm: Seitenkopf, dann die vier Kaesten einzeln, jeder mit
   seiner Ueberschriftzeile und seinem Panel. Die Seite selbst steigt dabei NICHT, sonst laegen
   zwei Bewegungen uebereinander. */
.ulh-root .ulh-main.is-kommt { animation: none; }
.ulh-root .ulh-main.is-kommt .dph-root { animation: ulhRise 600ms var(--ulh-expo) backwards; }
/* DIE VIER KAESTEN STEIGEN NUR NOCH UEBER is-inhalt AUF (06.10. spaet). Hier standen bis dahin
   eigene Animationen fuer sie -- ein zweiter Weg neben is-inhalt, und zwischen beiden fielen die
   Kaesten auf unsichtbar zurueck ("das fertige Dashboard blitzt kurz auf, verschwindet, dann
   legt die Animation los"). landing-hero.js setzt is-inhalt im Kreislauf NEUSTART_INHALT (140ms)
   nach dem Seitenkopf -- derselbe Auftritt wie beim ersten Laden. */
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-main.is-kommt .dph-root { animation: none; }
}
/* KEINE DECKKRAFT-TRANSITION AN DEN CHART-KAESTEN. visibility-chart.css gibt .vot-box
   "transition: height 200ms, opacity 200ms" mit. Im Hero ist das eine Falle: eine laufende
   Transition schlaegt in der Kaskade jede Animation -- beim Setzen von is-inhalt blendete der
   Kasten 200ms linear auf statt mit ulhRise, und beim Wegnehmen blendete er sichtbar AUS (das war
   das Aufblitzen im Kreislauf, gemessen 33 Prozent Deckkraft). Die Hoehe behaelt ihren Uebergang. */
.ulh-root .vot-box { transition: height 200ms ease; }

/* ---------- Der Auftritt des Opportunities-Bretts ----------
   Wie bei der Prompts-Seite: die Seite selbst steigt nicht herauf, ihre Teile tun es nacheinander.
   Erst der Seitenkopf, dann die Werkzeugleiste des Bretts, dann die vier Spalten von links nach
   rechts. Die Karten darin kommen mit ihrer Spalte -- eine zweite Staffelung innerhalb einer
   Spalte, die selbst noch aufblendet, waere dieselbe Falle wie bei der Prompts-Tabelle. */
.ulh-root .ulh-chancen.is-kommt { animation: none; }
.ulh-root .ulh-chancen.is-kommt .up-ph-root { animation: ulhRise 680ms var(--ulh-expo) backwards; }
.ulh-root .ulh-chancen.is-kommt .uo-top { animation: ulhRise 680ms var(--ulh-expo) 140ms backwards; }
.ulh-root .ulh-chancen.is-kommt .uo-col { animation: ulhRise 680ms var(--ulh-expo) backwards; }
.ulh-root .ulh-chancen.is-kommt .uo-col:nth-child(1) { animation-delay: 300ms; }
.ulh-root .ulh-chancen.is-kommt .uo-col:nth-child(2) { animation-delay: 400ms; }
.ulh-root .ulh-chancen.is-kommt .uo-col:nth-child(3) { animation-delay: 500ms; }
.ulh-root .ulh-chancen.is-kommt .uo-col:nth-child(4) { animation-delay: 600ms; }
/* Und dann die Karten, eine nach der anderen. Dieselbe Falle wie bei der Prompts-Tabelle: eine
   Karte, die INNERHALB einer Spalte auftritt, die selbst noch aufblendet, tritt nicht auf -- man
   sieht nur die Spalte kommen. Also bleiben die Karten versteckt, solange die Spalten laufen
   (visibility, nicht opacity: die Spalte behaelt ihre Hoehe und springt nicht), und fallen danach
   einzeln ein. Der Zaehler --ulh-i steht an der Karte und kommt aus landing-hero.js, quer ueber
   alle Spalten in Lesereihenfolge. */
.ulh-root .ulh-chancen.is-kommt .uo-card { visibility: hidden; }
.ulh-root .ulh-chancen.is-karten .uo-card {
  visibility: visible;
  animation: ulhRise 550ms var(--ulh-expo) backwards;
  animation-delay: calc(var(--ulh-i, 0) * 90ms);
}
/* "Nothing here yet" tritt MIT auf (21.09. gemeldet: "sieht komisch aus, dass der schon sichtbar
   ist, bevor die Daten kommen"). Der Platzhalter stand von Anfang an da, waehrend die Karten
   daneben noch versteckt waren -- also las sich die leere Spalte als das Ergebnis, bevor es
   ueberhaupt eines gab.
   Dieselbe Mechanik wie bei den Karten und dieselbe Kurve, nur mit einer eigenen Verzoegerung:
   er gehoert zur LETZTEN Spalte, also kommt er, wenn die Karten durch sind -- vier Karten mal
   90ms Staffelung plus deren Laufzeit, aufgerundet auf 520. */
.ulh-root .ulh-chancen.is-kommt .uo-col-empty { visibility: hidden; }
.ulh-root .ulh-chancen.is-karten .uo-col-empty {
  visibility: visible;
  animation: ulhRise 550ms var(--ulh-expo) 520ms backwards;
}
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-chancen.is-karten .uo-col-empty { animation: none; }
  .ulh-root .ulh-chancen.is-kommt .uo-col-empty { visibility: visible; }
}
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-chancen.is-kommt .up-ph-root,
  .ulh-root .ulh-chancen.is-kommt .uo-top,
  .ulh-root .ulh-chancen.is-kommt .uo-col,
  .ulh-root .ulh-chancen.is-karten .uo-card { animation: none; }
  .ulh-root .ulh-chancen.is-kommt .uo-card { visibility: visible; }
}

/* Eine Karte wird angefasst, bevor sie wandert. Es gibt keinen Zeiger im Bild, also traegt eine
   Klasse den Hover -- die beiden Werte sind wortgleich die von .uo-card:hover in
   opportunities.css. Nachgebaut ist hier nichts: dort steht der Zustand fuer eine echte Maus,
   hier wird derselbe Zustand gesetzt, weil im Schaustueck niemand zeigen kann.
   Der Druck ist eine zweite, eigene Bewegung: drei Prozent kleiner und zurueck, mit derselben
   ease-in-out-Kurve wie alles andere im Fenster. transform steht deshalb in der Uebergangsliste
   der Karte -- die Liste aus opportunities.css wird dabei vollstaendig mitgefuehrt, sonst faellt
   der weiche Farbwechsel des Hovers weg. */
.ulh-app .uo-card {
  transition: border-color 160ms var(--ulh-ease), background 160ms var(--ulh-ease),
              transform 170ms var(--ulh-ease);
}
.ulh-app .uo-card.ulh-fass { border-color: var(--vc-hover-border); background: var(--vt-row-hover); }
.ulh-app .uo-card.ulh-druck { transform: scale(0.97); }
/* KEINE DURCHSICHTIGE KARTE WAEHREND DES ZUGES (21.09. gemeldet). opportunities.css setzt
   ".uo-card.is-dragging { opacity: 0.45 }" -- in der App richtig: dort ist die halbdurchsichtige
   Karte das Geisterbild, das unter dem Finger bleibt, waehrend die echte woanders einrastet.
   Hier zieht niemand: die Karte wandert von selbst, und eine, die dabei halb verschwindet, sieht
   nach einem Fehler aus. (0,3,0) gegen (0,2,0), also ueber Spezifitaet und nicht ueber die
   Reihenfolge. */
.ulh-app .uo-card.is-dragging { opacity: 1; }

/* DIE WANDERNDE KARTE: ausgegraut und angehoben, NIE durchsichtig (21.09. angefordert).
   opacity steht hier ausdruecklich auf 1 -- nicht als Schmuck, sondern weil genau das gemeldet
   wurde: die Karte wirkte beim Ziehen halbdurchsichtig. Sie ist es jetzt an keiner Stelle des
   Weges; was sie vom Rest unterscheidet, sind Farbe und Schatten.
   Die Farben sind die des Hover-Zustands aus opportunities.css -- dieselbe Sprache wie beim
   Anfassen, nur eine Stufe deutlicher. Der Schatten ist die dritte Stufe des Fensterschattens
   und sagt "diese Karte liegt gerade oben". 1.5 Prozent groesser, mehr nicht: eine Karte, die
   beim Wandern sichtbar waechst, sieht aus, als waere sie naeher an der Kamera. */
.ulh-app .uo-card.ulh-zieht {
  opacity: 1;
  background: var(--vc-heading-bg);
  border-color: var(--vc-hover-border);
  box-shadow: 0 10px 26px rgba(16, 20, 24, .14);
}

/* DIE DREI KENNZAHLENKARTEN UEBER DER PROMPT-TABELLE FALLEN WEG (21.09. gemeldet: "hab ich mit
   einem Wort gesagt, dass ich die Cards ueberm Table haben will? nein").
   prompts-table.js baut sie beim Aufbau immer (kpiBauen, ohne Bedingung) -- in der App gehoeren
   sie dorthin, im Schaustueck nehmen sie 245px, und zu sehen sein soll die TABELLE. Die
   Komponente bleibt unangetastet, hier faellt nur weg, was das Fenster nicht zeigen soll. */
.ulh-app .upt-kpis { display: none; }
/* Die Filterzeile der Shopping-Seite (Kalender und Filter) faellt im Fenster weg (06.10.): die
   Landingpage laedt date-range und filter-bar nicht, die Zeile stand als 34px leerer Streifen
   zwischen Reitern und Inhalt. Die anderen Seiten im Fenster zeigen ebenfalls keinen Kalender. */
.ulh-app .ush-filterzeile { display: none; }
/* SHOPPING IM FENSTER, ZWEITE FASSUNG (06.10. angefordert: "Summary und KPIs bleiben oben, darunter
   statt dem Linechart eine Produktliste im komfortablen Row-Height-Stil, nicht horizontal
   scrollbar"). Die Uebersicht der Komponente bleibt, wie sie ist -- im Fenster fallen nur die
   Abschnitte zwischen den Kennzahlen und den Top Products weg (Kurve, Brand Landscape) und die zwei
   kleinen darunter. Die Tabelle steht damit direkt unter den Kennzahlen.
   Komfortabel heisst die 72 der App (core: .up-row) statt der 55, die die Vorschau-Tabellen der
   Uebersicht tragen (.ush-vorschau.is-dense) -- auch fuer das Skelett, sonst springt die Hoehe,
   wenn die Daten kommen. Die Spalten waehlt landing-hero.js (Product, Visibility, Avg. Position,
   Rating, Price: zusammen rund 760px in 1078 -- kein Querscrollen). */
.ulh-app .ulh-shop [data-sek="chart"],
.ulh-app .ulh-shop [data-sek="landscape"],
.ulh-app .ulh-shop .ush-zwei { display: none; }
.ulh-app .ulh-shop .ush-vorschau.is-dense .up-tbody .up-row { height: 72px; flex: 0 0 72px; }
/* DER AUFTRITT DER SHOPPING-SEITE (06.10. spaet: "erst oberer Teil, dann die Tabelle Row fuer
   Row, wie z.B. bei Prompts Table"). Derselbe Bau wie .ulh-prompts.is-kommt, mit denselben Zahlen:
   die Seite selbst steigt NICHT, ihre Teile tun es nacheinander -- Seitenkopf und Reiter 0,
   Summary 140, Kennzahlen 220, Top Products 300. Der Tabellenkoerper bleibt verborgen, bis
   is-zeilen uebernimmt (ZEILEN_START, 760ms), dann fallen die Zeilen einzeln ein, 55ms
   auseinander. visibility und nicht opacity: der Platz bleibt stehen, die Tabelle springt nicht.
   Die Daten sind dabei schon da -- landing-hero.js fuellt im selben Zug (shopFuellen). */
.ulh-root .ulh-shop.is-kommt { animation: none; }
.ulh-root .ulh-shop.is-kommt .ush-pagehead,
.ulh-root .ulh-shop.is-kommt .ush-nav { animation: ulhRise 680ms var(--ulh-expo) backwards; }
.ulh-root .ulh-shop.is-kommt [data-sek="summary"] { animation: ulhRise 680ms var(--ulh-expo) 140ms backwards; }
.ulh-root .ulh-shop.is-kommt [data-sek="kpis"] { animation: ulhRise 680ms var(--ulh-expo) 220ms backwards; }
.ulh-root .ulh-shop.is-kommt [data-sek="topprodukte"] { animation: ulhRise 680ms var(--ulh-expo) 300ms backwards; }
.ulh-root .ulh-shop.is-kommt .ush-vorschau .up-tbody { visibility: hidden; }
.ulh-root .ulh-shop.is-zeilen .ush-vorschau .up-tbody { visibility: visible; }
.ulh-root .ulh-shop.is-zeilen .ush-vorschau .up-tbody .up-row {
  animation: ulhRise 550ms var(--ulh-expo) backwards;
  animation-delay: calc(var(--ulh-i, 0) * 55ms);
}
/* Das Product Detail nach dem Klick: dieselbe Staffel fuer seine Teile -- Kopf mit Foto und
   Kennzahlen, wo es erscheint, Haendler, Auftritte. Seitenkopf und Reiter bleiben stehen, wie in
   der App, wenn man ein Produkt oeffnet. is-detail setzt shopProduktKlick. */
.ulh-root .ulh-shop.is-detail [data-sek="hero"] { animation: ulhRise 680ms var(--ulh-expo) backwards; }
.ulh-root .ulh-shop.is-detail [data-sek="wo"] { animation: ulhRise 680ms var(--ulh-expo) 140ms backwards; }
.ulh-root .ulh-shop.is-detail [data-sek="dhaendler"] { animation: ulhRise 680ms var(--ulh-expo) 220ms backwards; }
.ulh-root .ulh-shop.is-detail [data-sek="tabelle"] { animation: ulhRise 680ms var(--ulh-expo) 300ms backwards; }
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-shop.is-kommt .ush-pagehead,
  .ulh-root .ulh-shop.is-kommt .ush-nav,
  .ulh-root .ulh-shop.is-kommt [data-sek],
  .ulh-root .ulh-shop.is-zeilen .ush-vorschau .up-tbody .up-row,
  .ulh-root .ulh-shop.is-detail [data-sek] { animation: none; }
}
/* Der Klick auf das Produkt (shopProduktKlick): erst der Hover der Zeile aus core, dann der Druck --
   wie bei der Karte im Brett, nur ein Prozent statt drei: eine Zeile ist breit, drei Prozent
   wuerden sie sichtbar schrumpfen lassen. */
.ulh-app .ulh-shop .ush-vorschau .up-row { transition: background 160ms var(--ulh-ease), transform 170ms var(--ulh-ease); }
.ulh-app .ulh-shop .ush-vorschau .up-row.ulh-fass,
.ulh-app .ulh-shop .ush-vorschau .up-colscroll .up-row.ulh-fass > .up-td:first-child { background: var(--vt-row-hover); }
.ulh-app .ulh-shop .ush-vorschau .up-row.ulh-druck { transform: scale(0.99); }


/* ---------- Die Karten im Fenster: flacher ----------
   Gemessen: eine Karte war 285px hoch, ueber dem Brett stehen 261px Kopf und Werkzeugleiste, und
   im Fenster bleiben 814. Drei Karten in Pending brauchten damit 879 von 553 verfuegbaren -- die
   dritte lag ganz unter der Kante, die zweite zur Haelfte.
   Gekuerzt wird an den Abstaenden und an einer Zeile, die im Schaustueck nichts sagt (das
   Anlagedatum in der Fusszeile) -- nicht an der Schrift und nicht an den Themen: die sollen
   lesbar bleiben. Zusammen 74px je Karte, dazu einzeilige Begruendungen in landing-hero.js.
   Die dritte Karte bleibt trotzdem angeschnitten, und das ist so gewollt: ein Brett, dessen
   unterste Karte am Rand endet, sagt "hier geht es weiter" -- eines, das genau aufgeht, sagt
   "das ist alles". */
.ulh-app .uo-card { padding: 13px; }
.ulh-app .uo-card-top { margin-bottom: 10px; }
.ulh-app .uo-card-reason { margin-top: 6px; }
.ulh-app .uo-source { margin-top: 12px; }
.ulh-app .uo-tags { margin-top: 10px; }
/* Das Anlagedatum: im Bild steht ueberall derselbe Tag (die Daten entstehen beim Laden), es
   traegt also nichts bei und kostet 32px je Karte. */
.ulh-app .uo-card-foot { display: none; }

/* ---------- Zweite Sektion: die Kernfunktionen ----------
   Ueberschrift links und nicht mittig: der Text darueber im Hero ist mittig, weil er allein
   steht -- hier beginnt ein Abschnitt, der gelesen wird, und der beginnt an der Kante der Spur.
   Die Werte der Ueberschrift sind vorgegeben: Geist, 40px, mittleres Gewicht, -0.04em, 1.1. */
.ulh-feat { position: relative; z-index: 1; padding: clamp(72px, calc(11 * var(--ulh-fh)), 132px) 0 clamp(80px, calc(12 * var(--ulh-fh)), 144px); }
/* MITTIG seit 14.10. abends ("Die H2 mittig zentrieren"). Die Begruendung fuer links oben (ein
   Abschnitt beginnt an der Kante der Spur) war eine Entscheidung, keine Messung -- mittig stehen
   jetzt Chip, Ueberschrift und Unterzeile wie im Hero und in der Quellen-Sektion, und die Seite
   hat EINE Achse. Die Breitengrenzen bleiben (20ch, 62ch); auto-Raender stellen sie in die Mitte. */
.ulh-feat-kopf { padding: 0 24px; text-align: center; }
/* Der Chip ueber der Ueberschrift. Eine Stufe heller als der Pitch-Chip im Team-Fenster oben
   (#1d4ed8): dort steht er auf Weiss in einem kleinen Fenster, hier auf einer hellen Seite neben
   einer 40px-Ueberschrift, und der dunkle Ton wirkte daneben schwer. #3b82f6 ist derselbe blaue
   Faden, nur heller -- naeher am Blau des Bildes im Hero. */
.ulh-feat-chip {
  display: inline-flex; align-items: center; height: 22px; padding: 0 9px;
  border-radius: 7px; background: rgba(59, 130, 246, 0.10);
  font-size: 11.5px; font-weight: 600; letter-spacing: .02em; color: #3b82f6;
  margin-bottom: 18px;
}
.ulh-feat-h {
  margin: 0;
  font-family: inherit;
  /* Der Kopf traegt das Polster jetzt selbst (ulh-feat-kopf) -- hier waere es zweimal. */
  padding: 0;
  font-size: 40px; font-weight: 500; letter-spacing: -0.04em; line-height: 1.1;
  color: var(--ulh-text);
  text-align: center;
  margin-left: auto; margin-right: auto;
  /* Zwei Zeilen gleich lang statt einem einzelnen Wort unten (06.10. spaet, mit den neuen
     Ueberschriften: "From AI answers to your next / move"). */
  text-wrap: balance;
  /* KEIN eigenes Polster. Hier stand noch das alte "padding: 0 24px" -- und weil der Kopf um die
     drei Elemente dasselbe Polster schon traegt, begann der Text der Ueberschrift 24px weiter
     rechts als Chip und Unterzeile darunter. Genau das war die gemeldete Luecke davor. */
  max-width: 20ch;
}
.ulh-feat-sub {
  margin: 18px auto 0;
  padding: 0;
  max-width: 62ch;
  font-size: 16px; font-weight: 400; line-height: 1.55; letter-spacing: -0.01em;
  color: var(--ulh-sub);
  /* Wie die Unterzeilen der Karten: kein einzelnes Wort allein unten (06.10. nachts gemessen,
     375px: "... as your / visibility."). */
  text-wrap: pretty;
}

/* Zwei Reihen, je zwei Karten, 8px dazwischen -- waagerecht wie senkrecht derselbe Abstand.
   Die Anteile stehen als Zahl an der Karte (--ulh-w: 40 oder 60) und werden hier zur Flexbasis:
   flex-grow im Verhaeltnis 40 zu 60, flex-basis 0, damit allein das Verhaeltnis zaehlt und nicht
   die Textmenge darin. Ein Raster koennte das nicht -- seine Spalten gelten fuer alle Zeilen. */
/* Der Kasten um die vier Karten: 8px Polster ringsum, 8px Abstand zu den Schienen des Gitters,
   off-white, mit Rahmen und demselben leisen Schatten wie die Karten darin. Er ist der Grund, auf
   dem die Karten ueberhaupt als Karten lesbar sind -- reines Weiss auf 95%-Weiss waere nur ein
   Rahmen. */
.ulh-cards-box {
  margin: clamp(36px, calc(5 * var(--ulh-fh)), 56px) 8px 0;
  padding: 4px;
  background: var(--ulh-kasten);
  border: 1px solid var(--ulh-border);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .035), 0 6px 16px rgba(11, 13, 24, .028);
}
.ulh-cards { display: flex; flex-direction: column; gap: 4px; }
.ulh-cards-row { display: flex; gap: 4px; align-items: stretch; }

.ulh-card {
  flex: var(--ulh-w, 50) 1 0;
  min-width: 0;
  display: flex; flex-direction: column;
  /* Reines Weiss auf dem off-white Grund der Seite, dazu Rahmen und ein Schatten, der die Karte
     einen Millimeter anhebt und nicht mehr. Die Werte sind die der App (--vc-border) und die
     leiseste Stufe des Fensterschattens oben. */
  background: #ffffff;
  border: 1px solid var(--ulh-border);
  border-radius: 16px;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .035), 0 6px 16px rgba(11, 13, 24, .028);
  padding: 26px 26px 24px;
}
/* Die Karte beginnt mit der Ueberschrift. Vorher stand darueber eine Zeile aus Zeichen und
   Beschriftung -- zwei Etiketten vor dem Satz, den sie erklaeren sollten. */
.ulh-card-h {
  margin: 0;
  font-size: 19px; font-weight: 500; letter-spacing: -0.022em; line-height: 1.25;
  color: var(--ulh-text);
  max-width: 26ch;
  /* Kein einzelnes Wort allein in der zweiten Zeile (06.10. spaet gemessen, auf dem Telefon:
     "Know which buyer questions you / win"): Ueberschrift ausgeglichen, Unterzeile ohne Waise. */
  text-wrap: balance;
}
.ulh-card-p {
  margin: 9px 0 0;
  font-size: 14.5px; font-weight: 400; line-height: 1.55; letter-spacing: -0.005em;
  color: var(--ulh-sub);
  max-width: 54ch;
  text-wrap: pretty;
}
/* ---- Die Vorschau in der Karte ----
   Sie steht unten, haelt die zwei Karten einer Reihe auf derselben Hoehe (margin-top: auto) und
   zeigt das ECHTE Bauteil, von dem die Karte spricht.
   Der innere Kasten hat die VOLLE Groesse des Bauteils und wird als Ganzes verkleinert -- dieselbe
   Machart wie in den Nebenfenstern oben (--ulh-fz dort, --ulh-vs hier). Damit behaelt die Vorschau
   die Geometrie der App (45px Kopfzeile, 12px Zellenpolster) statt einer zweiten, kleineren, die
   nirgends sonst vorkommt. Die Breite des inneren Kastens ist entsprechend groesser gerechnet,
   damit er nach der Verkleinerung genau die Karte fuellt. */
.ulh-vis {
  /* 40 statt 26 (06.10. angefordert: "Gap zwischen Text und Grafik etwas groesser, auch wenn die
     Cards dadurch minimal groesser werden"). Jede --ulh-vh unten ist um dieselben 14px mitgewachsen
     -- die sichtbare Vorschau bleibt also genau so gross, nur der Abstand darueber waechst. */
  margin-top: auto; padding-top: 40px;
  --ulh-vs: 0.86;
  position: relative;
  /* Die Hoehe steht am AEUSSEREN Kasten und nicht am inneren: transform: scale aendert die
     Layouthoehe nicht, der innere Kasten belegt also weiter seine unskalierten Pixel. Gemessen
     stand unter der Tabellenvorschau dadurch 64px Luft, die niemand sehen konnte. */
  overflow: hidden;
}
.ulh-vis-in {
  width: calc(100% / var(--ulh-vs));
  transform: scale(var(--ulh-vs));
  transform-origin: top left;
}
/* Die Hoehe der Vorschau steht am AUSSEREN Kasten und nicht am inneren: der innere ist skaliert,
   seine Hoehe zaehlt fuer das Layout also nicht mehr in gezeichneten Pixeln. Ohne diese Angabe
   waere unter jeder Vorschau der Rest ihrer unskalierten Hoehe als Leerraum stehengeblieben.
   Die Werte sind gemessen (unskalierte Hoehe des Bauteils mal Massstab), nicht geschaetzt. */
/* BEIDE Reihen gleich hoch, und dazu 30px mehr als zuerst. Gerechnet und nicht geraten: die
   Reihenhoehe entsteht aus Text plus Vorschau, und die Texte sind verschieden lang -- die Zahlen
   hier sind so gesetzt, dass beide Reihen auf dieselbe Hoehe kommen (gemessen 428px). */
.ulh-vis-linie   { --ulh-vs: 1;    --ulh-vh: 288px; }
.ulh-vis-zeilen  { --ulh-vs: 0.74; --ulh-vh: 288px; }
/* DIE ERSTE REIHE IST HOEHER (07.10. angefordert: "die Cards mit der Prompts-Tabelle und die links
   daneben groesser in height"). Ueber der Tabelle steht jetzt der Kopf der App (Ueberschrift,
   Zahl, Werkzeugleiste, Export), und sie ist kleiner skaliert (0.74 -> 0.66) -- 400px zeigen damit
   den Kopf, die Spaltenkoepfe und gut fuenf Zeilen, bevor der Auslauf unten beginnt. Die Kurve
   links waechst auf dieselbe Hoehe, sonst stuende die Reihe schief.
   ueber [data-ulh-vis] und nicht ueber .ulh-vis-zeilen: diese Klasse traegt auch die
   Produkttabelle im Handel-Block (stil: "zeilen"), und die bleibt, wie sie ist. Zwei Klassen
   Spezifitaet gegen eine. */
.ulh-vis.ulh-vis-linie, .ulh-vis[data-ulh-vis="zeilen"] { --ulh-vh: 400px; }
.ulh-vis[data-ulh-vis="zeilen"] { --ulh-vs: 0.66; }
/* Die untere Reihe ist hoeher als die obere, und das hat einen Grund und keine Symmetrie: im
   Drilldown sollen VIER Seiten ganz zu sehen sein. Ueber ihnen stehen drei Domainzeilen, die
   Kopfzeile, die Werkzeugleiste und die Kopfzeile des Drilldowns -- zusammen 253px, die vier
   Zeilen kommen mit 136 dazu. Gemessen: die vierte endet bei 389. */
.ulh-vis-domains { --ulh-vs: 0.74; --ulh-vh: 410px; }
/* DIE DOMAIN-KARTE WIE DIE PROMPT-KARTE (07.10.): kleiner skaliert (0.66), und weil jetzt der Kopf
   der App und der Rahmen darum stehen, 30px hoeher -- der Drilldown zeigt weiter seine vier
   Seiten. Ueber [data-ulh-vis] wie dort, zwei Klassen gegen eine. */
.ulh-vis[data-ulh-vis="domains"] { --ulh-vs: 0.66; --ulh-vh: 440px; }
.ulh-vis-chancen { --ulh-vs: 0.82; --ulh-vh: 344px; }
/* Die Customer Journey (14.10.): beide Vorschauen in voller Groesse, gleich hoch, damit die Reihe
   gerade steht. 372px fassen die drei Stufen samt Hinweis und rechts Schalter, Zahl und drei
   Prompts (gemessen). */
.ulh-vis-reise, .ulh-vis-branded { --ulh-vs: 1; --ulh-vh: 372px; }
/* ---------- Dritte Sektion: die Quellen -------------------------------------------------------
   Der Kopf steht mittig und ist der groesste Text der Seite -- er traegt die Aussage der Sektion
   allein, ohne Karte darum. Zwei Toene in EINEM Satz: der erste Teil dunkel, der Rest in der
   zweiten Farbe. Das ist ein <strong> im Text und keine zwei Zeilen: es soll ein Satz bleiben,
   der umbricht, wo der Platz endet, und nicht ein Titel mit einer Unterzeile. */
.ulh-quell {
  position: relative;
  /* Als Variablen, weil der Strich im Kopf genau diese zwei Strecken ueberbruecken muss: von der
     Oberkante der Sektion bis zur Oberkante des Kastens. Zwei Zahlen an drei Stellen waeren drei
     Zahlen, die auseinanderlaufen. */
  --ulh-quell-luft: clamp(72px, calc(11 * var(--ulh-fh)), 132px);
  /* 50px mehr als vorher: zwischen dem Knopf und dem Kasten soll Ruhe stehen. */
  --ulh-quell-abstand: calc(clamp(44px, calc(6 * var(--ulh-fh)), 76px) + 50px);
  /* Die Luft ueber dem Knopf -- steht zweimal: am Knopf und in der Rechnung, wo das Muster endet. */
  --ulh-quell-ctaluft: clamp(22px, calc(3 * var(--ulh-fh)), 34px);
  /* Die Luft ueber und unter der Domainzeile -- EINE Zahl, weil beide gleich sein sollen:
     16 plus 30 Prozent. */
  --ulh-quell-kopfluft: 21px;
  /* Bis hierher ist von den Strichen NICHTS zu sehen. Gerechnet und nicht geschaetzt: die Luft
     ueber der Sektion plus die Hoehe des Chips (22) plus sein Abstand nach unten (18) -- also
     genau bis unter den Chip. Steht in einer Variablen, weil vier Verlaeufe denselben Wert
     brauchen. */
  --ulh-quell-frei: calc(var(--ulh-quell-luft) + 40px);
  padding: var(--ulh-quell-luft) 0 8px;
}
/* Die Trennlinie unter der Sektion laeuft ueber die ganze Breite und nicht nur ueber die Spur.
   left/width in vw und nicht 100% -- 100% waere die Spur. Ein Ueberlauf entsteht dadurch nicht:
   .ulh-root schneidet waagerecht ab (overflow-x: clip). */
.ulh-quell::after {
  content: ""; position: absolute; left: 50%; bottom: 0; width: 100vw; height: 1px;
  transform: translateX(-50%); background: var(--ulh-border); pointer-events: none;
}

/* Die zwei Striche hinter dem Kopf. Sie liegen genau auf den Schienen der Seite (.ulh-schienen,
   also die Kanten der Spur) und laufen von durchsichtig oben bis zur vollen Rahmenfarbe unten --
   dort, wo der Kasten beginnt. Damit wandert der Blick die Linie hinunter in die Komponente.
   Zwei Verlaeufe als Hintergrundbilder und keine Raender: ein Rand kann keinen Verlauf tragen.
   Der durchsichtige Halt ist --ulh-border mit Alpha 0 und nicht "transparent" -- ausgeschrieben,
   damit kein Browser durch ein anderes Grau blendet.
   z-index am Kopf und an seinen Kindern, NICHT -1 am Strich: ein negativer Wert haette ihn hinter
   den Grund der Seite gelegt (dort liegt die Fuellung von .ulh-root), und dann waere er weg. */
/* ---- DER AUFTRITT DER QUELLEN-SEKTION IN STUFEN (24.09. angefordert) ------------------------
   "Erst sichtbar ist der Bereich mit Container, Backgrounds und der gesamten Heading Row, dann
   kommen mit etwas Verzoegerung die oberen beiden Elemente rein, dann die unteren beiden."
   .ulh-quell-box ist der Kasten samt Hintergrund und Domainzeile -- er kommt wie bisher ueber
   .ulh-auf herein. Die vier Karten darin warten und laufen danach zu zweit nach: oben links,
   oben rechts, dann unten links, unten rechts. Dieselbe Bewegung wie im Hero (ulhRise).
   Die Verzoegerungen zaehlen ab is-da, also ab dem Moment, in dem der Kasten hereinkommt; 260ms
   Vorsprung lassen ihn zuerst ankommen. */
.ulh-quell-box:not(.is-da) .udd-card { opacity: 0; }
.ulh-quell-box.is-da .udd-chartcard            { animation: ulhRise 730ms var(--ulh-expo) 260ms backwards; }
.ulh-quell-box.is-da .udd-funnelcard           { animation: ulhRise 730ms var(--ulh-expo) 330ms backwards; }
.ulh-quell-box.is-da .udd-row2 .udd-typecard   { animation: ulhRise 730ms var(--ulh-expo) 470ms backwards; }
.ulh-quell-box.is-da .udd-row2 .udd-modelcard  { animation: ulhRise 730ms var(--ulh-expo) 540ms backwards; }
/* Die Abschaltung oben (bei den Auftritten) stand VOR diesen Regeln und war gleich stark oder
   schwaecher -- die Karten stiegen trotz "weniger Bewegung" auf (14.10., Audit). */
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-quell-box.is-da .udd-card,
  .ulh-root .ulh-quell-box.is-da .udd-row2 .udd-card { animation: none; }
}

.ulh-quell-kopf { position: relative; z-index: 0; }
.ulh-quell-kopf > * { position: relative; z-index: 1; }
.ulh-quell-kopf::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  left: 0; right: 0;
  top: calc(-1 * var(--ulh-quell-luft));
  bottom: calc(-1 * var(--ulh-quell-abstand));
  background-image: linear-gradient(to bottom, rgba(231, 233, 236, 0) var(--ulh-quell-frei),
                      var(--ulh-border) 100%),
                    linear-gradient(to bottom, rgba(231, 233, 236, 0) var(--ulh-quell-frei),
                      var(--ulh-border) 100%);
  background-size: 1px 100%, 1px 100%;
  background-position: left top, right top;
  background-repeat: no-repeat;
}
/* Und dazwischen ein MUSTER aus 1px-Strichen im 8er-Raster -- demselben Raster, in dem auf dieser
   Seite alle Abstaende stehen. Dicht genug, dass es als Flaeche liest und nicht als Linien.
   Heller als die beiden aussen (--ulh-schiene statt --ulh-border): aussen steht das Gitter der
   Seite, innen eine Textur. Gleich stark waere beides dasselbe und der Aufbau weg.
   Der senkrechte Verlauf steckt in einer Maske und nicht in der Farbe: das Muster IST schon ein
   Verlauf (quer), und ein Hintergrundbild kann nicht zwei tragen. */
.ulh-quell-kopf::after {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  left: 0; right: 0;
  /* Es beginnt UNTEN, direkt ueber dem Kasten, und laeuft nach oben aus -- an der Unterkante der
     Ueberschrift ist nichts mehr zu sehen. Andersherum, oben angefangen und nach unten staerker,
     lief es hinter der Ueberschrift durch: die Striche kamen aus dem Text und endeten im Nichts.
     Die Hoehe ist die Strecke zwischen beidem: das Stueck Kopf unter der Ueberschrift (Knopf plus
     seine Luft) und der Abstand zum Kasten. */
  top: auto;
  /* 16px TIEFER als die Oberkante des Kastens: dessen Ecken sind rund, und ein Muster, das an der
     geraden Kante endet, ist neben den Ecken nicht mehr da. So laeuft es hinter der Rundung
     weiter. Und 50px hoeher als vorher, damit es frueher anfaengt auszulaufen. */
  height: calc(36px + var(--ulh-quell-ctaluft) + var(--ulh-quell-abstand) + 66px);
  bottom: calc(-1 * var(--ulh-quell-abstand) - 16px);
  background-image: repeating-linear-gradient(to right,
    var(--ulh-schiene) 0 1px, rgba(237, 237, 240, 0) 1px 8px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 100%);
}
.ulh-quell-kopf { display: flex; flex-direction: column; align-items: center; text-align: center; }
/* Breiter als der Chip darueber vermuten laesst: mit 22ch stand der Satz bei 46px Schrift in
   sechs Zeilen und sah gestaucht aus. Die Grenze ist jetzt in px und nicht in ch -- ch haengt an
   der Schriftgroesse, und die waechst hier mit dem Fenster: dieselbe Zahl haette bei 30px eine
   andere Breite ergeben als bei 46px. */
.ulh-quell-h {
  margin: 0; max-width: min(100%, 1150px);
  font-size: clamp(30px, 3.4vw, 46px); font-weight: 500; line-height: 1.14; letter-spacing: -0.035em;
  color: var(--ulh-sub);
}
/* Die Ueberschrift steht in der zweiten Farbe und wandert beim Scrollen von vorne nach hinten in
   die erste. Jedes Wort traegt seinen eigenen Stand als --t (0 bis 1), landing-hero.js rechnet ihn
   aus der Lage im Fenster. color-mix und keine zwei Klassen: so gibt es Zwischenwerte, und die
   Front liest sich als Verlauf statt als Reihe von Schaltern.
   Kein Uebergang an der Farbe: der Stand kommt aus dem Scrollen und ist schon stufenlos -- ein
   zusaetzlicher Uebergang wuerde ihm hinterherlaufen. */
/* ---------- Auftritte beim Scrollen ----------------------------------------------------------
   Jedes Stueck der Seite ausser der Ueberschrift des Hero kommt leicht von unten herein, sobald es
   hochgescrollt wird. Die Klasse setzt landing-hero.js (auftritte), sobald die Oberkante 88 Prozent
   der Fensterhoehe erreicht hat -- gerechnet aus der ECHTEN Lage, siehe dort.
   Der Versatz steht als --auf am Element: Geschwister kommen nacheinander, nicht im Block. */
/* Die Bewegung laeuft ueber translate und NICHT ueber transform. Der Unterschied ist hier kein
   Geschmack: mehrere dieser Stuecke tragen schon ein eigenes transform (der Quellenhinweis wird
   damit zentriert, die Marken auf der Kurve damit auf ihren Punkt gerueckt), und ein zweites
   transform an derselben Stelle loescht das erste. Gemessen: der Hinweis stand 720px zu weit
   rechts, weil translateX(-50%) verloren ging. translate ist eine eigene Eigenschaft und kommt
   dem transform nicht in die Quere. */
.ulh-auf {
  opacity: 0; translate: 0 28px;
  /* Weiter und laenger als zuerst (16px/760ms), und mit einer Kurve, die vorne schnell ist und
     lange ausklingt -- das ist die Bewegung, die man von grossen Seiten kennt: das Stueck schiebt
     sich herein und kommt zur Ruhe, statt gleichmaessig zu fahren. */
  transition: opacity 900ms var(--ulh-gleit), translate 1000ms var(--ulh-gleit);
  transition-delay: calc(var(--auf, 0) * 110ms);
  will-change: opacity, translate;
}
.ulh-auf.is-fertig { will-change: auto; }
.ulh-auf.is-da { opacity: 1; translate: none; }
/* Wer keine Bewegung will, bekommt die Seite fertig -- landing-hero.js setzt is-da dann sofort,
   und diese Regel nimmt auch den Uebergang heraus. */
@media (prefers-reduced-motion: reduce) {
  .ulh-auf { transition: none; }
}

.ulh-qwort { white-space: nowrap; }
.ulh-qw { color: color-mix(in srgb, var(--ulh-text) calc(var(--t, 0) * 100%), var(--ulh-third)); }
/* Wer keine Bewegung will, bekommt die Ueberschrift fertig. */
@media (prefers-reduced-motion: reduce) { .ulh-qw { color: var(--ulh-text); } }
/* Der Knopf ist der Sekundaerknopf des Hero (.ulh-btn .ulh-btn-sec), hier steht nur sein Abstand
   nach oben. Vorher war es ein zweiter Knopf mit eigener Hoehe, eigenem Radius und eigenem Hover --
   zwei Knoepfe derselben Rolle auf einer Seite, die sich unter der Maus verschieden verhalten. */
.ulh-quell-cta { margin-top: var(--ulh-quell-ctaluft); }

/* Der Kasten mit dem Stueck App: oben ein Rahmen wie bei einer Karte, unten OFFEN -- er wird
   angeschnitten, und der Inhalt laeuft weiter. Deshalb kein unterer Rahmen und kein Radius unten,
   und eine feste Hoehe: der Anschnitt ist gewollt und darf nicht davon abhaengen, wie hoch die
   Komponente gerade rechnet. */
/* Derselbe Aufbau wie bei den Karten oben: aussen der graue Kasten mit 4px Polster und 8px
   Abstand zu den Schienen des Gitters, darin die weisse Karte. Der Unterschied ist nur, dass hier
   EINE grosse Karte darin steht statt vier kleiner -- Abstaende und Polster sind dieselben.
   Der Kasten ist rundum geschlossen: die Komponente steht ganz da, also endet auch der Rahmen.
   Unter ihm liegen 8px Luft -- dieselben 8px, die er zu den Schienen haelt -- und darunter die
   Trennlinie ueber die ganze Breite. */
.ulh-quell-box {
  /* Kein unterer Rand: er faellt mit dem der Sektion zusammen (margin collapsing) und landet
     ausserhalb -- die Trennlinie lag dann direkt auf dem Kasten. Die 8px stehen jetzt als Polster
     an der Sektion, und Polster kollabiert nicht. */
  margin: var(--ulh-quell-abstand) 8px 0;
  padding: 4px;
  background: var(--ulh-kasten);
  border: 1px solid var(--ulh-border);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .035), 0 6px 16px rgba(11, 13, 24, .028);
}
/* WEISS, und zwar als Zahl. --vc-bg ist die Grundfarbe der App, aber sie ist an .up-root
   gebunden, und dieser Kasten steht darueber: die Angabe war ungueltig, der Grund blieb
   durchsichtig, und durchgeschienen hat der graue Kasten darunter. Genau das war "die Komponente
   ist grau".
   KEINE feste Hoehe mehr: die Komponente steht ganz da, nichts wird abgeschnitten. Damit faellt
   auch der Verlauf am unteren Rand weg -- er hatte nur die Aufgabe, einen Schnitt weich zu machen,
   und ueber sichtbarem Inhalt wuerde er die letzte Kartenreihe aufhellen.
   Das Polster oben ist so gross wie der Abstand zwischen Domainzeile und Karten, damit Oberkante,
   Ueberschrift und Karten gleichmaessig stehen. Links und rechts bleiben es 8px. */
.ulh-quell-panel {
  border: 1px solid var(--ulh-border);
  border-radius: 16px; background: #ffffff;
  /* Ringsum dasselbe Polster (14.10. abends, "Paddings checken"): gemessen standen oben 21px,
     seitlich 8 -- die Kurve begann 9px neben dem Rand, das Logo darueber 17. Jetzt beginnen Logo,
     Kurve, Trichter und die zwei Karten auf einer Kante, so weit vom Rand wie von oben. */
  padding: var(--ulh-quell-kopfluft) var(--ulh-quell-kopfluft) 8px; overflow: hidden; position: relative;
}
/* Die Hoehe ist gemessen und nicht geschaetzt. In 1266px Breite baut die Komponente: 32 Kopfzeile,
   412 Chartkarte (mit der Legende der fuenf URLs), 299 Trichterkarte, 322 fuer die zwei Karten
   darunter -- zusammen 1105. Bei 980 stehen Chart mit Tooltip und Trichter ganz da, und die
   Karten mit URL-Typen und Modellen werden auf halber Hoehe angeschnitten. Genau so ist es
   gemeint: die Seite laeuft weiter, sie endet nicht.
   Eine feste Zahl und kein Anteil der Inhaltshoehe: der Anschnitt soll immer an derselben Stelle
   sitzen und nicht davon abhaengen, wie hoch die Komponente heute rechnet. */
/* Die Zeile mit Logo und Domain: das Logo ist .up-logo-box aus core, also derselbe Kasten wie in
   jeder Tabelle der App. */
/* 8px Einzug links (21.09., dritter Anlauf -- die zwei davor sassen an den falschen Elementen:
   erst an der Kopfzeile der Chartkarte, dann an den Zeilen der URL-Liste. Gemeint war DIESE
   Zeile, die mit dem Zeichen der Domain und ihrem Namen ueber dem Fenster steht. */
/* Seit 14.10. der Kopf des Domain-Drawers (.up-dk-heldzeile aus core). Hier steht nur, was der
   Drawer in domain-drawer.css dazu setzt -- Titel und Chip in einer Zeile --, und der Einzug von
   8px, den die Zeile vorher schon hatte. Die Masse von Zeichen, Titel und Unterzeile kommen aus
   core, wie im Drawer. */
.ulh-quell-held { margin-bottom: var(--ulh-quell-kopfluft); }
.ulh-quell-titelzeile { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; min-width: 0; }
.ulh-quell-typ { display: inline-flex; flex: 0 0 auto; }
.ulh-quell-typ:empty { display: none; }


/* ---- Die Komponente darin enger stellen ----
   Die App atmet, weil dort eine ganze Seite steht. Hier steht sie in einem Ausschnitt, und
   dieselben Abstaende wirken darin leer. Kleiner werden nur ABSTAENDE und Kartenpolster -- keine
   Schriftgroesse, keine Zeilenhoehe, kein Radius: sonst waere es nicht mehr die Komponente,
   sondern eine zweite, die ihr aehnelt. */
/* Die Sektion ist ein BILD der App und wird nicht bedient: der Umschalter der beiden unteren
   Karten soll zu SEHEN sein, aber nichts tun. Und das gilt nicht nur fuer ihn -- ein Klick auf den
   D/W/M-Schalter der Kurve loeste in der Komponente eine Datenanfrage aus, die auf einer
   Landingpage niemand beantwortet: die Karte fiele in ihr Ladeskelett und blieb darin stehen.
   Deshalb liegt die Sperre an der Wurzel und nicht an den einzelnen Schaltern. Der Tooltip steht
   ohnehin ohne Maus (landing-hero.js setzt ihn). */
.ulh-quell-app .udd-root { pointer-events: none; }
/* Kurve OBEN, Trichter DARUNTER, die zwei Aufschluesselungen darunter nebeneinander (14.10.
   abends angefordert; vorher standen Kurve und Trichter in einer Zeile). Eine Spalte wie die
   Abschnittsform der Komponente selbst; nur die Aufschluesselungen teilen sich eine Zeile
   (.udd-row2). 48 statt der 64 der Komponente: hier steht sie in einem Ausschnitt, nicht auf
   einer ganzen Seite -- dieselbe Ueberlegung wie bei den Kartenpolstern darunter. */
.ulh-quell-app .udd-root {
  display: grid; grid-template-columns: 1fr; row-gap: 48px; align-items: stretch;
}
/* 50-50 statt 1.15 zu 1: in der App ist die Typkarte breiter, weil dort neben dem Ring die
   Legende mit Prozenten steht und rechts nur vier Balken. Hier stehen beide fuer sich, und
   ungleiche Haelften lesen sich als Versehen. */
.ulh-quell-app .udd-row2 { grid-column: 1 / -1; gap: 8px; grid-template-columns: 1fr 1fr; }
.ulh-quell-app .udd-card { padding: 14px; }
/* HIER STANDEN ZWEI FEHLVERSUCHE fuer das 8px-Gap neben dem Forbes-Zeichen: erst an .udd-head
   (der Kopfzeile der Chartkarte, wo gar kein Zeichen steht), dann an .up-bar-row (den Zeilen der
   URL-Liste). Beide sind wieder raus -- sie haben Inhalt verschoben, den niemand verschoben
   haben wollte. Gemeint war die Zeile UEBER dem Fenster; die Einrueckung steht jetzt dort, an
   .ulh-quell-dom. */
/* Hier stand das Strecken des Trichters auf die Hoehe der Kurvenkarte -- beide teilten sich eine
   Zeile. Seit sie untereinander stehen, hat der Trichter wieder seine eigene Hoehe aus der
   Komponente. */
/* Die oberste Zeile faellt weg: darin stehen der Modus-Umschalter (der Modus ist hier gesetzt und
   soll nicht wechseln) und das Zahnrad fuer die Bereiche (hier gibt es nichts einzurichten). */
.ulh-quell-app .udd-toprow { display: none; }
/* Keine Legende. NICHT display: none: core misst die Legendenbreite, und solange sie 0 ist,
   versucht es alle 100ms erneut -- eine Uhr, die auf einer Landingpage bis in alle Ewigkeit
   laeuft, ohne dass etwas dabei herauskommt (core.js, legendLayout). Mit Hoehe 0 und
   overflow: hidden bleibt die Breite messbar, und zu sehen ist trotzdem nichts. */
.ulh-quell-app .udd-legend { height: 0; margin-top: 0; overflow: hidden; }
/* ---- Die Farbe des Trichters (28.09., Palette der Quellen-Sektion) ---------------------------
   domain-detail faerbt die drei Stufen mit der Farbe des Zitationstyps, und zwar als
   fill-ATTRIBUT am Pfad. Ein Praesentationsattribut verliert gegen jede CSS-Regel, auch gegen
   eine mit niedriger Spezifitaet -- diese eine Zeile reicht also, ohne Eingriff in die
   Komponente. Die Deckkraft je Stufe (fill-opacity 0.92/0.74/0.56) bleibt die der Komponente.
   Der Wert kommt aus QUELL_FARBEN in landing-hero.js und steht als Variable an .ulh-quell-app
   (Begruendung und Herleitung der ganzen Palette dort). */
.ulh-quell-app .udd-fn-band { fill: var(--ulh-q-trichter); }
/* Der Anschnitt unten: eine weiche Kante statt einer harten Linie. Ohne sie endet die Seite wie
   abgeschnitten, mit ihr laeuft sie aus. */

/* ---- Reihe drei: Band der Prompts und Bahnen der Modelle -------------------------------------
   Beide Vorschauen sind KEINE verkleinerten Bauteile der App, sondern eigene Bilder -- deshalb
   --ulh-vs: 1. Was aus der App kommt, sind die Teile darin: der Marktchip (core.css .up-market)
   und die Form der Karte (Radius, Rahmen, Schatten wie .ulh-vis-tab). */
.ulh-vis-sprachen { --ulh-vs: 1; --ulh-vh: 344px; }
.ulh-vis-modelle  { --ulh-vs: 1; --ulh-vh: 344px; }
/* ---- Der zweite Kartenblock: Shopping und Ads ("Beyond classic GEO", 06.10.) ----
   Dieselben Karten, derselbe Kasten, dieselben Anteile wie der Block darueber. Eigene Werte hat er
   nur, wo seine Vorschauen andere Bauteile zeigen:
     produkte   traegt die Geometrie der Prompt-Tabelle (ulh-vis-zeilen, siehe merkmalKarte)
     regal      die Balkenliste der App, unskaliert wie die Kurve der ersten Karte
     werber     dieselbe Balkenliste
     anzeigen   drei Ad-Karten der App, wie die Tabellen auf 0.74 verkleinert
   Alle vier auf 288, der Hoehe der ersten Reihe darueber -- beide Reihen gleich hoch. Seit dem
   07.10. ist die erste Reihe (produkte, bewegung) 400 hoch, wie die erste Merkmal-Reihe; siehe unten.
   Oben nur ein Teil des Sektionspolsters: der Block folgt direkt auf den ersten, und zwei volle
   Polster uebereinander (bis 144 + 132px) saehen nach Seitenende aus statt nach Fortsetzung. */
.ulh-feat.ulh-handel { padding-top: clamp(24px, calc(4 * var(--ulh-fh)), 48px); }
.ulh-vis-regal, .ulh-vis-werber { --ulh-vs: 1; --ulh-vh: 288px; }
/* Die Produktbewegung (06.10., oben rechts): die Liste aus Shopping, auf 0.8 verkleinert und so
   breit wie die Karte -- sie hat zwei Spalten und laeuft nicht seitlich hinaus. Ihre Zeilenhoehe
   steht in shopping.css nur unter .ush-root (eine Komponente gibt es hier nicht), also hier. */
.ulh-vis-bewegung { --ulh-vs: 0.8; --ulh-vh: 288px; }
/* DIE ERSTE SHOPPING-REIHE WIE DIE ERSTE MERKMAL-REIHE (07.10.): 400 hoch, die Produkttabelle auf
   0.66 mit dem Kopf der App im Rahmen (wie die Domain-Karte), die Produktbewegung mit ihrem
   Abschnittskopf auf 0.72 -- nicht ganz so klein, die Karte ist nur 40 Prozent breit. Ueber
   [data-ulh-vis], zwei Klassen gegen .ulh-vis-zeilen und .ulh-vis-bewegung. */
.ulh-vis[data-ulh-vis="produkte"] { --ulh-vs: 0.66; --ulh-vh: 400px; }
.ulh-vis[data-ulh-vis="bewegung"] { --ulh-vs: 0.72; --ulh-vh: 400px; }
/* Platz um den Rahmen fuer das Muster, wie bei der Domain-Karte. Drei Klassen gegen das seitliche
   Polster von .ulh-vis-zeilen (68px fuer die gehobene Zeile, die es hier nicht mehr gibt). */
.ulh-vis[data-ulh-vis="produkte"] .ulh-vis-in { position: relative; z-index: 1; padding: 24px 36px 0; box-sizing: border-box; }
.ulh-vis[data-ulh-vis="produkte"] .ulh-vis-tab .up-thead { height: 45px; flex-basis: 45px; letter-spacing: normal; }
.ulh-vis-bewegung .ulh-vis-in { width: calc(100% / var(--ulh-vs)); }
.ulh-vis .ush-bewegung .up-row { height: 64px; flex: 0 0 64px; }
/* Die Produktzelle: Foto, dann Titel und Marke -- der Abstand wie in der Tabelle von Shopping. */
.ulh-vis-prod { display: flex; align-items: center; gap: 12px; min-width: 0; }
/* ---- Der Block "Events" (06.10.) ----
   Links die Event-Karte der App (events.css): DREI KARTEN IM FAECHER (06.10. abends: "wie bei
   unseren Fanned Logo Chips, auch rechts hinter der vorderen Card ein Event, Versatz minimal
   groesser"). Die Bauart von .up-fan-c in core.css: alle drei liegen auf EINEM Platz und drehen um
   einen Punkt weit unter sich -- transform-origin 50% 260% liegt gut anderthalb Kartenhoehen unter
   der Karte, so stehen die seitlichen von selbst auf einem Bogen, aussen etwas tiefer. Bei 7 Grad
   ragen sie oben rund 90px und unten rund 55px heraus (vorher: eine Karte, 65px).
   Die Karte ist 15% breiter als die 296 des App-Rasters (angefordert), steht mittig, und die zwei
   hinteren sind kleiner und leiser. Sie faechern auf, wenn die Karte ins Bild kommt (is-da), mit
   der Kurve der Seite -- vorher liegen sie genau hinter der vorderen.
   Rechts die Kurve des Event-Details (UC.makeLine), so hoch wie der sichtbare Teil der Vorschau.
   Beide Reihen 344 hoch, wie die unteren Reihen des ersten Blocks. */
.ulh-vis-eventkarte, .ulh-vis-eventkurve { --ulh-vs: 1; --ulh-vh: 344px; }
.ulh-evkarten { position: relative; height: calc(100% - 40px); display: flex; justify-content: center; align-items: flex-start; padding-top: 6px; }
.ulh-evkarten .uev-karte { width: 340px; flex: 0 0 auto; }
.ulh-evkarte-links, .ulh-evkarte-rechts {
  position: absolute; top: 6px; left: 0; right: 0; margin-inline: auto;
  transform-origin: 50% 260%; rotate: 0deg; scale: .94; opacity: .6;
  transition: rotate 1000ms var(--ulh-expo) 350ms;
}
.ulh-card.is-da .ulh-evkarte-links  { rotate: -7deg; }
.ulh-card.is-da .ulh-evkarte-rechts { rotate: 7deg; }
.ulh-evkarte-vorn { position: relative; box-shadow: 0 1px 2px rgba(11, 13, 24, .04), 0 12px 28px rgba(11, 13, 24, .08); }
@media (prefers-reduced-motion: reduce) { .ulh-evkarte-links, .ulh-evkarte-rechts { transition: none; } }
/* Kein Tooltip in der Kurve (06.10. abends angefordert): die Leinwand nimmt keine Maus an. */
.ulh-evk canvas { pointer-events: none; }
.ulh-evk { height: calc(100% - 40px); display: flex; flex-direction: column; }
.ulh-evk-wrap { position: relative; flex: 1 1 auto; min-height: 0; }

.ulh-vis-anzeigen { --ulh-vs: 0.74; --ulh-vh: 288px; }
.ulh-vis-anzeigen .ulh-vis-in { padding: 0 24px; }
/* Drei Spalten wie das Raster der Ad Library (uad-auf-liste: mindestens 260px, 8px Abstand). */
.ulh-anz { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
/* Ein Schaustueck: die Karte hebt sich beim Ueberfahren wie in der App, oeffnet aber nichts. */
.ulh-anz .up-adcard { cursor: default; }
/* Die Illustrationen brauchen Luft im 16:10-Feld: contain allein setzt sie an die Kante. Ein
   Zehntel ringsum -- das Feld und sein Grund bleiben die der Ad-Karte. */
.ulh-anz .up-adcard-media img { inset: 10% 12%; width: 76%; height: 80%; }

/* Das Band. overflow: hidden am Kasten, die Spur darin faehrt ueber transform -- eine Bewegung
   ueber margin/top wuerde je Bild ein Layout ausloesen. */
.ulh-lauf { position: relative; height: 100%; overflow: hidden; }
.ulh-lauf-spur { display: flex; flex-direction: column; gap: 8px; will-change: transform; }
/* Die Prompt-Karte: dieselbe Form wie die Tabellen in den anderen Vorschauen (12px Radius, der
   Rahmen der Sektion, der flache Schatten), damit die drei Reihen eine Sprache sprechen.
   .check_reinvention.py meldet hier eine Sentiment-Pille (.up-sent), weil border, border-radius,
   gap und height zusammen vorkommen -- das ist ein Fehlalarm: eine 54px hohe Zeile mit Flagge,
   Satz und Volumen ist keine 24px-Pille mit einem Wert, und sie liegt in einem Bild der
   Landingpage, nicht in einer Tabelle der App. */
.ulh-pk {
  display: flex; align-items: center; gap: 12px;
  padding: 0 14px; height: 54px; box-sizing: border-box; flex: 0 0 auto;
  background: var(--vc-bg); border: 1px solid var(--ulh-border); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .04);
}
.ulh-pk .up-market { flex: 0 0 auto; }
/* Nur die Flagge, kein Kuerzel dahinter: in der App steht der Code neben der Flagge, weil dort in
   einer Spalte alle Maerkte untereinander stehen und die Flaggen sich aehneln. Hier steht je Zeile
   EINE Flagge neben einem Satz in genau dieser Sprache -- der Code sagt dann nichts mehr dazu.
   Ausgeblendet und nicht weggelassen: der Chip bleibt der aus core (UC.marketChip). */
.ulh-pk .up-market-code { display: none; }
/* Der Volumenbalken aus Prompt Research (renderVolume dort), Werte 1:1 -- 58x6, vier Felder, 3px
   dazwischen, und die Farbe traegt die Stufe. Hier und nicht von dort geerbt, WEIL die Landingpage
   prompt-research.css nicht laedt; weichen die Werte dort einmal ab, muessen sie hier mit. */
.ulh-pk .upr-volume-wrap { width: 58px; flex: 0 0 auto; display: block; }
.ulh-pk .upr-volume-track {
  width: 58px; height: 6px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px;
}
.ulh-pk .upr-volume-seg { border-radius: 2px; background: #e8eaee; }
.ulh-pk .upr-volume-seg.is-filled.level-1 { background: #d2865d; }
.ulh-pk .upr-volume-seg.is-filled.level-2 { background: #d9b85d; }
.ulh-pk .upr-volume-seg.is-filled.level-3 { background: #9fd25d; }
.ulh-pk .upr-volume-seg.is-filled.level-4 { background: #60d25d; }
.ulh-pk-t {
  flex: 1 1 auto; min-width: 0;
  font-size: 14.5px; font-weight: 500; line-height: 1.35; color: var(--ulh-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Blende oben UND unten: ein Band, das an einer harten Kante endet, sieht aus wie eine
   abgeschnittene Liste. Dieselbe Machart wie die Blende der anderen Vorschauen, nur zweimal --
   und ohne den Weichzeichner: hier laeuft etwas, und Unscharfes in Bewegung flimmert. */
.ulh-vis-sprachen::before, .ulh-vis-sprachen::after {
  content: ""; position: absolute; left: 0; right: 0; height: 15%;
  pointer-events: none; z-index: 3;
}
.ulh-vis-sprachen::before { top: 40px; background: linear-gradient(to bottom, #ffffff 12%, rgba(255,255,255,0) 100%); }
.ulh-vis-sprachen::after  { bottom: 0;  background: linear-gradient(to top,    #ffffff 12%, rgba(255,255,255,0) 100%); }

/* Die Bahnen. Der Kasten ist der Bezugsrahmen, jeder Ring liegt mittig darin und dreht als
   Ganzes; die Zeichen drehen ihren Ring wieder heraus (landing-hero.js, orbTakt).
   Die Ringe sind nur eine Linie -- sie sollen die Bahn andeuten, nicht Kreise zeichnen. */
/* Der Kasten der Vorschau ist --ulh-vh hoch, sichtbar sind davon 26px weniger: so viel Polsterung
   traegt .ulh-vis oben, und der Inhalt laeuft darunter durch. Fuer eine Tabelle ist das richtig
   (sie soll oben angeschnitten wirken), fuer einen Kreis nicht -- er stand unten an der Kante an.
   Also nimmt der Kasten der Bahnen genau die sichtbare Hoehe, und die Mitte rueckt hoch.
   38 und nicht 26: mit 26 lagen die Zeichen der AEUSSEREN Bahn an ihrem tiefsten Punkt noch ein
   paar Pixel unter der Kante. Der Kasten ist um 12 flacher, die Mitte damit um 6 hoeher -- und
   6px sind genau das, was unten gefehlt hat. */
/* 52 = die 38 von damals plus die 14px, um die Polster und Hoehe am 06.10. gewachsen sind. */
.ulh-orb { position: relative; height: calc(100% - 52px); }
.ulh-orb-ring {
  position: absolute; left: 50%; top: 50%; margin: 0; border-radius: 50%;
  transform-origin: center center; translate: -50% -50%;
  border: 1px solid var(--ulh-schiene);
  will-change: transform;
}
.ulh-orb-chip {
  position: absolute; width: 30px; height: 30px; border-radius: 9px;
  /* Die Zeichen drehen in JEDEM Bild ihren Ring wieder heraus. Ohne diese Ankuendigung entscheidet
     der Browser bei jedem Bild neu, ob er dafuer eine eigene Ebene anlegt -- und auf langsameren
     Rechnern zeigt sich das als Zittern. Gemeldet fuer Windows. */
  will-change: transform;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--vc-bg); border: 1px solid var(--ulh-border);
  box-shadow: 0 2px 6px rgba(11, 13, 24, .06);
  transform: translate(-50%, -50%);
  box-sizing: border-box; padding: 4px;
}
.ulh-orb-chip img { width: 100%; height: 100%; object-fit: contain; display: block; border-radius: 5px; }
/* Die Mitte: eine weisse Scheibe mit einem weichen Hof, damit die Marke vor den Bahnen liegt und
   nicht zwischen ihnen. Groesser als die Chips -- sie ist der Bezugspunkt, um den alles laeuft. */
.ulh-orb-mitte {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 84px; height: 84px; border-radius: 50%; z-index: 2;
  background: #ffffff; border: 1px solid var(--ulh-border);
  box-shadow: 0 6px 22px rgba(11, 13, 24, .10), 0 0 0 10px rgba(255, 255, 255, .92);
  display: flex; align-items: center; justify-content: center;
}
.ulh-orb-mitte img { width: 38px; height: 38px; display: block; }
@media (prefers-reduced-motion: reduce){
  .ulh-lauf-spur, .ulh-orb-ring { transform: none !important; }
}
.ulh-vis { height: var(--ulh-vh); }
.ulh-vis-in { height: calc(var(--ulh-vh) / var(--ulh-vs)); overflow: hidden; }

/* ---- Karte 1: die zwei Kurven ----
   Kein Achsenkreuz, keine Legende: das Feld ist die Kurve. An jeder Linie haengt ein Schild mit
   Marke und Wert -- gebaut wie der Tooltip des Charts in der App (Radius 16, 13px, 10/12 Polster,
   16px Logo, derselbe Schatten), nur ohne Datum und mit einer einzigen Marke darin.
   Die Schilder liegen absolut auf ihrem Punkt; die Koordinaten setzt landing-hero.js, sobald
   Chart.js gezeichnet hat. Bis dahin sind sie unsichtbar (is-da), sonst saessen sie einen
   Augenblick in der linken oberen Ecke. */
.ulh-krv { position: relative; height: 100%; }
/* Seit den Monaten unter der Kurve (14.10.) muss ihr Fuss sichtbar sein: der Ausschnitt beginnt
   40px tiefer als das Feld (gemessen: Feld 222-622, Karte endet bei 607 -- die Monatsnamen auf
   Hoehe 398 von 400 lagen unter der Kante). 52px weniger Hoehe, und die Monate stehen 10px ueber
   dem Rand der Karte. */
.ulh-vis-linie .ulh-krv { height: calc(100% - 52px); }
.ulh-krv-canvas { display: block; width: 100%; height: 100%; }
.ulh-krv-chip {
  position: absolute; opacity: 0; transform: translate(-50%, -100%);
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 11px; border-radius: 14px; white-space: nowrap;
  background: #ffffff; border: 1px solid var(--ulh-border);
  box-shadow: 0 1px 2px rgba(11, 13, 24, .05), 0 8px 20px rgba(11, 13, 24, .07);
  font-size: 12.5px; line-height: 1.2;
  transition: opacity 364ms var(--ulh-aus), filter 280ms var(--ulh-aus);
  pointer-events: auto;
}
.ulh-krv-chip.is-da { opacity: 1; }
/* Der Strich vom Schild zu seinem Punkt. In der Rahmenfarbe und 1px: er soll die Zuordnung
   herstellen und nicht selbst auffallen. Seine Laenge ist dieselbe Zahl, die landing-hero.js beim
   Setzen abzieht (KRV_ABSTAND) -- zwei Orte, ein Wert, deshalb steht sie in beiden Kommentaren. */
/* Mittig unter dem Schild, senkrecht bis auf die Linie. Damit er sie trifft, richtet sich die
   HOEHE des Schildes nach dem Wert der Kurve an dieser Stelle (landing-hero.js) -- nicht nach dem
   Wert seines Datenpunkts. */
.ulh-krv-chip::after {
  content: ""; position: absolute; top: 100%; left: 50%; width: 1px; height: 15px;
  background: var(--ulh-border);
}
/* Das Schild der Linie, die gerade NICHT gehoben ist, tritt mit zurueck -- sonst stuende ein
   volles Schild an einer grauen Linie. */
.ulh-krv-chip.is-aus { opacity: .45; filter: saturate(.2); }
.ulh-krv-logo { width: 16px; height: 16px; border-radius: 4px; display: block; object-fit: cover; }
.ulh-krv-name { color: var(--ulh-text); font-weight: 400; }
.ulh-krv-val { color: var(--ulh-text); font-weight: 500; font-variant-numeric: tabular-nums; }

/* ---- Die Kurve zeichnet sich ein (14.10.: "mehr Bewegung, professioneller") ----
   Ablauf, sobald die Karte zu 35% im Bild ist (is-zieht, landing-hero.js):
     0ms      die Leinwand oeffnet sich von links nach rechts -- Linien, Flaeche und Monate kommen
              in der Zeitrichtung herein (1600ms, easeOutExpo)
     700ms    das Schild der fremden Marke
     1050ms   das Schild der eigenen Marke, wenn ihre Linie fast oben ist
     1350ms   der Punkt am Ende, danach pulsiert er ruhig weiter
   Nur clip-path, opacity und transform: nichts davon loest Layout aus. */
.ulh-krv-canvas { clip-path: inset(0 100% 0 0); }
.ulh-krv.is-zieht .ulh-krv-canvas {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1600ms var(--ulh-expo);
}
.ulh-krv:not(.is-zieht) .ulh-krv-chip { opacity: 0; }
@keyframes ulhKrvSchild {
  from { opacity: 0; transform: translate(-50%, calc(-100% + 8px)); }
  to   { opacity: 1; transform: translate(-50%, -100%); }
}
.ulh-krv.is-zieht .ulh-krv-chip.is-da { animation: ulhKrvSchild 640ms var(--ulh-expo) 700ms backwards; }
.ulh-krv.is-zieht .ulh-krv-chip.is-da[data-krv="ac"] { animation-delay: 1050ms; }

/* Der Punkt: 8px in der Farbe der Kurve mit weissem Ring, dahinter eine Welle, die sich alle 2.4s
   ausbreitet und verblasst -- leise genug, um nicht vom Text abzulenken. */
.ulh-krv-punkt {
  position: absolute; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px;
  border-radius: 999px; background: var(--krv-farbe, #579cf1);
  box-shadow: 0 0 0 2px #ffffff, 0 1px 3px rgba(11, 13, 24, .18);
  opacity: 0; pointer-events: none;
}
.ulh-krv-punkt::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: var(--krv-farbe, #579cf1); opacity: 0;
}
@keyframes ulhKrvPunkt { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: none; } }
@keyframes ulhKrvWelle { 0% { opacity: .35; transform: scale(1); } 70%, 100% { opacity: 0; transform: scale(3.2); } }
.ulh-krv.is-zieht .ulh-krv-punkt { opacity: 1; animation: ulhKrvPunkt 520ms var(--ulh-expo) 1350ms backwards; }
.ulh-krv.is-zieht .ulh-krv-punkt::after { animation: ulhKrvWelle 2400ms cubic-bezier(.2, .6, .35, 1) 1900ms infinite; }
@media (prefers-reduced-motion: reduce) {
  .ulh-krv.is-zieht .ulh-krv-canvas { transition: none; }
  .ulh-krv.is-zieht .ulh-krv-chip.is-da, .ulh-krv.is-zieht .ulh-krv-punkt { animation: none; }
  .ulh-krv.is-zieht .ulh-krv-punkt::after { animation: none; }
}

/* ---- Karte 2 und 3: die Tabellen ----
   GERADE und nicht gekippt. Der schraege Blick war eine Runde lang drin und ist wieder heraus: er
   macht aus einer Tabelle ein Bild von einer Tabelle -- man liest sie nicht mehr, man sieht sie
   nur. Ohne Neigung bleibt sie lesbar, und das ist hier der Zweck. */
.ulh-vis-tab, .ulh-vis-dom {
  border: 1px solid var(--ulh-border); border-radius: 12px;
  background: var(--vc-bg);
  box-shadow: 0 1px 2px rgba(11, 13, 24, .04), 0 8px 20px rgba(11, 13, 24, .05);
}
/* EIN SCHNITT JE ROLLE, IN BEIDEN TABELLENKARTEN GLEICH (28.09. angefordert: "erhoehe das font
   weight in Prompts table und Domain table + Drilldown Cards Komponente leicht und mach es
   einheitlich. man kann einige Sachen da noch nicht so gut lesen").
   Gemessen standen in den zwei Karten drei Schnitte durcheinander: 400 (Seitenzahl im Knopf der
   Domainzeilen), 500 (fast aller Text, die Kopfzeilen beider Tabellen, die Themenchips) und 600
   (Kopfzeile des Drilldowns, Typ-Pillen, Marktkuerzel, Trend). Dieselbe Rolle trug je nach
   Bauteil einen anderen Wert, weil jede dieser Regeln aus einer anderen Datei kommt (core,
   domains-table, diese hier) -- die Kopfzeile des Drilldowns stand also eine Stufe ueber der
   Kopfzeile der Tabelle direkt darueber.
   Jetzt drei Namen und zwei Stufen, und jeder Wert ging nach oben oder blieb:
     --ulh-vis-kopf   600  Spaltenkoepfe, auch die des Drilldowns (dort stand es schon)
     --ulh-vis-text   550  alles, was man liest: Prompt, Domain, Seitentitel, Zahlen, Daten,
                           Seitenzahl, die Beschriftungen der Werkzeugleiste im Drilldown
     --ulh-vis-pille  600  was in einer Pille steht: Themen, Typen, Marktkuerzel, Trend
   550 und nicht 600 fuer den Text: die Vorschau ist auf 74 Prozent verkleinert, der Text steht also
   bei 9 bis 12.6px -- dort macht eine halbe Stufe ihn lesbar, eine ganze macht aus den Zeilen
   Balken. Geist ist als variable Schrift geladen (400..700), 550 rendert also wirklich als 550.
   Die Rangordnung zwischen Haupt- und Nebentext traegt weiter die Farbe, nicht der Schnitt.
   Die Selektoren tragen die Karte als Klasse davor und gewinnen damit ueber die Spezifitaet gegen
   die Regeln der Bauteile (je eine Klasse) -- nicht ueber die Reihenfolge der Dateien. */
.ulh-vis-tab, .ulh-vis-dom { --ulh-vis-kopf: 600; --ulh-vis-text: 550; --ulh-vis-pille: 600; }
/* Die Kopfzeile der Tabelle ist in der App gross, weil sie dort sortierbar ist. In einer Vorschau
   ist sie eine Beschriftung: kleiner und ruhiger -- das tragen Groesse und Farbe, der Schnitt ist
   der aller Kopfzeilen (siehe oben). */
.ulh-vis-tab .up-thead, .ulh-vis-dom .up-thead {
  position: static; border: 0; border-radius: 0;
  font-size: 11px; font-weight: var(--ulh-vis-kopf); letter-spacing: .01em; color: var(--vc-muted);
  height: 40px; flex: 0 0 40px; text-transform: none;
}
.ulh-vis-tab .up-thead .up-td, .ulh-vis-dom .up-thead .up-td,
.ulh-vis-dom .udt-sub-head { font-weight: var(--ulh-vis-kopf); }
.ulh-vis-tab .up-num, .ulh-vis-tab .up-stack-more,
.ulh-vis-dom .udt-dom-title, .ulh-vis-dom .udt-num, .ulh-vis-dom .udt-date,
.ulh-vis-dom .udt-pagesbtn .udt-pagesbtn-lbl, .ulh-vis-dom .udt-sub-title,
.ulh-vis-dom .udt-sub-share, .ulh-vis-dom .udt-sub-date, .ulh-vis-dom .udt-sub-disp-btn,
.ulh-vis-dom .up-filter-btn-lbl, .ulh-vis-dom .udt-sub-search-in { font-weight: var(--ulh-vis-text); }
.ulh-vis-tab .up-topicchip-lbl, .ulh-vis-tab .up-market-code,
.ulh-vis-dom .tct-tag-lbl, .ulh-vis-dom .up-trend { font-weight: var(--ulh-vis-pille); }
.ulh-vis-tab .up-row, .ulh-vis-dom .up-row { border-bottom: 1px solid var(--ulh-border); }
/* NICHT an der Kopfzeile (07.10. gemeldet: "unter der Heading Row ein doppelter Strich, 2px statt
   1"). Seit die Spaltenkoepfe .up-th aus core sind, tragen sie ihre eigene Unterkante -- wie in der
   App. Die Linie der Zeile darunter lag gemessen genau daneben. Eine Klasse mehr, damit die Regel
   ueber die Spezifitaet gewinnt. */
.ulh-vis-tab .up-row.up-thead, .ulh-vis-dom .up-row.up-thead { border-bottom: 0; }
.ulh-vis-tab .up-row:last-child, .ulh-vis-dom .up-row:last-child { border-bottom: 0; }
.ulh-vis-tab .up-td:first-child, .ulh-vis-dom .up-td:first-child { border-left: 0; }
/* Die Prompt-Zeile ist in dieser Karte der Gegenstand -- die Zahlen daneben erklaeren sie nur.
   21.09.: kraeftiger ("100 mehr weight"). 28.09.: noch einmal 1px groesser ("mach bitte die
   Prompt-Text font size 1px groesser"), also 17px.
   Die Zahl steht jetzt HIER. Vorher setzte in der ganzen Kette niemand eine Groesse: die 16px
   waren die Vorgabe des Browsers, geerbt ueber <html>, und damit eine Groesse, die der Gastgeber
   der Seite bestimmt und nicht diese Datei (gemessen: .up-td, .ulh-vis-in, .ulh-card und die
   Wurzel standen alle auf 16px, keine Regel dazu).
   29.09. spaet: 2px kleiner, 15 ("mach die Fontsizes der Prompts kleiner, 2px") -- eine Stufe
   ueber der Visibility daneben (14), die Zeile bleibt der Gegenstand. */
.ulh-vis-prompt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 15px; font-weight: var(--ulh-vis-text); }
/* Der Ring zwischen den Markenzeichen ist der ABSTAND zum Nachbarn und traegt die Farbe der Zeile
   (core.css, .up-stack-vis). Beim Ueberfahren nimmt core dafuer die Hoverfarbe der Zeile -- die
   Zeilen der Vorschau behalten ihren Grund aber (weiter unten: "Der eigene Hover der Bauteile
   faellt weg"), und der Ring stuende dann als grauer Kranz um jedes Zeichen. Also bleibt er beim
   Grund. Eine Klasse mehr als die Regel in core. */
.ulh-vis .up-row:hover .up-stack-vis { box-shadow: 0 0 0 4px var(--vc-bg); --up-stack-ring: var(--vc-bg); }

/* DER AUFGEKLAPPTE BEREICH IN DER QUELLENKARTE IST HELLER (21.09. angefordert). --vt-head-bg ist
   der Ton der Tabellenkoepfe; in einer Karte, die selbst schon auf --vc-bg steht, liest er sich
   als zweiter Kasten statt als Fortsetzung derselben Zeile. Auf halbem Weg zum Kartengrund bleibt
   die Abgrenzung erhalten und der Block wird ruhiger.
   NUR HIER: in der App steht die Tabelle auf der Seite und nicht in einer Karte, dort stimmt der
   kraeftigere Ton.
   BEIDE Scopes, und das war der Fehler beim ersten Versuch: der aufgeklappte Bereich, um den es
   geht, steht in der VORSCHAUKARTE (.ulh-vis) und nicht im Fenster (.ulh-app). Gemessen blieb
   die Farbe deshalb unveraendert.
   Und der ANTEIL ist 50 und nicht 25: der Ausgangston ist rgb(249,248,249), also ohnehin schon
   dicht an Weiss. Bei 25 Prozent kam rgb(253,253,253) heraus -- das ist kein hellerer Kasten
   mehr, sondern gar keiner. Bei 50 bleibt er als Flaeche erkennbar und ist eine Stufe ruhiger. */
.ulh-app .udt-subrows,
.ulh-vis .udt-subrows { background: color-mix(in srgb, var(--vt-head-bg) 50%, var(--vc-bg) 50%); }
/* DIE THEMEN WERDEN NICHT ABGESCHNITTEN (29.09. spaet: "die Topics sind truncated, mach das
   nicht"). Gemessen passten zwei Chips bei 1440 mit 161 in 162px gerade noch, bei jeder
   schmaleren Seite wurden sie gestaucht und bekamen die Auslassung aus core (.up-topicchip-lbl).
   Jetzt behaelt jeder Chip seine Breite und ein zweiter bricht notfalls in eine zweite Zeile --
   die Zeile ist 72px hoch, zwei Reihen passen hinein. */
.ulh-vis-themen { display: flex; flex-wrap: wrap; gap: 4px 5px; min-width: 0; }
.ulh-vis-themen .up-topicchip { flex: 0 0 auto; }
.ulh-vis-themen .up-topicchip-lbl { overflow: visible; text-overflow: clip; }

/* Der Auslauf nach unten: erst weich zeichnen, dann in den Kartengrund verlaufen. Zwei Ebenen,
   weil eine Maske BEIDES treffen wuerde -- die Unschaerfe soll von oben nach unten zunehmen, der
   Weissverlauf ebenfalls, aber sie sind zwei verschiedene Kurven.
   Beide setzen SPAET an und bleiben leise: 19 Prozent der Hoehe, Unschaerfe 1.6px, und die Maske
   laesst die oberen neun Zehntel dieses Streifens fast unberuehrt. Der Weg dahin: erst 46 Prozent
   und 3px (damit war die halbe Vorschau weich), dann 26 und 2, jetzt 19 und 1.6.
   backdrop-filter und nicht filter: die Zeilen selbst bleiben scharf gezeichnet, unscharf wird
   nur, was DURCH diese Flaeche zu sehen ist -- so bleibt der Rest der Vorschau unberuehrt. */
.ulh-vis-zeilen::after, .ulh-vis-domains::after, .ulh-vis-chancen::after, .ulh-vis-bewegung::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 19%;
  -webkit-backdrop-filter: blur(1.6px); backdrop-filter: blur(1.6px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 90%);
  mask-image: linear-gradient(to bottom, transparent, #000 90%);
  pointer-events: none; z-index: 2;
}
.ulh-vis-zeilen::before, .ulh-vis-domains::before, .ulh-vis-chancen::before, .ulh-vis-bewegung::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 18%;
  background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, #ffffff 94%);
  pointer-events: none; z-index: 3;
}

/* ---- Das Heben beim Ueberfahren der Karte ----
   KEIN Zeilen-Hover wie in der App: dort sagt die gefaerbte Zeile "die kannst du anklicken", und
   hier kann man nichts anklicken. Was bleibt, ist ein Zeichen, dass die Karte lebt: eine Zeile aus
   der Mitte wird um ein Zehntel GROESSER -- in beide Richtungen, nicht nur in der Hoehe --, bekommt
   8px runde Ecken und einen Schatten darunter.
   Damit dabei links und rechts nichts abgeschnitten wird, sind zwei Dinge noetig: die Tabelle gibt
   ihren Beschnitt beim Ueberfahren frei (sonst schneidet ihr eigener overflow die Zeile an ihren
   Seiten ab), und der Kasten der Vorschau haelt seitlich Platz frei -- ein Zehntel von 1030px sind
   rund 52px, also 52px je Seite. Ohne diesen Platz waere die Zeile am Rand der Karte abgeschnitten.
   Die Zitatkarte bekommt nichts: dort ist der aufgeklappte Drilldown das Bild, und eine gehobene
   Zeile darin waere ein zweiter Blickfang daneben. */
/* 68px je Seite: die Zeile waechst um 3 Prozent nach jeder Seite (rund 28px) und traegt einen
   Schatten mit 30px Weichzeichnung -- zusammen 58. Bei 52px war der Schatten abgeschnitten, bei
   64 blieben genau 28px und damit zwei Pixel zu wenig (gemessen). */
.ulh-vis-zeilen .ulh-vis-in, .ulh-vis-chancen .ulh-vis-in { padding: 0 68px; box-sizing: border-box; }
/* DIE PROMPT-TABELLE HAT KEINE GEHOBENE ZEILE MEHR (07.10.), also auch keinen Grund fuer die 68px
   seitlich: 20px bleiben, damit der Schatten der Tabelle nicht an der Kante der Vorschau endet.
   Drei Klassen gegen die Regel darueber. */
.ulh-vis[data-ulh-vis="zeilen"] .ulh-vis-in { padding: 0 20px; --upt-check-size: 18px; }
/* Der Kopf der App ueber der Tabelle (.up-head aus core: 32px hoch, 16px darunter). Ohne Polster
   oben -- die Vorschau hat ihren Abstand zum Kartentext schon (.ulh-vis, padding-top). */
.ulh-vis[data-ulh-vis="zeilen"] .ulh-vis-kopf { margin-top: 0; }
/* DIE SPALTENKOEPFE WIE IN DER APP (07.10. angefordert: "fonts in weight und color so wie in der
   Hauptapp"): Groesse, Schnitt und Farbe kommen jetzt aus core (.up-th: 12px, 400, --vc-muted),
   und die Zeile ist 45px hoch wie dort. Die Regeln weiter unten fuer .ulh-vis-tab .up-thead
   (11px, 600, Sperrung, 40px) gelten fuer die Domain-Tabelle weiter; hier gewinnt diese Regel
   ueber die Spezifitaet. */
.ulh-vis[data-ulh-vis="zeilen"] .ulh-vis-tab .up-thead { height: 45px; flex-basis: 45px; letter-spacing: normal; }
/* EIN PROMPT DARF UMBRECHEN (07.10. angefordert) -- wie in der App hoechstens zwei Zeilen
   (.upt-prompt-text: line-clamp 2), danach die Auslassung. Die Zeile ist 72px hoch, zwei Zeilen
   passen hinein. Nur in dieser Karte: die Regel fuer .ulh-vis-prompt selbst (eine Zeile) gilt
   anderswo weiter. */
.ulh-vis[data-ulh-vis="zeilen"] .ulh-vis-prompt {
  white-space: normal; line-height: 1.35;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}

/* ---------- Die Domain-Karte: Rahmen und Muster (07.10.) ----------
   Nach einer Vorlage des Nutzers, nur die GRAFIK: ein halbdurchsichtiger Rand um ein weisses
   Fenster, und dahinter ein Muster, das nach aussen ausblendet. Inhalt und Aufbau bleiben die der
   App (Kopf, Tabelle, Drilldown).
   Das Muster ist NICHT das der Vorlage (Quadrate), sondern das der Sektion "See what AI answers
   say about you." (.ulh-stimme): 1px-Punkte im 8px-Raster in --ulh-schiene. Eine Seite, ein
   Punktmuster. Die Blende ist ein Oval etwas unter der Mitte -- zur Kante hin und nach oben zum
   Kartentext verschwindet es, damit die Karte keinen gemusterten Rand bekommt. */
/* ETWAS KRAEFTIGER (07.10. angefordert: "etwas mehr Opacity"): die Punkte in --ulh-border (#e7e9ec,
   der Kante der Karten) statt --ulh-schiene (#ededf0) -- die naechste Stufe der eigenen Palette --,
   und der volle Kern der Blende reicht weiter nach aussen (45 statt 32 Prozent).
   NOCH EINE STUFE (07.10. spaeter: "noch mehr Opacity, etwas deutlicher zu erkennen"): #d8dbe0, so
   weit unter der Kartenkante (#e7e9ec) wie die Kante unter der Schiene liegt -- zwei gleiche
   Schritte derselben kuehlen Grauleiter. Als Marke, weil beide Muster sie tragen. */
.ulh-root { --ulh-punkt: #d8dbe0; }
.ulh-vis-muster {
  position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(circle, var(--ulh-punkt) 1px, transparent 1px);
  background-size: 8px 8px;
  -webkit-mask-image: radial-gradient(ellipse 64% 66% at 50% 56%, #000 45%, transparent 100%);
  mask-image: radial-gradient(ellipse 64% 66% at 50% 56%, #000 45%, transparent 100%);
}
/* DIE VARIANTE HINTER DER PRODUKTTABELLE (07.10.: "aehnlich, aber nicht genau gleich"): dieselben
   Punkte, aber versetzt im Rautenraster -- zwei Lagen im 12er-Abstand, die zweite um eine halbe
   Masche verschoben, ergibt Punkte im Abstand von gut 8px auf der Diagonalen. Die Blende liegt
   weiter rechts, das Muster laeuft also nicht spiegelgleich zur Domain-Karte aus. */
.ulh-vis-muster.is-versetzt {
  background-image: radial-gradient(circle, var(--ulh-punkt) 1px, transparent 1px),
                    radial-gradient(circle, var(--ulh-punkt) 1px, transparent 1px);
  background-size: 12px 12px; background-position: 0 0, 6px 6px;
  -webkit-mask-image: radial-gradient(ellipse 66% 64% at 60% 58%, #000 42%, transparent 100%);
  mask-image: radial-gradient(ellipse 66% 64% at 60% 58%, #000 42%, transparent 100%);
}
/* SENKRECHTE LINIEN (14.10. abends, hinter Branded: "nicht die Punkte, sondern das vertikale
   Grid wie im Hero, aber in Grau"). Dieselbe Machart wie .ulh-bg-grid im Hero -- 1px alle 8px,
   nur senkrecht --, in der Punktfarbe der anderen Muster statt in Weiss, und mit der Blende der
   versetzten Punkte, damit es zur Kante hin auslaeuft wie die anderen. */
.ulh-vis-muster.is-linien {
  background-image: repeating-linear-gradient(to right, var(--ulh-punkt) 0 1px, transparent 1px 8px);
  background-size: auto; background-position: 0 0;
  -webkit-mask-image: radial-gradient(ellipse 66% 64% at 50% 58%, #000 42%, transparent 100%);
  mask-image: radial-gradient(ellipse 66% 64% at 50% 58%, #000 42%, transparent 100%);
}
/* Die Vorschau liegt UEBER dem Muster. Platz rundherum, damit vom Muster etwas zu sehen ist
   (ungeskaliert, also mal 0.66 auf dem Schirm). */
.ulh-vis[data-ulh-vis="domains"] .ulh-vis-in { position: relative; z-index: 1; padding: 24px 36px 0; box-sizing: border-box; }
/* Der Rand: 16px halbdurchsichtiges Grau mit eigener Kante und weichem Schatten -- auf dem Schirm
   gut 10px, so breit wie in der Vorlage (bei 12px waren es 8 und der Rand kaum zu sehen).
   Konzentrisch: aussen 30 = innen 14 + 16. */
.ulh-rahmen {
  padding: 16px; border-radius: 30px;
  background: rgba(236, 237, 241, .78); border: 1px solid rgba(11, 13, 24, .06);
  box-shadow: 0 1px 2px rgba(11, 13, 24, .04), 0 22px 50px rgba(11, 13, 24, .08);
}
/* Das Fenster: weiss, eine Kante, darin der Kopf und die Tabelle buendig bis an den Rand -- die
   Tabelle verliert ihren eigenen Kasten (Rahmen, Radius, Schatten), sonst stuenden zwei Kanten
   ineinander. */
.ulh-rahmen-in {
  background: var(--vc-bg); border: 1px solid var(--ulh-border); border-radius: 14px; overflow: hidden;
}
.ulh-rahmen-in > .up-head {
  height: auto; margin: 0; padding: 12px 12px 12px 16px; border-bottom: 1px solid var(--ulh-border);
}
.ulh-rahmen-in > .ulh-vis-dom, .ulh-rahmen-in > .ulh-vis-tab { border: 0; border-radius: 0; box-shadow: none; }
/* Die Kopfzeile der Prompt-Tabelle traegt sonst 12px oben, damit sie die Ecken der Tabelle deckt --
   im Fenster steht sie unter dem Kopf der App und ist gerade. */
.ulh-rahmen-in .ulh-vis-tab .up-thead { border-radius: 0; }
/* Die Spaltenkoepfe wie in der App (.up-th aus core, 45px). */
.ulh-vis[data-ulh-vis="domains"] .ulh-vis-dom .up-thead { height: 45px; flex-basis: 45px; letter-spacing: normal; }
/* Die Seitenliste ohne Last Seen: vier Spuren statt fuenf (domains-table.css hat fuenf). */
.ulh-vis[data-ulh-vis="domains"] .udt-subrows { --udt-subcols: minmax(0, 1fr) 112px 128px 34px; }
/* Alle Hervorhebungen noch einmal halb so schnell: 476 -> 714ms. Und eine andere Kurve, denn
   daran lag das Hektische: --ulh-aus (.1, 1, .2, 1) startet mit voller Geschwindigkeit und klingt
   nur aus -- der Anfang war der Ruck. --ulh-weich ist an beiden Enden flach, die Zeile setzt sich
   in Ruhe in Bewegung und kommt in Ruhe an. */
.ulh-vis-tab .up-row, .ulh-vis-chancen .uo-row {
  transition: transform 714ms var(--ulh-weich), box-shadow 714ms var(--ulh-weich),
              border-radius 714ms var(--ulh-weich), background-color 714ms var(--ulh-weich);
}
/* Der Ruhezustand der gehobenen Zeile steht AUSDRUECKLICH da, sonst gibt es nichts, wovon der
   Uebergang ausgehen koennte: Rundung 0 und ein durchsichtiges WEISS (nicht "transparent" -- das
   waere durchsichtiges Schwarz, und der Grund blendete durch Grau). */
.ulh-vis-tab .up-row.is-mitte { border-radius: 0; background-color: rgba(255, 255, 255, 0); }
/* Die gehobene Zeile bekommt KEINEN eigenen Radius. Sie nimmt den, den ihr Bauteil ohnehin hat:
   die Chancenzeile hat 12px von sich aus, die Zeile der Tabelle hat keinen -- ihr Rahmen ist der
   der Tabelle, und deren 12px stehen hier. */
.ulh-card:hover .ulh-vis-tab .up-row.is-mitte { border-radius: 12px; }
/* Der Grund der gehobenen Zeile: WEISS als Zahl. Vorher stand hier var(--vc-bg) -- die Farbe der
   App, aber sie haengt an .up-root, und diese Vorschau steht darueber: die Angabe war ungueltig,
   die Zeile blieb durchsichtig, und man sah ihre Nachbarn hindurch. Dieselbe Falle wie beim Kasten
   der Quellen-Sektion. */
.ulh-card:hover .ulh-vis-tab .up-row.is-mitte { background-color: #ffffff; }
/* Der eigene Hover der Bauteile faellt weg. */
.ulh-vis .up-row:hover, .ulh-vis .uo-row:hover, .ulh-vis .udt-subrow:hover {
  background: var(--vc-bg); border-color: var(--vc-border);
}
/* AUSSER DIE KOPFZEILE (24.09. gemeldet: "auf der Topbar der Domains-Tabelle ist ein Hoverstate
   drauf"). Sie traegt in dieser Tabelle dieselbe Klasse wie eine Datenzeile, steht aber auf
   --vc-heading-bg -- die Regel darueber setzte sie beim Ueberfahren auf --vc-bg, und damit war
   ausgerechnet am Kopf ein Hover zu sehen, den sie abschalten sollte. Gemessen: Ruhe
   rgb(249,248,249), Hover var(--vc-bg).
   Eine Klasse mehr im Selektor statt einer Regel weiter unten: gleiche Spezifitaet haette die
   Reihenfolge entscheiden lassen. */
.ulh-root .ulh-vis .up-row.up-thead:hover { background: var(--vc-heading-bg); }
/* Die Tabelle der Prompt-Karte schneidet NICHT ab -- weder beim Heben noch beim Senken. Vorher
   stand overflow: hidden an ihr und wurde beim Hover auf visible gestellt; overflow kennt aber
   keinen Zwischenschritt, es springt. Beim Verlassen sprang es sofort zurueck auf hidden, und die
   gehobene Zeile verschwand hinter der Tabellenkante, WAEHREND sie noch 714ms lang schrumpfte.
   Die Zeile braucht die Sperre auch nicht: sie hat keinen eigenen Grund ausser beim Heben, und
   die runden Ecken der Tabelle deckt ihre Kopfzeile mit demselben Radius ab.
   .ulh-vis-dom behaelt die Sperre -- dort liegt ein aufgeklappter Drilldown mit Fuellung darin. */
.ulh-vis-dom { overflow: hidden; }
.ulh-vis-tab .up-thead { border-radius: 12px 12px 0 0; }
/* DIE EIGENE EBENE STEHT SCHON IN RUHE (24.09. gemeldet: "auf Windows kommen im gehoverten
   Zustand an den Ecken so komische Zacken und Striche, an allen vier Rahmen dieser Opportunity
   Card").
   Das ist die Treppe, die entsteht, wenn ein 1px-Haarstrich MIT der Flaeche skaliert wird: der
   Browser rastert das Element in seiner ungehobenen Groesse und zieht die Textur danach auf
   106 Prozent. Auf macOS faellt das kaum auf, unter Windows steht die Treppe an den vier Kanten
   und besonders in den Rundungen.
   will-change: transform IN RUHE ist der Griff dafuer -- dann legt der Browser die eigene Ebene
   an, BEVOR die Bewegung beginnt, und rastert sie fuer den Endzustand. Auf dem Hover gesetzt
   waere es zu spaet: die Ebene entstuende mitten in der Fahrt, mit genau der Textur, um die es
   geht. backface-visibility dazu, weil Windows-Chrome sonst je nach Treiber weiter auf dem
   alten Weg rastert.
   Nur diese zwei Zeilen tragen es und nicht jede Zeile der Vorschau: eine eigene Ebene je Zeile
   waere Speicher fuer nichts. */
.ulh-vis-tab .up-row.is-mitte,
.ulh-vis-chancen .uo-row.is-mitte { will-change: transform; backface-visibility: hidden; }
.ulh-card:hover .ulh-vis-tab .up-row.is-mitte,
.ulh-card:hover .ulh-vis-chancen .uo-row.is-mitte {
  transform: scale(1.06);
  /* Der Rahmen steckt im letzten Schatten und ist kein Rahmen: ein echter braucht Platz und
     schoebe die Zellen der Zeile um 1px zur Seite -- ein Innenschatten nicht. Und er faehrt mit
     derselben box-shadow-Kurve heraus wie der Schlagschatten, statt zu springen. Genau das war
     der Rest der Hektik: Rundung, Rahmen und Grund sprangen, waehrend die Groesse fuhr. */
  box-shadow: 0 12px 30px rgba(11, 13, 24, .14), 0 2px 6px rgba(11, 13, 24, .06),
              inset 0 0 0 1px var(--ulh-border);
  position: relative; z-index: 4;
}
/* DIE PROMPT-ZEILE KOMMT NUR ZU 60 PROZENT HERAUS (24.09. angefordert). 6 Prozent Zuwachs mal
   0.6 sind 3.6 -- also scale(1.036). Die Chancen-Zeile bleibt bei den vollen 1.06: dort ist die
   Karte das Thema der Vorschau, in der Tabelle ist es die Tabelle. */
.ulh-root .ulh-card:hover .ulh-vis-tab .up-row.is-mitte { transform: scale(1.036); }



/* ---- Karte 3: der aufgeklappte Drilldown ----
   Die Zeile darueber traegt is-expanded (wie in der App), der Kasten darunter ist der echte
   .udt-subrows. Er bekommt hier keine eigene Hoehenanimation -- er ist von Anfang an offen. */
.ulh-vis-dom .udt-subrows { max-height: none; }
.ulh-vis-dom .udt-sub-inner { overflow: visible; }
.ulh-vis-dom .udt-pagesbtn { pointer-events: none; }
/* Flachere Zeilen als in der App (72 -> 56px): in einer Vorschau zaehlt, WIE VIELE Zeilen man
   sieht, nicht wie luftig eine einzelne steht. */
/* NICHT die Kopfzeile: sie traegt in dieser Tabelle dieselbe Klasse wie eine Datenzeile, und mit
   56px war sie 16px hoeher als die der Prompt-Tabelle daneben -- gemessen 56 gegen 40. Der
   Ausschluss loest das ueber die Spezifitaet und nicht ueber die Reihenfolge. */
.ulh-vis-dom .up-row:not(.up-thead) { height: 56px; }
/* Name und Seitenknopf NEBENEINANDER statt uebereinander -- in der App stehen sie gestapelt, weil
   dort eine Zeile 72px hoch ist und der Knopf darunter Platz hat. Hier ist die Zeile flacher, und
   nebeneinander mit Abstand liest sich beides in einer Zeile. */
.ulh-vis-dom .udt-dom-wrap { flex-direction: row; align-items: center; gap: 12px; }
.ulh-vis-dom .udt-dom-title { flex: 0 1 auto; }
.ulh-vis-dom .udt-pagesbtn { flex: 0 0 auto; }

/* ---- Die Balken der Zitattypen im Hero-Fenster ----
   8px flacher als in der App (Spur 32 -> 24, Zeile 42 -> 34): in einem Schaustueck zaehlt, WIE
   VIELE Typen man sieht, nicht wie luftig ein einzelner steht -- bei gleicher Kastenhoehe sind
   das zwei Balken mehr (24.09. angefordert).
   Nur im Fenster dieser Sektion (.ulh-app), die App bleibt bei 32. */
.ulh-app .tcd-root .up-bar-track { height: 24px; }
.ulh-app .tcd-root .up-bar-fill  { border-radius: 6px; }   /* konzentrisch zur flacheren Spur */

/* ---- Karte 4: die Chancenliste ----
   .uo-row wie im Listenmodus der App. Der Kasten darum haelt nur den Abstand. */
/* Die zwei Marken der Prioritaetsbalken. Sie stehen in opportunities.css an .up-root.uo-root --
   und diese Klasse darf die Vorschau NICHT tragen: opportunities.js sucht sich seine Wurzeln
   danach und wuerde hier einen zweiten Controller aufziehen. Ohne sie ist der Farbverlauf der
   gefuellten Balken ungueltig und faellt auf "none" zurueck: gemessen standen alle vier Balken
   grau da, obwohl drei die Klasse is-on trugen. Die Werte sind die der Komponente. */
.ulh-vis-chancen .ulh-vis-in { --uo-pot-a: #c4c9d0; --uo-pot-b: var(--vc-text); }
.ulh-vis-chancen .uo-list-rows { gap: 8px; }
.ulh-vis-chancen .uo-row { cursor: default; }



/* Schmal: die zwei Karten einer Reihe stapeln, und die Ueberschrift darf ueber ihre 20ch hinaus.
   Dieselbe Schwelle wie beim Fenster oben (900px) -- dort endet auch die Buehne. */
@media (max-width: 900px) {
  .ulh-cards-row { flex-direction: column; }
  .ulh-feat-h { font-size: 32px; max-width: none; }
  .ulh-feat { padding-top: clamp(48px, calc(8 * var(--ulh-fh)), 80px); }
  .ulh-quell { --ulh-quell-luft: clamp(48px, calc(8 * var(--ulh-fh)), 80px); }
  /* NUR hier faellt die gehobene Zeile weg, auf dem breiten Schirm bleibt sie. Zwei Gruende: die
     Karte ist auf schmalen Schirmen selbst angeschnitten, die gehobene Zeile faehrt also aus ihr
     heraus statt in ihr zu stehen -- und auf einem Touchgeraet bleibt der Hover nach dem Tippen
     haengen, die Zeile stuende dann dauerhaft gross da.
     Mit .ulh-root davor, damit die Regel ueber die Spezifitaet gewinnt und nicht darueber, dass
     sie weiter unten steht. Alle vier Werte zusammen, sonst faehrt die Zeile nur halb zurueck. */
  .ulh-root .ulh-card:hover .ulh-vis-tab .up-row.is-mitte,
  .ulh-root .ulh-card:hover .ulh-vis-chancen .uo-row.is-mitte {
    transform: none; box-shadow: none; border-radius: 0;
    background-color: rgba(255, 255, 255, 0);
  }
}

/* ---------- Die drei Nebenfenster ----------
   Sie erscheinen, sobald die Seite bewegt wird -- dieselbe Schwelle, die das Hauptfenster
   verkleinert (is-klein an der Wurzel, gesetzt in landing-hero.js).

   Der RAHMEN ist derselbe wie am Hauptfenster: dieselbe Blende, derselbe Radius, dieselbe
   Punktreihe in derselben Groesse. Das ist keine Nachlaessigkeit, sondern die Regel echter
   Fenster -- ein kleines Fenster hat keinen kleineren Rand. Deshalb steht die Blende oben in
   einer Regel fuer .ulh-frame UND .ulh-fen, und die Punkte kommen aus derselben Funktion
   (chrom() in landing-hero.js).

   Die GROESSE steht je Fenster in zwei Zahlen: --ulh-fb und --ulh-fh sind der Schirm in
   Basispixeln, also in derselben Einheit, in der die App im Hauptfenster rechnet. Multipliziert
   mit --ulh-mass kommt genau die Groesse heraus, die ein Fenster dieser Groesse neben der
   verkleinerten App haette. --ulh-fz ist eine ZWEITE, eigene Verkleinerung des Inhalts (nur der
   Doughnut braucht sie); der Inhaltskasten wird dafuer um denselben Faktor groesser, damit der
   Schirm weiter genau gefuellt ist. */
.ulh-fen { position: absolute; z-index: 1; }
/* Zwei Fenster liegen VOR dem Hauptfenster (die URL-Typen und die Antwortkarte, is-vorn), eines
   dahinter (Teams). Ausdrueckliche Werte an allen drei Stellen: bei gleichem z-index entschiede
   die Reihenfolge im Markup, und die soll hier nichts bedeuten.
   Die Antwortkarte hat die Seite zweimal gewechselt: bis zum 28.09. davor, dann dahinter
   ("Teams und Antwortkarte schieben sich HINTER das Hauptfenster"), seit dem 29.09. spaet wieder
   davor ("ueber dem Hauptfenster, nicht dahinter"). Gleichgueltig ist das nur, solange sie mit
   4px Luft NEBEN dem Hauptfenster steht -- auf schmaleren Seiten schiebt sie sich ueber seine
   Kante (siehe "Lage" unten), und dann entscheidet die Ebene, was man sieht. */
.ulh-frame { position: relative; z-index: 2; width: 100%; }
.ulh-fen.is-vorn { z-index: 3; }

.ulh-fen-view {
  position: relative;
  width: calc(var(--ulh-fb) * var(--ulh-mass, 1));
  height: calc(var(--ulh-fh) * var(--ulh-mass, 1));
  overflow: hidden;
  /* Kleiner als der Rahmen, genau wie am Hauptfenster: 12 gegen 16. */
  border-radius: 12px;
  background: var(--ulh-schirm);
}
.ulh-fen-app {
  position: absolute; top: 0; left: 0;
  width: calc(var(--ulh-fb) / var(--ulh-fz, 1));
  height: calc(var(--ulh-fh) / var(--ulh-fz, 1));
  transform: scale(calc(var(--ulh-mass, 1) * var(--ulh-fz, 1)));
  transform-origin: top left;
}
/* Unter zwei Geraetepixeln je CSS-Pixel dieselbe Umstellung wie im Hauptfenster: verkleinert wird
   im Layout, damit die Haarlinien auf ganzen Geraetepixeln liegen. Gerade hier faellt es auf --
   in diesen kleinen Fenstern stehen die kleinen Umschalter. Die zwei Massstaebe multiplizieren
   sich: der der Buehne und der des Fensters. Kein contain noetig, weil hier nichts fixed steht. */
.ulh-root.is-zoom .ulh-fen-app {
  transform: none;
  zoom: calc(var(--ulh-mass, 1) * var(--ulh-fz, 1));
}

/* Der Auftritt: die Fenster kommen aus der Richtung, in der das Hauptfenster steht -- sie liegen
   davor verdeckt hinter ihm und fahren heraus. Nicht von aussen herein: dann kaemen sie ueber den
   Rand der Seite, von dem sie seit dem 28.09. ausdruecklich Abstand halten (siehe "Lage").
   Der Ausklang ist der lange der Verkleinerung (--ulh-aus), damit beide Bewegungen als eine
   gelesen werden. */
.ulh-fen { opacity: 0; transition: opacity 120ms var(--ulh-ease), transform 1100ms var(--ulh-aus); }
.ulh-fen-teams, .ulh-fen-urls { transform: translate(90px, 0) scale(.94); }
.ulh-fen-antwort { transform: translate(-90px, 0) scale(.94); }
/* 700ms beim KOMMEN, 120 beim GEHEN (28.09.). Beim Zurueckscrollen waechst die Buehne wieder auf
   ihre volle Groesse, und zwar schnell (--ulh-aus: nach 200ms steht sie schon bei Faktor 0.9) --
   die Nebenfenster wachsen dabei mit nach aussen. Mit 700ms Ausblenden waren sie da noch zu zwei
   Dritteln zu sehen und standen gemessen 23px UEBER dem Rand der Seite (1280px, 200ms nach dem
   Wechsel); mit 180ms erreichten sie die Kante nach 123ms noch mit 7 Prozent Deckkraft. 120 (die
   kurze Stufe der Dauern, --up-t-1) ist vorbei, bevor sie dort ankommen. Das Kommen bleibt, wie
   es war: die Dauer steht deshalb im Zustand is-klein, denn ein Uebergang nimmt die Dauer des
   Zustands, in den er laeuft. */
.ulh-root.is-klein .ulh-fen { opacity: 1; transform: none; transition-duration: 700ms, 1100ms; }
/* Sie kommen NACHEINANDER und nicht zu dritt: erst atmet die Verkleinerung durch, dann Teams, dann
   die Antwortkarte, dann die URL-Typen. Die Verzoegerung steht nur am Kommen -- beim Zurueckscrollen
   sollen sie zugleich verschwinden, sonst haengt eines hinter dem wieder wachsenden Fenster fest. */
.ulh-root.is-klein .ulh-fen-teams   { transition-delay: 260ms; }
.ulh-root.is-klein .ulh-fen-antwort { transition-delay: 580ms; }
.ulh-root.is-klein .ulh-fen-urls    { transition-delay: 900ms; }
@media (prefers-reduced-motion: reduce) {
  .ulh-fen { transition: none; }
}

/* ---- Lage ----
   Alle drei stehen an einer KANTE des Hauptfensters: Teams und die URL-Typen links, die
   Antwortkarte rechts. "right: calc(100% + 4px)" heisst, die rechte Kante des Nebenfensters
   liegt 4px links von der linken Kante des Hauptfensters.

   EINGEPASST IN ZWEI STUFEN (28.09. angefordert: "bei kleineren Bildschirmen verlassen die
   Fenster Team Switcher, URL Type und Response Detail einfach den Bildschirmrand. Das darf nicht
   sein ... erst noch ein Stueck HINTER (bzw. im Fall von URL Type VOR) die Hauptfenster-Komponente
   schieben. Dann soll alles anfangen herunterzuskalieren").
   Gemessen vorher, im kleinen Zustand: schon bei 1920px Seitenbreite stand die Antwortkarte 2px
   vor dem rechten Rand und ihr Schatten war abgeschnitten; bei 1280 lagen Teams 121px und die
   Antwortkarte 140px AUSSERHALB der Seite, bei 1024 noch 83 und 98.
     Stufe 1  --ulh-rein schiebt ein Fenster (in Pixeln der Buehne) auf das Hauptfenster zu, bis
              es --ulh-kante vom Rand steht -- hoechstens so weit, dass --ulh-rein-max seiner
              eigenen Breite hinter (bzw. ueber) dem Hauptfenster liegt, siehe gleich darunter.
     Stufe 2  Reicht das nicht, wird die ganze Buehne kleiner als ihr Deckel-Faktor (kleinFaktor
              in landing-hero.js) -- Hauptfenster und Nebenfenster zusammen, im selben transform
              wie bisher, also ohne einen zweiten Massstab daneben.
   Beide Werte rechnet einpassen() in landing-hero.js aus den gemessenen Kaesten; hier steht nur,
   wo sie angreifen. Ein Fenster rueckt nur so weit, wie es muss: auf einer 2560er Seite bleibt
   alles, wie es war.
   --ulh-rein steht als Inline-Angabe am Fenster selbst, nicht an der Wurzel: jedes Fenster
   braucht seinen eigenen Wert, und am Fenster gibt es keinen Vorfahren, der ihn ueberdecken
   koennte. */
/* DIE GRENZE DER ERSTEN STUFE: hoechstens 16 Prozent eines Fensters liegen hinter dem Hauptfenster
   (bzw. bei den URL-Typen ueber ihm). Die Zahl liegt zwischen zwei gemessenen Kanten, und jede
   Seite davon waere schlechter:
     - Das Modellzeichen der Antwortkarte endet bei 14.8 Prozent ihrer Breite (von innen gezaehlt).
       Darunter stuende es angeschnitten da -- ein halbes G ist schlechter als keins. Mit 16 ist
       es ganz dahinter, und Stimmung (ab 19.6), Rang und Zeit bleiben stehen.
     - Der Chip der Teams ("Pitch") endet bei 83.3 Prozent, also 16.7 von innen. Darueber wuerde er
       angeschnitten; mit 16 bleibt er ganz, und nur der Haken der aktiven Zeile geht -- die Zeile
       ist ohnehin hinterlegt.
   Ein Viertel (erster Versuch) nahm der Antwortkarte zusaetzlich die Stimmung und schnitt den Chip
   der Teams an. Den Anfang jeder Zeile des Auszugs kostet auch die 16 (der Text beginnt bei 6.5
   Prozent) -- das ist der Preis dafuer, dass die Karte ueberhaupt dahinter rueckt, und er ist
   angefordert. Danach wird verkleinert, nicht weiter versteckt. Die URL-Typen liegen davor und
   verlieren nichts; fuer sie begrenzt dieselbe Zahl, wie viel sie von der Leiste des
   Hauptfensters abdecken.
   Das alles gilt fuer die GRENZE. Auf dem Weg dorthin (Stufe 1, gemessen zwischen gut 2040 und
   1940px Seitenbreite) rueckt jedes Fenster stufenlos, und die Kante des Hauptfensters schneidet
   dabei auch einmal durch das Zeichen -- so sieht ein Fenster hinter einem anderen eben aus.
   SEIT DEM 29.09. SPAET 30 PROZENT ("die koennen sich bitte noch etwas weiter dahinter schieben,
   oder davor, bevor alles mitskaliert"). Wieder zwischen zwei gemessenen Kanten: in der
   Antwortkarte endet der Stimmungswert bei 28.3 Prozent (von innen) und das naechste Zeichen
   beginnt bei 32.2 -- 30 schneidet nichts an, die Stimmung liegt jetzt ganz dahinter. In den Teams
   stehen die Namen linksbuendig weit aussen, hinter der Grenze liegen nur Haken und Leerraum.
   Gemessen bringt das bei gleicher Seitenbreite ein groesseres Hauptfenster: 1440 -> 976 statt
   928, 1280 -> 856 statt 814, 1100 -> 721 statt 686.
   Seit die Antwortkarte wieder VOR dem Hauptfenster liegt (29.09. spaet), verliert sie dabei
   nichts mehr -- die 30 Prozent begrenzen jetzt, wie viel sie vom rechten Rand des Hauptfensters
   abdeckt, genau wie bei den URL-Typen links. */
.ulh-fen { --ulh-rein-max: .30; }
/* 300x254: die Breite ist die des Panels in der App (sidebar.css: .usn-bar .usn-menu), die Hoehe
   ist ADDIERT und nicht geschaetzt -- 8 Polster + 32 Suchfeld + 6 Abstand + drei Zeilen a 64 mit
   4px dazwischen + 8 Polster. Mit 250 stand die dritte Zeile 4px unter der Kante (gemessen:
   scrollHeight 200 in 196). */
/* Direkt NEBEN dem Hauptfenster statt darunter geschoben: die rechte Kante liegt 4px links von
   seiner linken. Keine Ueberdeckung, solange die Seite breit genug ist -- sonst rueckt es um
   --ulh-rein darunter (siehe oben). */
.ulh-fen-teams   { --ulh-fb: 300px; --ulh-fh: 254px; right: calc(100% + 4px - var(--ulh-rein, 0px)); top: -50px; }
/* 352 x 330, also ein Zehntel groesser als die 320 x 300 davor. Die Legende hat damit mehr Platz,
   und beide Verteilungen bleiben bei DREI Reihen: die redaktionelle bricht in 3/3/2 um, die der
   Marktplaetze in 2/2/2 (ihre Beschriftungen sind laenger, "Product / Service" allein ist 114px,
   also gehen dort nur zwei in eine Reihe -- deshalb hat sie auch nur fuenf benannte Typen plus
   Rest, siehe landing-hero.js).
   Nur 8px Ueberdeckung -- dieses Fenster liegt VOR dem Hauptfenster, und was davor liegt, soll so
   wenig wie moeglich davon verdecken. */
/* Fuenf Prozent kleiner als der Stand davor (352 x 330) und acht Pixel weiter nach rechts, also
   16px Ueberdeckung statt 8. */
/* Je Seite 5px kleiner als der Stand davor (334 x 314): die Masse gehen um 10 zurueck, und die
   Verankerung wandert um 5 mit -- der rechte Rand liegt 5px weiter links (16 -> 11), der untere
   5px hoeher (-50 -> -45). Nur die Masse zu kuerzen haette die Kachel an einer Ecke festgehalten
   und sie NUR an den zwei anderen Seiten schmaler gemacht. */
.ulh-fen-urls    { --ulh-fb: 324px; --ulh-fh: 304px; --ulh-fz: 0.8; --ulh-fs: .85;
                   right: calc(100% - 11px - var(--ulh-rein, 0px)); bottom: -45px; }
/* 15 PROZENT KLEINER, UND ZWAR WIRKLICH SKALIERT (24.09. angefordert). Nicht die Masse kuerzen --
   dann waeren Schrift, Ring und Legende unveraendert gross und nur enger. Ein transform
   verkleinert alles im selben Verhaeltnis.
   Der Ursprung liegt an der VERANKERTEN Ecke (rechts unten, siehe die Regel darueber), damit die
   Lage bleibt, wo sie war -- die Kachel schrumpft nach innen und wandert nicht.
   Zwei Zustaende, beide mit dem Faktor: der Startzustand des Auftritts (0.94 * 0.85 = 0.799) und
   der Ruhezustand, in dem die allgemeine Regel sonst transform: none setzt. Eine Klasse mehr im
   Selektor als dort, damit die Spezifitaet entscheidet und nicht die Reihenfolge.
   Der Faktor steht seit dem 28.09. als --ulh-fs in der Regel darueber und nicht mehr zweimal als
   Zahl hier: einpassen() in landing-hero.js muss wissen, wie breit die Kachel WIRKLICH dasteht,
   und liest ihn von dort -- eine dritte Stelle mit 0.85 waere die, die beim naechsten Wert
   vergessen wird. */
.ulh-fen.ulh-fen-urls { transform-origin: right bottom; transform: translate(90px, 0) scale(calc(.94 * var(--ulh-fs))); }
.ulh-root.is-klein .ulh-fen.ulh-fen-urls { transform: scale(var(--ulh-fs)); }
/* Rechts, auf einem Viertel der Hoehe: weit oben, aber nicht an der Oberkante. Direkt neben dem
   Hauptfenster, 4px Luft -- auf schmaleren Seiten um --ulh-rein darunter (siehe oben). Die Hoehe
   ist die, die die Karte bei dieser Breite von sich aus braucht -- so muss sie nicht gestreckt
   werden und traegt keine Luft: gemessen 234px bei 272px Breite (Kopf 24, Frage 38, vier Zeilen
   Auszug 78, Fusszeile 28, dazu Polster und Abstaende), plus 28px Polster des Fensters ergibt
   262. */
.ulh-fen-antwort { --ulh-fb: 325px; --ulh-fh: 262px; left: calc(100% + 4px - var(--ulh-rein, 0px)); top: 25%; }

/* Hier stand: "Die Nebenfenster ragen links und rechts aus der Sektion heraus, und genau so ist es
   gedacht". Das gilt seit dem 28.09. nicht mehr -- sie bleiben ganz im Bild, siehe "Lage" oben.
   Der Beschnitt bleibt trotzdem: er ist die Sicherung fuer die Augenblicke, in denen die
   Einpassung noch nicht gerechnet hat (verdeckter Tab, erster Aufbau), und fuer die schmale
   Fassung, in der das Hauptfenster rechts hinauslaeuft. overflow-x: clip und nicht hidden: clip
   erzeugt keine Scrollflaeche, es gibt also keine waagerechte Scrollleiste und auch kein
   "overscroll-behavior" auf einer Flaeche, die niemand bewegen darf. */
.ulh-root { overflow-x: clip; background: var(--ulh-seite); }

/* ---------- Das Gitter der Seite ----------
   Zwei Linien, die von oben bis unten durchlaufen, und alles dazwischen. Ein eigenes Element und
   keine Raender an den Sektionen: an der Beruehrung zweier Sektionen mit eigenen Raendern stuende
   eine doppelte Linie, und ueber die ganze Hoehe durchlaufen wuerden sie ohnehin nicht.
   inset statt Hoehe: die Wurzel waechst mit jeder Sektion, die dazukommt, und die Schienen
   wachsen von sich aus mit. */
.ulh-schienen {
  position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
  width: var(--ulh-spur);
  border-left: 1px solid var(--ulh-schiene);
  border-right: 1px solid var(--ulh-schiene);
  pointer-events: none; z-index: 0;
}
/* Die Hero-Sektion ist der Bezugsrahmen ihres Hintergrundbildes. Ohne diese Zeile bezieht sich
   dessen inset: 0 auf die Wurzel und laeuft ueber die ganze Seite. */
.ulh-hero { position: relative; }
/* Der Inhalt jeder Sektion steht in derselben Spur wie die Schienen. */
.ulh-spur { width: var(--ulh-spur); margin: 0 auto; }

/* ---- Erstes Fenster: das Team-Dropdown ----
   Das Panel ist .up-filter-menu aus core. Was hier faellt, ist nur der RAHMEN des Panels: es
   liegt nicht mehr frei ueber einer Seite, sondern ist der Inhalt eines Fensters, und ein Panel
   mit Schatten in einem Fenster mit Schatten sind zwei Raender uebereinander. */
.ulh-teampanel {
  position: static; width: auto; min-width: 0; max-width: none; max-height: none; height: 100%;
  opacity: 1; transform: none; box-shadow: none; border: 0; background: transparent;
}
/* Im Schaustueck tippt niemand: das Suchfeld zeigt seinen Platzhalter und faengt keinen Zeiger. */
.ulh-fen-teams .up-ddsearch-in { pointer-events: none; }
/* Die Liste scrollt hier nicht -- drei Zeilen stehen ganz im Fenster, und eine Scrollflaeche im
   Schaustueck ist eine Einladung, die niemand annehmen kann. */
.ulh-fen-teams .usn-teamlist { overflow: visible; }
/* Der Wechsel der mittleren Zeile: die alte geht nach oben weg, die neue kommt von dort nachge-
   ruckt. EINE Klasse fuer beide Haelften, damit die Bewegung durchlaeuft statt sich zu kreuzen. */
.ulh-fen-teams .usn-teamrow { transition: opacity 260ms var(--ulh-ease), transform 300ms var(--ulh-ease); }
.ulh-fen-teams .usn-teamrow.is-tausch { opacity: 0; transform: translateY(-8px); }
/* Der Chip am dritten Team. Die Toene sind die des "likely"-Signals in Miras Antworten
   (ask-mira.css): blauer Grund bei 8.5 Prozent Deckkraft, Text im dunkleren Blau derselben
   Reihe. Ein eigenes Blau zu waehlen haette einen zweiten blauen Ton in die App gebracht. */
.ulh-teamchip {
  flex: 0 0 auto; height: 20px; padding: 0 7px; border-radius: 6px;
  display: inline-flex; align-items: center;
  font-size: 11px; font-weight: 600;
  background: rgba(37, 99, 235, 0.085); color: #1d4ed8;
}

/* ---- Zweites Fenster: die URL-Typen, dunkel ----
   Blende und Schirm werden dunkel mit. Ein heller Rahmen um einen dunklen Schirm sieht aus wie
   ein Fehler, nicht wie ein Fenster im Dunkelmodus: bei einem echten Fenster gehoert der Rahmen
   zum System, und das ist hier ebenfalls dunkel. Die Punkte bleiben, wie sie sind -- die sind
   auch auf einem dunklen Mac dieselben drei Farben.
   Der Schirmton ist der GRUND der App im Dunkeln (--up-surface: #121212), nicht der einer Karte
   (#1b1b1b): der Doughnut steht auf der Seite und nicht in einer Kachel. */
.ulh-fen-urls { background: #2b2b2b; }
.ulh-fen-urls .ulh-fen-view { background: #121212; }
.ulh-fen-urls .ulh-fen-app { display: flex; flex-direction: column; padding: 14px 16px; box-sizing: border-box; }
/* Der Kopf ist der einer Kachel aus core (.up-head), nur ohne die Werkzeugleiste, die es hier
   nicht gibt -- deshalb faellt auch sein unterer Abstand kleiner aus. */
.ulh-fen-urls .up-head { padding: 0; min-height: 0; margin-bottom: 10px; }
.ulh-fen-donut { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; }
/* KEIN Tooltip an diesem Chart. Der Kasten von core haengt an .up-donut-tt und wird von Chart.js
   als "external tooltip" gerufen -- ausgeschaltet wird er hier und nicht an der Instanz, weil die
   Hoverbewegung der Scheibe (hoverOffset, hellere Farbe) BLEIBEN soll: die kommt aus derselben
   Konfiguration, und ein "tooltip: false" haette beides genommen. */
.ulh-fen-urls .up-donut-tt { display: none; }
/* Der Ring etwas kleiner im Durchmesser. Er kommt aus core mit width: min(75%, 220px) am
   .up-donut-wrap (aspect-ratio 1/1 haelt ihn rund, die Hoehe folgt also von selbst -- eine
   Breitenangabe ist hier das ganze Paket). Das SKELETT traegt in core dieselben Masse und wird
   mitgezogen: sonst ist der Ladekreis groesser als der Ring, der ihn ersetzt, und es ruckt beim
   ersten Zeichnen. Gilt nur in diesem Fenster, in der App bleibt der Ring, wie er ist. */
.ulh-fen-urls .up-donut-layout.is-collapsed .up-donut-wrap,
.ulh-fen-urls .up-donut-sk.is-collapsed .up-sk-chart { width: min(66%, 196px); }
/* Der Wechsel der Verteilung: der Ring geht WEICH ZEICHNEN und faellt dabei etwas zusammen, und
   die neue Verteilung kommt genauso zurueck. Nur Deckkraft war zu wenig -- ein Ausblenden und ein
   Einblenden hintereinander lesen sich als zwei Schnitte, ein Unschaerfeverlauf als eine
   Bewegung. Der Ring zeichnet sich beim Einblenden ausserdem selbst neu (200ms Eingangsanimation
   im Kit), das faellt in dieselbe Bewegung.
   Die Ruecklaufzeit ist laenger als die Hinlaufzeit (320 gegen 260ms): scharf werden darf laenger
   dauern als unscharf werden, das ist die Richtung, in der man hinsieht. */
/* NUR UNSCHAERFE UND DECKKRAFT, KEIN MASSSTAB MEHR (24.09. gemeldet: "das Chart selber resized
   waehrenddessen, das darf es nicht tun -- es muss einfach ausblurren und wieder einbluren, das
   war auch mal korrekt"). Hier stand transform: scale(0.965) mit dabei, und genau das sah aus
   wie ein Chart, das seine Groesse aendert. Die Messung dazu: Ring, Leinwand und Legende bleiben
   ueber den Wechsel hinweg gleich gross (268x223 / 141x141 / 268x48) -- was sich bewegt hat, war
   allein diese Skalierung. */
.ulh-fen-donut { transition: opacity 320ms var(--ulh-ease), filter 320ms var(--ulh-ease); }
.ulh-fen-donut.is-wechsel {
  opacity: 0; filter: blur(7px);
  transition: opacity 260ms var(--ulh-ease), filter 260ms var(--ulh-ease);
}

/* ---- Drittes Fenster: eine Antwortkarte ----
   Von der Komponente bleibt die KARTE. Kopf, Werkzeugleiste und Seitenzaehler sind weg: in ein
   Fenster dieser Groesse gehoert die Karte und nicht die halbe Seite um sie herum, und anfassen
   kann hier ohnehin niemand etwas. Die Tabellenansicht ist im Kartenmodus von sich aus weg
   (responses-table.css: .up-root.is-cards-view .up-box). */
.ulh-fen-antwort .up-head,
.ulh-fen-antwort .up-foot { display: none; }
/* KEIN Polster: das Fenster IST die Antwort. Vorher lag die Karte als eigener Kasten mit
   eigenem Rahmen in einem Fenster, das schon ein Kasten ist -- zwei Rahmen ineinander, und
   zwischen ihnen 14px Luft, die nichts trug. Die Karte fuellt das Fenster jetzt randlos; ihr
   eigenes Polster (16px aus responses-table.css) haelt den Text vom Rand weg. */
.ulh-fen-antwort .ulh-fen-app { padding: 0; box-sizing: border-box; }
/* Rahmen weg, Radius wie der Ausschnitt (12px, siehe .ulh-fen-view) -- eine Karte, die buendig
   im Fenster sitzt, darf an den Ecken nicht eckiger sein als das Fenster. */
.ulh-fen-antwort .urt-card { border: 0; border-radius: 12px; }
/* Die Karte fuellt das Fenster. In der App ist sie auf 250px gedeckelt und wird von der Zahl der
   Karten daneben in Form gehalten; hier steht sie allein, und ein Deckel liesse unten eine Luecke
   (gemessen: 188px Karte in 244px Platz). Das Raster darum muss die Zeile dafuer strecken --
   sonst ist "height: 100%" die Hoehe des Inhalts und nicht die des Fensters. */
/* Die Kette braucht eine FESTE Hoehe, sonst ist "height: 100%" am Raster die Hoehe des Inhalts:
   eine Prozenthoehe gegen einen automatisch hohen Vorfahren rechnet auf auto zurueck. Deshalb
   traegt auch die Wurzel der Komponente hier eine Hoehe -- gemessen blieb die Karte sonst bei
   ihren natuerlichen 200px und liess 42px unter sich frei. */
.ulh-fen-antwort .urt-root { height: 100%; }
.ulh-fen-antwort .urt-cards { height: 100%; align-content: stretch; }
.ulh-fen-antwort .urt-card { max-height: none; height: 100%; }
/* Vier Zeilen Auszug statt zwei: das Fenster ist schmaler geworden, und der Platz, den es dafuer
   in der Hoehe bekommt, soll Text tragen und nicht Luft. Die Klammer bleibt -- ein Auszug ohne
   Ende waere ein Textblock. */
.ulh-fen-antwort .urt-card-preview { -webkit-line-clamp: 4; }
/* Das Zeichen des Modells oben in der Karte groesser: 18 -> 22px. Es ist das einzige Logo in
   diesem Fenster und traegt die Aussage "diese Antwort kam von ChatGPT" -- in 18px ist sie eine
   Fussnote. Die Plakette darum zieht mit, sonst sitzt das Bild an ihrem Rand. */
/* Das Modellzeichen ist das erste, was man auf der Karte liest -- welches Modell hat das gesagt.
   28 statt 22px: bei 22 war es im kleinen Fenster ein Punkt. */
/* Mit .up-root.urt-root davor, und das ist der Punkt: core setzt fuer schmale Wurzeln
   .up-root.is-vnarrow .urt-model-chip .up-ment-logo auf 20x20, und das sind vier Klassen gegen
   drei. Die Regel hier gewann deshalb nur ueber flex-basis die BREITE -- 28 breit, 20 hoch, und
   ein Zeichen mit object-fit: cover verliert oben und unten je vier Pixel. Das war das
   "oben und unten abgeschnitten", und es traf auch schon das alte Zeichen.
   Das Bild fuellt seinen Kasten mit 100% statt mit 28px: der Kasten traegt 1px Polster aus core,
   ein Bild mit fester 28er Kante stuende ueber seinem Rand. Radius 7 statt 8, weil ein Innenrand
   um genau dieses eine Pixel kleiner rundet als der Aussenrand. */
.ulh-fen-antwort .up-root.urt-root .urt-model-chip .up-ment-logo {
  width: 28px; height: 28px; flex: 0 0 28px; border-radius: 8px;
}
.ulh-fen-antwort .up-root.urt-root .urt-model-chip .up-ment-logo img {
  width: 100%; height: 100%; border-radius: 7px;
}
/* Der Wechsel zwischen den drei Antworten: aus, tauschen, ein. Nur die Deckkraft und ein kleiner
   Weg nach oben -- die Karte soll wechseln, nicht springen. */
.ulh-fen-antwort .ulh-fen-view {
  transition: opacity 420ms var(--ulh-weich), translate 420ms var(--ulh-weich);
}
.ulh-fen-antwort .ulh-fen-view.is-blende { opacity: 0; translate: 0 -8px; }

/* Die Schublade bleibt IM Fenster: opportunities.js haengt Vorhang und Schublade bei
   data-portal="inline" in den Root statt in den <body>, und ihr position: fixed bezieht sich damit
   auf die Buehne (sie traegt das transform).
   RUNDUM 12px. Vorher waren nur die linken Ecken rund -- gedacht als "sie sitzt buendig an der
   rechten Kante des Fensters". Gemessen sitzt sie das nicht: 12px Abstand rechts, 38 oben, 10
   unten. Eine Karte, die frei im Fenster steht und rechts eckig ist, sieht abgeschnitten aus. */
.ulh-app .uo-modal { border-radius: 12px; }

/* Die vier Lichter im Hintergrundbild von Mira fallen weg. Gemeint war das unten rechts (core
   setzt sie auf 94/74, 94/0.5, 20/85 und -3/19 Prozent) -- entfernt wird die ganze Ebene und
   nicht ein Verlauf daraus: die vier stehen in EINER Kurzschreibweise, und drei davon hier noch
   einmal hinzuschreiben waere eine Kopie, die am Tag der naechsten Aenderung an core auseinander
   laeuft. Bogen, Raster und Kreuzmarken bleiben; die Lichter liegen bei 0.2 Deckkraft ueber 6
   Prozent Alpha und tragen am wenigsten. */
.ulh-app .am-bgart .up-bgart-licht { display: none; }

/* Mira zeigt auf dem Startbildschirm ihre Kategorien -- im Schaustueck nicht: dort tippt der Ablauf
   selbst eine Frage ein, und eine Wand aus Karten daneben zieht den Blick von genau dieser Bewegung
   weg. Der Rest des Startbildschirms bleibt, wie er ist. */
.ulh-app #am-suggested { display: none; }
/* DIE LEISTE SCROLLT NICHT (21.09. gemeldet). .usn-nav steht in der App auf overflow-y: auto --
   richtig dort, denn die Liste kann laenger sein als der Schirm. Im Schaustueck ist sie eine
   Scrollflaeche, in der das Rad haengen bleibt, statt die Seite weiterzubewegen. Dieselbe
   Entscheidung und derselbe Wert wie bei Miras Chat weiter unten: clip und NICHT hidden -- hidden
   macht weiter eine Scrollflaeche daraus, nur eine, die niemand bewegen darf.
   Spezifitaet: drueben ist es (0,1,0), hier (0,2,0). Reicht also. */
.ulh-app .usn-nav { overflow: clip; }

/* Das Panel der frueheren Chats bleibt zu und braucht seinen Knopf hier nicht -- ohne Bubble gibt
   es keine Chats, die es zeigen koennte. */
.ulh-app .am-prev-btn { display: none; }

/* MIRA ZEIGT IMMER IHRE EINGEKLAPPTE LEISTE (21.09. klargestellt). Ich hatte die Ansage
   umgekehrt gelesen und sie versteckt, und zusaetzlich die HAUPTleiste eingeklappt -- beides
   falsch herum. Richtig ist: die grosse Chatliste geht nie auf (dafuer sorgt schon der fehlende
   Knopf, .am-prev-btn oben), und was man sieht, ist die schmale Schiene daneben. Die zeigt
   ask-mira.css von sich aus, sobald die Leiste angehaengt und zu ist -- hier steht also nichts
   mehr, ausser diesem Satz, damit niemand sie noch einmal wegnimmt. */

/* Das Eingabefeld ist VOR dem Tippen vollstaendig leer. Mira laesst dort sonst Beispielfragen
   durchlaufen -- neben einer Frage, die sich gerade selbst tippt, sind das zwei Texte, die um
   dieselbe Zeile streiten. Der native Platzhalter ist waehrend des Durchlaufs ohnehin leer
   (updateLoopState in ask-mira.js), es bleibt also nichts stehen. */
.ulh-app #am-ph-loop { display: none; }

/* Nach dem Abschicken faellt das Eingabefeld weg. Mira faehrt es beim ersten Satz aus der Mitte
   nach unten (200ms, has-messages), und in genau dieser Bewegung blendet es aus: im Schaustueck
   kann niemand etwas eingeben, und ein leeres Feld unter einer fertigen Antwort ist eine
   Aufforderung, die ins Leere geht. Dieselbe Dauer wie die Fahrt, sonst laufen zwei Bewegungen
   verschieden lang. */
/* Der ganze Ablauf beim Abschicken um die Haelfte langsamer: der Kopf schrumpft, die Titelzeile
   kommt, das Eingabefeld faehrt nach unten und blendet aus. In der App sind das 200ms, hier 300 --
   im Schaustueck sieht man diesem Wechsel zu, in der App will man ihn hinter sich haben.
   Alle Teile derselben Bewegung zusammen, sonst laufen sie auseinander: der Kopf traegt die Hoehe,
   die Schale das untere Polster, das Feld die Deckkraft, und die neue Nachricht ihren Auftritt. */
.ulh-app #ask-mira .am-hero { transition: height 300ms ease, min-height 300ms ease, padding 300ms ease; }
.ulh-app .am-composer-shell { transition: background 300ms ease, padding-bottom 450ms cubic-bezier(.4,0,.2,1); }
.ulh-app .am-msg { animation-duration: 420ms; }

.ulh-app #ask-mira.has-messages .am-composer-area { opacity: 0; }

/* Der Zurueck-Knopf in der Titelzeile: 32 -> 24px, das Zeichen darin 16 -> 14px. Beide Werte
   zusammen, wie es §1 verlangt -- ein 16px-Zeichen in einem 24px-Knopf haette an den Rand
   gestossen. */
/* Der Knopf ist jetzt in der App selbst 24x24 (ask-mira.css) -- die Sonderregel hier ist damit
   hinfaellig. Was bleibt, waere ein zweiter Ort mit denselben Zahlen, und der laeuft beim
   naechsten Mal auseinander. */

/* Der Abstand zwischen Frage und Antwort: 38 -> 24px. Im Fenster steht genau ein Paar, und 38px
   dazwischen lassen es wie zwei Bloecke ohne Zusammenhang aussehen. */
.ulh-app .am-messages { gap: 24px; }

/* Der Chat muss OHNE Scrollen hineinpassen, und dafuer reicht es nicht, ihn kuerzer zu texten.
   Gemessen: 1026px Inhalt in 686px Flaeche, also 340px zu viel. Die Rechnung darunter:
     Polster oben 25 + unten 44          -> 12 und 24, das sind 33 gespart
     Schriftgroesse 16 -> 15px           -> rund 6 Prozent auf jeden Textblock
     Abstaende zwischen den Bloecken     -> etwa halbiert, das sind rund 60
     dazu ein deutlich kuerzerer Text (siehe landing-hero.js)
   Alles zusammen, nicht einzeln: jede einzelne Massnahme haette nicht gereicht. */
.ulh-app .am-chat { padding-top: 12px; padding-bottom: 24px; }
/* Schrift und Zeilenhoehe der Nachrichten stehen als ZWEI Marken und nicht als Zahlen in der
   Regel: das Eingabefeld darunter liest dieselben zwei (siehe gleich danach). Die Zeilenhoehe ist
   die aus ask-mira.css (.am-bubble, 1.7) -- hier nur benannt, nicht geaendert. */
.ulh-app { --ulh-mira-schrift: 15px; --ulh-mira-zeile: 1.7; }
.ulh-app .am-msg .am-bubble { font-size: var(--ulh-mira-schrift); line-height: var(--ulh-mira-zeile); }
/* DAS EINGABEFELD SCHREIBT WIE DIE NACHRICHT (28.09. angefordert: "mach die Font Settings des
   Inputs im Inputfeld genau so, wie die der Mira Nachricht im Chat"). In der App steht die Frage
   im Feld bei 14px und 1.5, nach dem Abschicken in der Blase bei 16 und 1.7 -- im Schaustueck bei
   15 und 1.7, siehe oben. Man sieht hier genau diesen Uebergang, wenn die getippte Frage in den
   Chat wandert, und dabei sprang die Schrift um eine Stufe.
   Gemessen stimmten Familie, Schnitt (400), Laufweite (normal) und Farbe (--am-text) schon
   ueberein, verschieden waren nur Groesse und Zeilenhoehe -- also werden nur diese zwei gesetzt.
   Mit der Kennung, damit die Regel ueber die Spezifitaet gewinnt: ask-mira.css setzt die Groesse
   an .am-textarea zweimal, einmal in der Grundregel und einmal fuer schmale Fenster (16px ab
   720px abwaerts). Nur im Fenster dieser Sektion (.ulh-app), die App bleibt, wie sie ist. */
.ulh-app #ask-mira .am-textarea { font-size: var(--ulh-mira-schrift); line-height: var(--ulh-mira-zeile); }
.ulh-app .am-msg .am-bubble > .am-table-scroll { margin: 10px 0 24px; }
.ulh-app .am-msg .am-bubble > p { margin-bottom: 8px; }
.ulh-app .am-msg .am-bubble > h3 { margin-bottom: 4px; }
/* Mehr Luft VOR der Ueberschrift der Empfehlungen: sie beginnt einen neuen Abschnitt der
   Antwort, und 10px lasen sich als Abstand innerhalb des Textes. */
.ulh-app .am-msg .am-bubble > h4 { margin: 20px 0 4px; }
.ulh-app .am-msg .am-bubble > ul { margin: 6px 0 8px; }
/* Eine Leerzeile mehr zwischen dem Ende der Antwort und der Zeile mit den Datenpunkten: die
   Zeile gehoert nicht mehr zum Text, sie sagt, woraus er entstanden ist. */
.ulh-app .am-msg .am-bubble > .am-evidence { margin-top: 28px; }
/* Die Knopfreihe unter der ANTWORT bleibt stehen (Export, Kopieren, Daumen) -- sie gehoert zum
   Bild einer fertigen Antwort, und der Nutzer hat sie ausdruecklich verlangt. Hier stand vorher
   display:none fuer beide Nachrichten, um 56px zu sparen; die Regel weiter oben, die die Reihe
   dauerhaft einblendet, konnte dagegen nichts ausrichten -- gleiche Spezifitaet, spaeter im
   Stylesheet. Das war der Grund, warum die Knoepfe nicht zu sehen waren.
   Unter der FRAGE faellt sie weiter weg: dort haengen dieselben 28px, und unter der eigenen
   Frage steht in der App auch nichts, was man exportieren wollte. */
.ulh-app .am-msg.is-user .am-msg-actions { display: none; }
/* Die Zellen der Antworttabelle 9 -> 6px hoch gepolstert. Fuenf Zeilen mal zwei Seiten sind 30px,
   und die Tabelle bleibt lesbar: 12px seitlich stehen unveraendert. */
.ulh-app .am-msg table td,
.ulh-app .am-msg table th { padding-top: 6px; padding-bottom: 6px; }

/* Der Chat SCROLLT NICHT. Ein Schaustueck mit einer Scrollflaeche darin ist eine Einladung, die
   niemand annehmen kann -- der Zeiger bleibt darin haengen, und die Sektion sieht kaputt aus.
   Reicht die Hoehe nicht, wird unten abgeschnitten; die graue Blende dafuer bringt das Fenster
   schon mit (.ulh-view::after), sie liegt ueber jeder Seite und nicht nur ueber dem Dashboard. */
/* Mit der Kennung davor: #ask-mira:not(.has-messages) .am-chat setzt overflow-y: auto und hat mit
   einer id eine hoehere Spezifitaet als .ulh-app allein -- auf dem Startbildschirm hat die Regel
   hier also nicht gewonnen. Gemessen an der Liste der zutreffenden Regeln.

   clip und NICHT hidden, und das ist der Kern: hidden macht aus dem Chat weiter eine Scrollflaeche
   (nur eine, die der Nutzer nicht bewegen darf), und auf ihr liegt in ask-mira.css
   "overscroll-behavior: contain !important". Damit endet jede Radbewegung ueber dem Chat DORT und
   geht nicht an die Seite weiter -- genau der Bericht "auf dem Mira-Chat passiert beim Scrollen
   nichts". clip erzeugt gar keine Scrollflaeche, also greift overscroll-behavior nicht mehr, und
   das Rad landet dort, wo es hingehoert: auf der Seite.
   Der Weg ueber ein eigenes !important waere der andere gewesen -- und der falsche: die Regel
   drueben ist ein bewusster Schutz fuer echte Telefone, und ich haette sie mit einem zweiten
   !important ueberschrieben, statt den Zustand herzustellen, in dem sie gar nicht zutrifft. */
.ulh-app #ask-mira .am-chat { overflow: clip; }

/* Das # vor dem Rang in der Antworttabelle, in der dritten Textfarbe -- so steht es auch in der
   Rangspalte des Dashboards.
   em und keine eigene Klasse: der Sanitizer von Mira laesst an einem span nur seine data-mira-*
   Attribute durch und an allem anderen KEIN class. Von den erlaubten Inline-Elementen ist em das
   am wenigsten belegte, also ist es hier der Haken -- die Kursivstellung wird zurueckgenommen. */
.ulh-app .am-msg table em { font-style: normal; color: var(--vc-third); }

/* Fuenf Zeilen statt sieben, und die Kaesten schrumpfen mit. .vot-box steht in der App fest auf
   427px; bei fuenf Zeilen a 55px (visibility-chart.css) bleiben sonst zwei Zeilen Luft unter der
   Tabelle stehen. 427 - 2 x 55 = 317. */
/* Der Kasten ist genau so hoch wie die Tabelle darin: Kopfbalken 32 plus sechs Zeilen a 47 = 314.
   Vorher standen 351 da, also 37px Leerraum unter der letzten Zeile. Der Kasten links (das Chart)
   teilt die Hoehe -- er wird um dieselben 37px flacher, das war die Ansage. */
.ulh-app .vot-box { height: 314px; }
.ulh-app .vot-root.is-max .vot-box-left,
.ulh-app .vot-root.is-max .vot-box-right { height: 314px; }

/* Der Umschalter subtiler: kleiner, flacher, und der aktive Knopf ohne Rahmen -- er hebt sich nur
   noch durch die weisse Fuellung ab. In der App darf er auffallen, hier ist er Beiwerk.
   Die Beschriftung kuerzt landing-hero.js auf D, W und M.
   .check_reinvention.py meldet das als gesprengtes Bauteil, und die Regel ist richtig: wer einen
   Wert aendert, muss die anderen mitziehen. Genau das passiert hier -- Hoehe 32 -> 26, Polster
   12 -> 8, und die Schrift geht ueber die Schriftgruppen von 12 auf 11. Nicht auf drei Stellen
   proportional, aber alle drei Masse zusammen und nur im Schaustueck: die Regel haengt an
   .ulh-app. */
/* Die Fassung der beiden Umschalter -- 26px hoch, Polster 8, leisere Flaeche, Zeichen 14px, oben
   ausgerichtet -- ist hier entstanden und steht jetzt in core: die App zeigt sie ueberall so. Hier
   bleibt nur, was im Schaustueck wirklich abweicht.
   Der Schatten unter dem aktiven Segment ist WEG, auch hier: er war eine Zutat aus dieser Sektion,
   und die App hat ihn nie gehabt. Ein Segment, das sich schon durch weisse Fuellung und Kante
   abhebt, braucht keinen dritten Hinweis. */
/* Nur der Rahmen um das aktive Segment: --vc-border ist im Schaustueck #e7e9ec, also der um ein
   Viertel zurueckgenommene Ton, waehrend die App --vc-switch-border (#e0e2e6) zeigt. */
.ulh-app .vc-gran-btn.is-active,
.ulh-app .tcl-seg-btn.is-active { border-color: var(--vc-border); }

/* Der Day-Knopf soll normal aussehen. Er ist gesperrt, und das ist RICHTIG: die Serie spannt sechs
   Monate, und ueber 100 Tagen ist eine Tageskurve unlesbar (UC.granAvailability) -- in der App
   gehoert der Knopf dann ausgegraut. Im Schaustueck erklaert das aber niemand, und ein
   halbdurchsichtiger Knopf sieht nach einem Fehler aus. pointer-events wieder auf auto, damit er
   sich auch unter der Maus verhaelt wie die beiden anderen; klicken kann ihn ohnehin niemand, das
   faengt nurSchauen() ab. */
.ulh-app .vc-gran-btn.is-disabled { opacity: 1; pointer-events: auto; }

/* Kein Zahnrad fuer die Chart-Einstellungen. Es erscheint erst beim Hover ueber dem Chart und
   verspricht damit etwas, das hier nicht geht. */
.ulh-app .vot-scale-btn { display: none; }

/* Die Typspalte in den Top Domains war zu eng: laengere Namen wie "Knowledge-Base" oder
   "Brand Platforms" wurden selbst auf voller Breite gekuerzt. Die Domain braucht den Platz nicht --
   sie hat ihn nur, weil sie die 1fr-Spalte ist. Aus 18% Typ werden 24%, und die Domain nimmt, was
   bleibt. Kopfzeile und Zeilen teilen dasselbe Raster, deshalb beide Selektoren. */
.ulh-app .tct-thead,
.ulh-app .tct-row { grid-template-columns: 44px minmax(100px, 1fr) 24% 18% 14%; }

/* Die Trendangaben eine Stufe kleiner: 13 -> 12px Schrift, 16 -> 15px Zeichen. Im Fenster stehen
   sie neben Zahlen, die durch die Verkleinerung ohnehin klein sind, und wirkten dadurch zu laut. */
.ulh-app .up-trend { font-size: 12px; }
.ulh-app .up-trend svg { width: 15px; height: 15px; }

/* Kein Maximieren-Zeichen bei Top Brands. Es verspricht eine Ansicht, die es hier nicht gibt.
   Nur das rechte: .vot-max-right sitzt in der Kopfzeile von Top Brands, .vot-max-top im Chart
   daneben -- und das war nicht gemeldet. */
.ulh-app .vot-max-right { display: none; }

/* Zwischen den oberen beiden Kaesten und dem Zitatteil 32px statt 16. Als margin am unteren Block
   und nicht ueber den gap von .ulh-main: der gilt fuer ALLE drei Bloecke, und der Abstand zwischen
   Seitenkopf und Chart soll bleiben, wie er ist. 16 vom gap plus 16 von hier. */
.ulh-app .tcd-root { margin-top: 16px; }

/* Die Ecken der vier grossen Kaesten auf 12px. In den Komponenten stehen 16 -- dieselbe Zahl wie
   der Fensterrahmen aussen, und zwei gleiche Radien ineinander sehen aus, als waere einer davon
   falsch. Der Zitatteil hat nur eine Klasse fuer beide Kaesten (.tcd-box), das Chart zwei
   (.vot-box-left und -right); .vot-box deckt beide ab. */
.ulh-app .vot-box,
.ulh-app .tcd-box { border-radius: 12px; }

/* Derselbe Leerraum wie bei Top Brands, nur im Zitatteil: .tcd-box steht auf 430px, gerechnet fuer
   45px Kopf und sieben Zeilen a 55. Mit 32er Kopf und 47er Zeilen sind es 361 -- also 69px Luft.
   Nicht gemeldet, weil dieser Kasten fast vollstaendig unter der Kante des Fensters liegt; es ist
   aber derselbe Fehler, und wenn das Fenster je hoeher wird, waere er zu sehen. */
.ulh-app .tcd-box { height: 361px; }

/* Die Rahmenfarbe 25% schwaecher: --vc-border ist #e0e2e6 (224,226,230), ein Viertel des Wegs zu
   Weiss ergibt #e7e9ec. Die Marke muss an den WURZELN der Komponenten stehen und nicht an .ulh-app:
   sie ist in core.css an .up-root deklariert, und ein Wert am eigenen Element schlaegt jeden
   geerbten -- dieselbe Falle wie bei --usn-w. Der Chip und die Knoepfe oben ziehen mit. */
.ulh-app .up-root { --vc-border: #e7e9ec; }
.ulh-root { --ulh-border: #e7e9ec; }
/* Der Ring um den Chip nur halb so kraeftig wie die uebrigen Rahmen: #e7e9ec liegt 24 Stufen unter
   Weiss, die Haelfte sind 12 -- also #f3f4f6. Der Funke bleibt, wie er ist; er zieht jetzt durch
   einen leiseren Ring. */
.ulh-root { --ulh-chip-ring: #f3f4f6; }

/* Zwischen Chart und Tabelle 8px statt 16. Beide Bloecke sind eine Reihe aus zwei Einheiten, und
   die 16px stehen in den Komponenten (.vot-root und .tcd-root). Der Abstand ZWISCHEN den Bloecken
   bleibt bei 16 -- der kommt von .ulh-main. */
.ulh-app .vot-root,
.ulh-app .tcd-root { gap: 8px; }

/* Die Kopfbalken beider Tabellen auf 32px statt 45. Der Inhalt bleibt mittig: .vt-th und .tct-th
   sind 100% hoch und richten mit align-items: center aus, das gilt weiter. height UND flex-basis,
   wie bei den Zeilen -- der Kopf ist ein Flex-Kind mit fester Basis. */
.ulh-app .vt-thead { height: 32px; flex: 0 0 32px; }
.ulh-app .tct-thead { height: 32px; flex: 0 0 32px; }

/* Die Leiste 16px schmaler. Die Marke muss an der LEISTE SELBST stehen und nicht am Vorfahren:
   .usn-bar traegt die Klasse .up-root, und sidebar.css setzt --usn-w genau dort auf 250px. Ein
   Wert am eigenen Element schlaegt jeden geerbten -- am Vorfahren gesetzt blieb es bei 250,
   gemessen. Die Spalte daneben misst die Breite ohnehin selbst nach. */
/* 224 statt 234, und 12px Polster statt 16: die Leiste gibt zusammen 10px ab, und die bekommt der
   Bereich rechts -- er richtet sich an ihrer Breite aus. */
.ulh-app .usn-bar { --usn-w: 224px; padding-left: 12px; padding-right: 12px; }

/* ---------- Alle Schriften im Fenster eine Stufe kleiner ----------
   Erzeugt aus einer Messung und nicht geschaetzt: alle Textelemente im Fenster durchgezaehlt, nach
   ihrer wirklichen Schriftgroesse gruppiert, und jede Gruppe genau 1px kleiner. Deshalb steht hier
   eine Liste von Klassen und keine Regel mit *: die Komponenten setzen ihre Groessen in px, es gibt
   keinen einzigen Hebel dafuer, und eine Regel auf * traefe auch Elemente, deren Groesse von einem
   Elternteil kommt -- die wuerden dann zweimal verkleinert.
   Nicht dabei: .up-logo-ltr. Diese Klasse kommt im Fenster in ZWEI Groessen vor (7px und 11px), und
   eine Regel koennte nur eine davon treffen -- die 7px waeren dann groesser geworden statt kleiner.
   Es ist der Rueckfallbuchstabe fuer ein fehlendes Logo, und unsere Marken haben Logos.

   .check_reinvention.py meldet diese Regeln als GESPRENGTES Bauteil, und die Regel dahinter ist
   richtig: wer einen Wert eines Bauteils aendert, muss die anderen im Verhaeltnis mitziehen -- 15px
   Text in einem Kasten fuer 13px stoesst an den Rahmen. Hier geht es aber in die ANDERE Richtung:
   der Text wird kleiner, die Kaesten bleiben. Kleinerer Text in einem unveraenderten Kasten kann
   nicht anstossen, er laesst nur mehr Luft. Und es gilt ausschliesslich im Schaustueck -- jede
   dieser Regeln haengt an .ulh-app und erreicht die App nicht.
   Die Zeilenhoehen der beiden Tabellen ziehen unten mit, das war die ausdrueckliche Ansage. */
/* ---- DER SEITENKOPF IST DER DER APP (29.09. spaet angefordert) -------------------------------
   "Hier bitte den gesamten Pageheader-Kram einbauen ... neben den Kopfzeilen sind noch 16px links
   und rechts, das soll genauso sein wie in der Hauptapp." Der kompakte Kopf mit Krumen kommt aus
   core und den Seitenkopf-Skripten, wie in der App. Die Seiten tragen data-up-kopfrahmen
   (landing-hero.js), damit er gegen sie misst: Inhalt 16 links und 8 rechts, Linien bis an die
   Kante -- gemessen in allen vier Koepfen genau so.
   Hier standen bis dahin die Masse des kleinen Kopfes vom 24.09. (Ueberschrift 20px, Polster
   8/40 am Dashboard-Kopf, Beschreibung aus). Gegen den kompakten Kopf griffen sie nicht mehr
   (gemessen: Krume 14px, Polster unten 0) und sind weg.
   Auf der Prompts-Seite 16 zwischen Reiterleiste und Tabelle ("genau wie zwischen Topbar und
   Tabellenanfang in der Komponente"): die Seite hat schon 16 Luecke zwischen ihren Bloecken,
   also ohne das Polster unter den Reitern -- gemessen vorher 32. */
.ulh-app .ulh-prompts > .up-ph-root.pph-root { padding-bottom: 0; }
.ulh-app .n { font-size: 15px; }
.ulh-app .dph-kpi-label,
.ulh-app .dph-kpi-value,
.ulh-app .tcd-head-count,
.ulh-app .tcd-head-label,
.ulh-app .tct-name,
.ulh-app .tct-num,
.ulh-app .tct-used,
.ulh-app .up-num,

.ulh-app .vot-head-count,
.ulh-app .vot-head-label,
.ulh-app .vot-heading,
.ulh-app .vt-brand-name { font-size: 13px; }
.ulh-app .up-pop-label,
.ulh-app .up-pop-opt-l { font-size: 12.5px; }

.ulh-app .tct-td,
.ulh-app .up-filter-submit,
.ulh-app .up-ment-name,
.ulh-app .up-sent-val,

.ulh-app .vt-td { font-size: 12px; }
.ulh-app .lbl,
.ulh-app .pph-metaname,
.ulh-app .tcd-brand-label,
.ulh-app .tcd-mode-btn,
.ulh-app .tct-tag-lbl,
.ulh-app .tct-th,
.ulh-app .up-company-name,
.ulh-app .up-ment-noresult,


.ulh-app .vc-gran-btn,
.ulh-app .vt-th { font-size: 11px; }

.ulh-app .up-bar-name,
.ulh-app .up-bar-name-out,
.ulh-app .up-bar-pct,
.ulh-app .up-bar-pct-out,
.ulh-app .up-filter-reset,
.ulh-app .up-filter-title,
.ulh-app .up-pop-action,
.ulh-app .up-pop-head,

.ulh-app .vot-filter-count { font-size: 10px; }

/* Das # in den Spaltenueberschriften eine Stufe kleiner: 12 -> 11px, in BEIDEN Tabellen.
   Nicht .up-hash: das ist das Zeichen in der RANGZELLE der Zeilen (gemessen -- alle sechs Treffer
   sassen in .up-rank-group). Das Zeichen der Ueberschrift ist ein blankes svg in .vt-th-idx bzw.
   .tct-th-idx, und dort steht die Groesse. */
.ulh-app .vt-th-idx svg,
.ulh-app .tct-th-idx svg { width: 11px; height: 11px; }

/* Die Trendangabe: im Schritt davor von 13 auf 12, jetzt mit allen anderen auf 11. Das Zeichen
   bleibt bei 15px -- kleiner wird der Pfeil unleserlich. */
.ulh-app .up-trend { font-size: 11px; }

/* Die Zeilen beider Tabellen 8px flacher: 55 -> 47. Die 55 stehen in visibility-chart.css und in
   topcitations-dashboard.css, beide mit height UND flex-basis -- eines allein reicht nicht, die
   Zeile ist ein Flex-Kind mit fester Basis. Die Luecke-Zeile im Chart geht von 50 auf 42 mit. */
.ulh-app .vt-row { height: 47px; flex: 0 0 47px; }
.ulh-app .vt-row.vt-gap { height: 42px; flex: 0 0 42px; }
.ulh-app .tct-row { height: 47px; flex: 0 0 47px; }

/* Die Balkenliste FUELLT den Kasten, statt mittig darin zu schweben. Die Komponente gibt ihr einen
   automatischen Rand oben und unten (gemessen 27px und 27px), also stand sie in der Mitte.
   Nur margin-bottom: auto haette sie nach oben geschoben und 54px Leerraum darunter gelassen --
   gemessen. Deshalb nicht schieben, sondern verteilen: die Liste nimmt die ganze Hoehe, und die
   sieben Zeilen teilen sie sich. Damit ist oben angesetzt und unten nichts uebrig, ohne dass hier
   eine Hoehe geraten werden muss. */
.ulh-app .up-bars { margin: 0; gap: 8px; }
/* Feste 8px zwischen den Balken statt verteilter Hoehe: die Zeilen behalten ihre eigene Hoehe, und
   das Polster von 5px oben und unten faellt weg -- sonst waere der Abstand 8 + 10 = 18px. Oben
   angesetzt bleibt es, weil der automatische Rand der Komponente weg ist. */
.ulh-app .up-bar-row { flex: 0 0 auto; padding: 0; }

/* ---------- Schmal ---------- */
/* Auf dem Telefon gar kein Fenster. Gemessen bei 700px Fensterbreite: das Mass faellt auf 0.50,
   die 13px-Schrift der App landet bei 6.5px und ist nicht mehr zu lesen -- ein Bild, auf dem
   niemand etwas erkennt, kostet nur Ladezeit und Platz. Die Vorlage macht es genauso: dort haengt
   der ganze Fensterblock in einem Kasten mit "hidden md:block".
   Die Ueberschrift traegt die Sektion auf dem Telefon allein. */
@media (max-width: 900px) {
  /* Oben weniger als auf dem breiten Schirm: dort sind es seit dem 14.10. 14 Fensterhoehen-
     prozent, auf dem Telefon waeren das 114px unter der Leiste -- der Chip rutschte fast in die
     Mitte des ersten Bildschirms. */
  .ulh-text { padding-top: clamp(80px, calc(12 * var(--ulh-fh)), 112px); padding-bottom: clamp(48px, calc(9 * var(--ulh-fh)), 88px); }
}

/* ---------- Fuenfte Sektion: Mira bei der Arbeit ----------------------------------------------
   Aufbau und Farben sind die des Kartenblocks darueber -- derselbe graue Kasten mit 4px Polster,
   dieselben weissen Karten mit 4px dazwischen. Neu ist allein die Aufteilung: links drei Karten
   uebereinander (20 Prozent), rechts Mira (80). Die Zahlen stehen als Flexanteil, wie bei den
   Karten oben (--ulh-w dort).
   HELL, nicht dunkel: die Sektion gehoert zur Seite und nicht zu einem eigenen Kapitel. */
/* Der Kopf steht wie der ueber den Karten: linksbuendig, Chip, Ueberschrift, Satz -- dieselben
   Klassen und damit dieselben Masse. Die Sektion traegt Luft nach oben und unten wie die
   Kartensektion. */
/* Der Abstand nach oben ist derselbe wie zwischen dem Kartenblock und den Quellen: dort sind es
   das untere Polster der Kartensektion plus das obere der Quellen. Hier bringt die Quellensektion
   nur 8px mit (die Trennlinie sitzt an ihrer Unterkante), also stehen die restlichen hier. */
.ulh-msc {
  padding: calc(clamp(80px, calc(12 * var(--ulh-fh)), 144px) + clamp(72px, calc(11 * var(--ulh-fh)), 132px) - 8px) 0 clamp(72px, calc(10 * var(--ulh-fh)), 120px);
}
.ulh-msc-row { align-items: stretch; }
/* Zwei Linien ueber die ganze Breite, je 8px vom Kasten entfernt -- dieselbe Machart wie die
   Trennlinie unter den Quellen: vw statt Prozent, denn Prozent waere die Spur. */
.ulh-msc-box { position: relative; }
.ulh-msc-box::before, .ulh-msc-box::after {
  content: ""; position: absolute; left: 50%; width: 100vw; height: 1px;
  transform: translateX(-50%); background: var(--ulh-border); pointer-events: none;
}
.ulh-msc-box::before { top: -9px; }
.ulh-msc-box::after { bottom: -9px; }
.ulh-msc-links { display: flex; flex-direction: column; gap: 4px; flex: 20 1 0; min-width: 0; }
/* Die drei Karten teilen sich die Hoehe der Reihe. flex-basis 0 und gleiches Wachstum: sonst
   entscheidet die Textmenge, und die mittlere Karte waere die groesste. */
.ulh-msc-karte {
  flex: 1 1 0; min-height: 0;
  display: flex; flex-direction: column;
  padding: 20px 20px 18px;
}
/* Zeichen oben, Text unten -- wie in der Vorlage. Der Titel schiebt sich mit margin-top: auto nach
   unten, damit die drei Titel nicht an unterschiedlichen Stellen haengen, wenn ein Satz umbricht. */
.ulh-msc-ic { display: block; width: 20px; height: 20px; color: var(--ulh-sub); flex: 0 0 auto; }
.ulh-msc-ic svg { width: 100%; height: 100%; display: block; }
.ulh-msc-t {
  margin-top: auto; padding-top: 26px;
  font-size: 15px; font-weight: 500; letter-spacing: -0.012em; color: var(--ulh-text);
}
.ulh-msc-s {
  margin-top: 7px;
  font-size: 13px; font-weight: 400; line-height: 1.5; color: var(--ulh-sub);
}
/* Die Karte rechts ist der Rahmen um Mira und traegt selbst keinen Inhalt: kein Polster, und der
   Ausschnitt schneidet die Ecken der Komponente auf den Radius der Karte zurueck. */
.ulh-msc-rechts { flex: 80 1 0; min-width: 0; padding: 0; overflow: hidden; }
/* Ein iframe hat immer die Hoehe, die man ihm gibt -- der Inhalt kann sie nicht setzen. 760px ist
   Die Hoehe ist GERECHNET und nicht gewaehlt, damit die Karte auf ein 13-Zoll-Notebook passt.
   Gemessen am laengsten der drei Faelle (Quellentabelle): Frage 85, Antwort 186, Arbeitsprotokoll
   119, Abstaende rund 20 -- zusammen 410. Rahmen minus Kopfzeile (111) und Eingabefeld (125) muss
   also mindestens 410 ergeben: 660 laesst 449, also 39px Luft.
   Zwei Fallen stecken in dieser Messung, beide erlebt:
   - Gemessen wird ERST, wenn das Tippen durch ist. Waehrend der Antwort baut Mira ihre Bloecke
     nacheinander ein, und jeder Zwischenstand ist kleiner.
   - Die Reservierung unter der letzten Nachricht muss RAUS. Mira legt sie an, damit die neue Frage
     oben steht und die Antwort gleich an ihrer endgueltigen Stelle landet (_pinSendScroll);
     landing-hero.js nimmt sie hier zurueck (obenHalten). Ohne das ist jede Zahl um eine halbe
     Chathoehe zu gross -- daran waren drei Messungen zu gross. */
.ulh-msc-rahmen { display: block; width: 100%; height: 660px; border: 0; background: #ffffff; }
@media (max-width: 900px) {
  /* Untereinander -- die Reihe bricht schon durch .ulh-cards-row um; hier stehen nur die drei
     Karten wieder NEBENeinander, denn 20 Prozent von wenig ist nichts. */
  .ulh-msc-links { flex-direction: row; flex: 0 0 auto; }
  .ulh-msc-t { padding-top: 20px; }
  .ulh-msc-rahmen { height: 600px; }
}

/* ---------- Sechste Sektion: warum es diese App gibt ------------------------------------------
   Aufbau wie im Vorbild: die Kurve liegt als Grund ueber die ganze Breite, links davor stehen Kopf
   und vier Zahlen. Die Sektion ist der einzige Ort der Seite mit einer FARBIGEN Flaeche -- deshalb
   nur eine, und die leiseste: das Blau des Chips, stark verduennt. */
.ulh-geo {
  position: relative; z-index: 1;
  /* Unten KEIN Polster: die Schraffur soll genau auf der Linie enden, die die Sektion
     abschliesst -- ein Streifen Weiss darunter laesst das Chart schweben. */
  padding: clamp(80px, calc(11 * var(--ulh-fh)), 132px) 0 0;
  overflow: hidden;
}
/* Der Grund: ueber die ganze Breite und hinter allem. Die Kurve steigt nach rechts, also steht der
   Text links -- unter ihm ist die Flaeche flach und der Text bleibt lesbar. */
/* Die Kurve steht in der SPUR und nicht ueber die ganze Seite: sie ist Inhalt der Sektion und
   nicht ihr Grund. Ueber die ganze Breite lag ihr Anfang ausserhalb des Gitters, und die erste
   Marke stand neben allem anderen.
   Und sie steht im FLUSS: der Abstand zu den Zahlen ist damit eine Zahl (105px) und keine Rechnung
   aus zwei Kanten, und ihr Boden liegt auf der Abschlusslinie der Sektion. */
.ulh-geo-bild {
  position: relative; width: var(--ulh-spur); margin: 105px auto 0; height: 300px;
  pointer-events: none; z-index: 0;
}
/* Zwei Linien ueber die ganze Breite, oben und unten an der Sektion -- dieselbe Machart wie um den
   Mira-Kasten (vw statt Prozent, denn Prozent waere die Spur). */
.ulh-geo::before, .ulh-geo::after {
  content: ""; position: absolute; left: 50%; width: 100vw; height: 1px;
  transform: translateX(-50%); background: var(--ulh-border); pointer-events: none;
}
.ulh-geo::before { top: 0; }
.ulh-geo::after { bottom: 0; }
.ulh-geo-svg { display: block; width: 100%; height: 100%; }
.ulh-geo-linie { fill: none; stroke: #2563eb; stroke-width: 1.6; vector-effect: non-scaling-stroke; }
/* Schraffur statt Fuellung: eine Flaeche legt sich als Block hinter die Zahlen, Striche nicht. */
.ulh-geo-flaeche { fill: url(#ulhGeoRaster); }
/* Der Abstand ist der der ganzen Seite: 8px. Er steht im SVG (pattern width) und hier nur die
   Farbe. */
.ulh-geo-raster { stroke: rgba(37, 99, 235, .18); stroke-width: 1; }
/* Die gestrichelte Linie steht als eigenes Element und nicht im SVG: das SVG wird mit
   preserveAspectRatio="none" gestreckt, und darin waeren weder die 64px noch das Strichmuster
   verlaesslich. So ist beides in echten Pixeln. */
.ulh-geo-mk-linie {
  position: absolute; bottom: 0; width: 0;
  /* Blau und nicht in der Rahmenfarbe: die Linie gehoert zum Punkt, den sie traegt, und in Grau
     verschwand sie in der Schraffur, die dieselbe Helligkeit hat. */
  border-left: 1px dashed rgba(37, 99, 235, .32);
  transform: translateX(-0.5px); pointer-events: none;
}
/* Die zwei Marken sitzen AUF der Kurve: die Lage kommt aus landing-hero.js (geoY tastet das
   Kurvenstueck ab). Sie stehen ausserhalb des SVG, damit die Schrift nicht mitverzerrt wird --
   preserveAspectRatio: none streckt alles im SVG, auch Buchstaben.
   Der Punkt liegt auf der Linie, der Text darunter: nach oben waere er im Weg der Kurve. */
.ulh-geo-mk {
  position: absolute; transform: translate(-4px, 4px);
  display: flex; flex-direction: column; align-items: flex-start;
  font-size: 11.5px; line-height: 1.35; white-space: nowrap;
}

/* Der Punkt sitzt auf der Kurve und atmet. Zwei Ringe, versetzt: einer traegt die Farbe, der
   zweite laeuft als Welle nach aussen -- ein Punkt, der nur die Groesse aendert, wirkt wie ein
   Fehler in der Zeichnung. */
.ulh-geo-mk-punkt {
  position: relative; display: block; width: 8px; height: 8px; border-radius: 999px;
  background: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}
.ulh-geo-mk-punkt::after {
  content: ""; position: absolute; inset: 0; border-radius: 999px;
  border: 1px solid #2563eb; animation: ulhGeoPuls 2600ms var(--ulh-weich) infinite;
}
@keyframes ulhGeoPuls {
  0%   { transform: scale(1);   opacity: .55; }
  70%  { transform: scale(3.2); opacity: 0; }
  100% { transform: scale(3.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .ulh-geo-mk-punkt::after { animation: none; } }
/* Der Puls wartet, bis seine Marke da ist (14.10., Audit): vorher atmete er schon ab dem Laden,
   unsichtbar unter einer Marke mit Deckkraft 0. */
.ulh-geo-mk:not(.is-da) .ulh-geo-mk-punkt::after { animation-play-state: paused; }
/* Ein weicher weisser Hof statt einer Plakette: die Beschriftung liegt auf der Schraffur und
   stellenweise auf der Linie selbst, und ohne ihn liest sie sich schlecht. Eine Plakette waere ein
   Kasten mehr im Bild. */
/* 64px ueber dem Punkt -- so weit, dass die Beschriftung ueber der Kurve UND ueber ihrer
   Schraffur steht. Darunter lief sie an der tiefen ersten Marke sogar unter die Linie. */
.ulh-geo-mk-txt {
  display: flex; flex-direction: column; gap: 1px; margin-bottom: 64px;
  text-shadow: 0 0 5px #fff, 0 0 5px #fff, 0 0 10px #fff;
}
/* Eigener Takt fuer die Marken: sie kommen NACH der Kurve und eine nach der anderen von links
   nach rechts. 260ms je Schritt statt der 110ms, die sonst auf der Seite gelten -- bei drei
   Punkten auf einer breiten Kurve liest man die Richtung erst, wenn zwischen ihnen wirklich eine
   Pause steht. 420ms Vorlauf, damit die erste Marke nicht mit der Kurve zugleich anfaengt.
   Die Reihenfolge steckt in --auf (landing-hero.js), und die kommt aus dem Datum -- das ist
   dieselbe Reihenfolge wie von links nach rechts. */
.ulh-geo-mk.ulh-auf { transition-delay: calc(420ms + var(--auf, 0) * 260ms); }
.ulh-geo-mk-t { font-weight: 500; color: var(--ulh-text); }
.ulh-geo-mk-s { color: var(--ulh-sub); }
.ulh-geo-in { position: relative; z-index: 1; }
/* Breiter (15.10.: "steht auf 5 Zeilen, ab ,Deine Kunden' bitte mehr Breite"): der erste Satz
   bleibt eine eigene Zeile, der Rest laeuft in bis zu 46 Zeichen Breite -- auf Deutsch drei
   Zeilen statt fuenf. Der Kopf darf dafuer bis 960px. */
.ulh-geo-kopf { max-width: 960px; margin-left: auto; margin-right: auto; }
/* Die Ueberschrift traegt beide Toene wie die der Quellen: erster Satz dunkel, Rest in der zweiten
   Farbe -- hier ohne Scrolleffekt, denn sie ist kurz und steht neben Zahlen, nicht allein. */
.ulh-geo-h { max-width: none; }
.ulh-geo-h2 { display: block; max-width: 46ch; margin: 0 auto; color: var(--ulh-sub); text-wrap: balance; }
/* Vier Zahlen, zwei mal zwei. Der Strich links ist dieselbe Machart wie im Vorbild: eine Kante,
   kein Kasten -- die Zahl soll stehen, nicht in einer Karte sitzen. */
.ulh-geo-kpis {
  margin-top: clamp(34px, calc(5 * var(--ulh-fh)), 54px);
  display: grid; grid-template-columns: repeat(2, minmax(0, 250px)); gap: 26px 40px;
}
.ulh-geo-kpi {
  display: flex; flex-direction: column; gap: 4px;
  padding-left: 16px; border-left: 2px solid #2563eb;
}
.ulh-geo-v { font-size: 30px; font-weight: 500; letter-spacing: -0.03em; color: var(--ulh-text); }
.ulh-geo-l { font-size: 13.5px; font-weight: 400; line-height: 1.45; color: var(--ulh-sub); }

@media (max-width: 900px) {
  .ulh-geo-kpis { grid-template-columns: 1fr; gap: 20px; }
  .ulh-geo-bild { height: 52%; opacity: .7; }
  .ulh-geo-mk-s { display: none; }
}

/* ---------- Die Leiste oben -------------------------------------------------------------------
   Sie klebt oben, solange die SEITE scrollt. In einem Embed-Rahmen kann sie das nicht (dort
   scrollt das Fenster darueber) -- dann steht sie einfach am Anfang der Seite. */
.ulh-nav {
  position: sticky; top: 0; z-index: 40;
  background: rgba(255, 255, 255, .82);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
          backdrop-filter: saturate(180%) blur(14px);
  /* Die Linie unten laeuft IMMER und ueber die ganze Breite -- sie ist die oberste der
     waagerechten Linien, mit denen die Seite ihre Abschnitte trennt, und keine Reaktion auf ein
     offenes Menue. */
  border-bottom: 1px solid var(--ulh-border);
  transition: background 200ms var(--ulh-ease);
}
.ulh-nav.is-offen { background: rgba(255, 255, 255, .94); }
/* Der Sprach-Toggle (14.10., nur auf der eigenen Website): zwei Kuerzel in einer leisen Pille, das
   aktive auf weissem Grund wie die gleitende Pille von .up-seg. Hoehe 28 (--up-h-seg), damit er
   neben den 36er-Knoepfen nicht als dritter Knopf auftritt. */
.ulh-sprache {
  display: inline-flex; align-items: center; gap: 2px; height: 28px; padding: 2px; margin-right: 4px;
  border-radius: 8px; background: var(--ulh-kasten); border: 1px solid var(--ulh-border);
}
.ulh-sprache a {
  display: inline-flex; align-items: center; height: 22px; padding: 0 7px; border-radius: 6px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .02em; color: var(--ulh-sub); text-decoration: none;
  transition: color 160ms var(--ulh-ease), background 160ms var(--ulh-ease), box-shadow 160ms var(--ulh-ease);
}
.ulh-sprache a:hover { color: var(--ulh-text); }
/* Die Flagge vor dem Kuerzel (14.10. abends angefordert). 16x12 im Seitenverhaeltnis 4:3 wie die
   Flaggen der App (core.css .up-flag), ein Haarrand, damit Weiss in der Flagge (England,
   Deutschland nicht) auf der hellen Pille eine Kante hat. Die nicht gewaehlte etwas leiser. */
.ulh-sprache a { gap: 5px; position: relative; }
.ulh-sprache-flagge {
  width: 16px; height: 12px; border-radius: 3px; object-fit: cover; display: block; flex: 0 0 auto;
  box-shadow: 0 0 0 1px rgba(11, 13, 24, .08); opacity: .7; transition: opacity 160ms var(--ulh-ease);
}
.ulh-sprache a:hover .ulh-sprache-flagge, .ulh-sprache a.is-an .ulh-sprache-flagge { opacity: 1; }
.ulh-sprache a.is-an { background: #ffffff; color: var(--ulh-text); box-shadow: 0 1px 2px rgba(11, 13, 24, .08); }
/* Ganz schmal wird es eng: Logo, Toggle und "Kostenlos starten" brauchten bei 375px 6px mehr als
   da war (gemessen 14.10. auf /de). Toggle und Knopf ruecken zusammen, nichts faellt weg. */
@media (max-width: 420px) {
  .ulh-sprache { margin-right: 0; }
  .ulh-sprache a { padding: 0 4px; }
  /* Schmal nur die Flaggen: mit Kuerzel ragte die Leiste bei 375px 17px ueber den Rand (gemessen
     14.10.). Das Kuerzel bleibt fuer Vorleser im Markup; lang und hreflang am Link sagen den Rest. */
  .ulh-sprache-t {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .ulh-nav-rechts .ulh-btn-pri { padding: 0 10px; font-size: 13px; }
}
/* Der Kasten, in dem die Leiste steht, wenn sie in der Seite DARUEBER haengt (landing-hero.js,
   leisteAuslagern). Er traegt .ulh-root, weil die Masse und Farben der Leiste aus dessen Variablen
   kommen -- und diese Regel nimmt ihm alles andere wieder ab: keinen Grund, keinen Ausschnitt,
   keine Hoehe. Fixiert und ganz oben, denn das ist der Sinn der Uebung. */
.ulh-nav-host {
  position: fixed; top: 0; left: 0; right: 0; z-index: 2147483000;
  background: none; overflow: visible; width: auto; min-height: 0;
}
/* Dort drin braucht die Leiste kein sticky mehr -- der Kasten ist schon fixiert. */
.ulh-nav-host .ulh-nav { position: static; }
/* Die zwei senkrechten Schienen der Seite laufen durch die Leiste weiter. Sie kommen hier aus
   eigenen Raendern und nicht aus .ulh-schienen: die Leiste hat einen eigenen Grund und liegt
   darueber, die durchlaufenden Schienen waeren also verdeckt. */
.ulh-nav-in {
  position: relative; box-sizing: border-box;
  display: flex; align-items: center; gap: 24px; height: 62px;
  padding: 0 16px;
  border-left: 1px solid var(--ulh-schiene);
  border-right: 1px solid var(--ulh-schiene);
}
/* Eine Datei, kein Zeichen mit Text daneben: die Wortmarke bringt Zeichen und Wort in den
   offiziellen Abstaenden mit.
   20px und nicht 28: die 28 kamen aus der Rechnung "das Zeichen darin soll so gross bleiben wie
   das einzelne vorher" -- damit stand die Marke aber breiter im Kopf als die ganze Navigation
   daneben. 30 Prozent kleiner, also 20; das Zeichen darin misst dann 17px. Im Fuss bleibt es bei
   den 31, dort traegt die Marke die Spalte. */
.ulh-nav-logo { display: inline-flex; align-items: center; text-decoration: none; }
.ulh-nav-logo img { display: block; height: 20px; width: auto; }
/* Die Mitte ist wirklich die Mitte: absolut gesetzt, damit die Punkte nicht wandern, wenn links
   oder rechts etwas laenger wird. */
.ulh-nav-mitte {
  position: absolute; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 14px;
}
/* .check_reinvention.py meldet hier den Segmented-Control-Knopf der App (.up-seg-btn), weil
   background, border, Radius, Hoehe und Uebergang zusammen vorkommen. Fehlalarm: das ist ein
   Navigationspunkt einer Landingpage -- kein Umschalter in einer Gruppe, ohne aktiven Zustand,
   ohne Nachbarn, an die er sich anschliesst, und ausserhalb jeder .up-root. */
.ulh-nav-punkt {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font-family: inherit; font-size: 14px; font-weight: 400; letter-spacing: -0.01em;
  color: var(--ulh-sub); text-decoration: none;
  height: 34px; padding: 0 12px; border-radius: 9px;
  display: inline-flex; align-items: center;
  transition: color 160ms var(--ulh-ease), background 160ms var(--ulh-ease);
}
.ulh-nav-punkt:hover { color: var(--ulh-text); background: var(--ulh-kasten); }
.ulh-nav-rechts { margin-left: auto; display: flex; align-items: center; gap: 8px; }
/* ---- Das Panel, das dem Zeiger folgt ----
   EINES fuer beide Menues. Es faehrt und waechst, statt zu schliessen und neu aufzugehen -- die
   Groesse setzt landing-hero.js aus dem Inhalt, der schon darin steht.
   overflow: hidden, weil waehrend der Fahrt die groessere der beiden Karten ueberstehen wuerde. */
/* Und hier meldet der Waechter die aufziehende Suche der App (.up-search), weil overflow, Breite
   und ein Uebergang zusammenkommen. Auch das ist ein Fehlalarm: das hier ist ein Menue mit zwei
   Karten darin, das seine Groesse aus seinem Inhalt nimmt -- die Suche zieht ein Feld auf. */
.ulh-nav-pop {
  position: absolute; top: calc(100% - 6px); left: 0;
  width: 0; height: 0; overflow: hidden;
  background: #ffffff; border: 1px solid var(--ulh-border); border-radius: 14px;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .04), 0 18px 44px rgba(11, 13, 24, .12);
  /* Es faehrt nach UNTEN auf: aus 10px ueber seiner Stelle in die Stelle. Die Lage (transform)
     springt beim ersten Oeffnen ohne Uebergang dorthin -- sonst kaeme es von der linken Kante
     herueber. Erst beim Wechsel zwischen den zwei Menues wandert es wirklich. */
  opacity: 0; translate: 0 -10px; pointer-events: none; transform-origin: top center;
  transition: width 320ms var(--ulh-weich), height 320ms var(--ulh-weich),
              transform 320ms var(--ulh-weich), opacity 200ms var(--ulh-ease),
              translate 260ms var(--ulh-gleit);
}
.ulh-nav-pop.is-offen { opacity: 1; translate: none; pointer-events: auto; }
.ulh-nav-karte {
  position: absolute; top: 0; left: 0; width: max-content;
  display: flex; flex-direction: column; gap: 2px; padding: 14px;
  opacity: 0; pointer-events: none;
  transition: opacity 200ms var(--ulh-ease);
}
.ulh-nav-karte.is-da { opacity: 1; pointer-events: auto; }
.ulh-nav-spalte {
  padding: 4px 10px 8px;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ulh-third);
}
.ulh-nav-eintrag {
  display: flex; flex-direction: column; gap: 1px;
  padding: 9px 10px; border-radius: 10px; text-decoration: none; min-width: 232px;
  transition: background 160ms var(--ulh-ease);
}
.ulh-nav-eintrag:hover { background: var(--ulh-kasten); }
.ulh-nav-e-t { font-size: 14px; font-weight: 500; letter-spacing: -0.01em; color: var(--ulh-text); }
.ulh-nav-e-s { font-size: 12.5px; font-weight: 400; color: var(--ulh-sub); }

/* ---- TASTATUR (14.10., Audit) ----
   Ein sichtbarer Fokus fuer jeden Link und Knopf der Seite, wie in der App (2px in der Textfarbe):
   vorher gab es :focus-visible genau einmal, und Tastaturnutzer sahen nur den Standardring des
   Browsers -- auf dunklen Knoepfen kaum, im Fuss gar nicht. Was hover zeigt (Pfeil, Ring), zeigt
   jetzt auch der Fokus. Im Hero-Fenster und in den Vorschauen nicht: dort ist nichts bedienbar. */
.ulh-root :is(a, button):focus-visible { outline: 2px solid var(--ulh-text); outline-offset: 2px; }
.ulh-root :is(.ulh-app, .ulh-stage, .ulh-vis-in) :is(a, button):focus-visible { outline: none; }
.ulh-fuss :is(a, button):focus-visible { outline-color: rgba(255, 255, 255, .7); }
.ulh-chip-link:focus-visible { --ulh-chip-ring: #c9ccd2; }
.ulh-chip-link:focus-visible .ulh-chip-pfeil { transform: translateX(2px); color: var(--ulh-text); }
.ulh-nav-punkt:focus-visible, .ulh-nav-eintrag:focus-visible { background: var(--ulh-kasten); color: var(--ulh-text); }

/* ---- Das Menue auf dem Telefon (14.10., Audit) ----
   Unter 900px faellt die Mitte der Leiste weg; dafuer steht rechts ein Knopf, der ein Blatt unter
   der Leiste aufmacht. Bewegung wie ueberall: Deckkraft und ein kurzer Weg von oben, Expo-Kurve,
   heraus schneller als herein. visibility haelt das geschlossene Blatt aus der Tab-Reihenfolge;
   ihr Uebergang wartet beim Schliessen, bis das Ausblenden vorbei ist.
   Die Zeichen im Knopf tauschen ueber Deckkraft und Groesse und drehen sich nie. */
.ulh-nav-menue, .ulh-nav-mobil { display: none; }
@media (max-width: 900px) {
  /* .check_reinvention.py meldet hier den Segment-Knopf der App (.up-seg-btn). Fehlalarm wie bei
     .ulh-nav-punkt: ein einzelner Menueknopf der Website-Leiste, ausserhalb jeder .up-root, ohne
     Gruppe und ohne aktiven Zustand -- seine Masse sind die der 36er-Knoepfe daneben. */
  .ulh-nav-menue {
    position: relative; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 36px; height: 36px; padding: 0; border-radius: 10px;
    border: 1px solid var(--ulh-border); background: #ffffff; color: var(--ulh-text); cursor: pointer;
    transition: background 160ms var(--ulh-ease);
  }
  .ulh-nav-menue:hover { background: var(--ulh-kasten); }
  .ulh-nav-menue:focus-visible { outline: 2px solid var(--ulh-text); outline-offset: 2px; }
  .ulh-nav-menue svg {
    position: absolute; width: 18px; height: 18px;
    transition: opacity 200ms var(--ulh-ease), transform 320ms var(--ulh-expo);
  }
  .ulh-nav-menue-zu, .ulh-nav.is-mobil .ulh-nav-menue-auf { opacity: 0; transform: scale(.6); }
  .ulh-nav.is-mobil .ulh-nav-menue-zu { opacity: 1; transform: none; }
  .ulh-nav.is-mobil { background: #ffffff; }
  .ulh-nav-mobil {
    display: block; position: absolute; top: 100%; left: 0; right: 0;
    max-height: calc(100dvh - 63px); overflow-y: auto; overscroll-behavior: contain;
    background: #ffffff; border-bottom: 1px solid var(--ulh-border);
    /* Der Schleier ueber der Seite darunter ist ein Schatten ohne Unschaerfe, so gross wie der
       Schirm, und nach oben weggeschnitten (clip-path), damit er die Leiste nicht trifft. Ein
       eigenes Element waere ein zweites, das auf- und zugehen muss -- so blendet er mit dem Blatt. */
    box-shadow: 0 18px 40px rgba(11, 13, 24, .1), 0 0 0 100vmax rgba(11, 13, 24, .18);
    clip-path: inset(0 -100vmax -100vmax -100vmax);
    visibility: hidden; opacity: 0; transform: translateY(-8px);
    transition: opacity 180ms var(--ulh-ease), transform 320ms var(--ulh-expo), visibility 0s linear 320ms;
  }
  .ulh-nav-mobil.is-offen {
    visibility: visible; opacity: 1; transform: none;
    transition: opacity 240ms var(--ulh-ease), transform 560ms var(--ulh-expo), visibility 0s;
  }
  .ulh-nm-in { display: flex; flex-direction: column; padding: 6px 16px 20px; }
  .ulh-nm-gruppe { display: flex; flex-direction: column; padding: 8px 0; border-bottom: 1px solid var(--ulh-schiene); }
  /* Die Spaltennamen ohne Versalien (Hausregel fuer Ueberschriften) -- leiser Ton, normale Schreibung. */
  .ulh-nm-spalte { padding: 6px 10px 4px; font-size: 12px; font-weight: 500; color: var(--ulh-third); }
  /* 44px Mindesthoehe: die Groesse, die ein Finger trifft. */
  .ulh-nm-eintrag {
    display: flex; flex-direction: column; justify-content: center; gap: 1px; min-height: 44px;
    padding: 8px 10px; border-radius: 10px; text-decoration: none;
    transition: background 160ms var(--ulh-ease);
  }
  .ulh-nm-eintrag:hover, .ulh-nm-eintrag:active { background: var(--ulh-kasten); }
  .ulh-nm-eintrag:focus-visible { outline: 2px solid var(--ulh-text); outline-offset: -2px; }
  .ulh-nm-einzeln { margin-top: 6px; }
  .ulh-nm-sprache { display: none; }
  .ulh-nm-knoepfe { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 14px; }
  .ulh-nm-knoepfe .ulh-btn { justify-content: center; height: 44px; }
}
@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .ulh-nav-mobil, .ulh-nav-mobil.is-offen, .ulh-nav-menue svg { transition: none; }
}
/* Ganz schmal steht der Sprachschalter nicht mehr in der Leiste (dort ist der Platz fuer den
   Menueknopf), sondern als eigene Zeile im Blatt -- mit Namen statt Kuerzeln, 44px hoch. */
/* Unter 360px passen Logo, "Start for free" und der Menueknopf nicht nebeneinander (gemessen
   14.10. bei 320: 12px ueber dem Rand). Der Knopf steht dann nur im Menue, dort gross. */
@media (max-width: 360px) {
  .ulh-nav-rechts > .ulh-btn-pri { display: none; }
}
@media (max-width: 560px) {
  .ulh-nav-rechts > .ulh-sprache { display: none; }
  .ulh-nav-mobil .ulh-nm-sprache { display: flex; height: auto; width: 100%; margin: 14px 0 0; padding: 3px; box-sizing: border-box; }
  .ulh-nav-mobil .ulh-nm-sprache a { flex: 1 1 0; justify-content: center; height: 38px; gap: 8px; padding: 0 8px; font-size: 13px; letter-spacing: 0; }
}

/* ---------- Der Satz zum Schluss --------------------------------------------------------------
   Ein Zitat, gross und mittig, auf einer gepunkteten Flaeche. Punkte und keine Striche: die Seite
   arbeitet sonst mit senkrechten Linien, und die wuerden den Satz durchschneiden. Der Abstand ist
   derselbe 8er-Raster wie ueberall. */
.ulh-stimme {
  position: relative; z-index: 1;
  /* Luft ueber und unter der Sektion: sie steht zwischen zwei waagerechten Linien, und die sollen
     als eigener Abschnitt lesbar sein und nicht als Fortsetzung der Zahlen darueber. */
  margin: clamp(72px, calc(9 * var(--ulh-fh)), 120px) 0;
  padding: clamp(84px, calc(12 * var(--ulh-fh)), 148px) 0;
  /* Der weisse Grund gehoert dazu und ist nicht Kosmetik: die zwei senkrechten Schienen der Seite
     laufen ueber die ganze Hoehe, und ohne eine deckende Flaeche stehen sie mitten in dieser
     Sektion. Sie soll aber allein stehen -- nur die zwei waagerechten Linien. */
  background-color: #ffffff;
  background-image: radial-gradient(circle, var(--ulh-schiene) 1px, transparent 1px);
  background-size: 8px 8px;
  border-top: 1px solid var(--ulh-border);
  border-bottom: 1px solid var(--ulh-border);
}
/* KEIN Verlauf an den Enden mehr. Er sollte die Flaeche weich auslaufen lassen, aber die Sektion
   hat oben und unten eine Linie -- und dazwischen stand damit ein Streifen Weiss, bevor die Punkte
   anfingen. Das Muster laeuft jetzt bis an beide Linien. */
.ulh-stimme-in { position: relative; display: flex; flex-direction: column; align-items: center;
  text-align: center; }
.ulh-stimme-satz {
  margin: 0; max-width: 22ch;
  font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
  font-size: clamp(30px, 3.6vw, 48px); font-weight: 400; line-height: 1.24;
  letter-spacing: -0.02em; color: var(--ulh-text);
}
.ulh-stimme-sub {
  margin: clamp(18px, calc(2.4 * var(--ulh-fh)), 26px) 0 0; max-width: 52ch;
  font-size: 16px; font-weight: 400; line-height: 1.55; letter-spacing: -0.01em;
  color: var(--ulh-sub);
}
.ulh-stimme-knoepfe { margin-top: clamp(28px, calc(3.6 * var(--ulh-fh)), 42px); display: flex; gap: 10px; }

/* ---------- Der Fuss --------------------------------------------------------------------------
   Schwarz und ueber die ganze Breite: das Ende der Seite soll auch wie eines aussehen. Die Farben
   sind die des dunklen Themas der App, als Zahl -- die Variablen der App gelten hier nicht
   (dieselbe Falle wie beim Kasten der Quellen). */
/* ---------- Preise -------------------------------------------------------------------------
   Die Karte ist die des Onboardings (.uob-plan), nur groesser: dort muss der ganze Ablauf ohne
   Scrollen auf einen 760px-Schirm passen, hier hat die Seite Platz. Uebernommen sind Rahmen,
   Radius, das Etikett auf der mittleren Karte und die aufklappende Ersparnis; groesser sind
   Polster (28/26/30 statt 16/16/18), Radius (18 statt 16), Name (17 statt 15), Betrag (38 statt
   26) und Merkmale (13.5 statt 12).

   ALLE Farben kommen aus --ulh-* oder tragen einen Rueckfall. Grund: --vc-* haengt in core an
   .up-root, und diese Sektion steht ausserhalb -- eine Regel mit unaufloesbarem var() ist als
   Ganzes ungueltig. Genau daran hatte die erste Fassung ihren Rahmen und ihren Grund verloren:
   border: 1px solid var(--vc-border) fiel weg, die Karten standen ohne Kontur auf der Seite. */
.ulh-preis { padding: clamp(64px, 9vw, 128px) 0 clamp(48px, 7vw, 96px); }
/* DIE EIGENE PREISSEITE (15.10., /pricing): oben steht das Kopfband (.ulh-seitenkopf), darunter
   ohne die grosse Luft des Startseiten-Abschnitts der Schalter -- der Kopf ist schon der Anfang.
   Auf den Unterseiten laeuft das Einblenden beim Scrollen nicht (es steckt in landing-hero.js):
   .ulh-auf stuende dort fuer immer auf Deckkraft 0. Also gleich sichtbar. */
.ulh-preis.ulh-preis-seite { padding: 0 0 clamp(64px, 9vw, 120px); }
.ulh-root.ulh-teil .ulh-auf { opacity: 1; translate: none; }
/* Der Schalter ist .up-seg aus core. Seine Marken stehen hier lokal -- ohne sie bekaeme er weder
   Grund noch Rahmen. Die Schiene ist genau der Ton, den diese Sektion bis zum 22.09. gerendert
   hat (rgba(245,245,245,0.6) auf --ulh-grund), jetzt deckend ausgeschrieben. Damit bleibt die
   Landingpage unveraendert, obwohl core seither einen kraeftigeren Schienenton fuehrt: hier steht
   der Schalter auf einer hellen, ruhigen Flaeche und soll leiser sein als in der App. */
.ulh-preis-schalter {
  --vc-switch-rail: #f9f9f9;
  --vc-switch-pill: #ffffff;
  --vc-switch-border: #e0e2e6;
  --vc-text: var(--ulh-text);
  --vc-muted: var(--ulh-muted);
  margin-top: 30px; padding: 0 24px; display: flex; justify-content: center;
}
.ulh-preis-reihe {
  margin-top: 26px;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px;
  padding: 0 24px;                     /* dasselbe Polster wie .ulh-feat-kopf, sonst versetzt */
  align-items: start;
}
.ulh-preis-karte {
  position: relative;
  display: flex; flex-direction: column; text-align: left;
  padding: 28px 26px 30px; border: 1px solid var(--ulh-border); border-radius: 18px;
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .035), 0 6px 16px rgba(11, 13, 24, .028);
}
/* Die empfohlene Karte traegt einen zweiten Ring statt eines 2px-Rahmens -- sonst spraenge ihr
   Inhalt um einen Pixel gegen die Nachbarn. Derselbe Kniff wie .uob-plan.is-on. */
.ulh-preis-karte.is-top {
  border-color: var(--ulh-text);
  box-shadow: 0 0 0 1px var(--ulh-text), 0 2px 4px rgba(11, 13, 24, .05), 0 12px 28px rgba(11, 13, 24, .06);
}
.ulh-preis-tag {
  position: absolute; top: -11px; left: 26px; height: 22px; padding: 0 10px; border-radius: 999px;
  background: var(--ulh-text); color: #ffffff;
  font-size: 11.5px; font-weight: 600; letter-spacing: .02em;
  display: inline-flex; align-items: center;
}
.ulh-preis-name { font-size: 17px; font-weight: 600; color: var(--ulh-text); letter-spacing: -0.01em; }
/* Die Unterzeile sagt, fuer WEN der Tarif ist -- sie gehoert vor die Zahl, nicht darunter. */
.ulh-preis-desc { margin: 6px 0 0; font-size: 14px; line-height: 1.45; color: var(--ulh-sub);
  text-wrap: pretty; min-height: 40px; }
.ulh-preis-betrag { margin-top: 18px; display: flex; align-items: baseline; gap: 6px; }
.ulh-preis-zahl { font-size: 38px; font-weight: 600; color: var(--ulh-text); letter-spacing: -.035em;
  font-variant-numeric: tabular-nums; line-height: 1.05; }
.ulh-preis-je { font-size: 14px; color: var(--ulh-sub); }
/* Die Ersparnis klappt auf und zu (0fr -> 1fr) statt zu erscheinen: so schiebt sie die Kartenhoehe
   weich mit, und die drei Karten springen beim Umschalten nicht. */
.ulh-preis-notiz {
  display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows 220ms var(--ulh-ease, ease), opacity 220ms var(--ulh-ease, ease);
}
.ulh-preis-notiz > span { overflow: hidden; min-height: 0; display: block;
  font-size: 13px; font-weight: 500; color: #2e9e63; padding-top: 6px; }
.ulh-preis-notiz.is-on { grid-template-rows: 1fr; opacity: 1; }
/* Der Knopf traegt die Form der Seite (.ulh-btn-pri) und nur Breite und Hoehe hier: eine eigene
   Farbe waere ein zweiter Hauptknopf mit einem anderen Ton als der im Hero. */
.ulh-preis-btn { margin-top: 20px; width: 100%; height: 40px; justify-content: center; }
.ulh-preis-liste { margin: 22px 0 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 9px; }
.ulh-preis-zeile { display: flex; align-items: flex-start; gap: 9px;
  font-size: 13.5px; line-height: 1.4; color: var(--ulh-sub); }
.ulh-preis-zeile b { font-weight: 600; color: var(--ulh-text); }
.ulh-preis-ic { display: inline-flex; flex: 0 0 auto; margin-top: 2px; color: var(--ulh-text); }
.ulh-preis-ic svg { width: 15px; height: 15px; display: block; }
.ulh-preis-fuss { margin: 30px 0 0; padding: 0 24px; font-size: 13px; color: var(--ulh-sub); }
/* Unter 900px stehen die drei untereinander -- drei Listen nebeneinander sind auf einem Telefon
   nicht mehr zu lesen. Dieselbe Grenze wie die uebrigen Sektionen der Seite. Die Unterzeile
   braucht dort keine Mindesthoehe mehr: die haelt nur die drei Koepfe nebeneinander auf einer
   Linie. */
@media (max-width: 900px){
  .ulh-preis-reihe { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .ulh-preis-desc { min-height: 0; }
}

/* "Cookie settings" ist ein Knopf und kein Link (14.10.): er oeffnet etwas auf der Seite. Er
   uebernimmt die Form der Links daneben -- hier nur, was ein Knopf von sich aus anders macht. */
.ulh-fuss-knopf { background: none; border: 0; padding: 0; font: inherit; text-align: left; cursor: pointer; }
.ulh-fuss {
  position: relative; z-index: 1;
  width: 100vw; margin-left: calc(50% - 50vw);
  background: #0b0b0b; color: #e0e0e0;
  padding: clamp(84px, calc(12 * var(--ulh-fh)), 132px) 0 0;
}
.ulh-fuss-in {
  display: flex; gap: 48px; align-items: flex-start; flex-wrap: wrap;
  padding-bottom: clamp(72px, calc(10 * var(--ulh-fh)), 114px);
}
.ulh-fuss-marke { display: flex; flex-direction: column; gap: 14px; margin-right: auto; max-width: 300px; }
.ulh-fuss-logo { display: inline-flex; align-items: center; text-decoration: none; }
/* Die Wortmarke ist dunkle Tinte auf Durchsichtigem -- auf Schwarz waere sie unsichtbar. invert
   dreht sie um, statt eine zweite Datei zu fuehren, die auseinanderlaufen kann. 31px, damit das
   Zeichen darin auf denselben 26 steht wie vorher das einzelne. */
.ulh-fuss-logo img { display: block; height: 31px; width: auto; filter: invert(1); }
.ulh-fuss-netz { display: flex; gap: 8px; margin-top: 4px; }
.ulh-fuss-ic {
  width: 32px; height: 32px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  color: #9a9a9a; background: rgba(255, 255, 255, .05);
  transition: color 160ms var(--ulh-ease), background 160ms var(--ulh-ease);
}
.ulh-fuss-ic:hover { color: #ffffff; background: rgba(255, 255, 255, .1); }
.ulh-fuss-ic svg { width: 17px; height: 17px; display: block; }
.ulh-fuss-spalten { display: flex; gap: clamp(40px, 6vw, 88px); flex-wrap: wrap; }
.ulh-fuss-spalte { display: flex; flex-direction: column; gap: 10px; min-width: 128px; }
.ulh-fuss-kopf { font-size: 13px; font-weight: 500; color: #6f6f6f; margin-bottom: 2px; }
.ulh-fuss-link {
  font-size: 13.5px; font-weight: 400; color: #cfcfcf; text-decoration: none;
  transition: color 160ms var(--ulh-ease);
}
.ulh-fuss-link:hover { color: #ffffff; }
/* TREFFFLAECHE AUF TOUCH (14.10., Audit): die Links im Fuss waren 18px hoch, die Mail-Adresse auf
   den Terminseiten ebenso -- mit dem Finger trifft man daneben. Auf groben Zeigern (Telefon,
   Tablet) bekommen sie Polster statt Abstand: die Zeilen liegen fast wie vorher, aber jede Zeile
   ist 34px hoch und ganz anklickbar. Mit der Maus bleibt alles, wie es war. */
@media (pointer: coarse) {
  .ulh-fuss-spalte { gap: 0; }
  .ulh-fuss-link { padding: 8px 0; }
  .ulh-termin-info a[href^="mailto:"] { display: inline-block; padding: 8px 0; margin: -8px 0; }
}
.ulh-fuss-unten {
  border-top: 1px solid rgba(255, 255, 255, .08);
  padding: 26px 0 34px;
  font-size: 12.5px; color: #6f6f6f;
  /* Rechts der Knopf "Image credits" (06.10. nachts) -- Abstand statt Trenner, umbrechend auf
     dem Telefon. */
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 24px;
}
/* Der Knopf sieht aus wie die Verweise darueber (.ulh-fuss-link traegt Farbe und Hover), nur in
   der Groesse dieser Zeile. Ohne den Rahmen und Grund eines Knopfs. */
.ulh-fuss-nachweis-knopf {
  font: inherit; font-size: 12.5px; color: #8f8f8f;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.ulh-fuss-nachweis-knopf:focus-visible { outline: 2px solid rgba(255, 255, 255, .5); outline-offset: 3px; border-radius: 4px; }
/* Der Nachweis selbst: klein und leise, die Verweise unterstrichen, damit man sieht, dass sie
   klickbar sind. hidden ausdruecklich: jede spaetere display-Regel an diesem Feld schluege sonst
   das Attribut, und der Nachweis stuende dauerhaft offen. */
.ulh-fuss-nachweis { padding: 0 0 34px; margin-top: -14px; font-size: 12px; line-height: 1.6; color: #7a7a7a; }
.ulh-fuss-nachweis[hidden] { display: none; }
/* Weich herein statt hart da (15.10.): dieselbe Bewegung wie die Stuecke der Seite, kurz. */
.ulh-fuss-nachweis:not([hidden]) { animation: ulhNachweisRein 420ms var(--ulh-expo) both; }
@keyframes ulhNachweisRein { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ulh-fuss-nachweis:not([hidden]) { animation: none; } }
.ulh-fuss-nachweis p { margin: 0 0 8px; max-width: 120ch; }
.ulh-fuss-nachweis a { color: inherit; text-decoration: underline; text-decoration-color: rgba(255, 255, 255, .25); text-underline-offset: 2px; }
.ulh-fuss-nachweis a:hover { color: #e0e0e0; }
@media (max-width: 900px) {
  .ulh-nav-mitte { display: none; }
  .ulh-fuss-in { gap: 32px; }
  .ulh-stimme-satz { max-width: none; }
}

/* ==============================================================================================
   KLEINE SCHIRME
   Eine Stelle fuer alles, was auf schmalen Schirmen anders ist -- verteilt auf sechs Bloecke waere
   es nicht mehr zu ueberblicken.

   Der Grundsatz: die App wird NICHT kleiner gerechnet, bis sie unlesbar ist. Sie behaelt ihre
   Groesse, steht links an der Kante und laeuft rechts aus dem Bild. Das ist die einzige Art, eine
   Oberflaeche auf einem Telefon ehrlich zu zeigen -- eine auf 320px gequetschte Tabelle ist keine
   Tabelle mehr, sondern ein Bild von einer. Abgeschnitten liest man wenigstens, was da steht.
   ============================================================================================== */
/* ---- DIE NEBENFENSTER GEHEN AB 1200px (29.09. spaet angefordert: "der Umbruch in den Tablet-/
   Mobile-Modus kann frueher kommen, bevor da alles zu klein wird") ----
   Vorher erst ab 900. Gemessen stand die App darueber am kleinsten: bei 901px auf 0.39 ihrer
   Groesse (Krume 5.5px auf dem Schirm), bei 1100 auf 0.50 -- kleiner als im Mobilmodus darunter
   (0.59). Mit der Einrueckung von 30 Prozent sind es bei 1180 noch 0.57, bei 1280 schon 0.62: die
   Grenze liegt dazwischen. Darunter traegt das Hauptfenster die ganze Breite der Spur und wird
   nicht mehr verkleinert (landing-hero.js, anwenden -- dieselbe Zahl als NEBENFENSTER_BIS). Der
   Mobilmodus darunter (16px Rand, festes 900er Fenster, hoeheres Verhaeltnis) bleibt bei 900. */
@media (max-width: 1200px) {
  .ulh-fen { display: none; }
}
@media (max-width: 1100px) {
  /* Die Karten stehen frueher untereinander als der Rest umbricht: zwei Karten nebeneinander
     brauchen mehr Platz als eine Ueberschrift. */
  .ulh-cards-row { flex-direction: column; }
  /* Und ZUSAMMEN damit die Mira-Reihe: in einer Spalte bedeutet flex-basis 0 nicht Breite 0,
     sondern HOEHE 0 -- das Fenster war weg, nur die drei Karten standen noch da. Gemessen: 2px
     hoch. Beide Teile nehmen ab hier ihre eigene Groesse. */
  .ulh-msc-links, .ulh-msc-rechts { flex: 0 0 auto; }
  .ulh-msc-links { flex-direction: row; }
}
@media (max-width: 900px) {
  /* 16px links und rechts statt 64. Beides steht in einer Zahl: die Spur ist die Kante, an der
     sich ALLES ausrichtet -- Leiste, Karten, Kasten der Quellen, Kurve. Wer sie aendert, aendert
     die ganze Seite auf einmal, und genau deshalb steht sie an einer Stelle. */
  /* Die Kante steht seit dem 28.09. als eigene Marke (siehe .ulh-root oben) -- hier also 16px, und
     die Spur rechnet daraus, damit beide dasselbe sagen. Der Wert der Spur ist derselbe wie vorher
     (100% - 32px). */
  .ulh-root { --ulh-rand: 0px; --ulh-kante: 16px; --ulh-spur: calc(100% - 2 * var(--ulh-kante)); }

  /* ---- Das Fenster im Hero ----
     Es bleibt. Vorher war es ab 900px ganz weg -- die Sektion bestand dann aus einer Ueberschrift
     und sonst nichts.
     Die drei Nebenfenster sind hier laengst weg (ab 1200px, siehe den Block darueber).
     Das Fenster behaelt 900px Breite und steht links. Was rechts nicht mehr passt, schneidet die
     Wurzel ab (overflow-x: clip) -- gewollt, kein Fehler: so bleibt die Schrift darin lesbar. */
  /* 16px von links wie alles andere -- die Buehne haengt nicht in der Spur, also steht die Zahl
     hier noch einmal. Rechts laeuft sie hinaus, das ist der Sinn. */
  .ulh-buehne { width: 900px; margin-left: 16px; margin-right: 0; }
  .ulh-stage { overflow: visible; }
  /* Und das Fenster hoeher: 0.605 ist an einer 1058x640-Vorlage gemessen, also fuer ein Fenster
     im Breitformat. Auf einem Telefon steht daneben nichts mehr, und ein flaches Fenster zeigt
     dort vom Inhalt zu wenig -- gemeldet als "unten abgeschnitten". 0.72 macht es ein Fuenftel
     hoeher; zusammen mit dem um 10 Prozent kleineren Mass (landing-hero.js, mass()) passt damit
     deutlich mehr hinein, ohne dass die Schrift darin unlesbar wird.
     Der Schnitt unten BLEIBT -- er ist gewollt. Er faellt nur nicht mehr mitten in eine Karte. */
  .ulh-root { --ulh-verhaeltnis: 0.72; }

  /* ---- Die Vorschauen in den Karten ----
     Feste Breite statt Anteil. Sonst rechnet die Komponente darin ihre schmale Ansicht: die
     Prompt-Tabelle blendet Spalten aus, die Domain-Tabelle klappt zusammen, und man sieht nicht
     mehr, wovon die Karte spricht. Mit fester Breite bleibt die Tabelle die Tabelle und wird
     rechts angeschnitten. */
  .ulh-vis-in { width: 760px; }
  /* Die Prompt-Tabelle etwas breiter als die anderen: ihre erste Spalte traegt den ganzen Satz,
     und der ist auf einem schmalen Schirm das einzige, was zaehlt.
     830 statt 880: die Prompt-Spalte ist die einzige mit 1fr, alle anderen haben feste Breiten --
     die 50px gehen also genau von ihr ab. */
  .ulh-vis-zeilen .ulh-vis-in { width: 830px; }
  /* Und OHNE das seitliche Polster. Auf breiten Schirmen ruecken die zwei Vorschauen damit in die
     Mitte der Karte; hier schob es die erste Spalte aus dem Ausschnitt -- gemessen begann der
     Prompt-Text bei 116px statt an der Kante, und genau das war "die Spalte Prompt ist weg". Die
     Domain-Tabelle hat das Polster nie gehabt, und dort stimmte es. */
  .ulh-vis-zeilen .ulh-vis-in, .ulh-vis-chancen .ulh-vis-in { padding: 0; }
  /* Die Prompt-Karte (07.10.): ihr Polster von 20px kommt von einer spezifischeren Regel und muss
     hier ausdruecklich auf 0, sonst stuende es auch auf dem Telefon. 850 statt 830: die festen
     Spalten sind zusammen 20px breiter geworden (570 statt 550), die Prompt-Spalte bleibt so
     genau so breit wie vorher. */
  .ulh-root .ulh-vis[data-ulh-vis="zeilen"] .ulh-vis-in { padding: 0; width: 850px; }
  /* Die zwei Bilder in der dritten Reihe sind KEINE Bauteile der App, sondern Zeichnungen: sie
     brauchen keinen Ausschnitt, sie passen. Also keine feste Breite, sondern etwas kleiner
     rechnen -- damit werden Schrift, Abstaende und der Kreis gemeinsam kleiner, statt dass eines
     davon gequetscht wird. */
  .ulh-vis-sprachen .ulh-vis-in, .ulh-vis-modelle .ulh-vis-in,
  .ulh-vis-linie .ulh-vis-in, .ulh-vis-regal .ulh-vis-in, .ulh-vis-werber .ulh-vis-in,
  .ulh-vis-bewegung .ulh-vis-in, .ulh-vis-eventkarte .ulh-vis-in,
  .ulh-vis-eventkurve .ulh-vis-in { width: calc(100% / var(--ulh-vs)); }
  /* Die Event-Karten auf schmalem Schirm: so breit, dass links und rechts noch je eine hervorsieht. */
  .ulh-evkarten .uev-karte { width: min(340px, 70%); }
  .ulh-vis-sprachen, .ulh-vis-modelle, .ulh-vis-linie, .ulh-vis-regal, .ulh-vis-werber { --ulh-vs: 0.86; }
  /* Die Ad-Karten laufen wie die Tabellen rechts hinaus -- zwei ganz, die dritte angeschnitten. */
  .ulh-vis-anzeigen .ulh-vis-in { padding: 0; }

  /* ---- Die Leiste ----
     Sie steht in derselben Spur wie alles andere; die zwei Knoepfe rechts bekommen weniger
     Polster, damit sie neben dem Logo Platz haben. */
  /* Die Legende eines echten Charts bleibt: core blendet sie unter 500px FENSTERbreite aus -- in
     der App richtig, denn dort steht sie unter einem Chart, das die ganze Seite fuellt. */
  .ulh-vis .up-legend.is-hidden { display: block; }

  /* Hier stand margin: auto an der Leinwand der Kurve, gegen eine Luecke rechts. Die Luecke kam
     daher, dass Chart.js die VERKLEINERTE Breite mass; die Raender zog es beim naechsten Messen ab,
     und die Kurve schrumpfte auf 0 (07.10.). Die Groesse setzt jetzt landing-hero.js (groesse() in
     krvZeichnen) aus der Layoutbreite -- die Leinwand fuellt ihr Feld, Raender braucht es nicht. */

  /* ---- Das Chancen-Brett ----
     Es laeuft hier NICHT rechts hinaus wie die Tabellen daneben, sondern passt ganz in die Karte.
     Grund: die Zeile besteht aus Titel, Domain und dem Prioritaetswert, und der Wert stand bei
     574px Zeilenbreite in einer 278px breiten Karte ausserhalb des Bildes -- die Zeile zeigte
     also genau das nicht, worum es in ihr geht. Eine Tabelle abzuschneiden ist ehrlich, eine
     dreiteilige Zeile um ihren dritten Teil zu bringen nicht. */
  .ulh-vis-chancen .ulh-vis-in { width: 100%; }
  /* Titel oben ueber beide Spalten, darunter Domain links und Wert rechts, beide auf einer Linie.
     display: contents an den zwei Zwischenkaesten, damit ihre Kinder Zellen DIESES Rasters werden
     -- ohne das Markup anzufassen, das dieselbe Zeile in der App baut. */
  .ulh-vis-chancen .uo-row { row-gap: 6px; column-gap: 12px; padding: 10px 12px; border-radius: 10px; }
  .ulh-vis-chancen .uo-row-main, .ulh-vis-chancen .uo-row-right { display: contents; }
  .ulh-vis-chancen .uo-row-title { grid-column: 1 / -1; font-size: 12.5px; }
  .ulh-vis-chancen .uo-row-sub { grid-column: 1; }
  .ulh-vis-chancen .uo-row-domain { font-size: 11px; }
  .ulh-vis-chancen .uo-row-sub .up-logo-box { width: 16px; height: 16px; border-radius: 4.5px; }
  .ulh-vis-chancen .uo-row-sub .up-logo-box img { width: 14px; height: 14px; border-radius: 3.5px; }
  .ulh-vis-chancen .uo-pot { grid-column: 2; justify-self: end; align-self: center; }
  /* Die Themenchips fallen weg. Sie sind der vierte Teil der Zeile, und zu dritt auf einer
     332px-Karte bliebe fuer Domain und Wert nichts uebrig -- gefragt war die Zeile mit genau
     diesen beiden. */
  .ulh-vis-chancen .uo-row-tags { display: none; }
  .ulh-vis-chancen .uo-list-rows { gap: 16px; }

  /* ---- Mira ----
     Die drei Karten untereinander: nebeneinander sind sie je 108px breit, und darin steht ein
     Zweizeiler als Wortsalat. */
  .ulh-msc-links { flex-direction: column; }
  /* UND die Karten nehmen ihre eigene Hoehe. Das war der Grund, warum von ihnen nur ein Zeichen
     uebrig war: .ulh-msc-karte traegt flex: 1 1 0 -- in einer REIHE heisst die 0 "Breite 0, dann
     gleichmaessig wachsen", in einer SPALTE aber "HOEHE 0". Und die Spalte hat hier keine Hoehe
     zu verteilen (.ulh-msc-links ist flex: 0 0 auto, sie richtet sich nach ihrem Inhalt), also
     blieb jede Karte auf ihrem Polster stehen.
     Derselbe Fehler ist der Reihe schon einmal passiert -- siehe der 1100px-Block weiter oben, wo
     genau das fuer .ulh-msc-links selbst behoben wurde. Hier sind es ihre Kinder. */
  .ulh-msc-karte { flex: 0 0 auto; min-height: auto; }
  /* Das Fenster: die feste Hoehe ist nur noch der ANFANG. Sie reichte nicht -- 600 nicht und 820
     auch nicht --, weil die Antwort auf einem Telefon in mehr Zeilen umbricht. landing-hero.js
     misst darum den Ueberhang des Chats und schlaegt ihn dem Rahmen zu (hoeheNachfuehren); die
     Zahl hier ist die Untergrenze, unter die er nie faellt. */
  .ulh-msc-rahmen { height: 820px; }

  /* ---- Die Quellen-Sektion ----
     Der Ring 15% kleiner: beide Zahlen der Regel aus core mal 0.85 (75% -> 63.75%, 220 -> 187),
     damit er auf jeder Breite dasselbe Verhaeltnis behaelt statt an einer Stelle zu springen. */
  .ulh-quell-app .up-donut-layout.is-collapsed .up-donut-wrap,
  .ulh-quell-app .up-donut-sk.is-collapsed .up-sk-chart { width: min(63.75%, 187px); }
  /* Und derselbe Faktor auf den NICHT eingeklappten Fall: der gilt, sobald die Legende wieder
     neben dem Ring steht (gemessen ab 620px Rahmenbreite), und dort stand er sonst weiter auf
     seinen vollen 200px. 52% -> 44.2%, 200 -> 170. */
  .ulh-quell-app .up-donut-wrap,
  .ulh-quell-app .up-donut-sk .up-sk-chart { width: min(44.2%, 170px); }

  /* ---- DAS GITTER WIRD DER RAND (30.09. angefordert) ----
     "Alles bis auf die Hero-Seite: das Gitter kann die Aussengrenze werden, als wenn wir
     reinzoomen. Die vertikalen Linien kommen weg, aber der Platz links und rechts von ihnen auch."
     Gemessen bei 375px lag Text in einer Karte 56px vom Rand: 16 Kante, 8 zur Schiene, 1+4+1
     Kasten, 26 Polster der Karte. Die Kante ist hier doppelt -- die Kaesten bringen ihren
     Abstand selbst mit.
     Also bekommen die Sektionen mit Kaesten die ganze Breite als Spur. Ueber die Variable und
     nicht ueber .ulh-spur: sie erbt, und so ziehen auch die Kurve der GEO-Sektion und alles
     andere mit, was mit der Spur rechnet. Die Kaesten behalten ihre 8px, jetzt zum Bildrand.
     --ulh-kante bleibt, wo es ist: landing-hero.js rechnet damit das Hero-Fenster ein.
     NICHT hier: Leiste und Hero (sie behalten ihre 16px, so war es verlangt), die Stimme und der
     Fuss -- dort steht Text ohne eigenes Polster direkt in der Spur, und ohne die 16px stiesse er
     an den Bildrand. */
  .ulh-feat, .ulh-quell, .ulh-msc, .ulh-geo, .ulh-preis { --ulh-spur: 100%; }
  /* Die Schienen selbst, und die zwei Striche hinter dem Kopf der Quellen, die genau auf ihnen
     lagen. Das Muster zwischen den Strichen bleibt -- es ist Textur, kein Gitter. */
  .ulh-schienen, .ulh-quell-kopf::before { display: none; }
  /* Was bisher ohne eigenes Polster an der Schiene stand, braucht jetzt eines: die Ueberschrift der
     Quellen (24 wie die Koepfe der anderen Sektionen, .ulh-feat-kopf) und die vier Zahlen der
     GEO-Sektion (16 -- ihr Strich bleibt, wo er war). */
  .ulh-quell-kopf { padding: 0 24px; }
  .ulh-geo-kpis { padding: 0 16px; }

  /* Die Leiste zeichnet die Schienen mit eigenen Raendern weiter (siehe .ulh-nav-in). Ohne die
     Schienen darunter stuenden dort zwei Stummel, die beim Scrollen ueber jeder Sektion mitfahren.
     Ihr Inhalt bleibt, wo er war -- die Leiste gehoert zum Hero. */
  .ulh-nav-in { gap: 12px; border-left: 0; border-right: 0; }
  .ulh-nav-rechts .ulh-btn { padding: 0 10px; }
}
@media (max-width: 560px) {
  /* Auf dem Telefon bleibt vom Fenster der linke Teil -- die Seitenleiste und der Anfang des
     Inhalts. Mehr passt nicht, und mehr braucht es nicht: man sieht, dass da eine App laeuft. */
  .ulh-vis-in { width: 700px; }
  /* Die zwei Zeichnungen noch etwas kleiner -- auf dem Telefon ist die Karte 332px breit. */
  .ulh-vis-sprachen, .ulh-vis-modelle, .ulh-vis-linie, .ulh-vis-regal, .ulh-vis-werber { --ulh-vs: 0.78; }
  .ulh-nav-rechts .ulh-btn-sec { display: none; }
  .ulh-fuss-in { flex-direction: column; }
  .ulh-geo-mk-t, .ulh-geo-mk-s { font-size: 10.5px; }
  /* Der Faecher auf dem Telefon flacher: die Karte ist hier schmal und dadurch hoch (rund 370px),
     und mit 260% und 7 Grad lag der Drehpunkt so tief, dass die hinteren oben 117px herausragten --
     gemessen weit ueber den Rand der Vorschau hinaus, sichtbar abgeschnitten. 200% und 4 Grad
     lassen rund 50px stehen, und das passt in die 46px Rand neben der vorderen. */
  .ulh-evkarte-links, .ulh-evkarte-rechts { transform-origin: 50% 200%; }
  .ulh-card.is-da .ulh-evkarte-links  { rotate: -4deg; }
  .ulh-card.is-da .ulh-evkarte-rechts { rotate: 4deg; }
}

/* ---------- Vorgerendert in der Framer-Seite (13.10.) ---------------------------------------
   Seit dem 13.10. steht die Seite nicht mehr in einem Embed-Rahmen, sondern als Code-Komponente
   IN der Framer-Seite (framer/UpstreemLanding.tsx), und ihr Markup kommt vorgerendert mit dem
   HTML -- damit Crawler ohne JavaScript sie lesen koennen. Drei Folgen fuer die Form:

   1. Die Leiste liegt UEBER dem Hero, wie auf der Seite bisher. Im Rahmen hing sie fixiert in der
      Seite darueber (leisteAuslagern) und nahm keinen Platz; hier steht sie in der Wurzel und
      klebt dort (sticky). Ohne die Zeile rutschte der ganze Hero um ihre Hoehe nach unten:
      62px Leiste plus 1px Linie (.ulh-nav-in, .ulh-nav). */
.ulh-root[data-ulh-inline] > .ulh-nav { margin-bottom: -63px; }
/*    Dazu die Fensterhoehe (Begruendung an --ulh-fh in .ulh-root): 20px je vh liegt ueber jeder
      Obergrenze der Datei (die groesste braucht 13.4px je vh, clamp(72px, 9vh, 120px)) -- alle
      Abstaende stehen damit auf ihrem Hoechstwert, wie im Rahmen. */
.ulh-root[data-ulh-inline] { --ulh-fh: 20px; }
/* 2. Der Hero-Text wartet auf seinen Auftritt. Vorgerendert stuende er sofort da, und sobald
      landing-hero.js laeuft, setzte is-entering seine Animationen mit "backwards" -- er verschwaende
      und kaeme noch einmal herein. Unsichtbar bis is-shown, wie im Rahmen, wo er erst mit dem
      Skript entstand. Crawler lesen den Text unabhaengig davon (sie werten keine CSS aus).
      Faellt das Skript aus, setzt landing-hero.js is-shown trotzdem (Aufgeben nach 10 s). */
.ulh-root[data-ulh-ssr]:not(.is-shown) :is(.ulh-eyebrow, .ulh-h1, .ulh-sub, .ulh-cta, .ulh-modelle) { opacity: 0; }
/* 3. Die Anker der Leiste (#overview, #sources, #mira) kommen von anderen Seiten: die Sektion
      soll beim Sprung unter der Leiste stehen, nicht dahinter. */
.ulh-root > section[id] { scroll-margin-top: 63px; }

/* ---------- Leiste und Fuss als eigene Komponenten (13.10.) ----------------------------------
   framer/UpstreemTopbar.tsx und framer/UpstreemFooter.tsx tragen DIESELBE Leiste und DENSELBEN
   Fuss wie die Startseite, in einer .ulh-root, weil Masse und Farben aus deren Variablen kommen.
   Den weissen Grund der Wurzel brauchen sie nicht: unter der halbdurchsichtigen Leiste soll die
   Seite durchscheinen wie auf der Startseite, und der Fuss bringt seinen eigenen mit. */
/* Die Fensterhoehe wie auf der Startseite (siehe --ulh-fh): der Fuss traegt Abstaende auf vh
   (clamp(84px, 12vh, 132px)), und die stehen dort auf ihrer Obergrenze. Ohne die Zeile war der
   eigene Fuss bei 1440x900 gemessen 48px niedriger als der auf der Startseite. */
.ulh-root.ulh-teil { background: none; --ulh-fh: 20px; }

/* ---------- Der Kopf der Unterseiten (14.10.) ------------------------------------------------
   Demo, Contact, Blog und Rechtsseiten der Website: ein Band ueber die ganze Breite mit Chip,
   Ueberschrift und Satz, unten eine Linie und ein leises Punktraster, das nach unten ausblendet.
   Damit geht das Raster der Leiste (Schienen, waagerechte Linie) in der Seite weiter -- dieselbe
   Machart wie der Satz zum Schluss der Startseite (.ulh-stimme), nur leiser. */
.ulh-seitenkopf {
  position: relative; z-index: 1;
  border-bottom: 1px solid var(--ulh-border);
  background-color: #ffffff;
  background-image: radial-gradient(circle, var(--ulh-schiene) 1px, transparent 1px);
  background-size: 8px 8px;
}
/* Das Raster nur unten sichtbar: oben weiss, damit die Ueberschrift auf ruhigem Grund steht. */
.ulh-seitenkopf::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, #ffffff 35%, rgba(255, 255, 255, .55) 100%);
}
.ulh-seitenkopf-in {
  position: relative; display: flex; flex-direction: column; align-items: flex-start;
  padding: clamp(56px, 7vw, 96px) 32px clamp(40px, 5vw, 64px);
}
.ulh-seitenkopf-h {
  margin: 0; max-width: 18ch;
  font-size: clamp(36px, 4.6vw, 60px); font-weight: 600; line-height: 1.02;
  letter-spacing: -0.03em; color: var(--ulh-text);
}
.ulh-seitenkopf-sub {
  margin: 20px 0 0; max-width: 56ch;
  font-size: 17px; font-weight: 400; line-height: 1.55; letter-spacing: -0.01em;
  color: var(--ulh-sub);
}

/* ---------- Die zwei Terminseiten (14.10.) ---------------------------------------------------
   upstreem.ai/demo und upstreem.ai/contact: unter dem Kopf der Kasten mit Karten wie auf der
   Startseite (.ulh-cards-box) -- links schmal, was es gibt, rechts gross der Kalender. */
.ulh-root.ulh-teil[data-ulh-teil="termin"] { background: none; }
/* Oben 8 wie links und rechts (14.10. abends gemeldet: "das Top-Padding ist groesser als left und
   right"). Gemessen bei 1600: 32px unter der Linie des Kopfbands, aber 8px zu den Schienen -- der
   Kasten sass oben locker und seitlich knapp. Das Band endet mit einer Linie wie die Schienen;
   zu beiden steht der Kasten jetzt gleich weit. */
.ulh-termin-box { margin: 8px 8px clamp(72px, 9vw, 120px); }
.ulh-termin-info { padding: 28px 28px 26px; }
.ulh-termin-h { margin: 0; font-size: 19px; font-weight: 600; letter-spacing: -0.015em; color: var(--ulh-text); }
.ulh-termin-t { margin: 10px 0 0; font-size: 15px; line-height: 1.55; color: var(--ulh-sub); }
/* Die drei Punkte als nummerierte Liste mit runden Zahlen -- ohne Zeichen, weil core hier nicht
   laeuft (siehe termin() in landing-hero.js). */
.ulh-termin-liste { list-style: none; margin: 20px 0 0; padding: 0; counter-reset: ulhTermin; display: flex; flex-direction: column; gap: 14px; }
.ulh-termin-liste li {
  counter-increment: ulhTermin; position: relative; padding-left: 36px;
  font-size: 15px; line-height: 1.45; color: var(--ulh-text);
}
.ulh-termin-liste li::before {
  content: counter(ulhTermin); position: absolute; left: 0; top: -1px;
  width: 24px; height: 24px; border-radius: 999px; display: grid; place-items: center;
  background: var(--ulh-kasten); border: 1px solid var(--ulh-border);
  font-size: 12px; font-weight: 600; color: var(--ulh-text);
}
.ulh-termin-fakt { margin: 24px 0 0; padding-top: 18px; border-top: 1px solid var(--ulh-border); font-size: 13.5px; color: var(--ulh-sub); }
.ulh-termin-alt { margin: 10px 0 0; display: flex; flex-wrap: wrap; gap: 6px; font-size: 13.5px; color: var(--ulh-sub); }
.ulh-termin-mailgross {
  display: inline-block; margin-top: 18px;
  font-size: 20px; font-weight: 500; letter-spacing: -0.01em; color: var(--ulh-text);
  text-decoration: underline; text-decoration-color: var(--ulh-border); text-underline-offset: 5px;
  transition: text-decoration-color 160ms var(--ulh-ease);
}
.ulh-termin-mailgross:hover { text-decoration-color: currentColor; }
.ulh-termin-alt-kal { margin-top: 24px; padding-top: 18px; border-top: 1px solid var(--ulh-border); width: 100%; }
.ulh-termin-alt a, .ulh-termin-fehler a {
  color: var(--ulh-text); font-weight: 500;
  text-decoration: underline; text-decoration-color: var(--ulh-border);
  text-underline-offset: 3px; transition: text-decoration-color 160ms var(--ulh-ease);
}
.ulh-termin-alt a:hover, .ulh-termin-fehler a:hover { text-decoration-color: currentColor; }
.ulh-termin-kalkarte { padding: 10px; }

/* Der Kalender. Die Mindesthoehe haelt seinen Platz frei, solange er laedt: ohne sie stuende der
   Fuss erst unter dem Satz und spraenge dann um gut 500px nach unten. 570 ist die Hoehe, die Cal.com
   in der Monatsansicht einnimmt (gemessen 14.10. im Pruefstand ter01). Steht er, gilt seine
   eigene Hoehe. Ladebalken gibt es keine mehr (14.10.: "brauch ich nicht"). */
.ulh-termin-kal { position: relative; width: 100%; min-height: 570px; }
.ulh-termin-kal.is-da, .ulh-termin-kal.is-fehler { min-height: 0; }
.ulh-termin-ziel { width: 100%; }

/* Vor der Einwilligung: ein ruhiger Hinweis mitten in der Karte statt eines leeren Kastens. */
.ulh-termin-kal.is-wartet { min-height: 420px; display: grid; place-items: center; }
.ulh-termin-zustimmung[hidden] { display: none; }
.ulh-termin-zustimmung {
  max-width: 420px; padding: 24px; text-align: center;
  display: flex; flex-direction: column; align-items: center;
}
.ulh-termin-z-h { margin: 0; font-size: 16px; font-weight: 600; color: var(--ulh-text); }
.ulh-termin-z-t { margin: 8px 0 0; font-size: 14px; line-height: 1.55; color: var(--ulh-sub); }
.ulh-termin-z-knoepfe { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 20px; }

/* Kommt der Kalender nicht, steht hier ein Satz mit dem direkten Link: ein roter Satz ohne Kasten,
   in der Fehlerfarbe der App (--vt-down hell, die Website ist immer hell). Er erscheint auf zwei
   Wegen: landing-teile.js setzt .is-fehler (Skript blockiert, Cal.com meldet einen Fehler, 20s ohne
   Kalender), und OHNE jedes Skript -- landing-teile.js selbst kam nicht an -- holt ihn nach 25s
   diese Regel hervor. Wartet der Kalender auf die Einwilligung (is-wartet), ist das kein Fehler. */
.ulh-termin-fehler {
  position: absolute; top: 216px; left: 0; right: 0; margin: 0; padding: 0 24px; text-align: center;
  font-size: 14px; line-height: 1.5; color: #b0200c;
  visibility: hidden;
}
.ulh-termin-kal:not(.is-da):not(.is-wartet) .ulh-termin-fehler { animation: ulhTerminSpaet 1ms 25s forwards; }
@keyframes ulhTerminSpaet { to { visibility: visible; } }
.ulh-termin-kal.is-fehler .ulh-termin-fehler { position: static; visibility: visible; animation: none; padding: 24px; }
.ulh-termin-kal.is-da .ulh-termin-fehler, .ulh-termin-kal.is-wartet .ulh-termin-fehler { display: none; }
/* Meldet Cal.com linkFailed, steht im Rahmen schon SEINE Meldung ("Error Code: 404. Cal Link seems
   to be wrong", gemessen am 14.10. mit einem falschen Link) -- zwei Meldungen uebereinander, eine
   davon englisch-technisch. Der Rahmen geht dann weg, unser Satz bleibt. Kommt der Kalender nach
   der Frist doch noch (is-da), steht er wieder da. */
.ulh-termin-kal.is-fehler:not(.is-da) .ulh-termin-ziel { display: none; }

/* Schmal stehen die zwei Karten untereinander, der Kalender unten. */
@media (max-width: 860px) {
  .ulh-termin-box .ulh-cards-row { flex-direction: column; }
  .ulh-seitenkopf-in { padding-left: 20px; padding-right: 20px; }
}
.ulh-termin-kal.is-da .ulh-termin-fehler { display: none; }


/* ---------- Die Customer Journey: Intents und Branded (14.10.) --------------------------------
   Zwei Fenster auf dem Punktraster, wie die Domain- und die Produktkarte: weiss, Haarrand, Radius
   12, der leise Schatten der Karten. Darin die Bauteile aus core (Intent-Label, Branded-Chip,
   Trend-Chip, Schalter); hier steht nur die Anordnung. Alle Bewegung ueber transform und opacity. */
.ulh-rs, .ulh-br {
  position: relative; z-index: 1;
  margin: 18px auto 0; max-width: 100%;
  background: #ffffff; border: 1px solid var(--ulh-border); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(11, 13, 24, .04), 0 10px 28px rgba(11, 13, 24, .06);
}
.ulh-rs { width: calc(100% - 40px); padding: 16px 18px 16px; }
.ulh-rs-oben { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ulh-rs-titel { font-size: 13px; font-weight: 500; color: var(--vc-text, #1f1f1b); }
.ulh-rs-legende { display: flex; gap: 14px; font-size: 12px; color: var(--vc-muted, #6b6f76); }
.ulh-rs-legende span { display: inline-flex; align-items: center; gap: 6px; }
.ulh-rs-legende i { width: 10px; height: 10px; border-radius: 3px; display: block; box-sizing: border-box; }

/* ---- Die Saeulen (14.10. abends, Variante B) ----
   Eigene Marke voll in der Textfarbe, der Wettbewerber als hellblaue gestrichelte Huelle mit einem
   Hauch Blau darin: Schwarz und Blau sind die zwei Toene der ganzen Seite (Hero, Chips, Kurven),
   das Orange der BMW-Linie im Hero haette hier eine dritte Farbe eingefuehrt. */
.ulh-rs { --rs-du: var(--vc-text, #1f1f1b); --rs-wett: #8db6ee; --rs-wett-grund: rgba(87, 156, 241, .07); }
.ulh-rs-legende i.is-du { background: var(--rs-du); }
.ulh-rs-legende i.is-wett { border: 1px dashed var(--rs-wett); background: var(--rs-wett-grund); }
/* Das Feld: feste Hoehe, die Saeulen stehen auf seiner Unterkante. Oben 26px Luft fuer die Werte
   ueber der hoechsten Saeule (52.4 von 60 Prozent). */
.ulh-rs-feld {
  position: relative; height: 176px; margin-top: 18px; padding-top: 26px;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px;
  border-bottom: 1px solid var(--vc-border, #e7e9ec);
}
.ulh-rs-linie {
  /* --rs-y als Anteil (0 bis 1) der 150px, in denen die Saeulen stehen (.ulh-rs-s). */
  position: absolute; left: 0; right: 0; bottom: calc(var(--rs-y) * 150px);
  border-top: 1px dashed rgba(31, 31, 27, .07); pointer-events: none;
}
.ulh-rs-sp { position: relative; display: flex; justify-content: center; align-items: flex-end; gap: 8px; height: 100%; }
/* Eine Saeule: oben der Wert, darunter der Koerper. Die Hoehe des Koerpers ist ein Anteil des
   Feldes OHNE den Platz fuer die Werte -- deshalb steht die Saeule als Spalte mit fester Hoehe. */
.ulh-rs-s { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 150px; width: 36px; }
.ulh-rs-s.is-wett { width: 30px; }
.ulh-rs-k {
  /* flex 0 0 auto: sonst drueckt der Wert darueber die Saeule zusammen (gemessen 126 statt 131px
     bei 52.4 von 60) -- der Wert ragt stattdessen in die 26px Luft oben im Feld. */
  display: block; flex: 0 0 auto; width: 100%; height: var(--rs-h); box-sizing: border-box;
  border-radius: 6px 6px 3px 3px;
  /* Aufgedeckt von unten (clip-path) statt gestaucht (scaleY): beim Stauchen liefen Rundung und
     Strichelung mit und waren 1.1s lang verzerrt (14.10., Audit). */
  clip-path: inset(100% 0 0 0 round 6px 6px 3px 3px);
  /* Nacheinander von links nach rechts (15.10.): je Saeule 130ms spaeter, die erste nach 200ms --
     so ist die Karte schon da, wenn die Saeulen wachsen. 900ms je Saeule, Expo-Kurve. */
  transition: clip-path 900ms var(--ulh-expo);
  transition-delay: calc(var(--rs-k, 0) * 130ms + 200ms);
}
.ulh-rs-s.is-du .ulh-rs-k { background: var(--rs-du); }
.ulh-rs-s.is-wett .ulh-rs-k { border: 1px dashed var(--rs-wett); border-bottom: 0; background: var(--rs-wett-grund); }
.ulh-rs-w {
  margin-bottom: 6px; white-space: nowrap; font-variant-numeric: tabular-nums;
  opacity: 0; transform: translateY(6px);
  transition: opacity 500ms var(--ulh-ease), transform 800ms var(--ulh-expo);
  /* Der Wert kommt, wenn seine Saeule gut halb gewachsen ist. */
  transition-delay: calc(var(--rs-k, 0) * 130ms + 560ms);
}
.ulh-rs-s.is-du .ulh-rs-w { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; color: var(--vc-text, #1f1f1b); }
.ulh-rs-s.is-wett .ulh-rs-w { font-size: 12px; color: var(--vc-muted, #6b6f76); }
/* Wo der Wettbewerber vorn liegt, steht sein Wert eine Stufe kraeftiger -- leise, ohne Rahmen. */
.ulh-rs-sp.is-hinten .ulh-rs-s.is-wett .ulh-rs-w { font-weight: 600; color: #3f7fd6; }
.ulh-rs.is-da .ulh-rs-k { clip-path: inset(0 0 0 0 round 6px 6px 3px 3px); }
.ulh-rs.is-da .ulh-rs-w { opacity: 1; transform: none; }
/* Unter jeder Stufe ihr Name (Intent-Label aus core) und der Satz, was der Kaeufer dort will. */
.ulh-rs-fuss { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-top: 12px; }
.ulh-rs-f { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px; min-width: 0; }
.ulh-rs-f .up-intent { height: 22px; font-size: 13px; }
.ulh-rs-will { font-size: 12px; line-height: 1.35; color: var(--vc-muted, #6b6f76); }

/* Branded: der Schalter der Filterleiste, die Zahl, drei Prompts. */
.ulh-br { width: calc(100% - 40px); padding: 6px 6px 10px; }
.ulh-br-schalter { padding: 10px 12px; border-radius: 9px; background: var(--vc-heading-bg, #f9f8f9); cursor: default; }
/* Name und Unterzeile untereinander, wie im Menue der App -- ausserhalb des Menues kennt core diese
   Anordnung nicht, gemessen liefen beide in einer Zeile ineinander ("Branded promptsLeft out"). */
.ulh-br-schalter .up-pop-label { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.ulh-br-schalter .up-pop-label > span:first-child { font-size: 13px; font-weight: 500; color: var(--vc-text, #1f1f1b); }
.ulh-br-hint { font-size: 12px; color: var(--vc-muted, #6b6f76); transition: opacity 200ms ease; }
.ulh-br-zahl { display: flex; align-items: baseline; gap: 10px; padding: 16px 12px 10px; }
.ulh-br-t { font-size: 12.5px; color: var(--vc-muted, #6b6f76); }
.ulh-br-wert {
  font-size: 30px; font-weight: 600; letter-spacing: -0.02em; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--vc-text, #1f1f1b);
}
/* Der Sentiment-Baustein der App in Gross (14.10. abends: "das korrekte Sentiment-Styling aus der
   Hauptapp, mit dem Shape davor, der sich smooth in Farbe aendert"). Faktor auf alles (CLAUDE.md
   1): in der Zelle steht 3x12 neben 14px, hier steht die Zahl in 30px -- Faktor gut 2, also
   6x26 und 12px Abstand. Die Zahl erbt Groesse und Schnitt von .ulh-br-wert. Die Farbe gleitet in
   derselben Zeit, in der die Zahl zaehlt (700ms). */
.ulh-br-wert .up-sent { gap: 12px; }
.ulh-br-wert .up-sent-dot { width: 6px; height: 26px; transition: background-color 700ms var(--ulh-ease); }
.ulh-br-wert .up-sent-val { font-size: inherit; font-weight: inherit; letter-spacing: inherit; }
@media (prefers-reduced-motion: reduce) { .ulh-root .ulh-br-wert .up-sent-dot { transition: none; } }
/* Der Trend erscheint nur, solange Branded mitzaehlt: er ist der geschoente Teil. */
/* Nur noch Huelle fuer den Trend-Chip aus core (Farbe, Pfeil, Schrift kommen von .up-trend). */
.ulh-br-diff {
  display: inline-flex; align-self: center;
  opacity: 0; transform: translateY(4px);
  transition: opacity 320ms var(--ulh-ease), transform 420ms var(--ulh-expo);
}
.ulh-br.is-mit .ulh-br-diff { opacity: 1; transform: none; }
.ulh-br-liste { display: flex; flex-direction: column; border-top: 1px solid var(--vc-border, #e7e9ec); margin: 0 6px; }
.ulh-br-z {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 6px; border-bottom: 1px solid var(--vc-border, #e7e9ec);
  /* Aus der Wertung heisst: treten zurueck. Mit Branded kommen sie nach vorn. */
  opacity: .42; transition: opacity 360ms var(--ulh-ease);
}
.ulh-br-z:last-child { border-bottom: 0; }
.ulh-br.is-mit .ulh-br-z { opacity: 1; }
/* Das Branded-Label aus core behaelt seine volle Breite ("Branded" plus Zeichen); kuerzen darf
   nur der Prompt davor (14.10. abends gemessen: das Label stand als "Brand..." da). */
.ulh-br-z .up-branded { flex: 0 0 auto; }
.ulh-br-z .up-branded-lbl { overflow: visible; }
.ulh-br-p { font-size: 13px; color: var(--vc-text, #1f1f1b); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
  .ulh-rs-k, .ulh-rs-w, .ulh-br-z, .ulh-br-diff { transition: none; }
}
/* Schmal bleiben die drei Saeulenpaare nebeneinander (der Weg liest sich von links nach rechts),
   nur schmaler; das Intent-Label darf dann umbrechen. */
@media (max-width: 760px) {
  .ulh-vis-reise { --ulh-vh: 400px; }
  .ulh-vis-branded { --ulh-vh: 300px; }
  .ulh-rs-feld, .ulh-rs-fuss { gap: 6px; }
  /* 14px zwischen den Saeulen eines Paars: der Wert ueber der schmalen BMW-Saeule ist breiter als
     sie und stiess bei 4px an die Nachbarsaeule, bei 10px lagen die Werte Kante an Kante (375px). */
  .ulh-rs-sp { gap: 14px; }
  .ulh-rs-s { width: 26px; }
  .ulh-rs-s.is-wett { width: 20px; }
  .ulh-rs-s.is-du .ulh-rs-w { font-size: 12px; }
  .ulh-rs-s.is-wett .ulh-rs-w { font-size: 11px; }
  /* "Transaktional" passt in 13px nicht in ein Drittel von 280px und wurde gekuerzt. */
  .ulh-rs-f .up-intent { height: auto; font-size: 12px; text-align: center; }
  .ulh-rs-f .up-intent-lbl { overflow: visible; }
  .ulh-rs-f .up-intent > svg { display: none; }
  /* Volle Kartenbreite statt der festen 760px aller anderen Vorschauen: dort stehen echte
     App-Tabellen, die schmal in ihre Telefonansicht klappen wuerden. Diese zwei sind eigene
     Zeichnungen und stapeln sich -- mit 760px ragten sie 400px aus der Karte (gemessen 14.10.). */
  .ulh-vis-reise .ulh-vis-in, .ulh-vis-branded .ulh-vis-in { width: 100%; }
  .ulh-rs, .ulh-br { width: calc(100% - 16px); }
}

/* ---- WENIGER BEWEGUNG, DER REST (14.10., Audit) ----
   Die Uebergaenge, die bis heute keine Abschaltung hatten -- jeder mit .ulh-root davor, damit er
   die Regel, die er abschaltet, ueber die Spezifitaet schlaegt und nicht ueber die Reihenfolge
   (kein !important, CLAUDE.md 1). Die Unschaerfe des Donuts faellt mit weg: sie ist Bewegung. */
@media (prefers-reduced-motion: reduce) {
  .ulh-root .ulh-perf .uhm-root,
  .ulh-root .ulh-fen-teams .usn-teamrow,
  .ulh-root .ulh-fen-antwort .ulh-fen-view,
  .ulh-root .ulh-app .am-send,
  .ulh-root .ulh-app .vt-row,
  .ulh-root .ulh-preis-notiz,
  .ulh-root .ulh-fen-donut,
  .ulh-root .ulh-fen-donut.is-wechsel,
  .ulh-root .ulh-nav-pop,
  .ulh-root .ulh-nav-karte,
  .ulh-root .ulh-vis-tab .up-row,
  .ulh-root .ulh-vis-chancen .uo-row { transition: none; }
  .ulh-root .ulh-fen-donut.is-wechsel { filter: none; }
}
