/* upstreem core.css — geteilte Basis für alle Tabellen-/Chart-Komponenten.
   Klassen-Prefix: up-  |  Event-Namen bleiben pro Komponente (uut/udt).
   Enthält: Tokens+Farben, Icon-Buttons, Switcher, Dropdowns/Portal, Tooltips,
   Skeleton, Suche, Sticky-Header-Mechanik, generische Tabellenstruktur. */

/* Der ganze Bereich statt einzelner Staerken: Google liefert dafuer die VARIABLE
   Schnittdatei, und damit sind Zwischenwerte wie 550 wirklich verfuegbar. Mit einer Liste
   einzelner Staerken faellt jeder Zwischenwert auf die naechste vorhandene zurueck -- 550
   waere dann 600, und eine halbe Stufe liesse sich gar nicht setzen.

   400..700 UND NICHT MEHR 400..600 (21.09.). Der Bereich endete bei 600, aber an 15 Stellen
   stand 650, 700 oder 750 -- und ein Wert ausserhalb des geladenen Bereichs wird still auf
   den Rand gezogen. Diese 15 Stellen sahen also aus wie 600, obwohl sie mehr wollten, und
   zwei davon (die Treffermarkierung in Miras Auswahl und die in den Quick Actions) hatten
   sogar ZWEI Stufen -- 700 und 750 -- die beide als 600 herauskamen. Die Unterscheidung, die
   dort gebaut wurde, gab es auf dem Bildschirm nie.
   Kostet keine zweite Datei: es ist dieselbe variable Schnittdatei mit einem breiteren
   Bereich. */
/* Geist kommt von der Website selbst (lib/fremd.ts) */

/* Die Form des Globus als Maske -- EINMAL, damit sie nicht in jeder Regel steht. Dieselben Pfade
   wie UC.icon("globe"): Kreis, Meridian, Aequator. Gebraucht vom Rueckfall der Favicon-Kaesten
   (.up-fav weiter unten) und vom freistehenden Globus (.up-globus-frei).
   An :root und nicht an .up-root: der Rueckfall trifft auch Bilder in Karten, die am <body>
   haengen (Tooltips, Menues) -- dort loest eine Variable von .up-root nicht auf, und eine Maske
   mit unaufloesbarem Wert waere kein Globus, sondern ein volles Rechteck. */
:root {
      --up-globus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E");

  /* ================= DIE MASSE ==========================================================
     AN :root UND NICHT AN .up-root (21.09., nach einem selbst gebauten Fehler). Diese Werte
     haengen an KEINEM Thema und an keiner Akzentfarbe -- eine 12px bleibt eine 12px. Sie
     gehoeren deshalb an die Wurzel des Dokuments, nicht an die der Komponente.
     WARUM DAS WICHTIG IST: mehrere Teile der App haengen bewusst am <body> und damit
     AUSSERHALB von .up-root -- die Massenleiste der Prompts-Tabelle (position: fixed, sie
     muesste sonst gegen jeden transformierten Bubble-Container ankaempfen), Tooltips, Menues.
     Dort loest eine Variable aus .up-root nicht auf. Bei einer FARBE faellt das sofort auf;
     bei einer Kurve in einer transition-KURZSCHREIBWEISE ist die ganze Angabe ungueltig, und
     dann verschwindet nicht die Kurve, sondern die ANIMATION. Genau so sind am 21.09. das
     Ein- und Ausgleiten der Massenleiste und das Aufklappen ihrer Themenauswahl ausgefallen,
     nachdem ich ihre Uebergaenge auf var(--up-ease) umgestellt hatte -- vier Regeln, alle
     still.
     core macht es bei --up-globus direkt darueber aus demselben Grund genauso.
     WAS HIER NICHT STEHT: --up-e-* (die Tiefen). Die aendern sich mit dem Thema und stehen
     deshalb im naechsten Block, der eine dunkle Fassung hat -- dieser hier hat keine.

  /* Schrift. SIEBEN Stufen, und die Zahl ist nachgeschlagen, nicht geschaetzt: Polaris faehrt
     8 Stufen (12/14/16/20/24/28/32/40), Nathan Curtis (EightShapes) empfiehlt "mit wenigen
     anfangen und nur wachsen, wenn eine Komponente es wirklich verlangt", die Ratgeber fuer
     dichte Dashboards nennen 6.
     ERSTE FASSUNG DIESES BLOCKS HATTE SECHS UND WAR FALSCH: sie endete bei 22 und haette
     damit .up-ph-heading (28px, der Kopf ALLER SIEBEN Seiten) auf Dialogtitelgroesse
     gedrueckt -- eine sichtbare Entwertung der obersten Stufe der App. Die Hierarchie WAR
     da, sie war nur je Stufe uneinheitlich; das ist etwas anderes, als keine zu haben.
     KEIN MODULARES VERHAELTNIS. Die Stufen sind von Hand gesetzt, nach dem, was die App
     wirklich braucht -- ein Faktor wie 1.2 erzeugt an dieser Dichte Bruchteile (13.2, 15.8),
     die je Browser anders runden. Genau daher kommen die 216 halben Pixel im Bestand. */
  --up-fs-xs:  11px;   /* Nebenbeschriftung, Einheiten, Zaehler        (52) */
  --up-fs-s:   12px;   /* Tabellenzelle, Chip, Menueeintrag           (176) */
  --up-fs-m:   13px;   /* Fliesstext, Knopfbeschriftung               (164) */
  --up-fs-l:   14px;   /* Zeilentitel, Chart-Ueberschrift             (100) */
  --up-fs-xl:  16px;   /* Abschnittstitel, Seitenbeschreibung          (26) */
  --up-fs-2xl: 22px;   /* Dialogtitel UND die grosse Zahl einer Kennzahl -- in der App
                          heute 20, 21, 22, 23 und 23.5; eine Rolle, fuenf Werte    (22) */
  --up-fs-3xl: 28px;   /* .up-ph-heading, der Kopf jeder Seite              (core:3601) */
  --up-fs-display: 38px; /* DIE EINE grosse Zahl einer Karte -- die Aussage, nicht der
                          Rahmen daneben. Eigene Stufe, weil das eine eigene Rolle ist und
                          keine Ueberschrift: sie steht allein und wird nicht gelesen,
                          sondern abgelesen. Heute nur .upt-kpi-quota-used; ask-mira.css
                          traegt dieselbe Rolle auf 34px und zieht nach, wenn die Datei
                          drankommt.                                     (prompts-table) */
  /* AUSSERHALB DER SKALA, mit Grund: neun font-size in clamp() (auth-page, onboarding,
     ask-mira, prompt-research). Das sind Ueberschriften auf ganzseitigen Flaechen, die mit
     dem Fenster mitwachsen -- dieselbe Ueberlegung wie bei der Landingpage: eine ganze Seite
     ist kein Dashboard. .check_skala.py laesst clamp() deshalb ausdruecklich durch. */

  /* Schriftschnitt. Die vier Stufen, die die App traegt -- und alle vier liegen im
     geladenen Bereich (siehe @import oben); das war vor dem 21.09. bei 700 nicht so.
     SIE SIND KEINE AUSSCHLIESSLICHE LISTE. Geist wird als variable Schrift geladen, also
     rendert auch 450 oder 550 wirklich, und an 37 Stellen steht genau das -- als halbe
     Stufe zwischen zwei Nachbarn, bewusst gesetzt. Was NICHT geht, ist ein Wert ausserhalb
     von 400..700: der wird still auf den Rand gezogen und sieht dann aus wie der Nachbar.
     .check_skala.py prueft deshalb den BEREICH und nicht diese vier Zahlen. */
  --up-fw-n:  400;     /* Fliesstext, Nebentext                        (67) */
  --up-fw-m:  500;     /* der Normalfall der App                      (324) */
  --up-fw-h:  600;     /* Ueberschrift, aktiver Zustand               (172) */
  --up-fw-b:  700;     /* echtes Fett: Treffermarkierung, <strong>          */

  /* Radius. Sechs Stufen plus rund -- und DIE HAELFTE DER ARBEIT MACHT DIE KONZENTRIK.
     Nachgezaehlt ueber alle 763 Radiusangaben der App:
         443  liegen auf der Basis unten
         185  liegen auf Basis minus 1 -- das sind KEINE Abweichungen, sondern die
              konzentrischen Kinder: ein Element mit 1px Rahmen in einer 8er-Ecke braucht
              innen 7, sonst laeuft der Spalt zwischen beiden Rundungen ungleich breit.
              Die Regel steht seit langem im STYLEGUIDE (Zeile 424: "Logo-Box 24x24px,
              radius 8px, Bild 22x22px radius 7px"), sie hatte nur keinen Namen.
          69  sind voll rund
          35  sind wirklich krumm (3.23, 4.28, 7.04, 8.9 ...) -- nur DIE sind der Fehler,
              und sie kommen aus der Skalierungsregel in §1 ohne Raster zum Einrasten.
     DIE FORMEL, die die Fachliteratur uebereinstimmend nennt: innen = aussen minus Abstand.
     Also `calc(var(--up-r-m) - 1px)` bei 1px Rahmen -- oder eben 7px hingeschrieben, was
     dasselbe ist und lesbarer bleibt. .check_skala.py laesst Basis minus 1 durch. */
  --up-r-xs:   4px;    /* Punkt, Haken, Plakette -- das Kleinste       (66) */
  --up-r-s:    6px;    /* Chip, Pille, kleines Feld                    (64) */
  --up-r-m:    8px;    /* Knopf, Eingabe, Segment, Logoplatte         (180) */
  --up-r-l:   10px;    /* Kachel, Vorschaubild                         (35) */
  --up-r-xl:  12px;    /* Karte innen, Drawer-Kopf                     (58) */
  --up-r-2xl: 16px;    /* Chart-Box, Modal, grosse Karte               (31) */
  --up-r-3xl: 32px;    /* GROSSE WEICHE FLAECHE -- Miras Eingabefeld, ihr Auswahlpanel, die
                          schwebende Massenleiste. Nicht "Pille": diese Flaechen sind mehrere
                          hundert Pixel hoch, 32 ist dort eine bewusst weiche Ecke und kein
                          Rundlauf. Eigene Stufe statt Ausnahme, weil die Rolle ueber die App
                          verteilt rund zehnmal vorkommt.                        (10) */
  --up-r-voll: 999px;  /* alles, was ganz rund ist                     (69) */

  /* Hoehen der Bedienelemente. Vier Stufen, eine Zeilenhoehe -- und das ist die Leiter, die
     GitHub Primer unabhaengig davon fuehrt (24/28/32/40/48). Dass beide auf dieselben vier
     Zahlen kommen, ist der beste Hinweis darauf, dass sie richtig sind. */
  --up-h-chip: 24px;   /* .up-pille (Tarif, Status), Plaketten         (53) */
  --up-h-seg:  28px;   /* kleines Segment                              (47) */
  --up-h-btn:  32px;   /* Knopf, Eingabe, Werkzeugleiste              (112) */
  --up-h-gross: 40px;  /* Hauptknopf, Suchfeld                         (33) */
  --up-h-row:  55px;   /* Tabellenzeile -- steht so im STYLEGUIDE           */

  /* Bewegung. Drei Dauern, zwei Kurven -- nach dem, WAS sich bewegt, nicht wie weit.
     Die zwei Kurven sind die, die in der App schon am haeufigsten ausgeschrieben stehen. */
  --up-t-1: 120ms;     /* Farbe, Deckkraft, Hover -- reiner Zustand   (133) */
  --up-t-2: 200ms;     /* Groesse und Position                        (129) */
  --up-t-3: 260ms;     /* Flaechen: Drawer, Modal, Seitenwechsel       (16) */
  --up-ease:     cubic-bezier(.2, 0, .38, .9);   /* hin und zurueck   (13) */
  --up-ease-auf: cubic-bezier(.22, 1, .36, 1);   /* nur Auftritte      (9) */
  /* Der Auftritt einer ganzen Seite (11.10.): eigene, benannte Stufe AUSSERHALB der UI-Dauern.
     Eine Seite ist keine Schaltflaeche -- sie kommt in einem Zug mit Staffel, und unter 500ms
     liest sich das als Ruck statt als Ankommen. Die Kurve ist easeOutExpo, gemessen auf
     mobbin.com (dem Massstab des Nutzers fuer Bewegung): schnell da, lang auslaufend. Dort sind
     es 1s; in einer App, in der man zehnmal am Tag die Ansicht wechselt, sind 600ms richtig --
     dieselbe Dauer wie die Einfahrt der Diagramme, damit Seite und Chart in einem Zug ankommen. */
  --up-t-auftritt: 600ms;
  --up-ease-expo: cubic-bezier(.19, 1, .22, 1);
  /* easeOutQuart, wie Chart.js es fuer Linie und Ring faehrt (600ms, core.js makeLine und
     makeTypeChart). Fuer alles, was im selben Bild wie ein Diagramm einfaehrt (Balken). */
  --up-ease-quart: cubic-bezier(.25, 1, .5, 1);
  /* Der Lichtlauf ueber Skelett-Platzhaltern (.up-sk-*::after, upskt 1200ms). Als Token, seit ihn
     auch Bausteine ausserhalb von core brauchen (14.10.: die Geister-Chips in Prompt Research) --
     vorher stand der Verlauf nur hier, und jede Kopie waere eine eigene Farbe gewesen. */
  --up-sk-lauf: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.14) 50%, rgba(255,255,255,0) 100%);
}
/* ================= AUFTRITT VON SEITE UND ANSICHT (11.10.) ==================================
   Eine Seiten-Komponente (makeAuftritt in core.js, Klasse .up-seite) laesst ihren Inhalt warten,
   bis Stylesheets und erste Daten da sind -- hoechstens einige hundert Millisekunden. Bis dahin
   steht er unsichtbar, aber mit seiner vollen Hoehe da (kein Springen des Kopfs).
   Ansichten OHNE Seiten-Komponente bekommen den Auftritt hier, in derselben Kurve. Das ersetzt
   den viewfade aus dem Kopf-Skript (View-System v7): Deckkraft an der Ansicht, der Weg nur am
   Koerper ([id^="viewbody-"]) -- ein transform an der Ansicht selbst wuerde alles darin mit
   position:fixed waehrend der Bewegung verschieben. */
.up-auftritt-zu { opacity: 0; }
[id^="view-"].view-on:not(:has(.up-seite, .uvb-root, [data-up-vorab-seit], .uds-root, .ucs-root, .upi-root, .upf-root, .uop-root, .urs-root, .utp-root, .ubr-root, .uss-root)) { animation: up-ansicht-auf var(--up-t-auftritt) var(--up-ease-expo); }
[id^="view-"].view-on:not(:has(.up-seite, .uvb-root, [data-up-vorab-seit], .uds-root, .ucs-root, .upi-root, .upf-root, .uop-root, .urs-root, .utp-root, .ubr-root, .uss-root)) [id^="viewbody-"] { animation: up-ansicht-weg var(--up-t-auftritt) var(--up-ease-expo); }
/* Die Seiten werden an ihrer WURZEL erkannt (uds, ucs, upi, upf, uop, urs, utp, ubr), nicht nur an
   .up-seite (11.10.): die Wurzel steht von Anfang an im Markup, .up-seite setzt erst die Seite beim
   Einrichten -- rund 40ms nach dem Einblenden. In dieser Luecke lief der Auftritt der ganzen
   Ansicht an und brach dann ab: die Ansicht sprang auf sichtbar, gleich danach ging der Inhalt fuer
   den eigenen Auftritt wieder auf unsichtbar ("flasht ein paar Mal auf und zu"). */
/* Der Platzhalter (.uvb-root, core.js seitenVorab) blendet sich selbst ein, und die Seite danach
   uebernimmt seine Bewegung (data-up-vorab-seit, 13.10. abends) -- ein Einblenden der ganzen
   Ansicht dazu liefe gegen beide, und eine Seite ohne eigenen Auftritt (Mira, Events) blendete
   beim Austausch noch einmal von unsichtbar ein: der Kopf blinkte. */
/* Ansichten MIT Seiten-Komponente: keine Bewegung an der Ansicht -- die Seite bewegt ihre Teile
   selbst, und zwei Auftritte uebereinander laufen gegeneinander. Ueber Spezifitaet (0,3,0 bzw.
   0,4,0 gegen die 0,2,0 bzw. 0,3,0 des Kopf-Skripts), damit es auch gilt, solange der Kopf seine Zeilen noch hat. */
[id^="view-"].view-on:has(.up-seite, .uvb-root, [data-up-vorab-seit], .uds-root, .ucs-root, .upi-root, .upf-root, .uop-root, .urs-root, .utp-root, .ubr-root, .uss-root), [id^="view-"].view-on:has(.up-seite, .uvb-root, [data-up-vorab-seit], .uds-root, .ucs-root, .upi-root, .upf-root, .uop-root, .urs-root, .utp-root, .ubr-root, .uss-root) [id^="viewbody-"] { animation: none; }
@keyframes up-ansicht-auf { from { opacity: 0; } }
@keyframes up-ansicht-weg { from { transform: translateY(10px); } }
@media (prefers-reduced-motion: reduce) {
  [id^="view-"].view-on:not(:has(.up-seite, .uvb-root, [data-up-vorab-seit], .uds-root, .ucs-root, .upi-root, .upf-root, .uop-root, .urs-root, .utp-root, .ubr-root, .uss-root)) [id^="viewbody-"] { animation: none; }
}

/* ================= TIEFEN UND UMGEKEHRTE KARTE, AUCH AUSSERHALB EINER WURZEL ================
   (28.09.) Die Tiefen standen bis heute nur an .up-root. Was am <body> haengt -- Erklaerkarte,
   Tooltip, die Menues und Leisten mit eigener Palette --, erreichte sie nicht: dort loeste
   var(--up-e-2) ins Leere auf, und es galt der Rueckfallwert, der an jeder Stelle ein anderer
   war (.28, .22, .20, .16). Gemessen an der Erklaerkarte: am <body> 0 12px 32px .28, dieselbe
   Karte im Portal von create-with-ai 0 10px 28px .14 -- ein Bauteil, zwei Schatten, je nachdem,
   wo es haengt.
   Deshalb stehen die Werte jetzt HIER, an :root und an .up-root zugleich, dunkel an jedem
   [data-theme="dark"] -- dieselbe Loesung wie bei den vier Schriftstufen (24.09.), aber mit EINER
   Quelle: im Block von .up-root stehen sie nicht noch einmal.
   .up-root[data-theme="dark"] (0,2,0) steht in der dunklen Liste, damit an einer Wurzel die
   Spezifitaet entscheidet und nicht die Reihenfolge -- .up-root aus der hellen Liste ist (0,1,0).
   [data-theme="light"] steht in der hellen Liste, damit ein Element, das sein Thema selbst traegt
   (die Erklaerkarte, ein Menue), auch in einem dunklen Vorfahren hell aufloest statt zu erben. */
:root, .up-root, [data-theme="light"] {
      /* Tiefe. Vier Stufen, EINE Schattenfarbe -- es aendern sich nur Versatz und Unschaerfe.
         Vier ist auch, was Atlassian fuehrt (sunken, default, raised, overlay); Material und
         Carbon fahren eine 0..24-Leiter, die aber fuer eine App dieser Groesse Zierrat ist.
         --up-dd-shadow bleibt daneben bestehen: die Menues tragen ihn seit langem, und
         e-2 ist absichtlich derselbe Ton, damit beides nebeneinander stimmt.
         DIE ROLLEN (28.09., beim Aufraeumen von 42 Einzelwerten festgelegt):
             e-1  liegt auf der Flaeche: Knopf, Feld, Regler, Chip, Kachel
             e-2  haengt an einem Ausloeser: Menue, Popover, Tooltip, Erklaerkarte, Hover-Anhub
             e-3  schwebt frei ueber dem Inhalt: Leiste, hervorgehobener Kasten
             e-4  liegt ueber einem Schleier: Dialog, Palette, Benachrichtigung */
      --up-e-1: 0 1px 2px rgba(0,0,0,.06);
      --up-e-2: 0 10px 28px rgba(0,0,0,.14);   /* = --up-dd-shadow, hell */
      --up-e-3: 0 14px 34px rgba(0,0,0,.16);
      --up-e-4: 0 20px 48px rgba(0,0,0,.20);
      /* DIE FUENFTE TIEFE: SEITLICH (23.09.). Die vier Stufen darueber werfen nach UNTEN --
         fuer eine Schublade, die von rechts einfaehrt, ist das die falsche Richtung. Der Wert
         stand bis heute vier Mal einzeln in den Dateien (Opportunity-Karte, Prompt Research,
         Onboarding-Hilfe, Mira als Drawer), zweimal mit .15 und zweimal mit .18 -- vier
         Schubladen, die nebeneinander nie zu sehen sind und deshalb auseinanderliefen. */
      --up-e-drawer: -12px 0 32px rgba(0,0,0,.15);
      /* DIE SECHSTE TIEFE: DER GEHOBENE CHIP (28.09.). Zwei Lagen, und das ist keine Verzierung:
         die dichte Lage (1px) zeichnet die Kante, die weiche (4px) hebt den Chip ab -- eine Lage
         kann nur eines davon. Der Grund des Chips ist dafuer auf die Haelfte zurueckgenommen
         (quick-actions.css, .mqa-chip): der Schatten TRAEGT die Abgrenzung. e-1 hat eine Lage bei
         6 Prozent und haette den Chip auf der Eingabezeile verschwinden lassen.
         Der Wert stand achtmal einzeln da (core .up-entchip.is-lifted, quick-actions .mqa-chip und
         .mqa-mini, je hell und dunkel). Die kleine Fassung hatte die weiche Lage einen Pixel enger
         (1px 3px) -- sie traegt jetzt denselben Wert; "nicht staerker schweben als der grosse",
         die Bedingung von damals, haelt damit weiterhin. */
      --up-e-chip: 0 1px 1px rgba(0,0,0,.13), 0 2px 4px rgba(0,0,0,.07);
      /* DIE UMGEKEHRTE KARTE (28.09.): Grund und Tinte der Erklaerkarte (.up-explain), die sich
         gegen das Thema dreht -- hell eine dunkle Karte, dunkel eine helle. Sie stand fuenfmal
         als Literal da (core, domains-table, visibility-chart, quick-actions, create-with-ai),
         je mit eigener Dunkel-Regel.
         Kein vorhandenes Token trifft sie: --vc-text (#1f1f1b / #e0e0e0) waere als Grund im
         Dunkeln 14 Stufen grauer, --vc-text-invert (#e0e0e0 / #1f1f1b) als Tinte im Hellen 31
         Stufen matter als das Weiss der Ueberschrift. Beides haette die Karte sichtbar veraendert.
         Die helle Beispielplatte IN der Karte ist dieselbe Umkehrung noch einmal: ihr Grund ist
         die Tinte, ihre Schrift der Grund. Dafuer braucht es kein drittes Token. */
      --vc-inverse-bg: #1f1f1b;
      --vc-inverse-ink: #ffffff;
      /* DER TOAST (10.10., Handoff Stil 1a "Inverted"): die umgekehrte Karte wie die Erklaerkarte --
         hell eine dunkle Flaeche, dunkel eine helle (10.10. korrigiert: "invertiert heisst, im
         Darkmode hell"; die erste Fassung blieb nach dem Handoff in beiden Themen dunkel).
         Der Zustand steht nur im Zeichen. Auf der dunklen Flaeche ist das Fehler-Rot heller als
         --vt-down: #d25d5d erreicht auf #1f1f1b nur 3.9:1, #ff7a66 traegt. Erfolg ist dort das
         --vt-up des dunklen Themas; auf der hellen Flaeche (Dunkel-Thema) die Werte des hellen. */
      --vc-toast-bg: var(--vc-inverse-bg);
      /* Die Schrift ist die des ANDEREN Themas (10.10.: "immer in Fontfarbe des je anderen
         Themes, nicht weiss und schwarz"): Titel #e0e0e0 (--vc-text dunkel), Untertitel und
         Zeichen #a0a0a0 (--vc-third dunkel), das X #6c717a (--vc-fourth dunkel). Das dunkle
         Thema unten traegt spiegelbildlich die hellen Werte. */
      --vc-toast-ink: #e0e0e0;
      --vc-toast-sub: #a0a0a0;
      --vc-toast-ok: #60d25d;
      --vc-toast-fehler: #ff7a66;
      --vc-toast-neutral: #a0a0a0;
      --vc-toast-flaeche: rgba(255,255,255,.10);
      --vc-toast-flaeche-hover: rgba(255,255,255,.18);
      --vc-toast-zu: #6c717a;
}
[data-theme="dark"], .up-root[data-theme="dark"] {
      /* Die vier Tiefen im dunklen Thema. Sie sind NICHT die hellen mit mehr Deckkraft, auch
         wenn die Zahlen so aussehen: auf dunklem Grund traegt ein Schatten kaum, deshalb liegt
         die Arbeit dort beim helleren Rahmen (--vc-border, --up-menu-border), und der Schatten
         setzt nur die Kante ab. Mehr als .32 macht daraus einen Fleck.
         Die Masse selbst -- Schrift, Radius, Hoehe, Dauer -- stehen NICHT noch einmal hier:
         die aendern sich mit dem Thema nicht, und ein zweiter Satz waere ein zweiter Ort zum
         Auseinanderlaufen. */
      --up-e-1: 0 1px 2px rgba(0,0,0,.24);
      --up-e-2: 0 10px 28px rgba(0,0,0,.32);
      --up-e-3: 0 14px 34px rgba(0,0,0,.38);
      --up-e-4: 0 20px 48px rgba(0,0,0,.46);
      /* Dunkel kraeftiger, dieselbe Staffelung wie bei den vier Stufen darueber. */
      --up-e-drawer: -12px 0 32px rgba(0,0,0,.42);
      --up-e-chip: 0 1px 1px rgba(0,0,0,.24), 0 2px 4px rgba(0,0,0,.16);
      /* Die Erklaerkarte im Dunkeln: heller Grund, die Schrift im Ton der hellen Karte. */
      --vc-inverse-bg: #eeedef;
      --vc-inverse-ink: #1f1f1b;
      /* Der Toast als helle Karte (umgekehrt, siehe oben): dunkle Schrift, Zustandsfarben des
         hellen Themas, die Flaechen der Knoepfe aus Schwarz statt aus Weiss. */
      --vc-toast-bg: var(--vc-inverse-bg);
      --vc-toast-ink: var(--vc-inverse-ink);
      --vc-toast-sub: #6b6f78;
      --vc-toast-ok: #2ea84a;
      --vc-toast-fehler: #b0200c;
      --vc-toast-neutral: #6b6f78;
      --vc-toast-flaeche: rgba(31,31,27,.08);
      --vc-toast-flaeche-hover: rgba(31,31,27,.14);
      --vc-toast-zu: #80858e;
}
.up-root {
      /* --vc-muted ist um vier Einheiten dunkler als vorher (#6f737c), und das ist KEINE neue
         Schriftfarbe, sondern der Ausgleich fuer den Grund, den ich verschoben habe: auf Weiss
         stand dieser Ton bei Kontrast 4.75, auf der neuen Karte (#f9f8f9) waren es 4.49 -- einen
         Hundertstel unter dem, was AA verlangt. #6b6f78 ist derselbe Blaustich in derselben
         Helligkeitsfamilie und bringt die 4.75 zurueck, auf der Kopfzeile (#f4f2f4) noch 4.52.
         Wer den alten Wert buchstabengleich zurueckwill, aendert diese eine Stelle -- dann steht
         gedaempfter Text auf Karten aber unter AA.
         Die anderen beiden Schriftfarben sind unveraendert. --vc-third liegt mit 2.53 auf der
         Karte unter 3, war aber schon auf Weiss bei 2.68: das ist Beiwerk (Platzhalter, Rauten,
         Einheiten) und war nie AA-Text. */
      /* ---- DIE VIER SCHRIFTSTUFEN (24.09.) --------------------------------------------------
         Vorher waren es drei, und sie waren keine Reihe: hell ging es 16.5 -> 5.0 -> 2.7, also
         von sehr dunkel mit einem Sprung auf mittelgrau und dann unter die Wahrnehmungsgrenze.
         2.68 gegen Weiss reisst beide Schwellen (4.5 fuer normalen, 3.0 fuer grossen Text) --
         und die Stufe stand nicht auf grossem Text: von 248 Vorkommen lagen 53 auf 12px, 22 auf
         13px, 12 auf 11px. Im Dunkelmodus war dieselbe Stufe mit 4.30 in Ordnung; die zwei
         Themen waren also verschieden gebaut.
         Jetzt vier Sprossen in gleichmaessigen Schritten, nach dem Vorbild von Linear (dort
         14.3 / 10.3 / 5.0 / 3.7 im Hellen und 18.7 / 13.6 / 6.1 / 3.5 im Dunklen -- am 24.09.
         aus der laufenden App ausgelesen, nicht aus einer Token-Liste abgeschrieben, die
         gefundenen Listen geben den Hellmodus falsch wieder):
             --vc-text    16.53 / 12.88    Ueberschriften, Fliesstext
             --vc-muted   10.25 / 10.47    zweite Ebene: Beschriftungen, Werte neben dem Text
             --vc-third    5.04 /  6.50    Metadaten, Hilfstexte -- der alte --vc-muted-Ton
             --vc-fourth   3.71 /  3.47    Zaehler, Zeitstempel, inaktive Zustaende
         Die vierte Stufe unterschreitet 4.5 mit Absicht; bei Linear tut sie das auch. Sie
         traegt nur, was man ueberlesen darf. Was Inhalt ist, steht auf Stufe drei oder hoeher.
         ZWEITE ACHSE, noch offen: der Schnitt. Linear unterscheidet 12px/510 in der zweiten von
         12px/400 in der dritten Stufe -- dieselbe Groesse, zwei Ebenen. Die Schnittskala
         (400-700) gibt es hier auch, sie ist nur noch nicht an die Farbe gekoppelt. */
      --vc-text: #1f1f1b; --vc-muted: #3e4146; --vc-border: #e4e2e4;
      /* Die Primaerfarbe des ANDEREN Themas -- also genau der Wert, der zwei Bloecke weiter unten
         als --vc-text steht. Gebraucht fuer Zeichen AUF einer Flaeche in der Primaerfarbe: die
         16px-Plaettchen der Mira-Chips (16.09. angefordert). Kein Weiss und kein --vc-bg: auf
         #1f1f1b soll dasselbe gedaempfte Hell sitzen, das im dunklen Thema die Schrift traegt,
         sonst sticht das Zeichen heller heraus als jeder Text der App. */
      --vc-text-invert: #e0e0e0;
      /* ---- DIE AKZENTFARBE (18.09.) -------------------------------------------------------
         Bis hierher gab es sie nicht: was wie eine Primaerfarbe aussah, war --vc-text, also die
         SCHRIFTFARBE der App. Gezaehlt vor dem Umbau: 578 Vorkommen, davon 475 als color. Ein
         Umdefinieren haette die ganze Schrift umgefaerbt, deshalb ein eigenes Paar.
         --up-accent ist die Flaeche, --up-accent-fg das, was darauf liegt. Die Vorgabe ist
         buchstabengleich das, was vorher dastand (Flaeche --vc-text, Schrift --vc-bg) -- wer
         nichts einstellt, sieht keinen Unterschied.
         WAS NICHT AKZENT IST, und das ist Absicht: die Platte hinter Logos und Favicons
         (--vt-logo-bg), Zaehlerplaketten und Punkte, die Hintergrundgrafik, das Avatarrund, der
         Toast und die leisen Hover-Toene der Fensternavigation. Die bleiben neutral, egal was
         eingestellt ist. */
      --up-accent: #1f1f1b; --up-accent-fg: #ffffff; --up-accent-ink: #1f1f1b;
      --vc-bg: #ffffff; --vc-heading-bg: #f9f8f9; --vc-sk: #eeedef; --up-surface: #ffffff;
      /* ---- DIE HELLE LEITER (07.09.) --------------------------------------------------------
         Dieselbe Quelle und dieselbe Zuordnung wie die dunkle Leiter weiter unten, nur laeuft sie
         hier nach unten statt nach oben. Von linear.app gelesen, helles Thema:
             bg-primary #ffffff  >  bg-secondary #f9f8f9  >  bg-tertiary #f4f2f4
             bg-quaternary #eeedef  >  bg-quinary #e9e8ea
             border-primary #e9e8ea   border-secondary #e4e2e4   border-tertiary #dcdbdd
         IM HELLEN DREHT SICH DIE ERHOEHUNG UM, und das ist der eigentliche Unterschied zum
         Dunkeln: dort ist "liegt hoeher" heller, hier ist die Seite schon Weiss -- ueber Weiss
         gibt es nichts. Die Leiter geht also von oben nach unten, und die oberste Sprosse teilen
         sich Seite, Karte und Menue.
         JEDE ROLLE EINE STUFE HOEHER ALS AM VORMITTAG. Erst standen Karte auf bg-secondary und
         Kopfzeile auf bg-tertiary; gemeldet wurde "die Tabellen, Cards und Kopfzeilen sind mir
         etwas zu dunkel". In Linears hellem Spektrum gibt es dafuer genau einen Schritt, naemlich
         den naechsten nach oben -- zwischen bg-secondary und Weiss liegt nichts mehr. Also:
             Karte #ffffff   Kopfzeile #f9f8f9   Auswahl #f4f2f4   Skelett #eeedef
         Der Preis steht hier, damit ihn niemand spaeter suchen muss: die Karte hat damit
         DENSELBEN Wert wie die Seite und traegt sich allein ueber ihren Rahmen (#e4e2e4, 45
         Helligkeitsstufen unter Weiss -- deutlich sichtbar). Wer die Karte wieder schweben lassen
         will, setzt die Seitenfarbe in Bubble auf #f9f8f9 und --up-surface hier ebenso; dann
         rutscht die ganze Leiter um eine Sprosse und alles andere bleibt, wie es ist.
         WAS DIESER SCHRITT GERETTET HAT: die Kopfzeile jeder Tabelle stand vorher auf einem
         30-Prozent-Wert (auf Weiss #fcfcfc, Kontrast 1.01 zur Karte) und war schlicht nicht da.
         Jetzt ist sie #f9f8f9 auf einer weissen Karte, also 15 Stufen -- leise, aber vorhanden.
         Menues und Tooltips bleiben auf #ffffff. Was oben liegt, muss sich abheben; da hier
         alles Weiss ist, machen das Rahmen und Schatten, so wie es im Hellen ueblich ist.
         Die SCHRIFTFARBEN bleiben unveraendert (#1f1f1b / #6b6f78 / #9e9e9e) -- ausdruecklich so
         angefordert, genau wie im Dunkeln. Linears Toene sind #282a30 / #6f6e77 / #86848d, also
         etwas blaeulicher und in der dritten Stufe deutlich dunkler; das waere eine eigene
         Entscheidung. */
      /* One dropdown panel colour and one selected-row colour for the WHOLE app. Every component
         that draws its own menu reads these instead of picking something close -- that drift is
         exactly why the panels stopped matching each other.
         --up-sel-bg war einmal halbtransparent, damit die Flaeche darunter durchscheint. Das ist
         seit dem 07./08.09. in beiden Themen ein DECKENDER Wert aus Linears Leiter: eine Auswahl
         ist eine eigene Stufe, und zwei uebereinanderliegende Halbtransparenzen ergaben je nach
         Untergrund einen dritten Ton. */
      /* DIE AUSWAHL WAR HELLER ALS DIE KOPFZEILE, und damit lief die helle Leiter rueckwaerts:
         rgba(245,245,245,0.55) ergibt auf einem weissen Menue #fafafa -- Helligkeit 243.8, also
         elf Stufen UEBER --vc-heading-bg (232.8). Eine gewaehlte Zeile war damit heller als der
         Kopf darueber und gegen das weisse Panel praktisch unsichtbar (Kontrast 1.02).
         Jetzt Linears bg-quaternary, dieselbe Rolle und derselbe Wert wie im Dunkeln (#28282c
         dort): eine Stufe UNTER der Kopfzeile, deckend statt halbtransparent. Im Dunkeln stand
         die Auswahl schon am 08.09. deckend da -- hier war es noch der alte Aufbau.
         Gemessen mit _h_helle_leiter.html. */
      --up-menu-bg: #ffffff; --up-sel-bg: #f4f2f4; --up-menu-border: #dcdbdd;
      /* Der Topic-Chip "Dot" (06.10., Topic-Handoff): Grund und Rand je Zustand. Gewaehlt ist
         --up-sel-bg hell (#f4f2f4); die drei uebrigen Stufen gab es noch nicht. */
      --up-chip-rand-hover: #cfcdd0; --up-chip-an: #f4f2f4; --up-chip-an-hover: #eeedef; --up-chip-rand-an-hover: #b9b7bb;
      /* Event-Pins (Design-Handoff 02.10.): der Rand des Pins und die Hilfslinie, die die Vorgabe
         mit eigenen Werten fuehrt -- #CDD1D6 und #9AA1AB (dieselbe Stufe auch fuer die leiseste
         Schrift der Gruppenkarte, #9CA3AF, drei Werte daneben). Alles andere der Vorgabe liegt
         auf vorhandenen Marken, die um ein bis drei Werte abweichen: #1F2328 = --vc-text,
         #3F4652 = --vc-muted, #6B7280 = --vc-third, #E5E7EB = --vc-border, #F5F5F5 = --up-sel-bg. */
      --up-evm-rand: #cdd1d6; --up-evm-linie: #9aa1ab;
      /* Dropdown-Geometrie. Vorher hatte jedes Panel eigene Werte -- 12 vs 14 Radius, 6 vs 8
         Padding, 6 vs 8 Abstand zum Trigger. Keiner davon war eine Design-Entscheidung, es waren
         Kopien die auseinandergelaufen sind. Eine Zahl je Groesse, hier. */
      --up-dd-radius: 14px; --up-dd-pad: 8px; --up-dd-gap: 6px;
      /* Der Abstand NUR zwischen einem Dropdown und seinem Untermenue. Eigener Wert, weil
         --up-dd-gap auch den Abstand vom Trigger zum Panel setzt -- der bleibt 6. */
      --up-sub-gap: 4px;
      --up-dd-shadow: 0 10px 28px rgba(0,0,0,.14);
      --vc-switch-bg: #f9f8f9; --vc-switch-border: #e4e2e4; --vc-third: #6b6f78; --vc-fourth: #80858e;
      /* Die PILLE auf der Schiene -- das Gegenstueck zu --vc-switch-rail. Sie stand als
         Literal an sieben Stellen, jede mit einer eigenen Dunkel-Regel daneben (#ffffff und
         #28282c). Eine Marke statt vierzehn Werten; der Rahmen kam schon immer aus
         --vc-switch-border. Wer eine eigene Palette aufmacht, muss sie mitfuehren -- das sind
         genau die Geltungsbereiche, die auch --vc-switch-border setzen (fuenf, gezaehlt). */
      --vc-switch-pill: #ffffff;
      /* Die SCHIENE eines Schalters -- ein eigener Token und nicht --vc-switch-bg, der die
         Flaeche eines Segmentumschalters meint. Als TOKEN und nicht als fester Wert in der
         Regel: ein fester Wert erbt nicht ueber eine Attributgrenze, und der Schalter steht
         auch in Menues, die core selbst mit data-theme markiert. Genau daran stand er am
         08.09. hell im dunklen Menue. */
      --up-switch-off: #d6d6d4;
      /* DAS BLAU DER APP und sein Lila. --up-blau ist der Ton des "You"-Chips der Mitgliederliste
         (team-orga.css .uto-you) und des Chips der Landingpage -- also der eine Blauton, den die
         App schon hat, statt eines zweiten daneben.
         --up-lila ist daraus GERECHNET und nicht gewaehlt: in OKLCh liegt das Blau auf
         L=0.6231 C=0.1880 H=259.8; das Lila dreht den Farbton um 40 Grad auf H=299.8 und laesst
         Helligkeit und Saettigung unangetastet. Genau deshalb hat der Verlauf zwischen den
         beiden keinen Knick -- nachgerechnet: die Chroma wird auf dem ganzen Weg nicht
         beschnitten, und bei 40 Grad ist es noch Lila und nicht schon Magenta (das beginnt bei
         etwa H=330).
         Beide Toene sind in hell UND dunkel dieselben. Eine Flaeche in dieser Saettigung traegt
         auf beiden Gruenden, und weiss darauf misst 3.68 bis 3.92 -- ueber den 3.0, die WCAG
         1.4.11 fuer ein Bedienelement verlangt. Ein zweiter Satz Werte waere eine Aussage ohne
         Anlass. */
      --up-blau: #3b82f6;
      --up-lila: #9a65e6;
      /* DIE MITTE DES BOGENS, und sie ist noetig. Ein Verlauf zwischen zwei Farben rechnet der
         Browser in sRGB, und diese Gerade schneidet den Bogen, den OKLCh zwischen ihnen zieht:
         die Mitte landet auf #6a74ee, also L 0.6131 statt 0.6231 und Chroma 0.1809 statt
         0.1880 -- knapp vier Prozent Saettigung weniger, genau in der Mitte, genau dort, wo man
         hinsieht. Mit diesem Stopp bleibt die Abweichung auf dem ganzen Weg unter einem
         Prozent. Derselbe Ton, nur bei H=279.8 statt 299.8. */
      --up-lila-halb: #7473f4;
      /* UND EIN EIGENES PAAR FUER TEXT. Zwei Messskalen, wie ueberall in dieser Bibliothek:
         eine FLAECHE wird nach WCAG 1.4.11 beurteilt (3.0 reicht), ein WORT nach AA (4.5).
         --up-blau und --up-lila sind Flaechentoene und schaffen als Text keine 4.5 -- gerechnet
         auf dem Eingabefeld: hell 3.68 und 3.90, dunkel 3.90 und 3.68.
         Diese hier drehen nur die HELLIGKEIT, der Farbton bleibt (259.8 und 299.9), also ist es
         sichtbar dieselbe Familie. Hell auf #ffffff: 4.72 und 4.63. Dunkel steht weiter unten. */
      --up-blau-text: #286fe1;
      --up-lila-text: #8e58d8;
      /* DAS UPSTREEM-TEAM (28.09.): die Marke an Mitgliedern mit einer @upstreem.ai-Adresse in
         team-orga -- "neutral, aber eindeutig, eine Farbe, die wir so noch nirgends haben". Warmes
         Taupe: kein Signalton (nicht Rot, Gruen, Gelb, Blau), kein Violett oder Indigo (zwei der
         Akzentfarben), und bewusst WARM, damit es nicht mit dem kuehlen Grau der leisen Schrift
         (--vc-third #6b6f78) verwechselt wird. Die Braeune der Typpaletten (seit 13.10. die
         Familie commercial, Schrift #7B461E auf #FFF4EC) sind orange -- das hier ist entsaettigt.
         Gerechnet: Schrift #76634d auf der Flaeche #f2ede6 4.8:1, ueber AA fuer 11px. */
      --vc-staff-ink: #76634d; --vc-staff-bg: #f2ede6;
      /* DIE SCHIENE EINES UMSCHALTERS -- im Hellen ein einziger, DECKENDER Ton, fuer jede Groesse
         und jeden Grund. (Im Dunkeln seit dem 27.09. nicht mehr: dort hebt sie ihren Grund an,
         Begruendung im dunklen Block.) Zwei Fassungen liegen dahinter. Bis zum 07.09. stand hier
         ein aufgehellter Ton, und Aufhellen heisst, sich der Pille zu naehern, denn die Pille ist
         der hellere Teil: auf Weiss 4.1 Stufen, im dunklen Menue 3.0. Gemeldet als "die Switcher in allen
         Dropdowns brauchen andere Farben, im Darkmode sind sie kaum erkennbar". Danach eine
         schwarze Halbtransparenz -- die loeste den Kontrast, brachte aber je Grund einen anderen
         Ton und im Dunkeln eine zweite Fassung nur fuer Menues.
         Seit dem 22.09. ein fester Wert, gemessen gegen die Pille (#ffffff) und gegen die beiden
         hellen Gruende (Flaeche #ffffff, Kopfzeile #f9f8f9), rohe Kanaele mit Augengewichten:
             zur Pille 17 Stufen, zur Flaeche 17, zur Kopfzeile 11.
         Der Wert war erst #efedef (17 Stufen unter Weiss) und ist am 22.09. auf Anforderung
         aufgehellt worden -- "mir sind die so zu dunkel". #f2f2f2 ist GENAU der Ton, den die
         durchscheinende Fassung auf weissem Grund gerendert hat, also der Stand, den die App
         vor dem 22.09. gezeigt hat:
             zur Pille 13 Stufen, zur Flaeche 13, zur Kopfzeile 7.
         Auf der Kopfzeile (#f9f8f9) ist das die schwaechste Stelle -- vorher waren es dort 12,
         weil sich der Ton mit dem Grund mitbewegte. Das ist der Preis des festen Werts und war
         die bewusste Wahl. Noch heller ginge nur bis #f9f8f9 selbst, dann steht die Schiene auf
         der Kopfzeile gar nicht mehr und zur Pille bleiben 6 Stufen. */
      --vc-switch-rail: #f2f2f2;
      /* Die Flaeche eines Eingabefelds, SOLIDE. Es gab sie bisher nur als 60%-Variante
         (--vc-switch-bg im Dunkeln, --up-sel-bg, --vt-row-hover) -- und wo ein Chip auf einer Karte
         liegt, die selbst schon getoent ist, addieren sich zwei Halbtransparenzen zu einem dritten
         Ton. Deshalb ein eigenes Token mit dem Vollwert. */
      --vc-field: #f9f8f9;
      /* DECKEND UND AUF DER MENUESTUFE, wie im Dunkeln (#232326 an allen drei). Vorher waren es
         drei verschiedene Halbtransparenzen von #f5f5f5 -- 30, 75 und ein eigener Vollwert --,
         die auf Weiss #fcfcfc, #f8f8f8 und #f7f7f7 ergaben: drei Toene fuer zwei Rollen, und der
         Tabellenkopf mit 1.01 Kontrast zur Karte praktisch nicht vorhanden. Auf der neuen Karte
         (#f9f8f9) waere er ganz verschwunden.
         --vt-logo-bg bleibt Weiss: die Platte hinter einem Favicon liegt AUF der Karte, ist also
         eine Stufe hoeher -- dasselbe, was im Dunkeln #e0e0e0 dort tut. */
      --vt-head-bg: #f9f8f9; --vt-logo-bg: #ffffff; --vt-row-hover: #f9f8f9;
      /* Der Anfangsbuchstabe AUF dieser Platte, wenn das Logo fehlt (28.09.). Die Platte ist in
         BEIDEN Themen hell (#ffffff / #e0e0e0), der Buchstabe stand aber auf --vc-muted -- im
         Dunkeln #c9cbd0 auf #e0e0e0, gemessen 1.2:1, also unsichtbar. Hier im Hellen bleibt es
         --vc-muted (nichts aendert sich), im Dunkeln unten --vc-text-invert: das ist die Tinte
         fuer Zeichen auf #e0e0e0, genau diese Flaeche. power-dashboard hatte dasselbe schon
         einmal fuer sich allein geflickt (.upw-chip-ic, --vc-bg). */
      --vt-logo-ink: var(--vc-muted);
      --vt-row-hover-solid: #f9f8f9;
      --vt-up: #2ea84a; --vt-down: #b0200c;
      /* Die Flaeche eines Gefahr-Knopfs (Loeschen im Dialog, 10.10.: "schoen rot") und die Schrift
         darauf. Dasselbe Rot wie --vt-down hier, und zwar in BEIDEN Themen: das hellere Rot des
         dunklen Themas (#d25d5d) traegt weisse Schrift nur mit rund 3,4:1, dieses mit 6,6:1. Darum
         steht das Paar nur hier und nicht noch einmal im dunklen Block. */
      --vc-danger: #b0200c; --vc-danger-fg: #ffffff;
      /* Markenauszeichnung im Antworttext. Die Farbe ist die der alten Response-Elemente,
         wortgleich uebernommen -- Orange, nicht das Gruen der Trendpfeile: eine Marke im Text ist
         keine Bewertung. Der Hintergrund liegt 10 Prozentpunkte unter dem ersten Ansatz (8 -> 6,
         hover 15 -> 12), damit der Absatz beim Lesen nicht gestreift wirkt. */
      --vt-brand: rgba(255, 165, 0, 1);
      --vt-brand-bg: color-mix(in srgb, var(--vt-brand) 4%, transparent);
      --vt-brand-bg-hover: color-mix(in srgb, var(--vt-brand) 9%, transparent);
      --vt-brand-border: color-mix(in srgb, var(--vt-brand) 55%, transparent);
      --vt-brand-border-hover: var(--vt-brand);
      --vc-hover-border: #6f6f6f;
      /* Fokusfarbe der Eingabefelder. Der Ring ist derselbe Ton, nur transparent -- eine Farbe,
         zwei Deckkraefte. --up-focus-w steht hier und nicht in den Regeln, weil die Ringbreite
         sonst in zehn Dateien einzeln stuende und beim naechsten Mal auseinanderliefe. */
      --up-focus: #4A9EFF; --up-focus-ring: rgba(74,158,255,0.20); --up-focus-w: 2px;
      /* Kaestchen: EIN Timing fuer alle Haken der App. Die Anmeldeseite hatte es zuerst (dort
         --uau-t), die Tabellen und die Dropdown-Haken hatten gar keinen Uebergang und sprangen.
         Zwei Werte an einer Stelle, damit sie nicht wieder auseinanderlaufen. */
      --up-cb-t: 140ms; --up-cb-ease: cubic-bezier(.4, 0, .2, 1);
      /* Sticky-Header: Abstand von oben in px = Hoehe der Floating-Topbar. Wird auch ueber
         data-sticky-top am Root gesetzt (siehe oben bei den dynamischen Werten). */
      --up-sticky-top: 171px; --up-thead-off: 32px;
      display: flex; flex-direction: column; position: relative;
      font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
      color: var(--vc-text); width: 100%; box-sizing: border-box; -webkit-font-smoothing: antialiased;
    }
/* DIE EINE universelle box-sizing-Regel der App. .up-root selbst traegt es im Block darueber,
   hier stehen seine Nachfahren -- und jede Komponentenwurzel traegt up-root (siehe die Vorlagen
   in bubble/), ebenso jedes Portal (up-root up-portal). Damit ist eine zweite universelle Regel
   je Komponente doppelt, und neun davon sind am 03.09. entfernt worden.
   Warum das zaehlt: eine universelle rechte Seite macht JEDES Element der Seite zum Kandidaten.
   Im Trace des Nutzers 124283 Versuche je Selektor -- ein voller Durchlauf ueber das Dokument,
   je Style-Neuberechnung, rund 8ms pro Regel unabhaengig von den Treffern. 53 unserer Selektoren
   dieser Bauform kosteten zusammen 458 von 1205ms.
   Wer eine neue Komponente baut, braucht hier nichts: up-root an der Wurzel genuegt. */
.up-root * { box-sizing: border-box; }

/* ---------- [data-align] Ausrichtung in einer zu breiten Wirtsbox ----------
   Ein Filter schrumpft auf die Breite seines Triggers. Steckt er in einer Bubble-Box, die
   breiter gezogen ist als dieser Trigger -- und das ist der Normalfall, weil die Box von Hand
   auf eine runde Zahl gezogen wird --, sitzt der Trigger am LINKEN Rand dieser Box. In einer
   rechtsbuendigen Leiste sieht das aus, als haenge der Filter weit links, obwohl er formal
   richtig steht: sichtbar ist der Trigger, gemessen wird die Box.

   data-align="right" klebt die Komponente an die rechte Kante ihrer Wirtsbox, ohne dass die Box
   selbst passend gezogen werden muss. Bewusst opt-in: bestehende Platzierungen stehen links in
   einer Toolbar und sollen genau da bleiben. Hoehe und Umbruchverhalten bleiben unberuehrt, es
   wechselt nur die Ausrichtung im Fluss. */
.up-root[data-align="right"]  { display: block; width: max-content; max-width: 100%; margin-left: auto; }
.up-root[data-align="center"] { display: block; width: max-content; max-width: 100%; margin-left: auto; margin-right: auto; }

/* ---------- [popover] auf UA-Neutral ----------
   Ein Dropdown, das im Drawer in den Top Layer gehoben wird (UC.menuEscape), traegt kurzzeitig
   das Attribut `popover`. Damit greift das UA-Stylesheet mit einem ganzen Satz eigener Vorgaben:
   position/inset/margin/width/height/border/padding/background -- und `overflow: auto`.
   Alles ausser overflow gewinnt unsere eigene Panel-Regel ohnehin; overflow deklariert kaum ein
   Panel selbst, also schlug hier die UA-Vorgabe durch und machte aus jedem Panel einen
   Scroll-Container. Gemessen: `overflow: visible -> auto` beim Oeffnen im Drawer, mit sichtbarer
   Folge fuer die Flex-Panels (der Kalender legte sich neu aus).
   Bewusst nur ein Attribut-Selektor (0,1,0) und ganz oben in der Datei: jede Komponentenregel --
   gleiche Spezifitaet, aber spaeter, oder hoehere -- gewinnt weiterhin. Das hier raeumt nur den
   UA-Default weg, es setzt nichts durch. */
[popover] { overflow: visible; }
/* Scroll-Chaining schuetzen — gegen fremdes Seiten-CSS, das `overscroll-behavior: contain`
   pauschal per `*` auf alles legt (der Bubble-Header dieser App tat/tut genau das ueber
   `[id*="allow_scroll"] *`). `contain` gehoert nur auf einen ECHTEN Scroll-Container, der die
   Seite bewusst nicht mitscrollen lassen soll (z.B. ein Modal). Liegt es dagegen auf allem, trifft
   es auch jedes Element, das nur wegen runder Ecken/Clipping ein `overflow: hidden` hat — und
   `overflow: hidden` macht ein Element bereits zum Scroll-Container. Ergebnis: das Element kann
   selbst nichts scrollen, gibt das Scrollen aber auch nicht an die Seite weiter, und das Scrollen
   versickert genau ueber diesen Flaechen (gemessen an `.tct-table`, `.tcd-box`, `.vt-table`).
   `auto` ist der Browser-Default — das hier stellt also nur wieder her, was ohne die fremde Regel
   ohnehin gaelte, und kann fuer sich genommen nichts kaputt machen.
   Doppeltes `.up-root` nur wegen Spezifitaet: (0,2,0) schlaegt `[id*="..."] *` (0,1,0) unabhaengig
   von der Ladereihenfolge, weil beide Seiten `!important` verwenden. */
.up-root.up-root, .up-root.up-root * { overscroll-behavior: auto !important; }
    .up-root[data-theme="dark"] {
      /* DER RAHMEN IST border-SECONDARY, nicht -primary. Linear benutzt beide: #23252a liegt auf
         dem Grund (#08090a) und ist dort deutlich, auf einer Karte (#1c1c1f) sind es noch vier
         Helligkeitsstufen -- also praktisch nichts. Unsere Rahmen sitzen fast alle AUF Karten
         (Tabellen, Panels, Felder), darum ist #34343a der richtige der beiden: 24 Stufen ueber
         der Karte. Gemeldet als "die Border-Color macht einen komischen Eindruck", und das war
         sie auch: sichtbar auf dem Grund, verschwunden auf allem anderen. */
      --vc-text: #e0e0e0; --vc-muted: #c9cbd0; --vc-border: #34343a;
      /* Gegenstueck zum hellen Thema: dort steht hier #e0e0e0, hier der Textton von dort. */
      --vc-text-invert: #1f1f1b;
      --vc-bg: #1c1c1f; --vc-heading-bg: #232326; --vc-sk: #28282c; --up-surface: #101114;
      /* ---- DIE DUNKLE LEITER (08.09.) --------------------------------------------------------
         An Linears dunklem Thema ausgerichtet, ausgelesen aus deren eigener Seite:
             Grund #08090a  <  Karte/Panel #1c1c1f  <  Menue/Kopfzeile #232326  <  Auswahl #28282c
         Vorher lag der Grund bei #121212 und die Karte bei #1b1b1b -- neun Stufen Abstand. Jetzt
         sind es zwanzig, und genau das ist der Unterschied, den man sieht: die Karten stehen auf
         der Seite statt in ihr zu verschwimmen.
         EINE Umkehrung steckt darin, und sie ist Absicht: das Menue war mit #151515 DUNKLER als
         die Karte, jetzt ist es heller. Ein Menue liegt ueber allem, und was oben liegt, ist bei
         Linear heller -- sonst braucht es den Schatten, um ueberhaupt als eigene Ebene zu gelten.
         Auswahl und Menuegrund muessen auseinanderliegen: mit beiden auf #232326 waere eine
         gewaehlte Zeile im Menue unsichtbar gewesen.
         ALLE Werte sind Linears eigene, ausgelesen von linear.app:
             bg-primary #08090a   bg-secondary #1c1c1f   bg-tertiary #232326
             bg-quaternary #28282c   border-primary #23252a   border-secondary #34343a
         DER GRUND IST EINE SPUR HELLER ALS LINEARS EIGENER. Deren bg-primary ist #08090a, also
         fast Schwarz; angefordert am 08.09. wurde "#0f0f11 oder ein kleines kleines bisschen
         heller, so dass es aber noch super als Page-BG haelt". #101114 ist dieses bisschen: die
         Karte hebt sich weiterhin ab (Kontrast 1.11 statt 1.17 gegen #1c1c1f, also gut ein
         Zehntel weniger Abstand), und der Text bleibt bei 14.3 gegen den Grund -- weit ueber
         allem, was AA verlangt. Alles andere in dieser Leiter ist unveraendert Linears Wert.
         Auch die Auswahlflaeche ist ihre (quaternary) -- ausdruecklich so angefordert: "nimm
         einfach alles aus Linear, also auch die selected bg states".
         Die SCHRIFTFARBEN sind unveraendert (#e0e0e0 / #a0a0a0 / #808080) -- ebenfalls so
         angefordert. Linears Toene sind heller (#f7f8f8 / #d0d6e0 / #8a8f98) und leicht
         blaustichig; wer das auch will, aendert die drei --vc-Werte, aber das ist eine eigene
         Entscheidung und keine Folge dieser hier. */
      --up-menu-bg: #232326; --up-sel-bg: #28282c; --up-menu-border: #3e3e44;
      --up-chip-rand-hover: #4a4a52; --up-chip-an: #26262a; --up-chip-an-hover: #2c2c30; --up-chip-rand-an-hover: #5a5a62;
      --up-dd-shadow: 0 10px 28px rgba(0,0,0,.32);
      /* Event-Pins im Dunkeln: dieselben Stufen auf der dunklen Leiter -- der Rand eine Stufe
         ueber --up-menu-border, die Linie auf --vc-fourth (#6c717a), damit sie wie hell hinter
         den Daten bleibt und nicht heller als die Achsenschrift wird. */
      --up-evm-rand: #4a4a52; --up-evm-linie: #6c717a;
      /* Die vier Tiefen im dunklen Thema stehen seit dem 28.09. im Block vor .up-root, an
         [data-theme="dark"] -- dort erreichen sie auch, was am <body> haengt. */
      --vc-switch-bg: #232326; --vc-switch-border: #34343a; --vc-third: #a0a0a0; --vc-fourth: #6c717a;
      /* Die Pille hebt ihre Schiene noch einmal an, statt einen festen Ton zu tragen -- sie muss
         der Schiene folgen, die das seit dem 27.09. auch tut (Begruendung bei --vc-switch-rail).
         Der feste Ton #28282c laege im Menue UNTER der angehobenen Schiene (40 gegen 47). */
      --vc-switch-pill: rgba(255,255,255,.07);
      --up-switch-off: #444444;   /* siehe hell */
      /* Die Textfassung des Blau-Lila-Paares, hier HERAUFgedreht statt herunter -- der Text
         steht auf --vc-field #2a2a2a. Gerechnet: 4.61 und 5.07, beide ueber den 4.5 aus AA.
         Die Flaechentoene --up-blau und --up-lila erben absichtlich aus dem hellen Block: eine
         Flaeche in dieser Saettigung traegt auf beiden Gruenden. Nur der TEXT braucht ein
         zweites Paar, weil er gegen den Grund gelesen werden muss und nicht auf ihm liegt. */
      --up-blau-text: #4c90ff;
      --up-lila-text: #b280ff;
      /* Das upstreem-Team im Dunkeln (Begruendung im hellen Block): dieselbe Farbfamilie
         HERAUFgedreht. #c9b596 auf #2b2620 7.5:1; die Flaeche liegt eine Stufe ueber der Karte
         (#1c1c1f), damit der Chip als Kasten steht und nicht nur als Schrift. */
      --vc-staff-ink: #c9b596; --vc-staff-bg: #2b2620;
      /* DIE SCHIENE HEBT IM DUNKELN IHREN GRUND AN, statt einen festen Ton zu tragen (27.09.).
         Gemeldet: "Im Darkmode sind die Background der ganzen Switcher recht schwer zu erkennen
         auf unserem Page Background." Die Ursache stand hier als benannter Kompromiss: ein
         DECKENDER Ton muss zwischen drei Gruenden liegen, die 18 Stufen auseinander sind (Seite
         #101114 = 17, Karte #1c1c1f = 28, Kopfzeile und Menue #232326 = 35), und #17171a war das
         Maximum dieses Minimums -- auf der Seite 6 Stufen ueber dem Grund, auf der Karte 5
         darunter. Die Seite ist aber genau der Grund der meisten Umschalter (die Werkzeugleisten
         der Tabellen sind durchsichtig), und 6 Stufen bei L* 5 verschwinden auf jedem Bildschirm
         mit etwas Umgebungslicht. Dieselbe Meldung kam am 07.09. schon einmal.
         Ein Ton kann das nicht, ein ANHEBEN kann es: 5,5 Prozent Weiss legt auf JEDEN Grund
         dieselbe Stufe -- Seite +13, Karte +12, Kopfzeile und Menue +12, also so viel wie im
         Hellen (#f2f2f2 auf Weiss: 13). Die Pille geht mit (--vc-switch-pill oben) und steht auf
         jedem Grund 14 bis 15 Stufen ueber der Schiene (vorher 17, hell 13). Gemessen mit
         _h_sw_mess.html, rohe Kanaele mit Augengewichten wie ueberall in dieser Datei.
         Aufgegeben ist damit das Ziel vom 22.09., EIN Ton je Rolle: er hat den haeufigsten Fall
         unsichtbar gemacht. Eine Liste von Flaechen mit eigener Schienenfarbe braucht es auch
         jetzt nicht -- die Stufe haengt am Grund, nicht an der Flaeche. */
      --vc-switch-rail: rgba(255,255,255,.055);
      --vc-field: #232326;
      /* Die Kopfleiste jeder Tabelle: eine Stufe HELLER als die Tabellenflaeche (--vc-bg
         #1b1b1b), nicht dunkler. Hier stand #121212 -- die Farbe der SEITE, die Leiste sass
         damit optisch hinter der Karte statt darauf. #242424 steht ohnehin in dieser Palette
         (--vt-row-hover-solid), es bleibt also eine abgeleitete Farbe. Dass eine
         ueberstrichene Zeile fast denselben Wert traegt (Mischwert etwa #232323), kostet
         nichts: die Kante zwischen Leiste und Koerper zeichnet die Kopfzelle selbst mit
         border-bottom 1px --vc-border. */
      /* #242424 war ein Waise: ein Ton aus der Zeit vor Linears Leiter, den der Farbtausch am
         08.09. nicht erfasst hat, weil er in keiner der getauschten Zeichenketten vorkam. Auf
         einer Karte (#1c1c1f) sass damit ein Kopf, der zu keiner Stufe gehoerte -- genau der
         "komische Eindruck", der gemeldet wurde. Jetzt tertiary, also dieselbe Stufe wie jede
         andere Kopfzeile und jeder Hover. */
      --vt-head-bg: #232326; --vt-logo-bg: #e0e0e0; --vt-row-hover: #232326;
      --vt-logo-ink: var(--vc-text-invert);
      /* Die Vorgabe der Akzentfarbe im Dunkeln -- wieder buchstabengleich das, was vorher aus
         --vc-text und --vc-bg kam. */
      --up-accent: #e0e0e0; --up-accent-fg: #1b1b1b; --up-accent-ink: #e0e0e0;
      --vt-row-hover-solid: #232326;
      --vt-up: #60d25d; --vt-down: #d25d5d;
      /* Im Dunkeln ein aufgehellter Ton mit weniger Deckung -- auch das wortgleich aus den alten
         Elementen. Der Hintergrund darf hier etwas mehr tragen, sonst verschwindet er im Grund. */
      --vt-brand: rgba(255, 190, 60, 0.85);
      --vt-brand-bg: color-mix(in srgb, var(--vt-brand) 7%, transparent);
      --vt-brand-bg-hover: color-mix(in srgb, var(--vt-brand) 14%, transparent);
      --vt-brand-border: color-mix(in srgb, var(--vt-brand) 60%, transparent);
      --vt-brand-border-hover: var(--vt-brand);
      --vc-hover-border: #707070;
      /* Auf dunklem Grund braucht der Ring mehr Deckkraft, sonst verschwindet er im Panel. */
      --up-focus: #6DB2FF; --up-focus-ring: rgba(109,178,255,0.28);
    }

    /* ---------- DIE DREI ANDEREN AKZENTFARBEN ----------------------------------------------
       Gewaehlt wird im Einstellungsfenster, core schreibt die Wahl als data-accent an das
       <html> und merkt sie im localStorage.
       Warum html[data-accent] .up-root und nicht html[data-accent] allein: die Vorgabe steht als
       EIGENE Deklaration an .up-root. Ein geerbter Wert vom <html> verliert gegen jede eigene
       Deklaration, egal wie spezifisch er ist -- also muss der Selektor .up-root selbst treffen.

       DIE AKZENTFARBE HAT ZWEI ROLLEN, und nur eine davon kommt mit einem Wert aus:

         --up-accent      die FLAECHE. Was darauf liegt, traegt --up-accent-fg, und die dreht
                          sich mit der Farbe statt mit dem Thema. Ein Wert reicht also.
         --up-accent-ink  die SCHRIFT. Sie liegt auf der Seite, nicht auf einer Flaeche, und
                          konkurriert damit mit dem Grund des THEMAS -- hier braucht es zwei.

       DIE WERTE SIND AN DEN QUELLEN GEPRUEFT (19.09. verlangt: "check ob du wirklich Linear,
       Claude, Notion getroffen hast, mit Quelle"). Zwei von dreien stimmten nicht:

         Indigo      linear.app/brand nennt #5E6AD2 als Brand Indigo, Flaeche der primaeren
                     Schaltflaeche. Getroffen.
         Azur        Notions Palette fuehrt #2EAADC als Akzent, die primaere Schaltflaeche der
                     Anwendung ist aber #0075DE. Hier stand #1A73C7 -- ein von mir abgedunkeltes
                     #2383E2, also ein Wert, den es bei Notion gar nicht gibt. Jetzt #0075DE.
         Terrakotta  Anthropic fuehrt ZWEI Toene: Terracotta #C96442 fuer die Flaeche der
                     primaeren Schaltflaeche und Coral #D97757 fuer Schrift und Verweise auf
                     dunklem Grund. Ich hatte die SCHRIFTfarbe als Flaeche benutzt -- daher die
                     Meldung "schwarzer Text auf Orange wirkt komisch". Er war nicht zu schwach
                     (5.30), aber es war der falsche Ton fuer die Rolle.

       Gemessen, verlangt ist AA (4.5). Flaeche gegen die Schrift darauf, Tinte gegen den
       Seitengrund (hell #ffffff, dunkel #101114):
           Indigo      Flaeche #5E6AD2 weiss 4.70    Tinte hell 4.70   dunkel #7C8AF0 6.07
           Azur        Flaeche #0075DE weiss 4.57    Tinte hell 4.57   dunkel #3D97F0 6.19
           Terrakotta  Flaeche hell #BD5836 weiss 4.55, dunkel #D97757 mit dunkler Schrift 5.30
                       Tinte hell 4.55   dunkel 6.05
       #BD5836 ist Anthropics Terracotta, so weit abgedunkelt, dass weisse Schrift AA traegt
       (#C96442 selbst traegt weder weisse 3.90 noch dunkle 4.24). Im Dunkeln bleibt es bei
       Coral mit dunkler Schrift -- das ist dort der uebliche Bau und Anthropics eigener Ton fuer
       diesen Grund. Terrakotta ist damit die einzige Farbe mit zwei FLAECHEN; die anderen zwei
       brauchen nur zwei Tinten. */
    /* "Standard + Blau" (06.10.): nur die Linienfarbe der Charts (UC.chartInk), die Oberflaeche bleibt
       im Standard. Die Werte sind die erste Stufe von UC.ruhigeFamilie. */
    html[data-chartakzent="blau"] .up-root { --up-chart-ink: #3f66b0; }
    html[data-chartakzent="blau"] .up-root[data-theme="dark"] { --up-chart-ink: #83abf4; }
    html[data-accent="indigo"] .up-root { --up-accent: #5E6AD2; --up-accent-fg: #ffffff; --up-accent-ink: #5E6AD2; }
    html[data-accent="indigo"] .up-root[data-theme="dark"] { --up-accent-ink: #7C8AF0; }
    html[data-accent="azur"] .up-root { --up-accent: #0075DE; --up-accent-fg: #ffffff; --up-accent-ink: #0075DE; }
    html[data-accent="azur"] .up-root[data-theme="dark"] { --up-accent-ink: #3D97F0; }
    html[data-accent="terrakotta"] .up-root { --up-accent: #BD5836; --up-accent-fg: #ffffff; --up-accent-ink: #BD5836; }
    html[data-accent="terrakotta"] .up-root[data-theme="dark"] { --up-accent: #D97757; --up-accent-fg: #1f1f1b; --up-accent-ink: #D97757; }

    /* ---------- Fokus-Ring fuer Eingabefelder ----------
       EIN Fokuszustand fuer die ganze App: blauer Rand plus ein 4px weicher Auslauf dahinter.
       Vorher war der Fokus in jeder Komponente die Hover-Randfarbe, teils zusaetzlich mit einem
       Hintergrundwechsel. Beides ist falsch: der Hover-Rand sagt "der Mauszeiger ist hier", nicht
       "hier landet deine Eingabe" -- man konnte einem Feld nicht ansehen, ob es den Fokus hat,
       solange die Maus darauf stand. Und ein Hintergrundwechsel beim Fokussieren laesst das Feld
       beim Hineinklicken die Farbe wechseln, was sonst nirgends in der App passiert.

       .up-field ist eine ZUSATZKLASSE auf dem Eingabefeld, keine Ersetzung: Rahmen, Radius und
       Groesse bleiben beim Feld selbst, hier steht nur der Fokuszustand. Der Rand faerbt sich um,
       der box-shadow legt den Auslauf dahinter, und outline:0 nimmt den doppelten Ring des
       Browsers weg -- der Ersatz ist sichtbarer als das Original, es geht also nichts verloren.
       Wer eine echte Tastatur-Navigation ohne Maus prueft: der Ring erscheint bei :focus, also
       auch beim Tabben. */
    .up-field:focus, .up-field:focus-visible,
    .up-fieldwrap:focus-within {
      border-color: var(--up-focus);
      box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring);
      outline: 0;
    }
    /* Hover darf den Fokus nicht ueberschreiben: die Hover-Regel eines Feldes steht meistens in
       der Komponentendatei und laedt damit NACH core. Gleiche Spezifitaet haette bedeutet, dass
       die Ladereihenfolge entscheidet -- derselbe Fehler, der hier schon dreimal aufgetreten ist.
       Die doppelte Klasse hebt die Spezifitaet an, statt sich auf die Reihenfolge zu verlassen. */
    .up-field.up-field:focus, .up-field.up-field:focus-visible {
      border-color: var(--up-focus); box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring);
    }

    /* ---------- Hover der Eingabefelder (13.10.) ----------
       EIN Hover fuer jedes Feld mit Rahmen. Vorher drei Sorten: dunkler Rand (--vc-hover-border,
       #6f6f6f -- "sehr dunkel", Add Brand), Rand plus Flaeche (Suchfelder oben in den Dropdowns)
       und gar keiner (Add Prompts, Anmeldung, Dialoge). Jetzt nur der Rand, eine Spur kraeftiger als
       er ohnehin ist, keine Flaeche -- wie Linear. Gemischt am Feld selbst aus seinem Rand und
       seiner Schrift, damit derselbe Wert hell, dunkel und in den Menues (eigene Farbsaetze) passt.
       Spezifitaet 0,2,0: schlaegt die Grundregel jedes Feldes (0,1,0), verliert gegen echte Zustaende
       wie Fehler (.uab-field.is-err .uab-in, 0,3,0) und geoeffnet (.uob-ddwrap.is-open, 0,3,0). Fokus,
       deaktiviert und aria-invalid stehen im :where (ohne Gewicht) -- der Fokusring bleibt.
       Neue Felder: die Klasse hier eintragen ODER .up-field ans Feld setzen, keine eigene Hover-Regel.
       .am-composer (13.10.) ist Miras Eingabekasten -- der Rahmen sitzt am Kasten, nicht am Textfeld
       darin (das hat border: 0), also steht der Kasten hier. :focus-within greift, sobald man tippt. */
    :is(.up-field, .up-fieldwrap, .up-es-input, .up-ddsearch-in, .up-topicmodal-name, .up-ment-search,
        .up-cgm-search-in, .upt-gm-search-in, .uab-in, .uap-input, .uap-rowedit, .uau-input, .ube-feld,
        .uca-input, .uca-bld-input, .ums-in, .uob-input, .uob-select, .usb-dlg-in, .usb-sumin,
        .usb-urlin, .usn-pin-in, .uto-in, .upr-dd-search, .am-ct-input, .am-prev-item-input,
        .am-prev-proj-input, .am-composer):is(:hover):where(:not(:focus, :focus-within, :disabled, [aria-invalid="true"])) {
      border-color: color-mix(in srgb, var(--vc-border) 86%, var(--vc-text));
    }

    /* ---------- toolbar ---------- */
    .up-head { display: flex; align-items: center; height: 32px; margin-bottom: 16px; gap: 8px; }
    .up-heading { display: inline-flex; align-items: center; font-size: 14px; font-weight: 500; color: var(--vc-text); line-height: 1.2; white-space: nowrap; }
    /* DER ZAEHLER ALS CHIP (11.10., vom Nutzer so bestellt: "Separator weg, den Count in einen
       Chip mit grauem Background"). Vorher "Ueberschrift . 42" mit einem 4px-Punkt. Der Chip ist
       EIN Baustein fuer jede Zahl neben einer Ueberschrift -- .up-zaehler frei stehend,
       .up-head-count in der Kopfzeile einer Tabelle (dort erst sichtbar mit .has-count).
       Grund und Schrift sind die des leisen Etiketts (.up-marke.is-leise, --up-sel-bg): auf der
       Kartenflaeche deutlich genug, ohne mit dem Inhalt zu konkurrieren. 20px hoch, 12px Ziffern
       in gleicher Breite -- neben einer 14er-Ueberschrift die naechstkleinere Stufe.
       .up-head-sep bleibt als Klasse stehen (im Markup eingebauter Bubble-Elemente), zeigt aber
       nichts mehr. */
    .up-head-sep { display: none; }
    .up-zaehler, .up-head-count {
      display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
      box-sizing: border-box; height: 20px; min-width: 20px; padding: 0 7px;
      border-radius: var(--up-r-s); background: var(--up-sel-bg); color: var(--vc-muted);
      font-size: var(--up-fs-s); font-weight: var(--up-fw-m); line-height: 1;
      font-variant-numeric: tabular-nums; white-space: nowrap;
    }
    /* In der Kopfzeile OHNE eigene Mindestbreite (13.10. angefordert: "die Mindestbreite entfernen" --
       mit den frueheren 36px stand eine einstellige Zahl mit sichtbar mehr Luft links und rechts als
       eine dreistellige). Jeder Chip traegt jetzt dasselbe Polster (7) um seine Ziffern; dass ein
       Schalter rechts daneben beim Wechsel von 102 auf 9 ein Stueck wandert, ist der Preis dafuer. */
    .up-head-count { display: none; margin-left: 8px; }
    .up-heading.has-count .up-head-count { display: inline-flex; }
    .up-zaehler.is-sk, .up-head-count.is-sk { color: transparent; background: var(--vc-sk); animation: uutpulse 1.2s ease-in-out infinite; }
    .up-head-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; flex: 0 0 auto; }

    /* ── Einklappbare Werkzeuggruppe ─────────────────────────────────────────────────────────
       Die Kopfleiste einer grossen Tabelle traegt schnell sechs bis acht Bedienelemente. Diese
       Gruppe schiebt sie zusammen, ohne sie wegzunehmen: eingeklappt bleibt nur der Ausloeser
       stehen, ausgeklappt steht alles genau wie vorher.

       Warum grid-template-columns 0fr -> 1fr und nicht max-width: nur so laeuft die Bewegung
       exakt bis zur ECHTEN Inhaltsbreite. Ein geratenes max-width haelt entweder zu frueh an
       oder laeuft die letzten Pixel ins Leere und wirkt dabei traege. Gleiche Bauart wie der
       Meldungsstreifen im Onboarding, dort mit Zeilen statt Spalten.

       200ms ease, beide Richtungen gleich -- ausdrueckliche Vorgabe.

       Der negative Aussenabstand im eingeklappten Zustand raeumt die Luecke weg, die .up-head-tools
       ueber `gap` auch um ein NULL Pixel breites Kind legt. Ohne ihn stehen zwischen der
       Statusgruppe und dem Ausloeser 16px statt 8px, und die Leiste sieht schief aus. */
    .up-toolgroup { display: grid; grid-template-columns: 0fr; opacity: 0; margin-right: -8px;
      transition: grid-template-columns 200ms ease, opacity 200ms ease, margin-right 200ms ease; }
    /* min-width: 0 ist Pflicht. Ein Grid-Kind bekommt sonst seine Inhaltsbreite als Untergrenze,
       und die Spalte laesst sich gar nicht erst auf 0 ziehen -- die Gruppe bliebe offen stehen. */
    .up-toolgroup-in { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; }
    .up-root.is-tools-open .up-toolgroup,
    .up-root.is-tools-peek .up-toolgroup { grid-template-columns: 1fr; opacity: 1; margin-right: 0; }
    /* Die Kappung faellt erst NACH der Bewegung -- sonst haengt waehrend des Aufziehens ein Menue
       aus der Gruppe heraus. Und sie MUSS fallen: die Dropdowns darin sind absolut positionierte
       Kinder, und ein overflow:hidden am Vorfahren schneidet sie ab. Gesetzt wird die Klasse von
       einer Uhr mit benanntem Ende, NICHT von transitionend: in einem Bubble-Tab, der gerade
       nicht gemalt wird, laeuft der Uebergang nicht und das Ereignis kommt nie. */
    .up-root.is-tools-shown .up-toolgroup-in { overflow: visible; }

    /* ── Die Halteflaeche ist 16px groesser als die Leiste ────────────────────────────────────
       Solange der Zeiger hier drin ist, klappt die Werkzeuggruppe nicht zu. Ohne den Rand liegt
       die Grenze genau auf der Knopfkante: einmal am Rand vorbeigefahren, und die Leiste faehrt
       zusammen, obwohl der Nutzer sie noch benutzen wollte.

       padding wachsen lassen und die Aussenabstaende gegenrechnen -- so wird die Flaeche groesser,
       ohne dass sich im Layout ein Pixel verschiebt. margin-left bleibt `auto`: das ist die
       Rechtsausrichtung der ganzen Leiste, und ein Wert dort statt auto schoebe sie nach links.
       Nach links reicht die Flaeche trotzdem 16px weiter, weil das padding die Kastenkante
       verschiebt und `auto` nur den Rest verteilt.

       Nach unten sind die 16px genau die margin-bottom von .up-head -- die Flaeche endet also
       exakt an der Tabelle und legt sich nicht ueber ihren Kopf. Waere sie groesser, faenge sie
       Klicks ab, die dem Sortierkopf gehoeren.

       :has(.up-toolgroup) statt einer Regel fuer alle: nur eine Leiste mit einklappbarer Gruppe
       hat ueberhaupt etwas davon. Die anderen Tabellen sollen ihre Kastenmasse behalten. */
    /* :has(> .up-toolgroup) statt eines Klassennamens: die Leiste heisst je Komponente anders
       (.up-head-tools, .ubo-head-tools), aber sie ist immer der ELTERNTEIL der Gruppe. Die
       Ausrichtung (margin-left: auto) bleibt unangetastet -- ein Wert dort statt auto schoebe die
       ganze Leiste nach links. */
    /* .up-toolhost setzt makeToolGroup an tools, dem Elternteil der Gruppe (siehe dort). Vorher
       stand hier ":has(> .up-toolgroup)" -- in den Selector Stats des Nutzers 81ms fuer 78
       Treffer, zweitteuerster Selektor der Seite. Gleiche Spezifitaet (0,2,0), gleiche Wirkung. */
    .up-root .up-toolhost {
      /* Nach RECHTS ausdruecklich nicht. Dort ist die Kante der Komponente -- es gibt nichts,
         wohin man hineinfahren koennte, und die 16px kosten dort etwas: margin-right: -16px zieht
         zwar das Layout zurueck, aber scrollWidth zaehlt die Polsterung mit. Gemessen an allen
         sechs Komponenten und auf jeder Breite: der Kopf brauchte genau 16px mehr, als er hatte.
         Unsichtbar, weil die Polsterung durchsichtig ist -- aber ein Vorfahre mit
         overflow-x: auto haette daraus einen waagerechten Balken gemacht, und den haette
         niemand mit dieser Leiste in Verbindung gebracht.
         Links, oben und unten bleiben die 16px: dort liegt Platz, und dort faehrt der Zeiger
         wirklich vorbei. */
      padding: 16px 0 16px 16px; margin-top: -16px; margin-bottom: -16px;
    }
    /* Die Polsterung reicht 16px nach links in eine Flaeche, die normalerweise leer ist -- aber
       nicht immer: die Ueberschrift waechst mit der Auswahl ("0 selected" samt Loeschkreuz), und
       auf einer schmalen Tabelle kommt sie der Leiste nahe. Ueberlappen sich beide, gewinnt beim
       Klick die Leiste, weil sie im Markup SPAETER kommt -- und das Loeschkreuz waere tot.

       position+z-index statt eines kleineren Randes: dass ein positioniertes Element mit z-index 1
       ueber einem unpositionierten Geschwister liegt, ist eine Zusage der Spezifikation und
       braucht keine Messung. In dieser Testumgebung liesse sich der Klick auch gar nicht pruefen
       (das Fenster ist dort 0x0, elementFromPoint gibt fuer jeden Punkt null). */
    /* .has-toolgroup an der Wurzel, aus demselben Grund. Spezifitaet bleibt (0,3,0). */
    .up-root.has-toolgroup .up-heading { position: relative; z-index: 1; }

    /* ── Ausloeser und Einklapper ────────────────────────────────────────────────────────────
       Beide SIND .up-iconbtn -- 32x32, Radius 8, Farben und Hover kommen von dort. Hier steht
       kein einziger dieser Werte noch einmal, nur WANN sie sichtbar sind.

       Breite statt display, weil display sich nicht animieren laesst und die Vorgabe 200ms in
       beide Richtungen lautet. margin-right raeumt dabei die gap-Luecke weg, die die Leiste auch
       um ein null Pixel breites Kind legt -- ohne sie stuenden 16px statt 8px zwischen den
       Nachbarn. */
    /* ── Warum hier nur Deckkraft und Verschiebung animieren ─────────────────────────────────
       Beide Knoepfe behalten IMMER ihre 32px. Vorher animierten width UND margin-right mit -- zwei
       LAYOUT-Eigenschaften, und die Gruppe daneben ist ein Grid mit 1fr, dessen Spur bei jeder
       geaenderten Kinderbreite neu aufgeloest wird. Das war einmal pro Bild das ganze
       Kopfzeilen-Layout, waehrend darunter eine Tabelle steht. Genau daran hat es geruckelt.
       Deckkraft und transform erledigt der Browser dagegen ohne Layout und ohne Neuzeichnen.

       Der reservierte Platz kostet nichts Sichtbares: die Leiste ist rechtsbuendig
       (margin-left: auto), links von ihr liegt freie Flaeche. Ein 32px-Platz dort schiebt die
       Leiste in einen Bereich, der ohnehin leer war -- es wandert nichts, was der Nutzer ansieht.

       cubic-bezier(.2, 0, .2, 1) statt `ease`: ease bremst am Anfang UND am Ende, und auf sechs
       Pixel Verschiebung liest sich das als Zoegern. Diese Kurve geht sofort los und laeuft sanft
       aus. 200ms bleiben, wie vorgegeben. */
    .up-tbtrig, .up-tbcol { flex: 0 0 auto; overflow: hidden; }

    /* Der AUSLOESER darf seine Breite weiter mitanimieren. Das passiert genau einmal, beim
       Feststellen, gleichzeitig mit dem Aufziehen der Gruppe -- also waehrend das Layout ohnehin
       in Bewegung ist, und der Ruckler wurde auch nie dort gemeldet, sondern beim HOVER.
       Wichtiger noch: seinen Platz zu reservieren kostet echte Pixel. Gemessen bei 640px Breite
       und aufgeklappter Leiste lief prompts-table dadurch um 27px ueber -- die Tabelle mit den
       meisten Werkzeugen hat dort keine 32px zu verschenken. So wie es jetzt steht, gibt der
       Ausloeser seine 32px ab, und der Einklapper nimmt sie: die Leiste ist im festgestellten
       Zustand genau so breit wie vorher. */
    .up-tbtrig {
      width: 32px; opacity: 1;
      transition: width 200ms cubic-bezier(.2, 0, .2, 1), opacity 200ms cubic-bezier(.2, 0, .2, 1);
    }
    .up-root.is-tools-open .up-tbtrig { width: 0; opacity: 0; pointer-events: none; }

    /* Eingeklappt nimmt der Einklapper keinen Platz: dort gibt es nichts zu schliessen, und 32px
       waeren eine Luecke neben dem Ausloeser. Die Breite steht NICHT in der Uebergangsliste, sie
       schaltet also hart um -- und zwar in demselben Augenblick, in dem die Gruppe zu wachsen
       beginnt. In einem Kasten, der gerade aufzieht, sieht das niemand. */
    /* Der EINKLAPPER dagegen bewegt sich nur mit Deckkraft und Verschiebung -- das sind die zwei
       Eigenschaften, die der Browser ohne Layout und ohne Neuzeichnen erledigt. Genau daran hat
       es geruckelt: vorher animierten width UND margin-right mit, und die Gruppe daneben ist ein
       Grid mit 1fr, dessen Spur bei jeder geaenderten Kinderbreite neu aufgeloest wird -- einmal
       pro Bild das ganze Kopfzeilen-Layout, waehrend darunter eine Tabelle steht.
       Seine 32px sind im festgestellten Zustand reserviert; sie kosten nichts, weil der Ausloeser
       im selben Augenblick seine abgibt.

       cubic-bezier(.2, 0, .2, 1) statt `ease`: ease bremst am Anfang UND am Ende, und auf sechs
       Pixel Verschiebung liest sich das als Zoegern. Diese Kurve geht sofort los und laeuft sanft
       aus. 200ms bleiben, wie vorgegeben. */
    .up-tbcol {
      transition: opacity 200ms cubic-bezier(.2, 0, .2, 1),
                  transform 200ms cubic-bezier(.2, 0, .2, 1);
    }
    /* Doppelte Klasse, dieselbe Begruendung wie bei .up-field.up-field weiter oben: .up-iconbtn
       setzt selbst width: 32px, steht in derselben Datei und -- seit die Werkzeugleiste hier
       eingefuegt wurde -- WEITER UNTEN. Bei gleicher Spezifitaet entscheidet die Reihenfolge, und
       die hat gewonnen: der Einklapper war auch im eingeklappten Zustand 32px breit. Gemessen an
       der Kaskade selbst (welche Regel greift), nicht geraten. Sichtbar war es nicht, weil die
       Gruppe dort 0 breit und gekappt ist -- aber eine CSS, die etwas anderes tut als sie sagt,
       fuehrt den naechsten Leser in die Irre. */
    /* Die Ruhelage liegt RECHTS von der Endlage: der Knopf faehrt also von rechts nach links
       herein und beim Ausblenden nach rechts wieder hinaus. Das ist die Richtung, in die auch
       die Leiste selbst zusammenfaehrt -- vorher lief er ihr entgegen. */
    .up-tbcol.up-tbcol { width: 0; opacity: 0; transform: translateX(6px); pointer-events: none; }
    .up-root.is-tools-open .up-tbcol.up-tbcol { width: 32px; }
    /* Hover: NUR Deckkraft und Verschiebung. Kein Layout, keine Gridspur, kein Ruckeln. */
    /* .up-toolhost statt :has(> .up-toolgroup) -- s. den Kommentar oben. In der zweiten Zeile
       bleibt EIN :has() (fuer :focus-visible gibt es keine Klasse), aber es haengt jetzt an einem
       Kandidaten statt an jedem Nachfahren der Wurzel. Spezifitaet unveraendert (0,5,0). */
    .up-root.is-tools-open .up-toolhost:hover .up-tbcol,
    .up-root.is-tools-open .up-toolhost:has(:focus-visible) .up-tbcol {
      opacity: 1; transform: none; pointer-events: auto;
    }
    /* Haelt ein aktiver Filter die Leiste offen, gibt es keinen Ausgang -- ihn trotzdem
       anzubieten hiesse, den Nutzer auf einen Knopf klicken zu lassen, der nichts tut. */
    .up-root.is-tools-locked .up-tbcol { display: none; }

    /* ── Kit aus (unter cfg.minWidth) ────────────────────────────────────────────────────────
       Auf schmalen Komponenten verhaelt sich die Leiste wie vor dem Kit: alles sichtbar, kein
       Ausloeser, kein Einklapper, keine Bewegung. Ueber eine Klasse und nicht durch Umbauen des
       DOM -- bei jedem Ueberschreiten der Grenze Kinder zu verschieben wuerde Fokus, offene
       Menues und Zeigerzustand mitnehmen.
       Auch die Halteflaeche faellt weg: ohne Ausloeser gibt es nichts zu halten, und die 16px
       Polsterung waeren auf einer engen Leiste nur ein Kasten, der ueber seine Nachbarn reicht. */
    .up-root.is-tools-off .up-tbtrig, .up-root.is-tools-off .up-tbcol { display: none; }
    .up-root.is-tools-off .up-toolgroup { transition: none; }
    .up-root.is-tools-off .up-toolhost { padding: 0; margin-top: 0; margin-bottom: 0; }

    /* ---- DER ZEICHENKNOPF, und seit dem 22.09. mit einer LEISEN FASSUNG ------------------
       Fuenf Komponenten hatten diesen Knopf nachgebaut: .ubo-iconbtn, .vot-iconbtn,
       .tcd-iconbtn, .combo-iconbtn, .upt-kpi-eye -- jeweils dieselben acht Angaben, dazu je
       eine eigene Dunkel- und SVG-Regel. Vier davon trugen eine BEGRUENDUNG im Kommentar,
       warum sie das Bauteil nicht benutzen: sein :hover legt eine Flaeche unter das Zeichen,
       und diese Knoepfe wollen nur die Farbe wechseln. Die Begruendung war richtig -- aber
       sie beschreibt eine VARIANTE, keinen Grund fuer fuenf Kopien. Dieselbe Erklaerung stand
       viermal in vier Dateien.
       Jetzt: die Geometrie steht EINMAL hier, und der Unterschied heisst .is-quiet. Die fuenf
       Klassennamen stehen in den Selektorlisten mit, damit kein Markup in fuenf Komponenten
       angefasst werden muss; neuer Code schreibt .up-iconbtn bzw. .up-iconbtn.is-quiet.
       .ubo-iconbtn war die einzige BYTEGLEICHE Kopie -- inklusive Hover mit Flaeche; sie
       braucht die Variante nicht und haengt an der lauten Fassung.
       (Die Farbe #6f6f6f steht hier weiter als Literal. Sie gehoert in ein Token, aber das
       ist die Farbrunde und nicht diese.) */
    .up-iconbtn,
    :is(.ubo-iconbtn, .vot-iconbtn, .tcd-iconbtn, .combo-iconbtn, .upt-kpi-eye,
      .udt-actbtn, .uut-actbtn, .am-prev-mini, .upr-clear-input,
      .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
      .usb-ddcustom-clear, .upt-selcount-clear) {
      display: inline-flex; align-items: center; justify-content: center;
      width: 32px; height: 32px; border: 0; border-radius: 8px;
      background: transparent; color: var(--vc-third); cursor: pointer; padding: 0; flex: 0 0 auto; position: relative;
    }
    .up-root[data-theme="dark"] :is(.up-iconbtn,
      .ubo-iconbtn, .vot-iconbtn, .tcd-iconbtn, .combo-iconbtn, .upt-kpi-eye,
      .udt-actbtn, .uut-actbtn, .am-prev-mini, .upr-clear-input,
      .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
      .usb-ddcustom-clear, .upt-selcount-clear) { color: var(--vc-third); }
    /* the dark base rule above is more specific than .up-iconbtn:hover, so the hover colour
       needs its own dark rule or it never turns primary in dark mode. */
    .up-root[data-theme="dark"] :is(.up-iconbtn,
      .ubo-iconbtn, .vot-iconbtn, .tcd-iconbtn, .combo-iconbtn, .upt-kpi-eye,
      .udt-actbtn, .uut-actbtn, .am-prev-mini, .upr-clear-input,
      .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
      .usb-ddcustom-clear, .upt-selcount-clear):hover { color: var(--vc-text); }
    /* LAUT: Farbe und Flaeche. Der Normalfall -- ein Knopf in einer Werkzeugleiste. */
    .up-iconbtn:not(.is-quiet):hover,
    .ubo-iconbtn:hover { color: var(--vc-text); background: var(--vc-heading-bg); }
    /* LEISE: nur die Farbe. Fuer Knoepfe, die in einer Kopfzeile neben einem Titel sitzen --
       dort zieht eine Hover-Flaeche die Zeile optisch auf. */
    .up-iconbtn.is-quiet:hover,
    /* .udt-actbtn und .uut-actbtn stehen hier NICHT: ihr eigener :hover setzt weiterhin eine
       Flaeche (var(--vc-heading-bg)), sie gehoeren also zur lauten Fassung. Beim ersten Umbau
       waren sie faelschlich in dieser Liste -- wirkungslos, weil die oertliche Regel spaeter
       kommt, aber irrefuehrend. */
    :is(.vot-iconbtn, .tcd-iconbtn, .combo-iconbtn, .upt-kpi-eye,
        .am-prev-mini, .upr-clear-input,
        .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
        .usb-ddcustom-clear, .upt-selcount-clear):hover {
      color: var(--vc-text); background: transparent; }

    /* ---- DIE GROESSENLEITER (22.09.) --------------------------------------------------------
       Neun weitere Nachbauten waren kleinere Fassungen desselben Knopfes. Gemessen:
           28/7/15  Zeilenaktion in einer Tabelle      .udt-actbtn .uut-actbtn
           24/6/14  kleine Aktion im Fliesstext        .am-prev-mini .upr-clear-input
           20/6/13  das X in einem Suchfeld            vier Stueck
           18/5/12  das X in einer Plakette            .upt-selcount-clear -- EINE Verwendung
       DREI STUFEN UND NICHT VIER: 28 und 24 stehen schon auf der Hoehenleiter der App
       (--up-h-seg, --up-h-chip). 20 ist neu, aber belegt: VIER unabhaengige Komponenten sind
       auf genau diese Zahl gekommen, das ist eine Rolle und keine Streuung. 18 dagegen hatte
       EINE Verwendung, zwei Pixel neben 20 und in derselben Rolle -- eine Verwendung
       rechtfertigt keine Stufe, also geht sie auf 20.
       DER RADIUS WIRD MITGEZOGEN (§1): 32/8 ist 0.25, und 28/7 sowie 24/6 halten das bereits.
       20 stand auf 6 (0.30) und geht auf 5 -- vier Stellen um einen Pixel.
       DAS ZEICHEN WAECHST RELATIV MIT, und das ist kein Versehen: 16/32 = 0.50, aber
       13/20 = 0.65. Ein kleiner Knopf braucht ein verhaeltnismaessig groesseres Zeichen, sonst
       verschwindet es. Die gemessenen Werte bleiben. */
    .up-iconbtn.is-28, .udt-actbtn, .uut-actbtn { width: 28px; height: 28px; border-radius: 7px; }
    :is(.up-iconbtn.is-28, .udt-actbtn, .uut-actbtn) svg { width: 15px; height: 15px; }

    .up-iconbtn.is-24, .am-prev-mini, .upr-clear-input { width: 24px; height: 24px; border-radius: 6px; }
    :is(.up-iconbtn.is-24, .am-prev-mini, .upr-clear-input) svg { width: 14px; height: 14px; }

    .up-iconbtn.is-20,
    .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
    .usb-ddcustom-clear, .upt-selcount-clear { width: 20px; height: 20px; border-radius: 5px; }
    :is(.up-iconbtn.is-20,
        .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
        .usb-ddcustom-clear, .upt-selcount-clear) svg { width: 13px; height: 13px; }
    :is(.up-iconbtn,
      .ubo-iconbtn, .vot-iconbtn, .tcd-iconbtn, .combo-iconbtn, .upt-kpi-eye,
      .udt-actbtn, .uut-actbtn, .am-prev-mini, .upr-clear-input,
      .udt-sub-search-x, .upt-topicsearch-clear, .upt-grp-sidesearch-clear,
      .usb-ddcustom-clear, .upt-selcount-clear) svg {
      width: 16px; height: 16px; display: block; }

    /* ---------- der LEISE Trigger: derselbe Knopf wie .up-iconbtn, nur mit Beschriftung --------
       core unterscheidet weiter unten schon zwei Sorten Trigger: einen BESCHRIFTETEN mit Rahmen
       (.up-filter-btn, .up-ment-btn), der im aktiven Zustand eine Flaeche traegt, und einen
       ICON-Knopf ohne Rahmen, bei dem das Badge sagt, dass etwas gesetzt ist. Was fehlte, ist der
       Dritte: beschriftet UND leise -- ein Einstieg in ein Menue, der selbst kein Zustand ist.
       Der "More Filters"-Knopf der Filterleiste ist der erste Verbraucher.

       Alle Werte sind die von .up-iconbtn: 32px hoch, Radius 8, kein Rahmen, keine Flaeche in
       Ruhe, --vc-heading-bg beim Ueberfahren, Zeichen 16. Nur zwei Zahlen kommen dazu, weil ein
       Text da ist: 8px Polster links und rechts und 7px zwischen Zeichen und Wort.
       position: relative, damit .up-badge (absolut) an seiner Ecke sitzt -- genau wie bei
       .up-iconbtn. */
    .up-quietbtn {
      position: relative; flex: 0 0 auto;
      display: inline-flex; align-items: center; gap: 7px;
      height: 32px; padding: 0 8px; border: 0; border-radius: 8px; background: transparent;
      color: var(--vc-text); font-family: inherit; font-size: 13px; font-weight: 500; line-height: 1;
      white-space: nowrap; cursor: pointer; outline: 0;
      transition: background-color 120ms ease, color 120ms ease;
    }
    .up-quietbtn:hover { background: var(--vc-heading-bg); }
    /* Offen: dieselbe Flaeche wie jeder andere offene Trigger. Der Wrapper traegt .is-open, das
       setzt UC.makePopover -- also gilt die Regel fuer jedes kuenftige leise Dropdown mit. */
    .is-open > .up-quietbtn { background: var(--vc-heading-bg); }
    .up-quietbtn:focus-visible { box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); }
    .up-quietbtn svg { width: 16px; height: 16px; flex: 0 0 16px; display: block; }
    .up-quietbtn:disabled { opacity: .45; cursor: default; }
    .up-quietbtn:disabled:hover { background: transparent; }

    /* export: a real filled button, identical to the one in the export popup component */
    .up-export {
      display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 8px;
      border: 0; border-radius: 8px; background: var(--up-accent); color: var(--up-accent-fg);
      font: inherit; font-size: 12px; font-weight: 560; line-height: 1;
      white-space: nowrap; cursor: pointer; outline: 0; flex: 0 0 auto;
      transition: opacity .14s ease;
    }
    .up-export:hover { opacity: .9; }
    .up-export svg { width: 16px; height: 16px; flex: 0 0 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

    /* ---------- search (slides out from the magnifier) ---------- */
    .up-search { display: inline-flex; align-items: center; flex: 0 0 auto; position: relative; }
    /* Magnifier stays on the left; when opened, the box grows to its right and, because the tools
       are right-aligned, the whole search shifts left. Active = primary color, no background. */
    .up-search.is-open .up-search-btn { color: var(--vc-text); background: transparent; }
    .up-search-box {
      width: 0; opacity: 0; pointer-events: none; overflow: hidden;
      display: flex; align-items: center; position: relative;
      transition: width .22s cubic-bezier(.22,1,.36,1), opacity .16s ease, margin-left .22s cubic-bezier(.22,1,.36,1);
      margin-left: 0;
    }
    .up-search.is-open .up-search-box { width: 200px; opacity: 1; pointer-events: auto; margin-left: 2px; }
    /* No box/border — just free text like Notion's in-table search. */
    .up-search-input {
      flex: 1 1 auto; min-width: 0; height: 32px; padding: 0;
      border: 0; background: transparent; color: var(--vc-text);
      font: inherit; font-size: 13px; outline: 0;
    }
    .up-search-input::placeholder { color: var(--vc-third); }
    /* Active (open) search reads a touch larger/bolder — applies to typed text and placeholder. */
    .up-search.is-open .up-search-input { font-size: 14px; font-weight: 500; }
    /* The clear button's slot is ALWAYS reserved (visibility, not display), so text never shifts
       when the x appears. */
    .up-search-clear {
      flex: 0 0 24px; width: 24px; height: 24px; border: 0; border-radius: 7px; background: transparent;
      color: var(--vc-muted); cursor: pointer; padding: 0; margin-left: 4px;
      display: inline-flex; align-items: center; justify-content: center;
      visibility: hidden;
    }
    .up-search-clear svg { width: 16px; height: 16px; display: block; }
    .up-search-clear:hover { color: var(--vc-text); background: var(--vc-heading-bg); }
    .up-search.has-text .up-search-clear { visibility: visible; }
    /* Mobile: an open search takes over the toolbar row. Below MOBILE_SEARCH_MAX the other
       tools hide so the field spans from the heading to the right edge; they return on close. */
    .up-root.is-searchtakeover .up-head-tools { flex: 1 1 auto; min-width: 0; }
    /* :not(.up-toolgroup) seit der einklappbaren Werkzeuggruppe: die Suche liegt dann eine Ebene
       tiefer, und ohne die Ausnahme blendet diese Regel mit der Gruppe die Suche selbst aus --
       auf einer schmalen Tabelle waere die Suchuebernahme damit eine leere Zeile. */
    .up-root.is-searchtakeover .up-head-tools > :not(.up-search):not(.up-toolgroup) { display: none !important; }
    /* Waehrend der Uebernahme ist die Gruppe offen und ungekappt, zeigt aber nur die Suche. */
    .up-root.is-searchtakeover .up-toolgroup { grid-template-columns: 1fr; opacity: 1; margin-right: 0; }
    .up-root.is-searchtakeover .up-toolgroup-in { overflow: visible; }
    .up-root.is-searchtakeover .up-toolgroup-in > :not(.up-search) { display: none; }
    .up-root.is-searchtakeover .up-search { flex: 1 1 auto; min-width: 0; }
    .up-root.is-searchtakeover .up-search-box { width: auto; flex: 1 1 auto; }
    .up-root.is-searchtakeover .up-heading { display: none; }             /* search owns the whole row */
    .up-root.is-searchtakeover .up-search-clear { visibility: visible; }  /* far-right X closes/resets search */

    /* ---------- column info icon + explainer tooltip ---------- */
    
    /* ---- Variations-Abschnitt (aus performance-detail nach core, zweiter Verbraucher
       brand-detail). Kopfzeile, Untertitel und Tabellenrahmen stehen hier; die Spaltenbreiten
       bleiben bei der jeweiligen Komponente, weil die Tabelle dort verschieden breit steht. */
    /* Der Abschnitt polstert sich selbst. Vorher hing die Polsterung an einer Klasse der
       aufrufenden Komponente -- beim Umzug nach core fiel sie weg, und der Abschnitt klebte an
       allen vier Kanten. Steht sie hier, kann das keiner Seite mehr passieren. */
    .up-varsec { padding: 16px; }
    .up-sec-head { display: flex; align-items: center; gap: 12px; min-height: 32px; margin-bottom: 14px; }
    .up-sec-titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
    .up-sec-h { flex: 0 0 auto; }
    .up-sec-sub { font-size: 12px; font-weight: 400; color: var(--vc-muted); }
    /* 16px wie jeder andere Container mit Rahmen in dieser App (.up-box). Vorher 10 -- ein
       kleinerer Innenradius wirkt nur dort richtig, wo ein groesserer Rahmen direkt aussen
       anliegt; hier liegt Polsterung dazwischen, und dann liest sich die Abweichung als Fehler. */
    /* MIT EIGENER FLAECHE (08.10. gemeldet: "im Event Detail fehlt im Darkmode der Hintergrund in
       den Rows -- ich dachte, das sind core-Tabellen"). Die Tabelle war fuer die Variations gebaut,
       und die standen immer IN einer Karte (Brand Detail, frueher das Performance Detail): sie
       borgte deren Flaeche und hatte nie eine eigene. Das Event-Detail und das neue Performance
       Detail stellen sie offen auf die Seite -- gemessen im Dunkeln: Zeilen durchsichtig, also
       Seitengrund #101114 statt der Kastenflaeche #1c1c1f daneben. Jetzt dieselbe Flaeche wie
       .up-box, und im Dunkeln dieselbe Kante (siehe die zwei Listen weiter unten). */
    .up-vartable { border: 1px solid var(--vc-border); border-radius: 16px; overflow: hidden; background: var(--vc-bg); }
    /* Mit .up-vartable davor, weil .up-row aus core dieselbe Spezifitaet haette und dann die
       Ladereihenfolge entscheiden wuerde -- STYLEGUIDE: nie ueber die Reihenfolge gewinnen. */
    .up-vartable .up-row.up-vrow { height: 55px; flex: 0 0 55px; }
    .up-vartable .up-thead.up-vrow { height: 40px; flex: 0 0 40px; }
    /* .up-row ist in dieser App ein GRID mit var(--up-cols) -- das Spaltenraster der grossen
       Tabellen. Die Variationstabelle hat kein solches Raster, sie hat drei feste Zellen. Ohne
       diese Zeile erbt sie das Grid der zuletzt definierten Tabelle und schiebt ihre Zellen in
       fremde Spalten. Mit .up-vartable davor, damit die Spezifitaet hoeher liegt als die
       Grid-Regel und nicht die Ladereihenfolge entscheidet. */
    .up-vartable .up-row.up-vrow, .up-vartable .up-thead.up-vrow { display: flex; }
    .up-vartable .up-row { cursor: default; }
    /* Sieben Zeilen ganz, die achte angeschnitten: 7.5 x 55px. Daran sieht man, dass es
       weitergeht -- ein sauberer Schnitt sieht aus wie das Ende der Liste. */
    /* scrollbar-gutter: stable UND das gleiche Polster am Kopf -- beides zusammen, sonst stimmt einer
   der beiden Faelle nicht:
   - Ohne stable reserviert der Koerper die Leiste nur, WENN er scrollt. Eine kurze Liste haette
     dann ein Polster am Kopf, dem im Koerper nichts entspricht.
   - Ohne das Polster am Kopf verliert der Koerper unter Windows rund 15px Inhaltsbreite und der
     Kopf nicht. Genau das war die Meldung aus Performance Detail: die Spaltentitel standen versetzt
     zu den Spalten darunter.
   --up-sbw misst core.js einmal beim Start; auf macOS ist es 0, dort aendert sich also nichts. */
.up-vbody { max-height: 412.5px; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; }
.up-vartable .up-thead.up-vrow { padding-right: var(--up-sbw, 0px); }
    /* Die drei Zellbreiten. Der Name nimmt den Rest, die beiden Zahlenspalten stehen fest --
       sonst schiebt ein langer Variantenname die rechte Spalte aus der Tabelle heraus.
       min-width:0 am Namen, sonst weigert sich die Flex-Box zu schrumpfen und die Zeile laeuft
       ueber die rechte Kante hinaus. */
    .up-var-name { flex: 1 1 auto; min-width: 0;
      font-size: 13px; font-weight: 500; color: var(--vc-text); }
    .up-th-vname { flex: 1 1 auto; min-width: 0; }
    .up-var-sov, .up-th-vsov { flex: 0 0 150px; }
    .up-var-cnt, .up-th-vcnt { flex: 0 0 150px; }
    /* Der Name sitzt in einem eigenen Span: der Truncation-Tooltip misst scrollWidth gegen
       clientWidth, und das geht nur an dem Element, das den Text wirklich abschneidet. */
    .up-varname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Zahl links, Ring an der rechten Spaltenkante. Die 16px zum Zellenrand kommen aus der
       Polsterung von .up-td -- kein zweiter Wert, der irgendwann abweicht. margin-left:auto
       statt space-between: kaeme ein drittes Element dazu, saesse es sonst in der Mitte. */
    .up-var-sov { justify-content: flex-start; gap: 18px; }
    .up-var-sov .up-varring { margin-left: auto; }
    .up-var-sov .up-num { font-variant-numeric: tabular-nums; }
    .up-varring { flex: 0 0 auto; display: inline-flex; line-height: 0; }
    /* 40 Prozent groesser: 16 x 1.4 = 22.4. Die Groesse steht hier und nicht im SVG -- die
       viewBox bleibt 16x16, Radius und Strichstaerke muessen nicht nachgerechnet werden. */
    .up-varring svg { display: block; width: 22.4px; height: 22.4px; }
    .up-varring-track { stroke: var(--vc-heading-bg); }
    .up-varring-fill { stroke: var(--up-accent); transition: stroke-dasharray 240ms ease; }
    .up-var-cnt { gap: 6px; }
    /* Im Skelett sitzen die Balken dort, wo spaeter die Werte stehen: in den Zahlenspalten
       rechts. Ohne das wandert der Balken nach links und das Skelett kuendigt eine andere
       Tabelle an als die, die kommt. */
    .up-tsk .up-var-sov, .up-tsk .up-var-cnt { justify-content: flex-end; }
    /* Der Bezugswert steht kleiner daneben, nicht als eigene Spalte: er erklaert die Zahl links,
       er ist keine zweite Zahl. */
    .up-var-of { font-size: 12px; font-weight: 400; color: var(--vc-fourth); white-space: nowrap; }

    /* ---- Variantentabelle auf schmalen Breiten ----------------------------------------------
       Die zwei Zahlenspalten stehen fest auf je 150px. Auf einem Telefon ist das der ganze
       Bildschirm: bei 360px Traegerbreite bleiben dem Namen 326 - 300 = 26px. Das ist keine
       Spalte mehr, das ist ein Rest, und der Name ist die Spalte, um die es hier geht.
       Nur is-vnarrow, gemessen an der EIGENEN Box (UC.widthTiers) und nicht am Fenster. NICHT
       zusaetzlich is-narrow: das greift je Komponente ab 560 bis 768px, und dort ist Platz --
       eine Verengung waere Schaden ohne Not.

       Der Ring fliegt raus. Er ist die zweite Darstellung derselben Zahl, die unmittelbar links
       daneben steht: wenn Platz fehlt, geht die Wiederholung und nicht der Wert.
       Der Mention Count faellt ganz weg. Ausdruecklich so gewollt -- lieber eine Zahl weniger als
       ein Name, von dem drei Buchstaben und Punkte zu sehen sind.
       Die 130px sind gemessen, nicht gerundet: "Share of Voice" plus die Erklaer-Raute plus
       12/16 Polsterung und 6 Abstand brauchen 129px. Mit 96px war die SPALTENUEBERSCHRIFT
       abgeschnitten -- dann verschiebt man das Problem nur von der Zeile in den Kopf. */
    .up-root.is-vnarrow .up-var-sov .up-varring { display: none; }
    .up-root.is-vnarrow .up-var-sov, .up-root.is-vnarrow .up-th-vsov { flex: 0 0 130px; }
    .up-root.is-vnarrow .up-var-cnt, .up-root.is-vnarrow .up-th-vcnt { display: none; }
    .up-root[data-theme="dark"] .up-varring-track { stroke: var(--vc-border); }
    .up-root[data-theme="dark"] .up-varring-fill { stroke: var(--up-accent); }

    .up-th-info, .up-intent-erklaer {
      width: 14px; height: 14px; flex: 0 0 14px; margin-left: auto;
      color: var(--vc-third); opacity: 0; transition: opacity .14s ease;
    }
    .up-th-info svg, .up-intent-erklaer svg { width: 14px; height: 14px; display: block; }
    /* Im Intent-Menue (14.10.) dasselbe Zeichen, dauerhaft sichtbar wie an "Brand Colors": man
       soll sehen, DASS es eine Erklaerung gibt. Eigene Klasse, siehe intentMenuHtml. */
    .up-intent-erklaer { opacity: 1; cursor: pointer; }
    .up-intent-erklaer:hover { color: var(--vc-text); }
    /* DER KOPF DES INTENT-MENUES GLEICH WEIT VON OBEN UND RECHTS (14.10., gemeldet: "das Info-
       Zeichen hat rechts weniger Abstand als oben"). Vorher sass das 14px-Zeichen direkt an der
       Polsterkante (rechts 8px), oben kamen 4px Kopfpolster und die halbe Zeilenhoehe dazu (13px).
       Jetzt ein 22px-Feld mit dem Zeichen in der Mitte, Kopf ohne Polster oben: das Zeichen steht
       oben und rechts je 8 + 4 = 12px von der Kante, und das Feld traegt den Hover der anderen
       Icon-Knoepfe. 22 = 14 + 2 x 4, Radius 6 aus der Skala. */
    /* Mit .up-pop-head verbunden: dessen Polster (4 0 8) steht weiter unten in der Datei und
       gewaenne bei gleicher Spezifitaet. */
    .up-pop-head.up-intent-kopf { display: flex; align-items: center; gap: 8px; min-height: 22px; padding: 0 0 6px; }
    .up-intent-kopf .up-intent-erklaer { width: 22px; height: 22px; flex: 0 0 22px; display: grid; place-items: center;
      border-radius: var(--up-r-s); }
    .up-intent-kopf .up-intent-erklaer:hover { background: var(--vc-heading-bg); }
    .up-th:hover .up-th-info { opacity: 1; }
    .up-th-info:hover { color: var(--vc-text); }
    /* DIE ERKLAERKARTE -- EIN Bauteil fuer die ganze App (28.09. zusammengefuehrt).
       Sie haengt am <body> oder im Portal ihres Konsumenten (UC.makeExplain cfg.mount), also
       ausserhalb von .up-root. Deshalb stand hier jede Farbe literal, und vier Kopien daneben
       taten dasselbe (domains-table, visibility-chart, quick-actions, create-with-ai). Seit die
       umgekehrten Token (--vc-inverse-bg/-ink) und die Tiefen an :root und [data-theme="dark"]
       stehen, erreichen sie die Karte ueberall: sie traegt ihr Thema selbst als data-theme, und
       die dunkle Fassung kommt aus dem Token statt aus einer zweiten Regel je Farbe. */
    .up-explain {
      position: fixed; z-index: 2147483001; width: 248px; padding: 8px;
      /* border: 0 gegen den UA-Rahmen des Top Layers. UC.makeExplain hebt die Karte per
         showPopover() nach oben, und das UA-Stylesheet gibt jedem [popover] "border: solid" --
         gemessen 3px in Schwarz (CanvasText). Die Karten von makeExplain waren dadurch innen 6px
         schmaler als die der Tabellen (226 statt 232) und trugen im Dunkeln einen schwarzen
         Rahmen um den hellen Grund. core.js setzt margin und inset schon zurueck, den Rahmen
         nicht. Aus demselben Grund steht color hier: sonst gilt CanvasText aus derselben Quelle. */
      border: 0; border-radius: 14px;
      background: var(--vc-inverse-bg); color: var(--vc-inverse-ink);
      box-shadow: var(--up-e-2);
      /* Ausdruecklich links: die Quick Actions haengen die Karte in ihren Schleier, und was
         Bubble dort an Ausrichtung vorgibt, darf den Fliesstext nicht zentrieren. Stand bisher
         nur in ihrer Kopie. */
      text-align: left;
      opacity: 0; pointer-events: none; transition: opacity .14s ease, transform .14s ease;
      transform: translateY(-4px);
      font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
      box-sizing: border-box;
    }
    .up-explain * { box-sizing: border-box; }
    .up-explain.is-on { opacity: 1; transform: translateY(0); }
    /* caret, positioned per-instance so it always points at the info icon that opened it */
    .up-explain::before {
      content: ""; position: absolute; top: -6px; left: var(--up-caret, 50%);
      transform: translateX(-50%);
      border-left: 12px solid transparent; border-right: 12px solid transparent;
      border-bottom: 11px solid var(--vc-inverse-bg);
    }
    /* flipped above the header when there is no room below */
    .up-explain.is-flipped::before {
      top: auto; bottom: -10px;
      border-bottom: 0; border-top: 11px solid var(--vc-inverse-bg);
    }
    /* the visual sits in its own light panel on top; explanation below it on the dark ground */

    
    /* dark mode inverts the whole card; the inner panel stays a light surface either way */

    

    
    
    /* Die drei Dunkel-Regeln fuer Grund und Spitze sind entfallen: das dreht jetzt das Token. */
    /* Explainer panel content — the light "visual sample" plate + heading/body text on the dark
       ground. prompts-table, urls-table, visibility-chart und domains-table trugen je eine
       wortgleiche Kopie unter eigenem Praefix; seit dem 28.09. zeigen alle vier hierher. */
    /* Column flex, not a plain block: the rows inside are inline-flex, so in a block container
       they flow side by side instead of stacking -- Share showed its up-row and down-row on one
       line and URL Type put all three chips in a row. domains-table's own .udt-explain-vis always
       had this and looked right; the core copy that urls-table migrated onto had dropped it.
       Values kept identical to that rule so both tables render the same panel. */
    /* Die Platte ist die Karte noch einmal umgekehrt: ihr Grund ist die Tinte der Karte, ihre
       Schrift der Grund der Karte. Im Hellen genau die alten Werte (#ffffff / #1f1f1b); im
       Dunkeln #1f1f1b statt #1c1c1f und #eeedef statt #f0f0f0 -- drei und zwei Stufen, und
       create-with-ai trug fuer seine Probe schon genau diese Schrift. color steht an der Platte,
       damit alles darauf die Schrift erbt, statt sie Zeile fuer Zeile zu setzen. */
    .up-explain-vis {
      background: var(--vc-inverse-ink); color: var(--vc-inverse-bg);
      border-radius: 9px; padding: 11px 12px;
      display: flex; flex-direction: column; gap: 7px; align-items: flex-start;
      margin-bottom: 9px; min-height: 46px; justify-content: center;
    }
    .up-explain-h { font-size: 14.5px; font-weight: 600; line-height: 1.2; padding: 0 3px; margin-bottom: 4px; color: var(--vc-inverse-ink); }
    /* 68 Prozent der Tinte, in beiden Themen. Im Dunkeln standen 66 -- eine Mischung statt zweier
       Werte, und die zwei Punkte mehr Deckung heben den Kontrast auf dem hellen Grund leicht an. */
    .up-explain-t { font-size: 13.5px; font-weight: 400; line-height: 1.5; padding: 0 3px; color: color-mix(in srgb, var(--vc-inverse-ink) 68%, transparent); }
    .up-explain-row { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 500; color: var(--vc-inverse-bg); }
    .up-explain-row svg { width: 14px; height: 14px; display: block; }
    /* Die Sentiment-Vorschau (UC.sentHtml) in der Zeile: Zahl in Schrift und Farbe der Zeile, wie
       "18.4%" daneben -- die Karte haengt ausserhalb von .up-root, --vc-text loest dort nicht auf. */
    .up-explain-row .up-sent-val { font-size: inherit; color: inherit; }
    .up-explain-up { color: #2ea84a; }
    .up-explain-down { color: #b0200c; }
    /* Matches the real .up-stack-item/.up-stack-vis chip exactly (28x28, radius 8, 4px ring) —
       this sample must look like the actual table cell, not an older circular approximation of it. */
    /* Ohne Rahmen, seit der echte Chip keinen mehr hat -- die Zeile darueber verspricht, dass
       diese Probe aussieht wie die Zelle. */
    /* Der 4px-Ring ist die Farbe der Platte (Tinte der Karte), damit sich die Punkte gegenseitig
       ausschneiden wie in der Zelle -- er folgt der Platte jetzt ueber das Token, auch im Dunkeln. */
    .up-explain-dot { width: 28px; height: 28px; border-radius: 8px; background: #eeedef;
      border: 0; box-shadow: 0 0 0 4px var(--vc-inverse-ink); display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600; color: #6f6f6f; }
    .up-explain-more { background: #f0f0f0; }
    .up-explain[data-theme="dark"] .up-explain-up { color: #6ee7b7; }
    .up-explain[data-theme="dark"] .up-explain-down { color: #fca5a5; }
    .up-explain[data-theme="dark"] .up-explain-dot { background: #28282c; border-color: #4a4a4a; color: #b8b8b8; }
    .up-explain[data-theme="dark"] .up-explain-more { background: #2c2c2c; }

    

    

    /* ---------- pagination ---------- */
    .up-foot {
      display: flex; align-items: center; justify-content: space-between; gap: 16px;
      margin-top: 16px; min-height: 32px; flex-wrap: wrap;
    }
    .up-pagesize { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }
    .up-pagesize-lbl { font-size: 12px; font-weight: 400; color: var(--vc-muted); white-space: nowrap; }
    .up-pagesize-seg { display: inline-flex; align-items: center; gap: 0; height: 32px; padding: 0;
      background: var(--vc-switch-rail); border-radius: 8px; }
    .up-pagesize-btn { height: 32px; min-width: 34px; padding: 0 9px; border: 1px solid transparent; border-radius: 8px;
      background: transparent; color: var(--vc-muted); cursor: pointer;
      font: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      display: inline-flex; align-items: center; justify-content: center; }
    .up-pagesize-btn:hover:not(.is-active) { color: var(--vc-text); }
    .up-pagesize-btn.is-active { background: var(--vc-switch-pill); color: var(--vc-text); border-color: var(--vc-switch-border); }
    .up-root[data-theme="dark"] .up-pagesize-btn.is-active { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }

    .up-pager { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; margin-left: auto; }
    .up-pager-info { font-size: 12px; font-weight: 400; color: var(--vc-muted); white-space: nowrap; margin-right: 8px; }
    .up-page {
      min-width: 32px; height: 32px; padding: 0 8px; border: 1px solid transparent; border-radius: 8px;
      background: transparent; color: var(--vc-muted); cursor: pointer;
      font: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    }
    /* :hover:not(:disabled) is (0,3,0) and outranks .is-active (0,2,0) — without excluding the
       active page here, hovering the page you are already on lightened it back up. */
    .up-page:hover:not(:disabled):not(.is-active) { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-page.is-active { background: var(--up-accent); color: var(--up-accent-fg); border-color: var(--up-accent); font-weight: 600; }
    .up-page:disabled { opacity: .35; cursor: default; }
    .up-page svg { width: 15px; height: 15px; display: block; }
    .up-page-gap { min-width: 20px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
      color: var(--vc-third); font-size: 12px; flex: 0 0 auto; }
    .up-root.is-vnarrow .up-pager-info, .up-root.is-vnarrow .up-pagesize-lbl { display: none; }
    /* Wrapped onto two lines (not enough room for both groups side by side) — centre each line
       instead of leaving page size hugging the left edge and page nav the right. See
       syncFootWrap() in UC.makePager, shared by every table using this footer. */
    .up-foot.is-wrapped { justify-content: center; }
    .up-foot.is-wrapped .up-pagesize { flex: 1 1 100%; justify-content: center; }
    .up-foot.is-wrapped .up-pager { flex: 1 1 100%; justify-content: center; margin-left: 0; }

    /* ---------- filter dropdown (citation types) ---------- */
    .up-filter { position: relative; flex: 0 0 auto; }
    /* a labelled control rather than a bare icon: the active selection has to be readable
       without opening the menu */
    .up-filter-btn {
      display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;
      border: 1px solid transparent; border-radius: 8px; background: transparent;
      color: var(--vc-text); font: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      cursor: pointer; white-space: nowrap; max-width: 220px;
    }
    /* Same hover/open treatment as every other dropdown trigger in the app: background goes to
       --vc-heading-bg, the border never changes (there isn't one to change). */
    .up-filter-btn:hover { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-filter-btn:hover .up-filter-btn-chev { color: var(--vc-text); }
    .up-filter.is-open .up-filter-btn { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-filter.is-open .up-filter-btn-chev { color: var(--vc-text); }
    /* An applied filter wears the same face as an open dropdown: background in --vc-heading-bg,
       label in --vc-text. "This control is doing something" and "this control is open" are the
       same visual weight on purpose — both mean "look here". */
    .up-filter.is-active .up-filter-btn { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-filter.is-active .up-filter-btn-chev { color: var(--vc-text); }
    /* Hovering an ACTIVE trigger swaps the chevron for an x: the shortcut out of the filter,
       without opening the menu to reach Reset. Inactive triggers keep their chevron. */
    .up-filter-btn-clear { display: none; width: 14px; height: 14px; flex: 0 0 14px; color: var(--vc-muted); }
    .up-filter-btn-clear:hover { color: var(--vc-text); }
    .up-filter.is-active .up-filter-btn:hover .up-filter-btn-chev { display: none; }
    .up-filter.is-active .up-filter-btn:hover .up-filter-btn-clear { display: block; }
    .up-filter-btn-lbl { overflow: hidden; text-overflow: ellipsis; }
    .up-filter-btn-chev { width: 14px; height: 14px; flex: 0 0 14px; color: var(--vc-muted); }
    .up-filter-menu {
      position: absolute; top: calc(100% + var(--up-dd-gap)); right: 0; z-index: 60;
      min-width: 280px; max-width: 320px; max-height: 400px;
      background: var(--vc-bg); border: 1px solid var(--vc-border); border-radius: var(--up-dd-radius);
      box-shadow: var(--up-dd-shadow); padding: var(--up-dd-pad);
      display: flex; flex-direction: column;
      opacity: 0; transform: translateY(-4px) scale(0.985); pointer-events: none; transition: opacity 140ms ease, transform 140ms ease;
    }
    .up-filter-menu.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
    .up-root[data-theme="dark"] .up-filter-menu { background: var(--up-menu-bg); }
    .up-filter-dim { flex: 0 0 auto; margin: 2px 2px 8px; display: inline-flex; padding: 0; gap: 0;
      background: var(--vc-switch-rail); border-radius: 8px; border: 0; height: 32px; width: 100%; }
    .up-filter-dim-btn { height: 32px; padding: 0 12px; border: 1px solid transparent; border-radius: 8px; background: transparent;
      color: var(--vc-muted); cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      display: inline-flex; align-items: center; justify-content: center; flex: 1 1 50%; }
    .up-filter-dim-btn:hover { color: var(--vc-text); }
    .up-filter-dim-btn.is-active { background: var(--vc-switch-pill); color: var(--vc-text); border-color: var(--vc-switch-border); }
    .up-root[data-theme="dark"] .up-filter-dim-btn.is-active { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }
    /* Fixed height so the row doesn't grow when the Reset button appears — otherwise the whole
       menu jumps a few pixels the moment you tick the first item. */
    /* KEINE waagerechte Polsterung. Sie stand bei 9px, und die Ueberschrift sass damit 9px
       rechts von allem anderen im Menue: Suchfeld, Zeilen und deren Hoverflaeche beginnen an
       der Polsterkante des Menues (8px). Gemessen im Firmen-Dropdown: Titel 18px, Suchfeld
       und Zeilen 9px. Gemeldet am 22.09. mit Bild. */
    .up-filter-head { flex: 0 0 auto; height: 26px; display: flex; align-items: center; justify-content: space-between; padding: 0; margin-bottom: 6px; }
    /* Werte siehe .up-label weiter unten -- hier stand dieselbe Kombination ein zweites Mal. */
    .up-filter-reset { border: 0; background: transparent; color: var(--vc-muted); font-family: inherit; font-size: 11px; font-weight: 500; cursor: pointer; padding: 2px 4px; border-radius: 6px; }
    .up-filter-reset:hover { color: var(--vc-text); }
    /* 4px between rows in every dropdown list. Was 2px here and something different in each
       component -- one number, one place. */
    .up-filter-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
    .up-filter-item { display: flex; align-items: center; gap: 10px; padding: 6px 9px; border-radius: 8px; cursor: pointer; min-width: 0; }
    .up-filter-item:hover { background: var(--vc-heading-bg); }
    .up-filter-check { width: 16px; height: 16px; border-radius: 5px; border: 1.5px solid var(--vc-border); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; color: var(--up-accent-fg);
      transition: background-color var(--up-cb-t) var(--up-cb-ease), border-color var(--up-cb-t) var(--up-cb-ease); }
    /* display:block, not inline: an inline SVG sits on the text baseline, which pushes the tick a
       little low inside the 16px box -- the "not quite vertically centred" report. block + the
       flex centring on the parent puts it on the true middle. */
    /* Der Haken wird ein- und ausgeblendet statt per display umgeschaltet -- display kennt
       keinen Zwischenschritt, und genau das war das Springen. Er waechst dabei aus 70%: dieselbe
       Bewegung wie beim Oeffnen der Dropdown-Panels, nur kleiner. */
    .up-filter-check svg { width: 11px; height: 11px; display: block; opacity: 0; transform: scale(.7);
      transition: opacity var(--up-cb-t) var(--up-cb-ease), transform var(--up-cb-t) var(--up-cb-ease); }
    .up-filter-item.is-checked .up-filter-check svg { opacity: 1; transform: scale(1); }
    .up-filter-item.is-checked .up-filter-check { background: var(--up-accent); border-color: var(--up-accent); }
    /* Gewaehlte Zeile traegt den App-Selected-Ton -- im Types-Dropdown immer, in einer
       Marken-Liste nur auf Ansage.

       Der Unterschied ist nicht kosmetisch, er haengt am Ausgangszustand: wo nichts vorbelegt
       ist (die Filter der Tabellen starten auf "All Brands"), markiert die gefuellte Zeile die
       wenigen Dinge, die der Nutzer selbst gewaehlt hat -- das ist genau die Auskunft, die man
       dort sucht. Wo dagegen ALLE Marken vorbelegt sind (Visibility-Chart, Heatmap: dort ist
       die Liste die Legende und startet vollstaendig angehakt), waere jede Zeile gefuellt und
       die Markierung sagt nichts mehr, sie macht das Panel nur unruhig.

       Darum eine Klasse, die die Komponente selbst setzt, statt einer Regel, die raet. */
    .up-filter-menu .up-filter-item.is-checked,
    .up-filter-list.is-fill-checked .up-filter-item.is-checked { background: var(--up-sel-bg); }
    /* the dropdown shows the SAME chip the table uses — identical to TopCitations, so a type
       looks the same wherever it appears */
    /* Rand 1px (Farbe inline aus UC.typeStyle) innerhalb von border-box, damit Hoehe und Polster bleiben; Schnitt 500 statt 600, seit Variante D (13.10.) traegt die Farbe den Chip, nicht die Staerke. */
    .up-filter-tag { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px; border-radius: 7px;
      border: 1px solid transparent; box-sizing: border-box;
      font-size: 12px; font-weight: 500; white-space: nowrap; max-width: 100%; overflow: hidden; }
    .up-filter-tag-lbl { overflow: hidden; text-overflow: ellipsis; }
    /* Nur noch die Lage -- Aussehen kommt aus .up-btn-pri weiter unten.
       flex: 0 0 auto ist PFLICHT (25.09. gemeldet: "Anwenden im URL-Type-Reiter nur 16px hoch").
       Das Menue ist eine Flex-Spalte mit max-height 400px, und ein Flex-Kind schrumpft von sich
       aus mit, sobald der Inhalt nicht passt -- bis auf seine Texthoehe. Bei den sieben Citation
       Types passte alles, bei den vierzehn URL Types lief die Liste ueber, und der Knopf wurde
       anteilig mitgestaucht. Schrumpfen soll allein die Liste; sie scrollt. */
    .up-filter-submit { width: 100%; margin-top: 6px; flex: 0 0 auto; }
    /* DERSELBE HOVER WIE .up-btn-pri, und das ist der Punkt (19.09. gemeldet: "im Darkmode kommt
       als Hoverstate der klassische Grund aus dem Standard-Setting -- das muessen einfach Core-
       Buttons sein"). Hier standen zwei feste Grautoene, einer je Thema: sie waren die
       aufgehellte bzw. abgedunkelte Fassung der FRUEHEREN Primaerfarbe und kannten die Wahl
       nicht. Eine Deckkraft kennt sie, weil sie nichts ueber die Farbe annimmt -- und sie ist
       das, was jeder andere Primaerknopf der App im Hover tut. */
    .up-filter-submit:not(:disabled):hover { opacity: .88; }

    /* ---------- mentioned brands filter (multi-select) ---------- */
    .up-ment { position: relative; flex: 0 0 auto; }
    .up-ment-btn {
      display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;
      border: 1px solid transparent; border-radius: 8px; background: transparent;
      color: var(--vc-text); font: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      cursor: pointer; white-space: nowrap; max-width: 200px;
    }
    .up-ment-btn:hover { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-ment-btn:hover .up-ment-chev { color: var(--vc-text); }
    .up-ment.is-open .up-ment-btn { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-ment.is-open .up-ment-chev { color: var(--vc-text); }
    .up-ment.is-active .up-ment-btn { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-ment.is-active .up-ment-chev { color: var(--vc-text); }
    .up-ment-clear { display: none; width: 14px; height: 14px; flex: 0 0 14px; color: var(--vc-muted); }
    .up-ment-clear:hover { color: var(--vc-text); }
    .up-ment.is-active .up-ment-btn:hover .up-ment-chev { display: none; }
    .up-ment.is-active .up-ment-btn:hover .up-ment-clear { display: block; }
    .up-ment-lbl { overflow: hidden; text-overflow: ellipsis; }
    /* Ein Zeichen statt eines Logos in der Zeile (UC.makeAuswahlFilter, Typen der Events): so breit
       wie .up-ment-logo, damit die Namen in derselben Flucht stehen. */
    .up-ment-zeichen { width: 18px; height: 18px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; color: var(--vc-third); }
    .up-ment-zeichen svg { width: 14px; height: 14px; }
    .up-ment-chev { width: 14px; height: 14px; flex: 0 0 14px; color: var(--vc-muted); }
    .up-ment-menu {
      position: absolute; top: calc(100% + var(--up-dd-gap)); right: 0; z-index: 60;
      min-width: 260px; max-width: 320px; max-height: 520px;
      background: var(--vc-bg); border: 1px solid var(--vc-border); border-radius: var(--up-dd-radius);
      box-shadow: var(--up-dd-shadow); padding: var(--up-dd-pad);
      display: flex; flex-direction: column;
      opacity: 0; transform: translateY(-4px) scale(0.985); pointer-events: none; transition: opacity 140ms ease, transform 140ms ease;
    }
    .up-ment-menu.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
    /* Linksbuendig, wenn rechtsbuendig links aus dem Bild liefe (makeAuswahlFilter misst das). */
    .up-ment-menu.is-links { right: auto; left: 0; }
    .up-root[data-theme="dark"] .up-ment-menu { background: var(--up-menu-bg); }
    .up-ment-logo { width: 18px; height: 18px; border-radius: 5px; flex: 0 0 auto; background: var(--vt-logo-bg); padding: 1px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; }
    .up-ment-logo img { width: 16px; height: 16px; border-radius: 4px; object-fit: cover; display: block; }
    .up-ment-name { font-size: 13px; font-weight: 500; color: var(--vc-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
    /* Die Anzahl eines Eintrags im Auswahl-Filter (makeAuswahlFilter, item.zahl): rechts aussen,
       leise, Tabellenziffern -- sie ordnet ein, sie ist nicht der Eintrag. */
    .up-ment-zahl { margin-left: auto; padding-left: 8px; flex: 0 0 auto; font-size: var(--up-fs-s); color: var(--vc-third); font-variant-numeric: tabular-nums; }
    .up-ment-empty { padding: 12px; font-size: 12px; color: var(--vc-muted); text-align: center; }
    .up-portal { display: contents; }   /* carries the theme + CSS vars to the portaled menus */
    /* Brand search inside the dropdown, plus a capped list so ~10.5 rows show and the 11th peeks. */
    .up-ment-search {
      width: 100%; height: 32px; margin: 2px 0 6px; padding: 0 10px; box-sizing: border-box; flex: 0 0 auto;
      border: 1px solid var(--vc-border); border-radius: 8px; background: var(--vc-bg);
      color: var(--vc-text); font: inherit; font-size: 13px; outline: 0;
    }
    .up-ment-search::placeholder { color: var(--vc-third); }
    .up-ment-search:focus { border-color: var(--up-focus); box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); outline: 0; }
    .up-ment-searchwrap { position: relative; margin: 0 0 8px; flex: 0 0 auto; }
    /* 34 statt 30: das X sitzt 6px vom Rand und ist 20px breit, es endet also bei 26. Mit 30 stand
   der letzte Buchstabe direkt an seiner Kante und wirkte, als liefe der Text darunter. */
    .up-ment-searchwrap .up-ment-search { margin: 0; padding-right: 34px; }
    .up-ment-searchclear {
      position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
      width: 20px; height: 20px; border: 0; border-radius: 6px; background: transparent;
      color: var(--vc-muted); cursor: pointer; padding: 0; display: none; align-items: center; justify-content: center;
    }
    .up-ment-searchclear svg { width: 13px; height: 13px; display: block; }
    .up-ment-searchclear:hover { color: var(--vc-text); }
    .up-ment-search:not(:placeholder-shown) + .up-ment-searchclear { display: inline-flex; }
    .up-ment-list { max-height: 336px; }   /* ~10.5 rows -> the 11th peeks and signals scrolling */
    .up-ment-noresult { padding: 12px; font-size: 12px; color: var(--vc-muted); text-align: center; }
    /* ---- "Not listed? Add your own" unten im Menue (03.10. nach core) --------------------------
       Die Bauart der Branche in Your Brand (settings-brand, dort noch .usb-ddcustom-*), Wert fuer
       Wert: durch eine Linie abgesetzt, ueber und unter ihr derselbe Abstand (12), darunter Feld und
       Knopf nebeneinander. Das X im Feld ist .up-iconbtn.is-20.is-quiet, der Knopf .up-btn-pri --
       hier steht nur die Lage. Das Event-Popup nimmt es fuer den eigenen Typ. */
    .up-ddcustom { --up-ddc-gap: 12px; flex: 0 0 auto; margin-top: var(--up-ddc-gap); padding-top: var(--up-ddc-gap);
      border-top: 1px solid var(--vc-border); display: flex; flex-direction: column; gap: var(--up-ddc-gap); }
    .up-ddcustom-row { display: flex; align-items: center; gap: 8px; }
    .up-ddcustom-field { position: relative; flex: 1 1 auto; min-width: 0; display: block; }
    .up-ddcustom-in.up-ment-search { width: 100%; min-width: 0; margin: 0; padding-right: 34px; }
    /* Zwei Klassen: .up-iconbtn setzt position und display mit einer -- gewonnen wird ueber die
       Spezifitaet, nicht ueber die Reihenfolge (CLAUDE.md 1). */
    .up-ddcustom-field .up-ddcustom-clear { position: absolute; top: 50%; right: 6px; transform: translateY(-50%); display: none; }
    .up-ddcustom-in:not(:placeholder-shown) ~ .up-ddcustom-clear { display: inline-flex; }

    /* ---------- column visibility (gear) ---------- */
    .up-cols { position: relative; flex: 0 0 auto; }
    /* Touch devices: 16px stops iOS from zooming into the search field on focus, and hover
       tooltips make no sense (a tap leaves them stuck on screen). */
    @media (hover: none) {
      /* 16px prevents the iOS focus-zoom and must beat the 14px active rule above (same
         specificity, later in the file), so it targets the open state too. */
      .up-search.is-open .up-search-input { font-size: 16px; }
      .up-tip { display: none !important; }
    }
    /* THE dropdown shell. .up-sort-menu / .up-cols-menu / .up-ment-menu were three near-identical
       copies of it; anything new that needs a dropdown uses .up-menu and adds only its own width.
       Do NOT borrow one of the named classes for a different dropdown: each component looks its
       own menu up with root.querySelector(".up-cols-menu") and a second element carrying that
       class silently steals the lookup -- that is exactly how Table Settings ended up rendering
       into the grouping dropdown. */
    .up-menu, .up-cols-menu { position: absolute; top: calc(100% + var(--up-dd-gap)); right: 0; z-index: 60; width: 230px;
      background: var(--vc-bg); border: 1px solid var(--vc-border); border-radius: var(--up-dd-radius); box-shadow: var(--up-dd-shadow); padding: var(--up-dd-pad);
      opacity: 0; transform: translateY(-4px) scale(0.985); pointer-events: none; transition: opacity 140ms ease, transform 140ms ease; }
    .up-menu.is-shown, .up-cols-menu.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
    .up-root[data-theme="dark"] .up-menu, .up-root[data-theme="dark"] .up-cols-menu { background: var(--up-menu-bg); }

    /* ══ Untermenue: ein Dropdown NEBEN einem Dropdown ═══════════════════════════════════════
       Die Bauart kommt aus ask-mira ("Move to Project" im Chat-Menue) und steht jetzt hier, weil
       die Filterleiste genau dasselbe braucht -- eine Zeile im Panel, aus der seitlich ein
       zweites Panel herausfaehrt. Mira behaelt seine eigenen Klassen: es faehrt mit einem eigenen
       Token-Satz (--am-*), und die beiden zu EINER Regel zu machen wuerde dort die Farben
       aendern. Die GEOMETRIE ist das Geteilte, und die steht ab hier einmal.

       Die Zahl, auf die es ankommt: der Versatz ist NICHT der Abstand allein. Die 100% beziehen
       sich auf die ZEILE, und zwischen deren Kante und der Kante des Panels liegen noch dessen
       Polster und Rahmen. Mit blossen 6px sass das Untermenue in Mira genau auf dem Menue --
       gemessen 1px Ueberlappung, also keine Luecke, kein Schatten dazwischen, und ein Rahmen, der
       auf der weissen Nachbarkarte nicht zu sehen war. Genau das war dort "dieses Dropdown hat
       keine border". Darum: Polster + Abstand + 1px Rahmen.
       Hier aus den Tokens gerechnet statt als feste 13, damit es mitgeht, wenn jemand
       --up-dd-pad oder --up-dd-gap aendert.

       top: negativ um das Polster -- so steht die erste Zeile des Untermenues auf einer Linie mit
       der Zeile, aus der es kommt, und nicht um das Polster tiefer. */
    .up-subwrap { position: relative; }
    .up-submenu {
      position: absolute; top: calc(-1 * var(--up-dd-pad));
      /* RECHTS ist die Vorgabe: der Winkel in der Zeile zeigt nach rechts, und dorthin gehoert
         dann auch das Panel. Nach links nur, wenn rechts kein Platz ist (.is-flipleft, die Klasse
         setzt UC.makeSubmenu nach einer Messung). */
      /* 100% ist die Polsterbox des Panels; +1 ueberspringt seinen Rahmen. Der sichtbare
         Abstand ist damit genau --up-sub-gap. Vorher stand hier zusaetzlich --up-dd-pad,
         und der sichtbare Abstand war 14px (gemessen: offsetLeft 261, Panel 246 + 1). */
      left: calc(100% + var(--up-sub-gap) + 1px);
      z-index: 2;
      /* FESTE Breite. Vorher stand hier nur min-width: 220px, und eine absolut positionierte
         Schale ohne Breite nimmt die ihres Inhalts -- ein langer Name (die eigenen Gruppierungen
         der Topics) machte den Kasten breiter, und beim Wechsel der Zeile sprang die Breite.
         Ueber ein Token, damit ein Aufrufer sie setzen kann, ohne die Regel zu ueberschreiben. */
      width: var(--up-sub-w, 300px); max-width: calc(100vw - 24px);
      background: var(--vc-bg); border: 1px solid var(--vc-border);
      border-radius: var(--up-dd-radius); box-shadow: var(--up-dd-shadow); padding: var(--up-dd-pad);
      opacity: 0; transform: translateX(-4px) scale(0.985); pointer-events: none;
      /* top und height: EINE Schale fuer alle Zeilen rutscht beim Wechsel an ihren neuen Platz
         statt aus- und wieder einzublenden (UC.makeSubmenu, cfg.shell). 100ms und nicht 200 --
         der Weg ist eine Zeilenhoehe, und bei 200 lief das Rutschen der Hand hinterher.
         Ohne Schalen-Modus stehen die beiden Uebergaenge still: dort aendert sich weder top
         noch height. */
      transition: opacity 110ms ease, transform 110ms ease,
                  top 100ms ease, height 100ms ease;
      /* Der Inhalt wird beim Wechsel getauscht, waehrend die Hoehe noch laeuft. Ohne das
         schaut er waehrend des Zuges unten heraus. */
      overflow: hidden;
    }
    /* ── Schalen-Modus: EIN Kasten, Huellen darin ────────────────────────────────────────────
       Die Schale haengt am Panel und nicht an einer Zeile, also entscheidet ihre eigene Klasse
       ueber Sichtbarkeit und Seite -- nicht die der Zeile. Beides steht hier zusaetzlich; die
       Regeln fuer den Bau mit einem Kasten je Zeile bleiben unberuehrt. */
    .up-submenu.is-shown { opacity: 1; transform: translateX(0) scale(1); pointer-events: auto; }
    .up-submenu.is-flipleft {
      left: auto; right: calc(100% + var(--up-sub-gap) + 1px);
      transform: translateX(4px) scale(0.985);
    }
    .up-submenu.is-flipleft.is-shown { transform: translateX(0) scale(1); }
    .up-submenu.is-flipleft::before {
      left: auto; right: calc(-1 * (var(--up-sub-gap) + var(--up-dd-pad) + 6px));
    }
    /* Die Huellen: genau eine ist sichtbar. display und nicht opacity -- eine unsichtbare
       Huelle im Fluss wuerde die Hoehe der Schale mitbestimmen, und die Hoehe ist hier die
       Aussage. */
    .up-subhost { display: none; }
    .up-subhost.is-on { display: block; }
    .up-root[data-theme="dark"] .up-submenu { background: var(--up-menu-bg); border-color: var(--vc-switch-border); }
    /* Offen wird ueber eine KLASSE, nicht ueber :hover. Der Unterschied ist nicht kosmetisch: die
       Zeile laesst sich auch anklicken und bleibt dann offen, bis eine andere Zeile oder ein Klick
       nach draussen kommt -- mit :hover waere das nicht ausdrueckbar. Den Zeiger verfolgt
       UC.makeSubmenu und setzt die Klasse; :hover bleibt zusaetzlich drin, damit ein Untermenue
       auch ohne das Kit aufgeht (und damit es bei stehendem Zeiger nicht unter der Maus zugeht). */
    /* NUR die Klasse, kein :hover. Ein :hover-Zweig stand hier als Weg ohne JS -- er kann aber
       gar nicht greifen: geschlossen ist das Untermenue pointer-events: none, also nie gehovert.
       Schlimmer, er lud dazu ein, ihn fuer den Grund zu halten, wenn ein geschlossenes Panel
       trotzdem Zeiger faengt (der wirkliche Grund war ein Kind, das pointer-events wieder
       aufhob -- siehe filter-bar.css). Eine Sichtbarkeit, eine Quelle. */
    .up-subwrap.is-subopen > .up-submenu { opacity: 1; transform: translateX(0) scale(1); pointer-events: auto; }
    /* Die Bruecke fuer die Maus muss die Luecke GANZ ueberdecken, sonst schliesst das Untermenue
       auf dem Weg dorthin. Sie waechst mit den Tokens mit, plus 2px Reserve.
       Nur die HOEHE DER ZEILE, nicht die des Untermenues: mit height:100% lag ein 300px hoher,
       unsichtbarer Streifen ueber den Zeilen darunter -- der Zeiger erreichte sie nicht mehr, und
       auf dem Weg ins Untermenue meldete jede Zeile darunter ihren eigenen Hover. Die Bruecke
       muss nur den Weg von DIESER Zeile zum Panel decken. */
    .up-submenu::before {
      content: ""; position: absolute; top: var(--up-dd-pad); height: 34px;
      /* Zurueck bis zur rechten Kante der ZEILE: Abstand + Rahmen des Panels + dessen Polster,
         plus 6px Reserve. Die Zeile endet an der Inhaltskante des Panels, nicht an seinem Rand. */
      left: calc(-1 * (var(--up-sub-gap) + var(--up-dd-pad) + 6px));
      width: calc(var(--up-sub-gap) + var(--up-dd-pad) + 6px);
    }
    /* Nach links, wenn rechts kein Platz ist. Die Klasse setzt UC.makeSubmenu nach einer Messung. */
    .up-subwrap.is-flipleft > .up-submenu {
      left: auto; right: calc(100% + var(--up-sub-gap) + 1px);
      transform: translateX(4px) scale(0.985);
    }
    .up-subwrap.is-flipleft > .up-submenu::before {
      left: auto; right: calc(-1 * (var(--up-sub-gap) + var(--up-dd-pad) + 6px));
    }
    .up-subwrap.is-flipleft.is-subopen > .up-submenu { transform: translateX(0) scale(1); }

    /* ── Schmal: hinein statt heraus ─────────────────────────────────────────────────────────
       Auf einem Telefon gibt es kein "daneben" -- ein Panel, das seitlich herausfaehrt, landet
       ausserhalb des Bildes. Und es gibt keinen Zeiger, der ueber einer Zeile stehen bleiben
       kann. Darum wird aus dem Nebeneinander ein Hineingehen: das Untermenue nimmt die Stelle der
       Liste ein, darueber steht eine Zurueck-Zeile. Das ist das Muster, das jede Filterleiste auf
       dem Telefon benutzt, und es braucht keine zweite Mechanik -- nur andere Regeln fuer
       dieselben Klassen. Die Stufe setzt UC.makeSubmenu als .is-drill am PANEL, gemessen an der
       Seitenbreite und nicht an der Breite dieses Elements: ob seitlich Platz ist, entscheidet
       das Fenster. */
    .is-drill .up-submenu {
      position: static; min-width: 0;
      border: 0; border-radius: 0; box-shadow: none; padding: 0;
      background: transparent;
      transform: none; transition: opacity 110ms ease;
      overflow: visible;
    }
    /* Doppelte Klasse: hier muessen die INLINE-Werte fallen, die der Schalen-Modus fuer den
       Zug setzt (top und height in Pixeln). Ein !important waere der andere Weg und ist
       verboten; eine zweite Klasse reicht, weil Inline nur von !important geschlagen wird --
       darum setzt UC.makeSubmenu die beiden im Drill-Modus selbst zurueck, und diese Regel ist
       das Netz fuer den Fall, dass die Stufe zwischen zwei Messungen wechselt. */
    .is-drill .up-submenu.up-submenu {
      top: auto; right: auto; left: auto; width: 100%; max-width: none; height: auto;
    }
    .is-drill .up-submenu::before { content: none; }
    /* Zu heisst hier WEG, nicht nur durchsichtig: ein Panel im Fluss belegt sonst seine Hoehe. */
    .is-drill .up-subwrap:not(.is-subopen) > .up-submenu { display: none; }
    .is-drill .up-subwrap.is-subopen > .up-submenu { opacity: 1; pointer-events: auto; }
    /* Dasselbe fuer die Schale: zu heisst WEG, nicht bloss durchsichtig -- ein Kasten im Fluss
       belegt sonst seine Hoehe unter den Zeilen. */
    .is-drill .up-submenu:not(.is-shown) { display: none; }
    /* Und die Zeilen der ersten Ebene treten zurueck, solange man drin ist. */
    .is-drill.is-inside .up-subrow { display: none; }
    .is-drill.is-inside .up-subwrap:not(.is-subopen) { display: none; }
    /* Die Zurueck-Zeile gibt es nur drinnen. Sie ist eine .up-optrow wie jede andere Zeile -- und
       genau daran haengt der Grund fuer die doppelte Klasse: .up-optrow setzt display: flex, hat
       dieselbe Spezifitaet und steht in dieser Datei WEITER UNTEN. Bei Gleichstand entscheidet die
       Reihenfolge, und die hat gewonnen: die Zeile stand auch auf einer breiten Seite da, wo es
       kein Drinnen gibt (im Bild als "Back" ueber den drei Filtern zu sehen). Ueber die
       Spezifitaet gewonnen, nicht ueber die Reihenfolge -- STYLEGUIDE. */
    .up-subback.up-subback { display: none; }
    .is-drill.is-inside .up-subback.up-subback { display: flex; }
    .up-subback svg { width: 16px; height: 16px; flex: 0 0 16px; display: block; }

    /* ---------------- shared selected row (sorters and any option list) ----------------
       Put .up-optrow on a row that can be picked and .is-on when it is. Hover and selected are
       the SAME tone on purpose: a row you are pointing at and a row that is chosen are both
       "this one", and two different greys next to each other read as two different meanings.
       Deliberately NOT used for selected brands / types / table settings / anything with a
       switch -- those are multi-select lists where a filled row per entry is too busy. */
    .up-optrow {
      display: flex; align-items: center; gap: 8px; width: 100%;
      border: 0; border-radius: 8px; background: transparent; color: var(--vc-text);
      font-family: inherit; text-align: left; cursor: pointer;
    }
    .up-optrow:hover, .up-optrow.is-on { background: var(--up-sel-bg); }
    .up-optrow + .up-optrow { margin-top: 4px; }
    /* 16x16 check, hard right -- the mark the component sorters already use. */
    .up-optrow-check { margin-left: auto; width: 16px; height: 16px; flex: 0 0 16px; display: grid; place-items: center; color: var(--vc-text); opacity: 0; }
    .up-optrow.is-on .up-optrow-check { opacity: 1; }
    .up-optrow-check svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
    /* DAS AKTIONSMENUE (03.10. nach core, Werte 1:1 aus team-orga .uto-menu): Handlungen wie
       "Edit" und "Delete" am Dreipunkt-Knopf einer Zeile oder Karte. Zeilen .up-optrow, 34 hoch,
       Schrift 13/500 -- .up-optrow setzt selbst keine Schrift und erbte sonst die der Seite (im
       Events-Menue gemessen: 16px). Die wegnehmende Zeile steht hinter einem Trennstrich, leise,
       und wird erst am Zeiger rot -- dauerhaft rot liest sich wie ein Fehler. */
    .up-aktionsmenue .up-optrow { min-height: 34px; padding: 0 9px; font-size: var(--up-fs-m); font-weight: var(--up-fw-m); }
    .up-aktionsmenue .up-optrow svg { width: 15px; height: 15px; flex: 0 0 15px; }
    .up-aktionsmenue-lbl { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .up-aktionsmenue-div { height: 1px; background: var(--vc-border); margin: 6px 4px; }
    .up-aktionsmenue .up-optrow.is-gefahr { color: var(--vc-muted); }
    .up-aktionsmenue .up-optrow.is-gefahr:hover { color: var(--vt-down); }
    /* icon buttons have no border, so their open state is the background */
    /* ---------- Icon-Trigger: Hintergrund NUR im offenen Zustand ----------
       Eine Regel fuer die ganze App statt einer pro Komponente. Jeder Popover-Wrapper bekommt
       .is-open von UC.makePopover, jeder Icon-Trigger ist ein .up-iconbtn -- damit greift das
       automatisch fuer jedes kuenftige Icon-Dropdown, ohne dass jemand daran denken muss.

       Und die Unterscheidung, die dabei immer wieder verrutscht: "aktiv" ist NICHT "offen".
       Ein BESCHRIFTETER Trigger (.up-filter-btn, .up-ment-btn: Text plus Chevron) traegt im
       aktiven Zustand einen Hintergrund, weil man ihm sonst nicht ansieht, dass ein Filter
       laeuft. Ein ICON-Button tut das nicht -- dort sagt das Badge, dass etwas gesetzt ist, und
       ein dauerhaft gefuellter Button liest sich als "dieses Menue steht offen". */
    .is-open > .up-iconbtn { color: var(--vc-text); background: var(--vc-heading-bg); }
    .up-cols.is-open .up-cols-btn { color: var(--vc-text); background: var(--vc-heading-bg); }
    /* a column that is the last one left can't be switched off — an empty table helps nobody */
    .up-pop-row.is-locked { opacity: .45; pointer-events: none; }


    /* ---------- Filter-Trigger, der Knopf VOR jedem Dropdown ----------
       Markets, Models, Topics und der Kalender haben diesen Knopf bisher jeder in seiner eigenen
       Datei stehen -- vier byte-identische Kopien. Beim fuenften Verbraucher (Settings) kommt er
       nach core, statt ein fuenftes Mal abgeschrieben zu werden (STYLEGUIDE 25).

       Die Form ist: [Icon] [Beschriftung] [Chevron], zusammengepackt und nur so breit wie sein
       Inhalt. NICHT ein breiter Kasten mit dem Chevron am rechten Rand -- das ist ein
       Formular-Select und sieht in dieser App an keiner Stelle so aus.
       Rahmen sichtbar, Grundfarbe --vc-muted, im Hover und offen --vc-heading-bg mit
       --vc-hover-border. Der Chevron dreht sich nicht. */
    .up-ddtrigger {
      display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;
      border: 1px solid var(--vc-border); border-radius: 8px; background: var(--vc-bg);
      color: var(--vc-muted); font-family: inherit; font-size: 13px; font-weight: 500;
      line-height: 1; white-space: nowrap; cursor: pointer; outline: 0;
      transition: color 120ms ease, background-color 120ms ease, border-color 120ms ease;
    }
    .up-ddtrigger:hover,
    .up-ddwrap.is-open .up-ddtrigger,
    .up-ddwrap.has-sel .up-ddtrigger {
      color: var(--vc-text); background: var(--vc-heading-bg); border-color: var(--vc-hover-border);
    }
    .up-ddtrigger:focus-visible { border-color: var(--vc-hover-border); box-shadow: 0 0 0 2px var(--vc-heading-bg); }
    .up-ddtrigger svg { width: 16px; height: 16px; flex: 0 0 16px; display: block; }
    .up-ddtrigger-ic { display: inline-flex; align-items: center; flex: 0 0 auto; }
    .up-ddtrigger-ic img { width: 20px; height: 14px; border-radius: 3px; object-fit: cover; display: block; }
    .up-ddtrigger-lbl { overflow: hidden; text-overflow: ellipsis; max-width: 22ch; }
    /* margin-left:-2px wie in den vier Filtern: der Chevron hat als Glyph links Luft in sich, ohne
       die Korrektur sieht der Abstand zur Beschriftung groesser aus als der zum Icon. */
    .up-ddtrigger-chev { margin-left: -2px; flex: 0 0 16px; }
    .up-ddwrap { position: relative; display: inline-flex; flex: 0 0 auto; }

    /* ---------- search field INSIDE a dropdown ----------
       Ein anderes Tier als .up-search im Tabellen-Header: das dort faehrt aus einem Icon auf und
       lebt in einer Toolbar; dieses steht dauerhaft offen im Panel. Deshalb ein eigener Typ statt
       eines Modifiers -- drei Komponenten hatten je eine eigene Kopie davon.
       Die Lupe sitzt INNEN RECHTS und wird zum Clear-X sobald Text drin ist, statt dass ein
       zweites Bedienelement daneben auftaucht. */
    .up-ddsearch { position: relative; flex: 1 1 auto; min-width: 0; }
    .up-ddsearch-in {
      width: 100%; height: 32px; box-sizing: border-box; padding: 0 36px 0 11px;
      border: 1px solid var(--vc-border); border-radius: 8px;
      background: var(--vc-bg); color: var(--vc-text);
      font-family: inherit; font-size: 12.5px; outline: none;
      transition: border-color 150ms ease, background 150ms ease;
    }
    .up-ddsearch-in::placeholder { color: var(--vc-third); }
    /* Hover kommt aus core (Hover der Eingabefelder, 13.10.). */
    .up-ddsearch-in:focus { border-color: var(--up-focus); box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); outline: 0; }
    .up-ddsearch-ic {
      position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
      width: 15px; height: 15px; color: var(--vc-third); pointer-events: none;
    }
    .up-ddsearch-ic svg { width: 15px; height: 15px; display: block; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
    .up-ddsearch-x {
      position: absolute; right: 7px; top: 50%; transform: translateY(-50%);
      display: none; place-items: center; width: 20px; height: 20px; padding: 0;
      border: 0; border-radius: 5px; background: transparent; color: var(--vc-third); cursor: pointer;
    }
    .up-ddsearch-x svg { width: 11px; height: 11px; display: block; fill: none; stroke: currentColor; stroke-width: 3.5; stroke-linecap: round; stroke-linejoin: round; }
    .up-ddsearch-x:hover { background: var(--vc-heading-bg); color: var(--vc-text); }
    .up-ddsearch.has-text .up-ddsearch-ic { display: none; }
    .up-ddsearch.has-text .up-ddsearch-x { display: grid; }

    /* ---------- topic chip ----------
       ONE topic chip for the whole app. It lived only in prompts-table.css, so the first other
       place that needed one (the grouping popup, in the SAME component) got a hand-made
       look-alike with different colours, a different check and a different height. Any surface
       that shows a topic uses this: emoji + label on the topic's own colour, the checkbox sliding
       in from the right when selected. Colour comes in through --ust-tag-color. */
    /* No resting border -- a plain tinted chip, background colour alone carries the topic's
       identity. border stays declared at 1px solid transparent (not dropped entirely) purely so
       .is-on below can still show a real border colour as its selected-state indicator without
       the chip shifting size by 2px the moment it's picked. */
    /* EIN Topic-Chip fuer die ganze App, und ohne Rahmen -- angefordert am 08.09.: "Topics muessen
       ueberall exakt gleich gestylt sein. 28px Hoehe, nur der Hintergrund gefaerbt, keine Border."
       Der Rahmen war 1px solid transparent und trug die is-on-Marke. Genau er war auch die
       Ursache des Windows-Befundes: bei 125-Prozent-Skalierung wird die halbtransparente Fuellung
       an der Grenze zwischen Rahmen- und Polsterkasten zweimal gerundet, und die Naht wird
       sichtbar -- auf einem Mac mit ganzzahliger Skalierung faellt das nie auf. Die Variante
       .is-static hatte ihn deshalb schon nicht mehr; jetzt hat ihn keiner mehr, und damit sind
       Anzeige und Schalter buchstabengleich.
       Die gewaehlte Marke traegt der Haken (.up-topicchip-check), der ohnehin schon einfaehrt --
       eine zweite Marke daneben war nie noetig. */
    /* ---- DER CHIP "DOT" (06.10., Topic-Handoff 2a/2b, Werte final) -------------------------
       Neues Gesicht fuer denselben Chip: Klassen, Aufbau und --ust-tag-color bleiben, damit jeder
       Verbraucher es ohne Aenderung bekommt. Statt der Toenung in Themenfarbe jetzt ein neutraler
       Chip mit Rand, die Farbe traegt ein 6px-Punkt vor dem Namen. 24px statt 28, Radius 6 statt 8,
       Schrift in --vc-text statt in der Themenfarbe. Das Emoji (2b) steht zwischen Punkt und Name,
       wenn eins gesetzt ist.
       Der Rand ist zurueck, den der 08.09. wegen der Naht bei 125-Prozent-Skalierung (Windows)
       entfernt hatte: die Naht kam von einer HALBTRANSPARENTEN Fuellung, die an der Grenze Rahmen-
       zu Polsterkasten zweimal gerundet wurde. Ohne Fuellung (transparent) gibt es nichts, was
       doppelt gerundet werden koennte -- an Windows bei 125 Prozent trotzdem einmal ansehen
       (im Handoff als "nicht gemessen" gefuehrt). */
    .up-topicchip { height: 24px; padding: 0 8px; border-radius: var(--up-r-s); display: inline-flex; align-items: center; gap: 6px;
      cursor: pointer; font-family: inherit; box-sizing: border-box;
      border: 1px solid var(--vc-border); background: transparent;
      color: var(--vc-text); font-size: 12px; font-weight: 500; line-height: 1;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
      transition: background-color var(--up-t-1) ease, border-color var(--up-t-1) ease; }
    /* Der Punkt als ::before -- so kommt er ohne neues Markup in jeden Chip der App. order -1:
       vor Emoji und Name; das Kaestchen (-2) steht noch davor. Im Dunkeln 72 Prozent Themenfarbe
       mit Weiss, sonst verschwinden die dunklen Topic-Farben auf dem dunklen Grund. */
    /* KEIN PUNKT MEHR, EIN STRICH (06.10. angefordert: "so aehnlich wie Sentiment, aber dass man es
       als eigenes erkennt"). Zweite Fassung am selben Abend: 1px breiter, 2px hoeher, 1px runder --
       4x10, Radius 2. Sentiment ist 3x12 und voll gerundet (.up-sent-dot); die Marke der Topic ist
       breiter, kuerzer und eckiger -- dieselbe Familie, auf einen Blick zu unterscheiden. Radius 2
       steht bewusst neben der Skala: es ist die Angabe (1px mehr als die 1 davor). --up-r-xs (4)
       ergaebe auf 4px Breite zwar dasselbe Bild -- der Browser kuerzt jeden Radius auf die halbe
       Seite --, stuende aber fuer eine Rundung, die nicht gemeint ist, und kippte bei jeder
       anderen Breite in eine volle Kappe. */
    .up-topicchip::before, .up-feldchip-av.is-strich::before { content: ""; order: -1; width: 4px; height: 10px; flex: 0 0 4px;
      border-radius: 2px; background: var(--ust-tag-color, var(--vc-fourth)); }
    [data-theme="dark"] .up-topicchip::before, [data-theme="dark"] .up-feldchip-av.is-strich::before { background: color-mix(in srgb, var(--ust-tag-color, var(--vc-fourth)) 72%, white); }
    .up-topicchip-e { font-size: 12px; line-height: 1; }
    /* 2b oder 2a ist eine Produktentscheidung (Handoff): mit UC.topicEmoji(false) traegt kein Chip
       der App ein Emoji, ohne dass ein Verbraucher etwas aendert. Vorgabe: mit (2b). */
    html.up-topic-ohne-emoji .up-topicchip-e { display: none; }
    /* Die Zahl im Chip (Topics Manager): leise, Tabellenziffern, 2px vom Namen abgesetzt. */
    .up-topicchip-zahl { font-size: 11px; font-weight: 600; color: var(--vc-fourth); font-variant-numeric: tabular-nums; padding-left: 2px; }
    /* Die Zustaende, nur an Schaltern: Ueberfahren hebt den Grund auf --vc-heading-bg und den
       Rand eine Stufe an; gewaehlt (is-on) ist der Grund eine Stufe tiefer, gewaehlt + Ueberfahren
       noch eine. Die Farbe bleibt --vc-text -- .up-chiphover faerbt sonst in die Akzent-Tinte.
       Die Zustaende stehen ueber :not(.is-static) und wiegen damit (0,3,0): mehr als
       .up-chiphover:hover (0,2,0), das an denselben Chips haengt. */
    .up-topicchip:not(.is-static):hover { background: var(--vc-heading-bg); border-color: var(--up-chip-rand-hover); color: var(--vc-text); }
    .up-topicchip.is-on { background: var(--up-chip-an); border-color: var(--up-chip-rand-hover); }
    .up-topicchip.is-on:not(.is-static):hover { background: var(--up-chip-an-hover); border-color: var(--up-chip-rand-an-hover); }
    /* Mit Kaestchen links 6 statt 8 -- das Kaestchen steht vor dem Punkt. */
    .up-topicchip:has(.up-topicchip-check) { padding-left: 6px; }
    /* "+2" in einer Tabellenzelle: derselbe Chip ohne Punkt, enger und leiser (Handoff). */
    .up-topicchip.is-mehr { padding: 0 6px; font-size: 11px; font-weight: 600; color: var(--vc-third); }
    .up-topicchip.is-mehr::before { display: none; }
    /* Eine geloeschte Topic (Gruppierungen im Topics Manager, Events): grauer Punkt, leiser Name. */
    .up-topicchip.is-weg { color: var(--vc-muted); }
    .up-topicchip.is-weg::before { background: var(--vc-fourth); }
    /* Ein Skelett-Chip (.up-tsk, Topics Manager) hat noch keine Topic -- also auch keinen Punkt. */
    .up-topicchip.up-tsk::before { display: none; }
    /* Nicht anklickbare Variante: im Prompts-Popover sind die Chips Schalter, im Radar, im
       Detailbereich und in einer Tabellenzelle sind sie reine Anzeige. Die Regel stand bisher nur
       in prompts-table.css -- der Radar gab die Klasse also schon aus und bekam sie nur dann, wenn
       zufaellig auch die Prompts-Table auf der Seite lag. Dritter Verbraucher, also hierher. */
    /* Randlos, und zwar 0 statt transparent: ein transparenter Rand bleibt ein Rand, den
       jede Hover- oder Themenregel wieder faerben kann -- und bei 125-Prozent-Skalierung
       (Windows) wird die halbtransparente Fuellung an der Grenze Rahmenkasten/Polsterkasten
       zweimal gerundet und die Naht sichtbar. Polster um denselben 1px nach, damit der Text
       an derselben Stelle steht und die 28px Hoehe unangetastet bleiben. Nur an der reinen
       Anzeige: die Schalter im Popover brauchen den Rahmen als is-on-Marke. */
    /* Bleibt als eigene Klasse -- sie sagt "nicht anklickbar" und nichts sonst mehr: Rahmen und
       Polster sind jetzt oben schon so. */
    .up-topicchip.is-static { cursor: default; }
    /* Die grosse Fassung (06.10. angefordert): 28 statt 24 im Topics Manager und ueberall, wo man
       Topics AUSWAEHLT (Gruppierungs-Popup, Topic-Popover der Prompts-Tabelle, Edit Topics). Dort
       ist der Chip ein Schalter mit Kaestchen und wird angeklickt, in Tabellenzellen nur gelesen.
       Die Schrift bleibt 12 -- genau so stand der Chip bis zum 06.10. auf 28px (Radius 8, Schrift
       12). Hoehe, Polster und Radius ziehen zusammen um eine Stufe an: Polster je Seite +2 wie die
       Hoehe, Radius von --up-r-s auf --up-r-m. (0,3,0) mit Kaestchen, damit die 8 links die 6 aus
       .up-topicchip:has(...) und die 10 aus .is-gross sicher schlaegt. */
    .up-topicchip.is-gross { height: var(--up-h-seg); padding: 0 10px; border-radius: var(--up-r-m); }
    .up-topicchip.is-gross:has(.up-topicchip-check) { padding-left: 8px; }

    /* KEIN RIESIGES ZEICHEN, BEVOR DIE KOMPONENTEN-CSS DA IST (19.09. gemeldet: "irgendwo in
       Mira flasht beim Laden noch ein altes SVG auf, ganz gross auf der Seite").
       Ein <svg> ohne width/height und ohne eigene Klasse faellt auf die Ersatzgroesse des
       Browsers zurueck -- 300x150 -- solange keine Regel dafuer da ist. core.css laedt zuerst,
       ask-mira.css danach: genau dieses Fenster war zu sehen. Die Regel greift NUR bei
       Zeichen ohne Groessenangabe UND ohne Klasse, laesst also jedes gestylte Bild (Skelette,
       Charts, alle Icons aus UC.icon, die ihre Groesse als Attribut tragen) unberuehrt. */
    #ask-mira svg:not([width]):not([height]):not([class]) { width: 16px; height: 16px; }

    /* ---------- Heat-Rampe ----------
       Fuenf Stufen, in JS interpoliert (UC.heatAt). Sie stehen in CSS statt in einem JS-Blob,
       damit Light und Dark EINE Palette aus der lebenden Kaskade sind statt zweier Listen, die
       auseinanderlaufen. Seit der Detailbereich die Farbe der 65-Prozent-Zelle fuer seine
       Kurve braucht, gibt es zwei Verbraucher -- und zwei Kopien einer Palette sind genau die
       Stelle, an der die eine irgendwann eine andere Blaustufe hat als die andere. Auf .up-root,
       damit jede Komponente sie erbt. */
    /* ---- DIE SCHRIFTSTUFEN GELTEN IM GANZEN DOKUMENT (24.09.) --------------------------------
     Sie standen nur auf .up-root -- und das reicht nicht: die Quick-Actions-Oberflaeche haengt
     am body (sie muss, .mqa-modal wuerde sonst beschnitten), ebenso die koerpermontierten
     Menues. Dort loesten var(--vc-*) ins Leere auf, sobald eine Komponente darauf zeigte statt
     eigene Werte zu fuehren -- gemeldet als "die Lupe fehlt und der Platzhalter ist zu stark":
     ohne Farbe faellt stroke auf keinen Wert und color auf die des Dokuments zurueck.
     Hier stehen sie deshalb noch einmal fuer alles ausserhalb einer Wurzel. .up-root und
     .up-root[data-theme="dark"] sind spezifischer und gewinnen weiterhin, diese Zeilen aendern
     also nichts an dem, was schon funktioniert. */
  /* --up-sel-bg seit dem 03.10. auch hier: die leise Marke (.up-marke.is-leise) steht als Beispiel
     in einer Erklaerkarte, und die haengt am <body> -- ohne den Token hatte sie keinen Grund
     (gemessen: rgba(0,0,0,0)). Dieselben Werte wie an .up-root. */
  :root {
    --vc-text: #1f1f1b; --vc-muted: #3e4146; --vc-third: #6b6f78; --vc-fourth: #80858e;
    --vc-text-invert: #e0e0e0; --up-sel-bg: #f4f2f4;
    /* Die Chip-Stufen auch hier: Topic-Chips stehen in Flaechen am body (Topic-Modal, Bulk-Leiste). */
    --vc-border: #e4e2e4; --vc-heading-bg: #f9f8f9;
    --up-chip-rand-hover: #cfcdd0; --up-chip-an: #f4f2f4; --up-chip-an-hover: #eeedef; --up-chip-rand-an-hover: #b9b7bb;
  }
  [data-theme="dark"] {
    --vc-text: #e0e0e0; --vc-muted: #c9cbd0; --vc-third: #a0a0a0; --vc-fourth: #6c717a;
    --vc-text-invert: #1f1f1b; --up-sel-bg: #28282c;
    --vc-border: #34343a; --vc-heading-bg: #232326;
    --up-chip-rand-hover: #4a4a52; --up-chip-an: #26262a; --up-chip-an-hover: #2c2c30; --up-chip-rand-an-hover: #5a5a62;
  }
  .up-root {
      --uhm-h0: #f0f3f8; --uhm-h1: #c8d4e5; --uhm-h2: #8aa4c4; --uhm-h3: #4a6e96; --uhm-h4: #1e3a5f;
    }
    .up-root[data-theme="dark"] {
      --uhm-h0: #1e2634; --uhm-h1: #283a58; --uhm-h2: #375582; --uhm-h3: #466ea8; --uhm-h4: #5587c8;
    }
    /* DIE RAMPE FOLGT DER AKZENTFARBE (19.09. angefordert: "wenn eine der 3 anderen Akzentfarben
       gewaehlt ist, die Farbskala des Matrix-Charts auf eine Skala mit der jeweiligen
       Akzentfarbe als Grundfarbe"). Am STANDARD aendert sich nichts -- die zwei Regeln darueber
       bleiben unberuehrt, und ohne data-accent greift keine der sechs hier.
       Gerechnet, nicht gegriffen: uebernommen sind Helligkeit UND Saettigung jeder der fuenf
       Stufen aus der blauen Vorlage, getauscht ist nur der Farbton. Damit hat jede Skala
       dieselbe Abstufung und denselben Kontrast wie die bisherige -- eine frei gewaehlte Rampe
       waere an einem Ende flach geworden. Hell laeuft sie von hell nach dunkel, dunkel
       umgekehrt; auch das ist die Vorlage. */
    html[data-accent="indigo"] .up-root {
      --uhm-h0: #f0f1f8; --uhm-h1: #c8cbe5; --uhm-h2: #8a90c4; --uhm-h3: #4a5296; --uhm-h4: #1e255f;
    }
    html[data-accent="indigo"] .up-root[data-theme="dark"] {
      --uhm-h0: #1e2134; --uhm-h1: #282e58; --uhm-h2: #374082; --uhm-h3: #4652a8; --uhm-h4: #5563c8;
    }
    html[data-accent="azur"] .up-root {
      --uhm-h0: #f0f4f8; --uhm-h1: #c8d7e5; --uhm-h2: #8aa9c4; --uhm-h3: #4a7296; --uhm-h4: #1e405f;
    }
    html[data-accent="azur"] .up-root[data-theme="dark"] {
      --uhm-h0: #1e2934; --uhm-h1: #284058; --uhm-h2: #375d82; --uhm-h3: #4677a8; --uhm-h4: #558fc8;
    }
    html[data-accent="terrakotta"] .up-root {
      --uhm-h0: #f8f2f0; --uhm-h1: #e5cfc8; --uhm-h2: #c4998a; --uhm-h3: #965d4a; --uhm-h4: #5f2e1e;
    }
    html[data-accent="terrakotta"] .up-root[data-theme="dark"] {
      --uhm-h0: #34231e; --uhm-h1: #583428; --uhm-h2: #824937; --uhm-h3: #a85e46; --uhm-h4: #c87155;
    }
    .up-topicchip-lbl { overflow: hidden; text-overflow: ellipsis; }
    /* ---- DER GEWAEHLT-ZUSTAND EINES THEMAS IST EINE CHECKBOX (21.09. angefordert) ----------
       Vorher war es ein Haken, der beim Waehlen EINFUHR: width 0 -> 14, Deckkraft 0 -> 1. Der
       eigentliche Unterschied ist nicht das Aussehen, sondern dass er vorher GAR NICHT DA war.
       Ein Chip ohne Zeichen sah aus wie ein Etikett; dass man ihn anklicken kann, erfuhr man
       erst danach. Die Checkbox steht von Anfang an da, leer, und sagt damit: hier waehlt man.
       Gemeldet als Unterschied zwischen zwei Oberflaechen -- die Themen-Filterliste
       (filters/topics-filter.js, .up-filter-item mit .up-filter-check) hatte die Checkbox
       laengst, die Chips in der Themenauswahl und im Bearbeiten-Popup nicht.

       ES IST DIESELBE CHECKBOX wie in .up-filter-check, nur eine Nummer kleiner: 14 statt 16,
       Radius 4 statt 5, Haken 10 statt 11, Rahmen 1.5 unveraendert -- alle Werte im Verhaeltnis
       14/16 mitgezogen, wie es §1 fuer ein verkleinertes Bauteil verlangt. 16px waeren in einem
       28px hohen Chip zu schwer; die 14 sind ausserdem die Breite, die der alte Haken schon
       belegte.
       Dieselben Uebergangstoken (--up-cb-t / --up-cb-ease) wie die grosse Fassung.

       DER RAHMEN LIEGT AUF currentColor UND NICHT AUF --vc-border: der Chip ist mit der Farbe
       des Themas eingetoent, und ein neutrales Grau verschwindet darauf je nach Thema mal mehr,
       mal weniger. currentColor ist die Schriftfarbe des Chips, also immer im selben Verhaeltnis
       zu seinem Grund -- auf jedem Thema und in beiden Themen gleich gut sichtbar.

       STATISCHE CHIPS SIND NICHT BETROFFEN: .up-topicchip.is-static traegt gar kein
       Haken-Element (geprueft ueber alle Komponenten), sie zeigen also weiterhin nur Emoji und
       Namen. */
    /* Seit dem 06.10. VOR dem Punkt (Handoff: "it sits before the dot"), ohne Abstand nach links. */
    .up-topicchip-check { display: inline-flex; align-items: center; justify-content: center; order: -2;
      width: 14px; height: 14px; flex: 0 0 auto; margin: 0;
      border-radius: 4px; border: 1.5px solid color-mix(in srgb, currentColor 32%, transparent);
      background: transparent; color: inherit;
      transition: background-color var(--up-cb-t) var(--up-cb-ease),
                  border-color var(--up-cb-t) var(--up-cb-ease); }
    .up-topicchip-check.is-on { background: var(--up-accent); border-color: var(--up-accent);
      color: var(--up-accent-fg); }
    .up-topicchip-check svg { width: 10px; height: 10px; display: block; flex: 0 0 auto;
      opacity: 0; transform: scale(.7);
      transition: opacity var(--up-cb-t) var(--up-cb-ease),
                  transform var(--up-cb-t) var(--up-cb-ease); }
    .up-topicchip-check.is-on svg { opacity: 1; transform: scale(1); }
    /* Im Dunkeln (06.10.): die Toenungs-Regeln (22 / 34 Prozent) sind mit dem "Dot" entfallen --
       Grund und Rand kommen aus Token, die das Thema selbst dreht; eigen bleibt nur der Punkt
       (oben). [data-theme="dark"] an JEDEM Vorfahren reicht dafuer, auch an den Flaechen am body
       (Bulk-Leiste, Topic-Modal), die die Chip-Token ueber :root / [data-theme] bekommen. */

    /* ONE chip grid for the whole app -- 8px row+column gap (flex-wrap's own `gap` gives both for
       free). Was missing a base rule entirely: every consumer either duplicated this declaration
       itself (prompts-table's .upt-gm-list) or, like the Edit Topics popup, simply had no gap at
       all and relied on nothing but the chips' own margins. */
    .up-topiclist { display: flex; flex-wrap: wrap; gap: 8px; }
    /* at the cap: chips you cannot add anyway say so instead of silently ignoring the click */
    .up-topiclist.is-full .up-topicchip:not(.is-on) { opacity: .4; cursor: default; pointer-events: none; }

    /* ---------- secondary button ----------
       Extracted from prompts-table's .upt-topicadd, which was the only place it existed. Solid
       border, plain surface, real hover -- the app's "second action next to a primary one". */
    .up-btn-sec { height: 32px; padding: 0 14px; border-radius: 9px;
      border: 1px solid var(--vc-border); background: transparent; color: var(--vc-text);
      cursor: pointer; font-family: inherit; font-size: 12.5px; font-weight: 500;
      display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
    .up-btn-sec svg { width: 14px; height: 14px; display: block; }
    .up-btn-sec:hover { color: var(--vc-text); border-color: var(--vc-hover-border); background: var(--vc-heading-bg); }
    .up-btn-sec:disabled { opacity: .45; cursor: default; }
    .up-btn-sec:disabled:hover { border-color: var(--vc-border); background: transparent; }

    /* ---------- primary button ----------
       Die Gegenstueck zu .up-btn-sec: dieselbe Hoehe, derselbe Radius, gefuellt statt umrandet.
       Aus settings-brand (.usb-savebtn) hierher gezogen, als brand-editor denselben Knopf
       brauchte -- zwei Kopien eines Hauptknopfes sind zwei Orte, an denen er sich auseinander
       entwickelt. settings-brand behaelt seine Klasse vorerst; sie traegt exakt diese Werte. */
    /* ---- DREI ZWILLINGE SIND HIER EINGEZOGEN (21.09.) --------------------------------------
       .up-filter-submit, .up-topicmodal-save und .upt-topicapply waren handgebaute Kopien dieses
       Knopfes: gefuellt in der Akzentfarbe, aber 34px statt 32, 12.5/13px statt 13, Schnitt 600
       statt 500, Polsterung 20 statt 14 -- und, das war der Fehler, ein EIGENER Deaktiviert-
       Zustand: background --vc-heading-bg, color --vc-third.
       DAS MACHT DEN KNOPF UNSICHTBAR. Die Massenleiste der Prompts-Tabelle fuehrt eine eigene
       Palette, in der --vc-heading-bg #232326 ist -- und ihr eigener Grund (--up-surface) ist
       #232326. Derselbe Wert. Der deaktivierte Apply verschwand also vollstaendig in der Leiste,
       uebrig blieb nur sein Text: gemeldet am 21.09. als "aktuell ist das nur text button".
       Im hellen Thema dasselbe eine Stufe schwaecher (#f9f8f9 auf #ffffff).
       Der Deaktiviert-Zustand des Bauteils macht es richtig: die Fuellung BLEIBT und wird auf
       opacity .4 gedimmt. Ein Knopf, den man nicht druecken kann, soll immer noch wie ein Knopf
       aussehen -- sonst weiss niemand, dass dort etwas freizuschalten ist.
       Die Freischaltlogik war uebrigens schon richtig: der Edit-Topics-Apply vergleicht
       etStaged gegen etOriginal, der Massen-Apply haengt an der Zahl der gewaehlten Themen (er
       FUEGT HINZU, es gibt dort also kein Vorher). Falsch war nur, wie "aus" aussieht. */
    .up-btn-pri,
    .up-filter-submit,
    .up-topicmodal-save,
    .upt-topicapply { height: 32px; padding: 0 14px; border-radius: 9px; white-space: nowrap;
      border: 0; background: var(--up-accent); color: var(--up-accent-fg);
      font-family: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
      display: inline-flex; align-items: center; justify-content: center; gap: 7px;
      transition: opacity 120ms ease; }
    .up-btn-pri svg, .up-filter-submit svg { width: 14px; height: 14px; display: block; }
    .up-btn-pri:disabled,
    .up-filter-submit:disabled,
    .up-topicmodal-save:disabled,
    .upt-topicapply:disabled { opacity: .4; cursor: default; }
    .up-btn-pri:not(:disabled):hover,
    .up-filter-submit:not(:disabled):hover,
    .up-topicmodal-save:not(:disabled):hover,
    .upt-topicapply:not(:disabled):hover { opacity: .88; }
    .up-btn-pri:focus-visible,
    .up-filter-submit:focus-visible,
    .up-topicmodal-save:focus-visible,
    .upt-topicapply:focus-visible { outline: 0; box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); }

    /* ---------- Die GROSSE Stufe: .is-lg an .up-btn-pri und .up-btn-sec (25.09.) ----------
       Die ganzseitigen Flaechen -- Anmeldung, Onboarding und jetzt die Einladungsseite -- tragen
       einen groesseren Hauptknopf als die App: 40px hoch, Radius 12, 14px in 600. Bisher stand er
       ZWEIMAL als Kopie (.uau-primary in auth-page.css, .uob-next in onboarding-page.css, dort
       ausdruecklich "Geometrie 1:1 .uau-primary"). Die Einladungsseite waere die dritte gewesen --
       also ist er jetzt hier, als Stufe des Core-Knopfes: Farbe, Zustaende, Hover und Fokus
       bleiben die von .up-btn-pri/.up-btn-sec, nur die Geometrie waechst, und zwar ALLE Werte
       zusammen (Hoehe, Polster, Radius, Abstand, Schrift, Zeichen).
       Die zwei bestehenden Kopien bleiben vorerst, wie sie sind -- sie tragen exakt diese Werte. */
    .up-btn-pri.is-lg, .up-btn-sec.is-lg {
      height: 40px; padding: 0 20px; border-radius: 12px; gap: 9px;
      font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
    .up-btn-pri.is-lg svg, .up-btn-sec.is-lg svg { width: 15px; height: 15px; }

    /* ---------- Formularfehler: der Kasten ueber dem Knopf (25.09. nach core gezogen) ----------
       Das "gewohnte Fehlerformat" der ganzseitigen Flaechen. Es stand ZWEIMAL wortgleich da
       (.uau-formerr-in in auth-page.css, .uob-banner-in in onboarding-page.css); die
       Einladungsseite waere die dritte Kopie gewesen.
       Die Huelle faehrt ueber grid-template-rows 0fr -> 1fr auf -- ohne gemessene Pixelhoehe, also
       auch bei zwei Zeilen Text richtig, und ohne dass der Rest darunter springt. .is-on zeigt ihn.
       --vc-neg-border/-bg sind nirgends gesetzt, es gelten die Rueckfallwerte -- dieselben wie in
       beiden Vorlaeufern, also sieht er genau so aus wie dort. */
    .up-formerr {
      display: grid; grid-template-rows: 0fr; opacity: 0; margin-top: 0;
      transition: grid-template-rows var(--up-t-2) var(--up-ease), opacity var(--up-t-2) var(--up-ease),
                  margin-top var(--up-t-2) var(--up-ease);
    }
    .up-formerr > * { overflow: hidden; min-height: 0; }
    .up-formerr.is-on { grid-template-rows: 1fr; opacity: 1; margin-top: 18px; }
    .up-formerr-in {
      padding: 10px 12px; border-radius: 10px;
      border: 1px solid var(--vc-neg-border, rgba(200, 60, 60, .28));
      background: var(--vc-neg-bg, rgba(200, 60, 60, .08));
      font-size: 13px; line-height: 1.45; color: var(--vc-text); text-align: left;
    }
    @media (prefers-reduced-motion: reduce){ .up-formerr { transition: none; } }

    /* ---------- Die Fehlerzeile (07.10.): der Fehlerzustand der App als Baustein ----------
       Ein roter Satz in --vt-down, KEIN Kasten, keine Flaeche, kein Rahmen. So stehen Fehler in der
       App seit langem -- team-orga (.uto-err, .uto-formerr), add-brand (.uab-err), settings-brand
       (.usb-fileerr) --, nur gab es dafuer keinen gemeinsamen Baustein, und der einzige Fehler in
       core war der Kasten .up-formerr. Der stand auf den Einzelseiten und wurde am 07.10.
       abgelehnt ("diese daemlichen Container mit der Primaerfont drin").
       Aufklappen ueber grid-template-rows 0fr -> 1fr: keine gemessene Hoehe, und was darunter
       steht, rutscht weich statt zu springen. .is-on zeigt sie. */
    .up-fehlerzeile {
      display: grid; grid-template-rows: 0fr; opacity: 0;
      transition: grid-template-rows var(--up-t-2) var(--up-ease), opacity var(--up-t-2) var(--up-ease);
    }
    .up-fehlerzeile > span {
      overflow: hidden; min-height: 0; display: block; padding-top: 5px;
      font-size: 12px; line-height: 1.4; color: var(--vt-down);
    }
    .up-fehlerzeile.is-on { grid-template-rows: 1fr; opacity: 1; }
    @media (prefers-reduced-motion: reduce){ .up-fehlerzeile { transition: none; } }
    /* Mittig (13.10., Onboarding): der Fehler einer ganzen Seite oder eines Formulars steht als
       derselbe rote Satz zentriert ueber bzw. unter dem Inhalt -- dort, wo der Blick ist. Zwei
       Klassen gegen .up-fehlerzeile > span. */
    .up-fehlerzeile.is-mitte { text-align: center; }
    .up-fehlerzeile.is-mitte > span { padding-top: 0; }

    /* ══ EINZELSEITE (07.10.) ══════════════════════════════════════════════════════════════════
       Der Rahmen der kleinen ganzseitigen Flaechen: check-email, forgot-password, reset-password,
       not-found (UC.makeEinzelseite in core.js baut das Markup). Vier eigene Komponenten, ein
       Rahmen -- vier Kopien davon liefen beim ersten Nachziehen auseinander.
       Das Raster ist das der Anmeldeseite (16px Kartenpolster, dasselbe Spaltenpolster, die
       Wortmarke oben links, der Theme-Knopf oben rechts), der Inhalt steht mittig.
       SO RUHIG WIE DIE APP (07.10. nachgezogen: "subtil professionell, wie der rest der app"):
       KEIN Grafikband, KEINE Kachel um das Zeichen, der Grund ist der Seitengrund der App
       (--up-surface, dasselbe wie html/body im Theme-Snippet), die Ueberschrift steht auf der
       App-Skala (28px, die Groesse der Seitenkoepfe) statt der 44px der Anmeldeseite.
       Zwei Klassen an der Wurzel: .up-root setzt weiter unten in dieser Datei eigene Werte, und
       ein Konflikt wird ueber Spezifitaet geloest, nicht ueber die Reihenfolge. */
    .up-root.up-es {
      min-height: 100vh; min-height: 100dvh;
      display: flex; align-items: stretch; justify-content: center;
      padding: 0; background: var(--up-surface); color: var(--vc-text);
    }
    .up-es-karte {
      width: 100%; min-height: 100vh; min-height: 100dvh;
      display: grid; grid-template-columns: minmax(0, 1fr); padding: 16px;
      background: var(--up-surface);
    }
    /* Das Polster der linken Spalte der Anmeldeseite: senkrecht an vh, waagerecht an vw. */
    .up-es-spalte {
      min-width: 0; display: flex; flex-direction: column;
      padding: clamp(20px, 4vh, 52px) clamp(28px, 4.4vw, 64px);
    }
    .up-es-top { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex: 0 0 auto; }
    .up-es-logo { height: 18px; width: auto; flex: 0 0 auto; display: block; }
    /* .up-iconbtn bringt keinen Fokusring mit (dieselbe Zeile wie in invite-page.css). */
    .up-es-themebtn:focus-visible { outline: 0; box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); }
    /* Etwas ueber der Mitte, nicht exakt darin: ein kurzer Block in der geometrischen Mitte wirkt
       optisch zu tief. Der Abstand unten entspricht der Kopfzeile oben. */
    .up-es-mitte {
      flex: 1 1 auto; min-height: 0; padding-bottom: 48px;
      display: flex; flex-direction: column; justify-content: center; align-items: center;
    }
    /* 360px: ein Feld, ein Knopf -- breiter wirkt es leer. minmax(0, 1fr): sonst waechst die Spalte
       auf ihr laengstes Wort, eine lange Adresse schob die Seite auf 375px hinaus (gemessen). */
    .up-es-block { width: 100%; max-width: 360px; display: grid; grid-template-columns: minmax(0, 1fr); }
    /* Formular und Erfolg im selben Raster uebereinander: die Seite behaelt beim Wechsel ihre Hoehe.
       visibility erst NACH dem Ausblenden: sonst laege der Tabulator in einem unsichtbaren Feld. */
    .up-es-block > .up-es-pane { grid-area: 1 / 1; }
    .up-es-pane {
      transition: opacity var(--up-t-2) var(--up-ease), transform var(--up-t-2) var(--up-ease),
                  visibility 0s linear 0s;
    }
    .up-es-pane.is-off {
      opacity: 0; transform: translateY(6px); pointer-events: none; visibility: hidden;
      transition: opacity var(--up-t-2) var(--up-ease), transform var(--up-t-2) var(--up-ease),
                  visibility 0s linear var(--up-t-2);
    }

    /* ---------- Kopf: Zeichen, Ueberschrift, Satz -- mittig ---------- */
    .up-es-kopf { display: flex; flex-direction: column; align-items: center; text-align: center; }
    /* Das Zeichen allein, ohne Kachel und ohne Flaeche -- in der Schriftfarbe, 28px wie die
       Ueberschrift, Strich 1.5 (dafuer sind die Hugeicons gezeichnet). */
    .up-es-ic { display: block; width: 28px; height: 28px; margin-bottom: 16px; color: var(--vc-text); }
    .up-es-ic svg { width: 28px; height: 28px; display: block; }
    /* Die Kennung ueber der Ueberschrift (die 404): klein, in der Drittfarbe, Ziffern in Spalten. */
    .up-es-oben {
      margin: 0 0 8px; font-size: 13px; font-weight: 500; color: var(--vc-third);
      font-variant-numeric: tabular-nums;
    }
    .up-es-h1 {
      margin: 0; font-size: 28px; line-height: 1.15; font-weight: 600; letter-spacing: -.02em;
      color: var(--vc-text); text-wrap: balance;
    }
    /* Die Unterzeile in der Drittfarbe (07.10. angefordert: "darunter Drittcolor"). */
    .up-es-sub {
      margin: 8px 0 0; font-size: 14px; line-height: 1.45; color: var(--vc-third); text-wrap: balance;
    }

    /* ---------- Felder: die Geometrie von .uau-field/.uau-input ---------- */
    .up-es-form { display: contents; }
    .up-es-felder { margin-top: 28px; display: flex; flex-direction: column; gap: 12px; }
    .up-es-feld { display: flex; flex-direction: column; }
    .up-es-label { font-size: 12px; font-weight: 600; color: var(--vc-muted); }
    .up-es-label + * { margin-top: 7px; }
    .up-es-input {
      height: 40px; width: 100%; padding: 0 14px;
      border: 1px solid var(--vc-border); border-radius: 12px; background: var(--vc-bg);
      font-family: inherit; font-size: 14px; color: var(--vc-text); outline: none;
      transition: border-color var(--up-t-2) var(--up-ease), box-shadow var(--up-t-2) var(--up-ease);
    }
    .up-es-input::placeholder { color: var(--vc-third); }
    .up-es-input:disabled { opacity: .55; cursor: default; }
    /* Der Rahmen faerbt sich NICHT, solange das Feld den Fokus hat: sonst wechselt die Farbe genau
       in dem Moment, in dem jemand den Fehler beheben will. Die Zeile darunter ist .up-fehlerzeile. */
    .up-es-feld.is-err .up-es-input:not(:focus) { border-color: var(--vt-down); }
    .up-es-feld.is-err .up-fehlerzeile { grid-template-rows: 1fr; opacity: 1; }

    /* ---------- Passwortstaerke (UC.pwStaerkeHtml): die Anzeige der Anmeldeseite ----------
       Oben 8px wie dort, unten keine: dort folgt eine Optionszeile, hier das naechste Feld mit
       seinem eigenen Abstand von 12px -- 8 mehr saehen wie eine Luecke aus. */
    .up-pwst {
      display: grid; grid-template-rows: 0fr; opacity: 0;
      transition: grid-template-rows var(--up-t-2) var(--up-ease), opacity var(--up-t-2) var(--up-ease);
    }
    .up-pwst > span { overflow: hidden; min-height: 0; }
    .up-pwst.is-on { grid-template-rows: 1fr; opacity: 1; }
    .up-pwst-in { padding-top: 8px; display: flex; align-items: center; gap: 10px; }
    .up-pwst-bars { display: flex; gap: 4px; flex: 1 1 auto; }
    .up-pwst-bar {
      height: 4px; flex: 1 1 0; border-radius: 999px; background: var(--vc-sk);
      transition: background var(--up-t-2) var(--up-ease);
    }
    /* Vier Stufen, vier Farben -- dieselben wie auf der Anmeldeseite. */
    .up-pwst[data-level="1"] .up-pwst-bar.is-on { background: var(--vc-neg, #c8412e); }
    .up-pwst[data-level="2"] .up-pwst-bar.is-on { background: #d98324; }
    .up-pwst[data-level="3"] .up-pwst-bar.is-on { background: #b8a02a; }
    .up-pwst[data-level="4"] .up-pwst-bar.is-on { background: var(--vc-pos, #2e9e63); }
    .up-pwst-txt {
      flex: 0 0 auto; min-width: 74px; text-align: right;
      font-size: 12px; font-weight: 500; color: var(--vc-muted);
    }

    /* ---------- Knoepfe, Fehlerkasten, leise Wege ---------- */
    /* 24px ueber dem Hauptknopf: nah genug, dass Feld und Knopf eine Gruppe sind. */
    .up-es-cta { width: 100%; margin-top: 24px; }
    /* Bot-Pruefung (UC.captchaPlatzHtml, 14.10.): ohne Hoehe, solange Turnstile unsichtbar arbeitet --
       abgeschnitten und nicht display:none, damit das Widget darin laeuft. Braucht es einen Haken,
       setzt der Gastgeber .is-sichtbar; dann 24px Abstand wie der Hauptknopf darunter. */
    .up-captcha { height: 0; overflow: hidden; }
    .up-captcha.is-sichtbar { height: auto; overflow: visible; margin-top: 24px; }
    /* Ein Link ist hier ein Knopf: ohne Unterstrich. */
    .up-es a.up-btn-pri { text-decoration: none; }
    /* Beschaeftigt ist nicht gesperrt: .4 hiesse "geht nicht", .55 ist der Wert der Anmeldeseite.
       Drei Klassen gegen .up-btn-pri:disabled. */
    .up-es .up-es-cta.is-busy:disabled { opacity: .55; cursor: progress; }
    /* Der Spinner tritt NEBEN die Beschriftung, er ersetzt sie nicht -- ein Knopf, dessen Text
       verschwindet, wirkt kaputt statt beschaeftigt. Die Drehung ist die von up-ph-spin. */
    .up-btn-spin {
      width: 15px; height: 15px; flex: 0 0 auto; display: none;
      border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
      animation: up-ph-spin 620ms linear infinite;
    }
    .is-busy > .up-btn-spin { display: block; }
    /* Der Fehler des ganzen Formulars: die Fehlerzeile direkt UNTER dem Knopf, mittig -- dort, wo
       der Blick nach dem Klick ist. Zwei Klassen gegen das Polster von .up-fehlerzeile > span. */
    .up-fehlerzeile.up-es-fehler { text-align: center; }
    .up-fehlerzeile.up-es-fehler > span { padding-top: 12px; }
    /* Der Weg zu einem neuen Link steht direkt hinter dem Satz, in derselben Farbe, unterstrichen. */
    .up-es-formerr-link {
      margin-left: 4px; font-weight: 500; color: inherit; white-space: nowrap;
      text-decoration: underline; text-underline-offset: 2px;
    }
    .up-es-formerr-link[hidden] { display: none; }
    /* Der leise Weg: mittig, klein, im Ton des Seitenlinks der Anmeldeseite ("Forgot password?").
       Als Link UND als Knopf (not-found "Go back", forgot-password "Resend link"). */
    .up-es-zurueck {
      display: flex; width: fit-content; margin: 20px auto 0; align-items: center; gap: 6px;
      padding: 0; border: 0; background: none; cursor: pointer; font-family: inherit;
      font-size: 13px; font-weight: 500; color: var(--vc-muted); text-decoration: none;
      transition: color var(--up-t-2) var(--up-ease);
    }
    .up-es-zurueck:hover:not(:disabled) { color: var(--vc-text); }
    .up-es-zurueck:disabled { color: var(--vc-third); cursor: default; }
    .up-es-zurueck svg { width: 14px; height: 14px; display: block; }
    .up-es-zurueck:focus-visible { outline: 0; border-radius: 4px; box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); }

    /* ---------- Einzug ----------
       Wie auf der Anmeldeseite, nur leiser: 8px Weg statt 18 -- auf einer Seite mit drei Zeilen
       soll man das Ankommen spueren, nicht zusehen. 420ms wie --uau-in/--uiv-in/--uob-in,
       backwards, damit die Stufen waehrend ihrer Verzoegerung nicht dastehen. Die Stufe steht am
       Element (data-es-auf), je 120ms Versatz. */
    @keyframes up-es-auf { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
    .up-es.is-entering [data-es-auf] { animation: up-es-auf 420ms var(--up-ease-auf) backwards; }
    .up-es.is-entering [data-es-auf="2"] { animation-delay: var(--up-t-1); }
    .up-es.is-entering [data-es-auf="3"] { animation-delay: calc(var(--up-t-1) * 2); }
    .up-es.is-entering [data-es-auf="4"] { animation-delay: calc(var(--up-t-1) * 3); }

    /* ---------- schmal ---------- */
    .up-es.is-narrow .up-es-spalte { padding: clamp(20px, 6vw, 36px); }
    .up-es.is-narrow .up-es-mitte { padding-bottom: 24px; }

    @media (prefers-reduced-motion: reduce){
      .up-es.is-entering [data-es-auf] { animation: none; }
      .up-btn-spin { animation: none; }
      .up-es-pane, .up-es-pane.is-off { transition: none; }
    }

    /* ---------- Zeilenknopf: die kleine Fassung von .up-btn-sec, die beim Zeilen-Hover kommt ----
       Entstanden als .ubo-editbtn in brands-overview ("Edit" je Marke). Zweiter Verbraucher ist
       "Show Pages" / "Hide Pages" in der Domains-Tabelle, und damit gehoert es hierher: zwei
       Kopien derselben Geometrie laufen beim naechsten Nachziehen auseinander. Die alte Klasse
       steht in jeder Regel mit, damit das Markup von brands-overview unangetastet bleibt --
       derselbe Kniff wie bei .up-seg und .cc-seg.

       Alle Werte von .up-btn-sec im Verhaeltnis 24/32 mitgezogen: Hoehe 32 -> 24, Radius 9 -> 7,
       Innenabstand 7 -> 6, Zeichen 14 -> 13. Das Polster steht bei 8, so vorgegeben. Der Ton ist
       der sekundaere und wird beim Hover der primaere; die Flaeche darunter ist der Hover von
       .up-btn-sec, unveraendert.
       Er kommt OHNE Verweildauer: 200ms Einblenden am Hover der ZEILE, sonst nichts. Der Vorlaeufer
       (.up-rowswap, weiter unten) tauschte erst nach einer Sekunde einen Pfeil gegen den Knopf --
       eine Wartezeit vor der Handlung, um die es in der Zeile geht. */
    .up-rowbtn, .ubo-editbtn {
      margin-left: auto; flex: 0 0 auto;
      height: 24px; padding: 0 8px; border-radius: 7px; gap: 6px;
      font-size: 12px; color: var(--vc-muted);
      opacity: 0; pointer-events: none;
      transition: opacity 200ms ease, color 140ms ease, border-color 140ms ease, background 140ms ease;
    }
    .up-rowbtn svg, .ubo-editbtn svg { width: 13px; height: 13px; }
    /* Sichtbar am Hover der ZEILE, nicht des Knopfes -- ein Knopf, der erst erscheint, wenn man
       ihn trifft, ist keiner. Tastatur: auch beim Fokus, sonst ist er nur mit der Maus da. */
    .up-row:hover .up-rowbtn, .up-rowbtn:focus-visible,
    .up-row:hover .ubo-editbtn, .ubo-editbtn:focus-visible { opacity: 1; pointer-events: auto; }
    .up-rowbtn:hover, .ubo-editbtn:hover { color: var(--vc-text); }
    /* Ganz schmal faellt er weg: dort ist die fuehrende Zelle schon auf ihre Mindestbreite
       gestrichen, und ein Knopf darin schiebt den Namen aus dem Bild. Dieselbe Stufe, an der
       auch der Pfeil des Vorlaeufers verschwindet. */
    .up-root.is-vnarrow .up-rowbtn { display: none; }

    /* ---------- sort dropdown ---------- */
    .up-sort { position: relative; flex: 0 0 auto; }
    .up-sort-menu { position: absolute; top: calc(100% + var(--up-dd-gap)); right: 0; z-index: 60; width: 220px;
      background: var(--vc-bg); border: 1px solid var(--vc-border); border-radius: var(--up-dd-radius); box-shadow: var(--up-dd-shadow); padding: var(--up-dd-pad);
      opacity: 0; transform: translateY(-4px) scale(0.985); pointer-events: none; transition: opacity 140ms ease, transform 140ms ease; }
    .up-sort-menu.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
    /* Dark mode: var(--vc-border) (#353535) against these menus' #151515 background reads as
       barely-there — almost the same darkness, so the whole floating card looks edgeless/blurry.
       A dedicated, brighter border-only override for the four dropdown menus (mentioned/cols/
       sort/filter), independent of --vc-border everywhere else, which stays as-is for borders on
       the page's own (lighter) surfaces where the contrast problem doesn't exist. */
    .up-root[data-theme="dark"] .up-ment-menu,
    .up-root[data-theme="dark"] .up-cols-menu,
    .up-root[data-theme="dark"] .up-sort-menu,
    .up-root[data-theme="dark"] .up-filter-menu { border-color: var(--vc-switch-border); }
    .up-root[data-theme="dark"] .up-sort-menu { background: var(--up-menu-bg); }
    /* Hier stand bis zum 22.09. eine zweite Schienenfarbe, nur fuer Umschalter INNERHALB dunkler
       Menues: die durchscheinende Schiene hellte ihren Grund auf und stand im Menue (#232326)
       damit drei Stufen unter der Pille, also praktisch auf ihr. Seit dem 27.09. hellt die
       Schiene im Dunkeln wieder auf -- aber die Pille hellt mit auf, damals stand sie fest. Der
       Abstand zwischen beiden ist damit auf jedem Grund derselbe, und die Ausnahme bleibt
       ueberfluessig -- siehe --vc-switch-rail. */
    .up-sort.is-open .up-sort-btn { color: var(--vc-text); background: var(--vc-heading-bg); }
    .up-pop-head-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 18px; }   /* reserve the Select-all button height up front so nothing resizes */
    .up-pop-action { border: 0; background: transparent; color: var(--vc-muted); font-family: inherit;
      font-size: 11px; font-weight: 500; cursor: pointer; padding: 2px 4px; border-radius: 6px; text-transform: none; letter-spacing: 0; }
    .up-pop-action:hover { color: var(--vc-text); }
    .up-pop-action.is-hidden { visibility: hidden; pointer-events: none; }
    /* ---- DIE ETIKETT-BEHANDLUNG (21.09.) --------------------------------------------------
       EIN Aussehen fuer alles, was einen Abschnitt BENENNT statt etwas zu sagen: Gruppenkopf im
       Menue, Feldbeschriftung im Dialog, Abschnittstitel in einem Panel. Gemessen waren es vor
       diesem Umbau 32 Versal-Etiketten in 13 verschiedenen Fassungen und 98 normale
       Ueberschriften in 50 -- fuer hoechstens vier Rollen.

       KEINE VERSALIEN, und das ist eine Ansage und kein Geschmack (21.09.): "Diese Art von
       Headings in Uppercase finde ich ganz schlimm, muss weg." Die Unterscheidung zum Fliesstext
       traegt jetzt Groesse, Schnitt und Farbe -- 12px statt 13, 600 statt 500, gedaempft statt
       Textfarbe. Das reicht, und es liest sich in jeder Sprache, in der Versalien nicht
       funktionieren.
       DIE LAUFWEITE FAELLT MIT WEG. 0.04em stand da, WEIL Versalien Sperrung brauchen; in
       normaler Schreibung sperrt man nicht, das sieht dann nach Fehler aus.
       11 -> 12px: die Grossbuchstaben trugen optisches Gewicht, das jetzt fehlt. Ein Pixel
       mehr gleicht es aus, ohne mit dem Inhalt darunter zu konkurrieren (der steht auf 13).

       NICHT BETROFFEN sind einzelne Buchstaben -- .up-logo-ltr, .up-stack-ltr, .up-model-ltr.
       Das sind Initialen im Favicon-Kaestchen, keine Ueberschriften; ein kleingeschriebenes
       "f" fuer forbes.com waere dort schlicht falsch.

       .up-label ist der Name fuer neuen Code. Die vier bestehenden Klassen stehen daneben,
       damit kein Markup in acht Komponenten angefasst werden muss -- eine Deklaration, vier
       gewachsene Namen. */
    .up-label,
    .up-pop-head,
    .up-pop-sub,
    .up-blockhead,
    .up-filter-title,
    .up-topicmodal-label { font-size: 12px; font-weight: 600; color: var(--vc-third); }
    /* Waagerecht 0, aus demselben Grund wie .up-filter-head: die Ueberschrift steht buendig
       mit der Kante der Zeilen darunter (deren Hoverflaeche beginnt an der Polsterkante des
       Menues), nicht eingerueckt gegen sie. */
    .up-pop-head { padding: 4px 0 8px; }
    /* ---------- Blockueberschrift (12.09., aus sidebar.css hierher gezogen) ----------
       Die kleine Versalzeile UEBER einem Inhaltsblock -- in der Hauptseitenleiste die Zeile vor
       jedem Menuebereich (.usn-head), im Power Dashboard vor "Recent chats" und "Overview". Nicht
       dasselbe wie .up-pop-head zwei Zeilen darueber: das ist die Ueberschrift IN einem Dropdown
       (Sekundaerton, eigenes Polster fuer den Menuekasten), das hier ist die Ueberschrift AUF DER
       SEITE selbst -- deshalb die ruhigere Drittfarbe und kein eigenes Polster (das legt jeder
       Aufrufer nach seiner eigenen Anordnung fest).
       Wer mehr braucht als nur die Schrift -- .usn-head ist zugleich ein Knopf mit Chevron und
       Hoverfarbe, siehe sidebar.css --, baut das ALS ZUSATZ zu dieser Klasse, nicht als Ersatz:
       zwei Stellen mit denselben vier Werten (Groesse, Gewicht, Laufweite, Versalien) sind die
       naechste Drift, sobald eine davon geaendert wird und die andere nicht. */
    /* --vc-third UND NICHT --vc-muted (21.09. entschieden, nachdem ich zuerst muted gesetzt
       hatte). Das ist eine bewusste Entscheidung gegen den Kontrast: --vc-third kommt auf Weiss
       auf 2.68:1, auf der Kopfzeile 2.53, im Menue 2.41 -- AA verlangt fuer 12px/600 4.5:1.
       Im dunklen Thema sind es 3.97:1. Die Etiketten sollen zuruecktreten und nur den Abschnitt
       markieren; der Inhalt darunter traegt die Aussage und steht auf --vc-text.
       Wer das drehen will, aendert diese eine Zeile -- alle sechs Etiketten haengen daran. */
    .up-pop-opt { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 10px; border-radius: 9px; cursor: pointer; font-size: 13.5px; font-weight: 500; color: var(--vc-text); }
    /* Hover UND selected tragen denselben Ton (--up-sel-bg, 55%/60%): eine Zeile auf die man zeigt
       und eine die gewaehlt ist meinen beide "diese hier", zwei verschiedene Graus nebeneinander
       lesen sich als zwei Bedeutungen. Vorher hatte selected ueberhaupt keinen Hintergrund, nur den
       Haken -- deshalb sah jeder Sorter anders aus als die Filterlisten daneben.
       .up-pop-opt ist der geteilte Optionstyp: diese eine Regel erreicht jeden Sorter der App. */
    .up-pop-opt:hover, .up-pop-opt.is-active { background: var(--up-sel-bg); }
    /* Die Farbe eines Typs vor einer Menuezeile (13.10., "Count as" im Domain-Drawer): dieselbe Form
       wie in den Chart-Legenden (10 x 10, Radius 4), die Farbe inline aus UC.typeColor. Steht mit
       dem Text in .up-pop-opt-l, damit der Haken rechts bleibt. */
    .up-pop-opt-l { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
    .up-pop-form { flex: 0 0 auto; width: 10px; height: 10px; border-radius: var(--up-r-xs); }
    .up-pop-opt .up-check { width: 16px; height: 16px; color: var(--vc-text); opacity: 0; flex: 0 0 auto;
      transform: scale(.7); transition: opacity var(--up-cb-t) var(--up-cb-ease), transform var(--up-cb-t) var(--up-cb-ease); }
    .up-pop-opt .up-check svg { width: 16px; height: 16px; display: block; }
    .up-pop-opt.is-active .up-check { opacity: 1; transform: scale(1); }
    .up-pop-div { height: 1px; background: var(--vc-border); margin: 7px 4px; }
    /* ---- Intent-Label und Branded-Label (13.10., Variante B / B2) ---------------------------
       Zeichen plus Text, OHNE Flaeche und ohne Rand: in einer Zeile neben Topic-Chips (Rand +
       Strich) muss die Absicht leiser sein als die Ordnung des Nutzers. Hoehe die Chip-Stufe (24),
       damit eine Zelle mit Label und eine ohne dieselbe Zeilenhoehe ergeben.
       Das Zeichen in --vc-fourth, der Text in --vc-muted: das Zeichen ist der Wiedererkenner, nicht
       die Aussage. Laden und "nicht zugeordnet" stehen ganz in --vc-fourth -- statisch, ohne
       Bewegung (Ansage 13.10.), dieselbe Form, damit nichts springt, wenn der Wert eintrifft.
       Klickbar (is-klick, ein <button>): 6px Polster und ein Hover-Grund aus --up-sel-bg, wie jede
       Menuezeile. Das Polster gleicht der BEHAELTER aus (-6px links), nicht der Knopf selbst: ein
       negativer Aussenabstand am Knopf machte den Behaelter 6px schmaler, und max-width: 100% zog
       das Label dann um genau diese 6px zusammen -- "Informatio…" in einer 264px breiten Zelle
       (gemessen im Pruefstand pi03: 100 statt 106px). */
    .up-intent, .up-branded { display: inline-flex; align-items: center; gap: 6px; height: var(--up-h-chip);
      min-width: 0; max-width: 100%; box-sizing: border-box; font-family: inherit; font-size: var(--up-fs-s);
      font-weight: var(--up-fw-m); line-height: 1; color: var(--vc-text); white-space: nowrap; }
    /* Schrift in der Hauptfarbe (14.10., Pruefauftrag "zu klein?"): gemessen stehen Topic- und
       Branded-Chip in der Tabelle mit 12px in der Hauptfarbe, das Intent-Label mit 12px in Grau --
       gleich gross, aber eine Stufe schwaecher, und darum las es sich kleiner. Das Zeichen bleibt
       leise (--vc-fourth), Laden und "nicht zugeordnet" bleiben grau. */
    .up-intent > svg, .up-branded > svg { width: 14px; height: 14px; flex: 0 0 14px; color: var(--vc-fourth); }
    .up-intent-lbl, .up-branded-lbl { overflow: hidden; text-overflow: ellipsis; }
    .up-intent.is-pending, .up-intent.is-failed { color: var(--vc-fourth); }
    /* "Classifying" glaenzt (13.10., zweite Runde: "wie im Loader bei Mira"; die erste Fassung war
       ausdruecklich statisch). Dieselbe Mechanik wie .am-run-txt in ask-mira.css: der Text wird
       zur Maske, ein heller Streifen laeuft darueber -- Werte von dort uebernommen (100 Grad,
       30/50/70 Prozent, 1.56s, rueckwaerts). Nur der Text, nicht das Zeichen: die Uhr bleibt
       ruhig. Ohne Bewegungswunsch: still, in der leisen Farbe. */
    .up-intent.is-pending .up-intent-lbl {
      background-image: linear-gradient(100deg, rgba(255,255,255,0) 30%, rgba(255,255,255,0.85) 50%, rgba(255,255,255,0) 70%);
      background-color: currentColor;
      background-size: 200% 100%; background-repeat: no-repeat; background-position: -120% 0;
      -webkit-background-clip: text; background-clip: text;
      -webkit-text-fill-color: transparent;
      animation: upIntentGlanz 1.56s linear infinite reverse;
    }
    @keyframes upIntentGlanz { from { background-position: -120% 0; } to { background-position: 220% 0; } }
    @media (prefers-reduced-motion: reduce){
      .up-intent.is-pending .up-intent-lbl { background: none; animation: none; -webkit-text-fill-color: currentColor; }
    }
    /* Vom Nutzer gesetzt (14.10.: UserCog in einem kleinen Chip mit Grund, statt des Stifts): 12px
       Zeichen, 2px Polster -- ein 16er Kaestchen, Radius --up-r-xs. Der Grund ist eine Mischung
       aus der Schrift und nicht --up-sel-bg: der klickbare Knopf traegt beim Ueberfahren genau
       --up-sel-bg, und der Chip waere darin verschwunden. Abstand zum Text: der gap des Labels. */
    .up-intent-user { display: inline-flex; padding: 2px; border-radius: var(--up-r-xs); color: var(--vc-third);
      background: color-mix(in srgb, var(--vc-text) 7%, transparent); }
    .up-intent-user svg { width: 12px; height: 12px; display: block; }
    :is(.up-intent, .up-branded).is-klick { border: 0; background: transparent; padding: 0 6px;
      border-radius: var(--up-r-s); cursor: pointer; transition: background-color var(--up-t-1) ease; }
    :is(.up-intent, .up-branded).is-klick:hover, :is(.up-intent, .up-branded).is-klick[aria-expanded="true"] { background: var(--up-sel-bg); }
    :is(.up-intent, .up-branded).is-klick:focus-visible { outline: var(--up-focus-w, 2px) solid var(--vc-text); outline-offset: 0; }
    /* BRANDED ALS EIGENER CHIP (14.10., eigene Spalte statt neben dem Intent): hellgrauer Grund
       (--up-sel-bg), 8px Polster, Radius 6 aus der Skala, Schrift in der Hauptfarbe -- es ist eine
       Aussage ueber den Prompt, kein leiser Status. Zwischen Wort und Zeichen ein senkrechter
       Strich, 12px hoch wie die Zeichen daneben. Klickbar wird der Grund beim Ueberfahren eine
       Stufe dunkler, statt wie das Intent-Label erst einen Grund zu bekommen. */
    .up-branded { padding: 0 8px; border-radius: var(--up-r-s); background: var(--up-sel-bg); color: var(--vc-text); }
    :is(.up-branded).is-klick { padding: 0 8px; background: var(--up-sel-bg); }
    :is(.up-branded).is-klick:hover, :is(.up-branded).is-klick[aria-expanded="true"] {
      background: color-mix(in srgb, var(--vc-text) 10%, var(--up-sel-bg)); }
    .up-branded-sep { flex: 0 0 1px; width: 1px; height: 12px; background: color-mix(in srgb, var(--vc-text) 18%, transparent); }
    /* Nicht branded: ohne Chip, nur der leise Ausloeser fuer das Menue ("–" oder "Not branded"). */
    :is(.up-branded).is-leer, :is(.up-branded).is-leer.is-klick { background: transparent; color: var(--vc-fourth); padding: 0 6px; }
    :is(.up-branded).is-leer.is-klick:hover, :is(.up-branded).is-leer.is-klick[aria-expanded="true"] { background: var(--up-sel-bg); }
    /* Branded: das Wort und die Favicons der genannten Marken. 14px mit Radius 4 -- die kleinste
       Logostufe, die als Logo noch lesbar ist; der Anfangsbuchstabe liegt darunter und wird nur
       sichtbar, wenn das Bild ausfaellt (has-img faellt dann weg). */
    .up-branded-favs { display: inline-flex; align-items: center; gap: 2px; }
    .up-branded-fav { position: relative; width: 14px; height: 14px; flex: 0 0 14px; border-radius: var(--up-r-xs);
      overflow: hidden; display: inline-grid; place-items: center; background: var(--vc-heading-bg);
      box-shadow: inset 0 0 0 1px var(--vc-border); }
    .up-branded-fav img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
    .up-branded-ini { font-size: var(--up-fs-xs); font-weight: var(--up-fw-h); color: var(--vc-third); }
    .up-branded-fav.has-img .up-branded-ini { visibility: hidden; }
    .up-branded-mehr { font-size: var(--up-fs-xs); color: var(--vc-fourth); font-variant-numeric: tabular-nums; padding-left: 2px; }
    /* Im Menue: das Zeichen vor jedem Wert in derselben Farbe wie am Label, 16px wie die Haken. */
    .up-intent-opt .up-pop-opt-l > svg { width: 16px; height: 16px; flex: 0 0 16px; color: var(--vc-fourth); }
    /* Die Herkunft unter den Werten (14.10., Entwurf A): eine leise Platte im Ton des
       Tabellenkopfs, ohne Trennlinie darueber -- sie gehoert zur Wahl. 40px hoch wie eine Zeile
       mit Knopf, damit KI- und Hand-Fall gleich hoch stehen und das Menue beim Wechsel nicht
       springt. Radius 10 in der 14er-Huelle mit 8 Innenabstand (konzentrisch gerundet). */
    .up-intent-herkunft { display: flex; align-items: center; gap: 8px; margin-top: 4px; min-height: 40px;
      box-sizing: border-box; padding: 5px 5px 5px 9px; border-radius: var(--up-r-l);
      /* Gestrichelt statt getoent (14.10. gewuenscht: "hebt sich besser ab"). Die Polsterung ist
         um den Rahmen kleiner, damit Inhalt und Hoehe (40) genau stehen bleiben. */
      border: 1px dashed var(--vc-border);
      font-size: var(--up-fs-s); font-weight: var(--up-fw-m); color: var(--vc-third); white-space: nowrap; }
    .up-intent-herkunft-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
    /* Mehr Luft zwischen "Von dir gesetzt" und dem Knopf (14.10.): 8 Abstand + 12 = 20. */
    .up-intent-herkunft .up-intent-reset { margin-left: 12px; }
    /* Das KI-Zeichen in der Akzentfarbe (gewuenscht 14.10.: "in der primary accent color"). */
    .up-intent-ki { display: inline-flex; color: var(--up-accent-ink); }
    .up-intent-ki svg { width: 14px; height: 14px; display: block; }
    /* Der Ruecksprung ist ein ECHTER Knopf: die Form von .up-btn-sec auf 28px gezogen (Hoehe aus
       der Skala, --up-h-seg), weiss auf der grauen Platte und mit der Tiefe e-1, die alles auf
       der Flaeche traegt. Radius: Platte 10 minus 6 Innenabstand = 4, auf die Stufe 6 gerundet. */
    .up-intent-reset { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; height: var(--up-h-seg);
      padding: 0 10px 0 8px; border: 1px solid var(--vc-border); border-radius: var(--up-r-s);
      background: var(--up-surface); box-shadow: var(--up-e-1); font: inherit; color: var(--vc-text); cursor: pointer; }
    .up-intent-reset svg { width: 14px; height: 14px; display: block; color: var(--up-accent-ink); }
    .up-intent-reset:hover { border-color: var(--vc-hover-border); background: var(--vc-heading-bg); }
    /* Branded mit Unterzeile: der Grund, warum der Schalter so steht. */
    .up-branded-row .up-pop-label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    /* Der Halter des Menues (UC.makeIntentMenu): steht absolut genau ueber dem Ausloeser (Lage aus
       JS), die Huelle haengt links buendig darunter. Der Halter faengt keine Klicks, sonst laege
       ein unsichtbarer Kasten ueber dem Label, solange das Menue zu ist. Das Aufklappen nach oben
       (is-oben, Sammelleiste am unteren Fensterrand) gibt es nur in der Prompt-Tabelle und steht
       in prompts-table.css. */
    .up-intentwrap { position: absolute; z-index: 60; pointer-events: none; }
    /* max-content mit 240 als Untergrenze: auf Deutsch ist die Herkunftszeile ("Von dir gesetzt"
       plus "Der KI ueberlassen") breiter als 240 und brach sonst in zwei Zeilen um. */
    /* Obergrenze 360 (14.10., vorher 320): mit dem Knopf "Automatisch erkennen" und 20px Luft davor
       wurde "Von dir gesetzt" auf Deutsch zu "Von dir ges..." gekuerzt -- gemessen 330px Bedarf. */
    .up-intentwrap .up-intentpop { left: 0; right: auto; width: max-content; min-width: 240px; max-width: min(360px, 92vw); pointer-events: none; }
    .up-intentwrap .up-intentpop.is-shown { pointer-events: auto; }
    /* Die Platte der Intent-Erklaerkarte: die Customer Journey als Weg. Drei Zeilen fuer die Stufen,
       ein senkrechter Strich verbindet ihre Zeichen (die Reise), "Other" abgesetzt ohne Strich.
       Farben der Platte (--vc-inverse-bg auf --vc-inverse-ink), der Nebentext gedaempft wie
       .up-explain-t in der Karte. */
    .up-intent-reise { display: flex; flex-direction: column; gap: 9px; width: 100%; }
    .up-intent-reise-st { position: relative; display: grid; grid-template-columns: 16px auto; column-gap: 9px; row-gap: 1px; align-items: center; }
    .up-intent-reise-ic { grid-row: 1 / span 2; display: inline-flex; align-self: start; padding-top: 2px; }
    .up-intent-reise-ic svg { width: 16px; height: 16px; display: block; }
    .up-intent-reise-name { font-size: var(--up-fs-s); font-weight: var(--up-fw-h); line-height: 1.2; }
    .up-intent-reise-will { font-size: var(--up-fs-xs); line-height: 1.3; color: color-mix(in srgb, var(--vc-inverse-bg) 62%, transparent); }
    /* Der Weg: von der Mitte eines Zeichens bis zum naechsten -- nicht bei der letzten Stufe und
       nicht bei "Other". 9px gap + Zeilenhoehe ueberbrueckt top: 22px bis bottom: -9px. */
    .up-intent-reise-st:not(.is-rest):not(:nth-last-child(2))::before { content: ""; position: absolute; left: 7.5px; top: 21px; bottom: -8px;
      width: 1px; background: color-mix(in srgb, var(--vc-inverse-bg) 30%, transparent); }
    .up-intent-reise-st.is-rest { padding-top: 7px; border-top: 1px solid color-mix(in srgb, var(--vc-inverse-bg) 18%, transparent); }
    /* Karte EINES Intents: seine Stufe voll, die anderen zurueckgenommen -- der Weg bleibt lesbar,
       der gemeinte Schritt steht vorn. */
    .up-intent-reise.has-aktiv .up-intent-reise-st:not(.is-aktiv) { opacity: .4; }
    /* Ausloeser kurz weg (Nachladen): unsichtbar und nicht klickbar, bis er wieder da ist. */
    .up-intentwrap.is-verwaist { visibility: hidden; }
    /* Am rechten Rand haengt das Menue an der rechten Kante des Ausloesers (UC.makeIntentMenu, seiteWaehlen). */
    .up-intentwrap.is-rechts .up-intentpop { left: auto; right: 0; transform-origin: top right; }

    /* Unten 8 statt 14: derselbe Abstand unter der Ueberschrift wie bei .up-pop-head. Es ist
       dieselbe Rolle, und zwei Werte dafuer waren nur gewachsen. Oben bleiben die 8, die
       zusammen mit dem Trenner darueber (7px Aussenabstand) die Zaesur bilden. */
    .up-pop-sub { padding: 8px 10px 8px; }

    /* ---- 4px ZWISCHEN DEN ZEILEN, in JEDEM Dropdown (21.09. angefordert) -----------------
       Gemessen war es vorher NULL: die Zeilen trugen nur ihre eigene Polsterung (9px 10px) und
       klebten aneinander. Mit 9px Radius und einem Hover-Grund, der die ganze Zeile faerbt,
       sahen zwei benachbarte Zeilen im Hover aus wie ein Block.

       Nachbarschaftsregel und KEIN gap am Menue: ein gap am Elternteil faenge auch die
       Ueberschrift und die Trenner mit ein -- die haben ihre eigenen Abstaende (4/10/8 bzw.
       7px Aussenabstand), und die waeren dann zweimal da. So bekommt der Abstand nur, was
       zwischen ZWEI Zeilen liegt: nie vor der ersten, nie nach der letzten, nie um einen
       Trenner herum.

       .up-dense-btn ist ABSICHTLICH nicht dabei: das ist der waagerechte Segmentstreifen
       (siehe .up-dense bei den Segmentleisten), dort waere ein senkrechter Abstand sinnlos. */
    :is(.up-pop-opt, .up-pop-row) + :is(.up-pop-opt, .up-pop-row) { margin-top: 4px; }
    .up-dense { display: inline-flex; width: 100%; padding: 0; gap: 0; background: var(--vc-switch-rail); border-radius: 8px; height: 34px; }
    /* 0 8px: die Beschriftung steht nicht mehr auf der Kante ihrer Haelfte. Wirkt dort, wo der
       Schalter sich nach seinem Inhalt richtet (die Zeile im Einstellungsfenster); in einem
       Chart-Menue hat .up-dense width:100%, dort bleibt die Breite wie sie war. */
    .up-dense-btn { flex: 1 1 50%; padding: 0 8px; height: 34px; border: 1px solid transparent; border-radius: 8px; background: transparent;
      color: var(--vc-muted); cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
    .up-dense-btn svg { width: 15px; height: 15px; }
    .up-dense-btn:hover { color: var(--vc-text); }
    .up-dense-btn.is-active { background: var(--vc-switch-pill); color: var(--vc-text); border-color: var(--vc-switch-border); }
    .up-root[data-theme="dark"] .up-dense-btn.is-active { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }
    /* icon-only variant (row-height 3-way switch): no label, so it never outgrows the menu */
    .up-dense-btn-icon { flex: 1 1 33.333%; padding: 0; gap: 0; }
    .up-dense-btn-icon svg { width: 16px; height: 16px; }

    /* ---------- chart settings (gear) trigger + its body-mounted dropdown ----------
       Shared by every Chart.js line-chart component (visibility-chart's own colors+line-width
       menu, citations-combo-chart's line-width-only menu) — one hover-reveal/open-state/
       transition-timing definition instead of a copy per component drifting out of sync, which is
       exactly how the pointer-events-fires-before-visible bug above got fixed in one place and
       then silently reintroduced in a duplicate.
       Deliberately keyed on each component's OWN existing button class (.vot-scale-btn,
       .ccl-settings-btn — add the next consumer's class to this same selector list, don't invent
       a shared marker class instead): visibility-chart's button already exists in every already-
       deployed Bubble page. A shared class name would only apply once every one of those pages'
       HTML elements gets manually re-pasted with the new markup — until then the button would
       silently lose ALL of this styling (which is exactly what happened the first time this was
       tried: the button rendered as a bare unstyled sliver). Targeting the class that's already
       there means the sizing/hover/position rules take effect immediately off the CDN pin alone;
       only the gear-vs-pencil SVG *content* genuinely requires a markup paste, because no amount
       of CSS can change what path data an inline <svg> contains.
       The MENU is body-mounted (component boxes are overflow:hidden — see visibility-chart.css's
       original comment on this), positioned in JS from the trigger's getBoundingClientRect(), so —
       living outside any .up-root — it redeclares the --vc- and --vc-switch- tokens here with
       this app's one universal light/dark palette (core.css's own .up-root tokens above) rather
       than per component, so every consumer's menu looks and themes identically by construction. */
    .vot-scale-btn, .ccl-settings-btn, .ubo-scale-btn {
      width: 28px; height: 28px; border-radius: 8px; z-index: 5;
      border: 0; background: transparent; color: var(--vc-third);
      cursor: pointer; padding: 0; display: inline-flex; align-items: center; justify-content: center;
      opacity: 0; pointer-events: none;
      transition: opacity 120ms ease, color 120ms ease;   /* fast hide */
    }
    .vot-scale-btn svg, .ccl-settings-btn svg, .ubo-scale-btn svg { width: 14px; height: 14px; display: block; }
    /* Hovering the host panel reveals it, after a delay — the button itself and its own open state
       keep it visible with no delay once already shown, so it never flickers mid-interaction.
       pointer-events is listed with the SAME delay as opacity's delay PLUS DURATION (900+160=
       1060ms) on purpose: pointer-events isn't animatable, so without its own transition entry it
       flips to "auto" the INSTANT the hover starts regardless of what opacity is doing — letting
       hover (and the tooltip) fire on a button that's still transparent mid fade-in. 1060ms is
       where opacity actually finishes landing on 1. Belt-and-suspenders: core.js's tooltip
       delegation ALSO refuses to tip any [data-tip] element still at opacity:0, so even a stale
       CDN pin or a browser that doesn't recompute hover state the instant pointer-events flips
       can't show a tooltip over an invisible button either way. */
    .vot-panel-body:hover .vot-scale-btn, .combo-panel-body:hover .ccl-settings-btn,
    .ubo-panel-body:hover .ubo-scale-btn, .ubo-scale-btn:focus-visible, .ubo-scale-btn.is-open,
    .vot-scale-btn:focus-visible, .ccl-settings-btn:focus-visible,
    .vot-scale-btn.is-open, .ccl-settings-btn.is-open {
      opacity: 1; pointer-events: auto;
      transition: opacity 160ms ease 900ms, pointer-events 0s 1060ms, color 120ms ease;   /* delayed show */
    }
    .vot-scale-btn:hover, .ccl-settings-btn:hover, .vot-scale-btn.is-open, .ccl-settings-btn.is-open,
    .ubo-scale-btn:hover, .ubo-scale-btn.is-open { color: var(--vc-text); }
    /* Skeleton is loading -> the button (and, if it was somehow already open, its menu) hides
       outright, no hover-reveal override. The component toggles .is-line-loading on its OWN root
       (not this button) the same way it already tracks every other loading state, so this is a
       plain descendant rule, not new JS wiring. !important because the alternative is duplicating
       every opacity/pointer-events rule above at higher specificity for one edge case. */
    .vot-root.is-line-loading .vot-scale-btn, .combo-root.is-line-loading .ccl-settings-btn,
    .ubo-root.is-line-loading .ubo-scale-btn {
      opacity: 0 !important; pointer-events: none !important;
    }
    @media (prefers-reduced-motion: reduce){ .vot-scale-btn, .ccl-settings-btn, .ubo-scale-btn { transition-duration: 1ms; } }

    /* Wer eine Palette aufmacht, macht sie VOLLSTAENDIG auf. Dieses Menue haengt am body und
       erbt darum nichts von .up-root. Die vier Geometrie-Tokens fehlten: --up-dd-radius,
       --up-dd-pad und --up-dd-shadow loesten nicht auf, also stand das Panel scharfkantig,
       schattenlos und ohne Innenabstand da, und --up-sel-bg fehlte, also faerbte sich keine
       Zeile beim Ueberfahren. Werte 1:1 aus dem .up-root-Block oben. */
    /* Ein Dropdown-Knopf, dessen Inhalt noch unterwegs ist (makePopover cfg.canOpen).
       Gedimmt statt versteckt: der Knopf soll an seinem Platz bleiben, damit die Werkzeugleiste
       nicht springt, sobald die Daten da sind. */
    .up-iconbtn.is-disabled, .up-filter-btn.is-disabled, .up-ment-btn.is-disabled,
    .up-ddtrigger.is-disabled, .combo-filter-btn.is-disabled, .ccl-settings-btn.is-disabled {
      opacity: .45; pointer-events: none; cursor: default;
    }

    .up-scale-menu {
      --vc-text: #1f1f1b; --vc-muted: #3e4146; --vc-third: #6b6f78; --vc-fourth: #80858e; --vc-border: #dcdbdd;
      --vc-heading-bg: #f9f8f9; --vc-hover-border: #6f6f6f; --vc-bg: #ffffff;
      --vc-switch-bg: #f9f8f9; --vc-switch-border: #e4e2e4; --vc-switch-pill: #ffffff;
      --vc-switch-rail: #f2f2f2;   /* fehlte -- siehe die dunkle Fassung darunter */
      --up-dd-radius: 14px; --up-dd-pad: 8px; --up-dd-shadow: 0 10px 28px rgba(0,0,0,.14);
      --up-sel-bg: #f4f2f4;
      position: fixed; z-index: 100050; width: 248px;
      background: var(--vc-bg); border: 1px solid var(--vc-border); border-radius: var(--up-dd-radius);
      box-shadow: var(--up-dd-shadow); padding: var(--up-dd-pad);
      color: var(--vc-text); font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      opacity: 0; transform: translateY(-4px) scale(.985); pointer-events: none;
      transition: opacity 140ms ease, transform 140ms ease;
      display: flex; flex-direction: column; gap: 4px;
    }
    .up-scale-menu.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
    /* Same brightened dark border as every other dropdown menu in the app — a plain --vc-border
       black-ish tone reads as blurry/edgeless against a dark surface. */
    .up-scale-menu[data-theme="dark"] {
      /* Rahmen einer SCHWEBENDEN Schicht: border-tertiary (#3e3e44), nicht -secondary. Im
         Hellen (#dcdbdd) ist das seit dem 07.09. so, im Dunkeln stand hier noch der Wert fuer
         Rahmen AUF Karten. Eine Schicht, die ueber allem schwebt, braucht die kraeftigere Kante,
         sonst loest sie sich gegen die Seite auf. Dieselbe Korrektur an allen sechs Stellen, die
         eine eigene Palette aufmachen: dieses Menue, die Themen-Karte weiter unten, die Bulkbar
         der Prompts-Tabelle, die zwei Panels von brands-overview, der Tooltip der Matrix und der
         Markendialog. Der letzte trug sogar noch #23252a -- border-primary aus der Zeit vor
         Linears Leiter, auf #1c1c1f also praktisch unsichtbar. */
      --vc-text: #e0e0e0; --vc-muted: #c9cbd0; --vc-third: #a0a0a0; --vc-fourth: #6c717a; --vc-border: #3e3e44;
      --vc-heading-bg: #232326; --vc-hover-border: #707070; --vc-bg: #1c1c1f; --up-switch-off: #444444;
      --vc-switch-bg: #232326; --vc-switch-border: #34343a;
      /* Schiene und Pille wie im dunklen .up-root-Block (27.09.): beide heben ihren Grund an.
         Die Schiene fehlte hier ganz -- .up-dense darin malte seine Bahn aus einer Marke, die an
         diesem Menue nicht aufloeste, also gar nicht. */
      --vc-switch-rail: rgba(255,255,255,.055); --vc-switch-pill: rgba(255,255,255,.07);
      --up-dd-shadow: 0 10px 28px rgba(0,0,0,.32); --up-sel-bg: #28282c;
    }
    /* .up-dense-btn.is-active's own dark override above is scoped to .up-root — this menu lives
       outside it, so the Line Width switch needs its own copy of the same colors. */
    .up-scale-menu[data-theme="dark"] .up-dense-btn.is-active { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }
    /* Und aus demselben Grund der Knopf des Schalters: seine dunklen Farben haengen an
       .up-root[data-theme="dark"], und dieses Menue haengt am <body>. Ohne die zwei Zeilen bliebe
       der Knopf im Dunklen weiss -- auf der hellen Bahn des eingeschalteten Zustands ist er dann
       nicht mehr zu sehen. Die Bahn selbst zieht von allein mit, sie liest --vc-border und
       --vc-text, und die stehen oben im Menue. */
    .up-scale-menu[data-theme="dark"] .up-switch::after { background: #e0e0e0; }
    .up-scale-menu[data-theme="dark"] .up-switch.is-on::after { background: var(--up-accent-fg); }
    @media (prefers-reduced-motion: reduce){ .up-scale-menu { transition-duration: 1ms; } }
    .up-scale-menu * { box-sizing: border-box; }

    /* Colour-scale option rows. These live here — not in a component file — because UC.makeScaleMenu
       (core.js) is what emits them: JS in core whose class names are styled in ONE component's CSS
       is exactly the split that has already produced a "renders as unstyled text" bug twice. They
       were .vot-scale-* in visibility-chart.css until brands-overview became the second consumer.
       Renaming to .up-* is safe despite already-deployed pages: this menu is built at RUNTIME by
       core.js, so no pasted Bubble markup contains these class names (unlike the TRIGGER button,
       which does — hence .vot-scale-btn keeps its component-specific name in the rule above). */
    /* Granularity switcher (Day/Week/Month). Lives here because visibility-chart AND
       brands-overview both show it; the vc- prefix is kept rather than renamed because it is in
       already-deployed visibility-chart markup (same reasoning as .vot-scale-btn above). */
    /* 26px hoch und nicht 32, Polster 8 statt 12, Flaeche --vc-switch-rail: die Fassung, die in der
       Hero-Sektion der Landingpage entstanden ist und von dort uebernommen wurde. Alle drei Masse
       zusammen (STYLEGUIDE 1) -- eine 32er-Pille mit 8px Polster waere zu weit, eine 26er mit 12px
       zu eng. Die Schrift bleibt bei 12px/500: die 11px im Schaustueck kommen aus dessen eigener
       Verkleinerung ALLER Schriften, nicht aus dem Bauteil.
       align-self: flex-start, damit der Umschalter oben in seinem Kasten sitzt und nicht mittig --
       neben einer Ueberschrift mit Unterzeile sass er sonst auf halber Hoehe. */
/* .cc-gran ist derselbe Umschalter im Combo-Chart und steht hier als ALIAS, genau wie bei
   .up-seg/.cc-seg weiter unten: sein Markup liegt in der von Hand eingefuegten Bubble-Seite, ein
   Umbenennen wuerde sie zerlegen. Vorher stand eine wortgleiche Kopie in citations-combo-chart.css
   -- und jede Aenderung hier (Mindestbreite, Farbe) erreichte ihn deshalb nicht. */
.vc-gran, .cc-gran { display: inline-flex; padding: 0; gap: 0; background: var(--vc-switch-rail); border-radius: 8px; border: 0; height: 26px; flex: 0 0 auto; align-self: flex-start; }
/* Im Combo-Chart steht er in einer Zeile mit Knoepfen und deshalb mittig. */
.cc-gran { align-self: center; }
/* min-width 26 = die Hoehe: mit den kurzen Beschriftungen D, W und M (core.js, stampGran) ist
   ein Knopf sonst so breit wie sein Buchstabe, und W waere breiter als D -- der Umschalter
   huepfte beim Wechsel. Quadratisch stehen die drei ruhig nebeneinander. */
    .vc-gran-btn, .cc-gran-btn { height: 26px; min-width: 26px; padding: 0 8px; border: 1px solid transparent; border-radius: 8px; background: transparent;
      color: var(--vc-muted); cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 500; line-height: 1;
      display: inline-flex; align-items: center; justify-content: center; }
    .vc-gran-btn:hover, .cc-gran-btn:hover { color: var(--vc-text); }
    .vc-gran-btn.is-active, .cc-gran-btn.is-active { background: var(--vc-switch-pill); color: var(--vc-text); border-color: var(--vc-switch-border); }
    .up-root[data-theme="dark"] .vc-gran-btn.is-active,
    .up-root[data-theme="dark"] .cc-gran-btn.is-active { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }
    .vc-gran-btn.is-disabled, .cc-gran-btn.is-disabled { opacity: 0.4; pointer-events: none; cursor: default; }

    .up-scale-opt { display: flex; flex-direction: column; gap: 8px; padding: 9px 10px; border-radius: 9px; cursor: pointer; }
    .up-scale-opt:hover, .up-scale-opt.is-active { background: var(--up-sel-bg); }
    /* 4px zwischen den Zeilen wie an jedem Dropdown aus core (.up-optrow + .up-optrow) -- ohne
       stiessen zwei getoente Zeilen (Hover und gewaehlt) Kante an Kante aneinander. */
    .up-scale-opt + .up-scale-opt { margin-top: 4px; }
    .up-scale-opt-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .up-scale-opt-lbl { font-size: 13.5px; font-weight: 500; color: var(--vc-text); }
    /* Das Info-Zeichen an "Brand Colors". Es IST .up-th-info -- Groesse, Farbe und Hover kommen
       von dort, damit es dasselbe Zeichen ist wie an jedem Spaltenkopf. Hier stehen nur die zwei
       Unterschiede, die eine Menuezeile braucht:
         - dauerhaft sichtbar (am Spaltenkopf erscheint es erst beim Ueberfahren der Zeile) -- man
           soll sehen, DASS es eine Erklaerung gibt, bevor der Zeiger darauf steht,
         - margin-right: auto, damit der Haken an der rechten Kante bleibt wie ohne Zeichen.
       KEIN cursor: help. Der Zeiger wird in dieser App nie zum Fragezeichen -- das gilt hier
       genauso wie an den Spaltenkoepfen, die ebenfalls keinen setzen. */
    .up-scale-opt-info { opacity: 1; margin-left: 6px; margin-right: auto; flex: 0 0 auto; }
    .up-scale-opt .up-check { width: 16px; height: 16px; color: var(--vc-text); opacity: 0; flex: 0 0 auto;
      transform: scale(.7); transition: opacity var(--up-cb-t) var(--up-cb-ease), transform var(--up-cb-t) var(--up-cb-ease); }
    .up-scale-opt .up-check svg { width: 16px; height: 16px; display: block; }
    .up-scale-opt.is-active .up-check { opacity: 1; transform: scale(1); }
    /* "Nebeneinander, etwas übereinandergeschoben, mit sichtbarem Gap dazwischen" — each dot overlaps
       the previous by a third, with a background-colored ring (border) standing in for the gap, so the
       seam between colors stays visible even though the dots physically overlap (the same idiom as an
       avatar stack). */
/* ---------- Chart-Settings am Chart: weg ----------
   Die Wahl steht im Einstellungsfenster (Seite "Charts"). Der Knopf hier stammt aus dem
   handgemachten Bubble-Markup der Chart-Elemente und ist vom Pin aus nicht zu entfernen -- also
   wird er ausgeblendet. NUR das Chart-Zahnrad: .uhm-set-btn (Performance Radar) und
   .uo-settings-btn (Opportunities-Board) sind andere Menues und bleiben.
   display:none und nicht visibility: der Knopf soll auch keinen Platz in der Werkzeugzeile
   beanspruchen. */
.vot-scale-btn, .ubo-scale-btn, .ccl-settings-btn { display: none !important; }

/* ---------- Filter nur als Funktion ----------
   Gesetzt von core (nurFunktionLauf), wenn der Wirt des Filters 1x1 gross ist: dann liegt er nur
   als Funktion auf der Seite, damit die Filterleiste ihn einziehen kann, und sein eigener Trigger
   soll weder zu sehen noch zu treffen sein.
   Nicht display:none -- ein Element ohne Kasten laesst sich nicht messen, und die Leiste prueft
   beim Einziehen die Groesse des Triggers. Also die uebliche Form fuer "da, aber unsichtbar":
   1x1, weggeschnitten, ohne Zeiger. Das MENUE bleibt unberuehrt: es haengt absolut und wird von
   der Leiste geoeffnet, sobald sie den Filter zu sich geholt hat -- und dann ist diese Klasse
   ohnehin wieder weg. */
.up-nurfunktion > .utf-trigger,
.up-nurfunktion > .umf-trigger,
.up-nurfunktion > .umk-trigger,
.up-nurfunktion .utf-trigger,
.up-nurfunktion .umf-trigger,
.up-nurfunktion .umk-trigger {
  position: absolute; width: 1px; height: 1px; min-width: 0; padding: 0; border: 0;
  opacity: 0; pointer-events: none; overflow: hidden; clip-path: inset(50%);
}
/* Die Wurzel selbst nimmt keinen Platz mehr ein: sonst steht in dem 1x1-Kasten ein 32px hoher
   Block, der ueber den Rand hinausragt und darunterliegende Bedienelemente abfaengt. */
/* Die Wurzel KLEMMT jetzt (overflow:hidden). Vorher stand hier visible, damit ein Menue
   herausragen darf -- aber genau dadurch ragte auch alles andere heraus und war sichtbar und
   treffbar. Ein Filter, der nur als Funktion daliegt, oeffnet sein Menue nie an dieser Stelle:
   geoeffnet wird er, nachdem die Filterleiste ihn zu sich geholt hat, und dann ist diese Klasse
   weg. */
.up-nurfunktion { width: 1px; height: 1px; overflow: hidden; pointer-events: none; }
/* Am MENUE nur, wenn es OFFEN ist. Hier stand pointer-events:auto ohne Bedingung -- und das war
   ein Fehler mit Folgen: ein geschlossenes Menue traegt pointer-events:none, damit es nicht im Weg
   ist, und diese Regel hat das aufgehoben. Der Filter liegt als 1x1 auf der Seite, sein Menue
   haengt absolut und ragt darueber hinaus, und ein Klick irgendwo in dem Bereich landete in einer
   unsichtbaren Menuezeile -- gemeldet als "ich klicke da irgendwo und der Models-Filter setzt
   sich". Genau das.
   is-shown setzt makePopover beim Oeffnen; solange es fehlt, faengt das Menue nichts. */
.up-nurfunktion .utf-menu.is-shown,
.up-nurfunktion .umf-menu.is-shown,
.up-nurfunktion .umk-menu.is-shown { pointer-events: auto; }

/* ---------- Flaggenplaettchen ----------
   Aus filters/markets-filter.css hierher gezogen, als die Sprachauswahl im Einstellungsfenster den
   zweiten Abnehmer bekam. Werte unveraendert.
   Der Laendercode liegt UNTEN und die Flagge deckt ihn zu, damit eine Flagge, die 404 liefert,
   "DE" zeigt statt eines kaputten Bildes -- daher beide Ebenen absolut in derselben Box.
   Das Plaettchen ist BREITER als hoch: Flaggen sind Querformat, eine quadratische Box wuerde sie
   entweder zu einem Streifen quetschen oder beschneiden. Die Groesse setzt der Aufrufer. */
.up-flag { position: relative; display: inline-flex; flex: 0 0 auto; overflow: hidden; }
.up-flag-fb {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: var(--vc-heading-bg); color: var(--vc-third);
  font-size: 9px; font-weight: 600; line-height: 1; letter-spacing: 0.02em;
}
/* cover, nicht contain: eine Flagge soll ihr Plaettchen fuellen wie in jeder Flaggenliste, und die
   Seitenverhaeltnisse der Laender gehen weit genug auseinander, dass contain Luecken laesst. */
.up-flag-img { position: relative; width: 100%; height: 100%; object-fit: cover; display: block; }
.up-flag.has-img { background: none; }
.up-flag.has-img .up-flag-fb { display: none; }

    .up-scale-dots { display: flex; }
    .up-scale-dot { width: 18px; height: 18px; border-radius: 50%; flex: 0 0 auto;
      border: 2px solid var(--vc-bg); margin-left: -6px; }
    .up-scale-dot:first-child { margin-left: 0; }

    .up-pop-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 10px; border-radius: 9px; }
    .up-pop-row:hover { background: var(--vc-heading-bg); }
    .up-pop-label { font-size: 13.5px; font-weight: 500; color: var(--vc-text); }
    /* Eine gewaehlte Spalte, die die Breite verdraengt (UC.makeColumns, 14.10.): der Name bleibt
       in der Schrift der anderen, darunter in Nebentext der Grund. Kein eigener Zustand am
       Schalter -- der steht auf aus, weil die Spalte nicht zu sehen ist. */
    .up-pop-row.is-platzlos .up-pop-label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .up-pop-hint { font-size: var(--up-fs-s); font-weight: var(--up-fw-n); color: var(--vc-third); white-space: nowrap; }
    /* DIE AUSGESCHALTETE SCHIENE, und warum sie nicht mehr --vc-border bzw. rgba(42,42,42,.6)
       ist: im Dunkeln war dieser Wert BUCHSTABENGLEICH mit --vc-heading-bg -- also mit dem Grund
       jeder Kopfzeile, jeder Seitenspalte und jedes Menuegrunds, auf dem ein Schalter sitzt. Der
       ausgeschaltete Schalter war dort unsichtbar; gemeldet am 06.09. fuer die Spalte des
       Kalenders ("kaum vom Hintergrund der Sidebar zu unterscheiden"), gilt aber ueberall.
       Hier und nicht im Kalender, weil es EIN Bauteil ist: ein Schalter, der an einer Stelle
       anders aussieht als an der naechsten, ist der Anfang des Auseinanderlaufens.
       16 bzw. 18 Prozent der Textfarbe -- in beiden Themen rund 34 Stufen Abstand zum Grund, und
       weit genug von der eingeschalteten Schiene entfernt (die traegt volle Textfarbe). */
    /* FESTE WERTE, KEIN color-mix(var(...)). Der erste Anlauf hier mischte aus --vc-text, und das
       ist in Chrome nicht verlaesslich: ein Element, das VOR dem Setzen des Themas gerendert
       wurde, behaelt den alten Wert -- auch dann noch, wenn data-theme von Hand weg und wieder
       hin gesetzt wird. Gemessen im Prueftand: derselbe Schalter meldete oklab(0.238 / 0.16),
       ein frischer Klon an derselben Stelle color(srgb 0.878 / 0.18). Auf der Seite heisst das:
       jeder Schalter, der beim Themenwechsel schon da ist, bleibt in der alten Farbe stehen.
       Deshalb steht im Bestand ringsum ueberall ein fester Wert je Thema -- das war kein Zufall.
       Die Zahlen sind die gemessenen Mischungen von vorher: #d6d6d4 liegt 41 Stufen unter Weiss
       und 31 unter dem Kopfzeilengrund, #444 liegt 41 ueber dem dunklen Panelgrund. */
    .up-switch { width: 38px; height: 22px; border-radius: 999px; background: var(--up-switch-off, #d6d6d4); position: relative; flex: 0 0 auto; transition: background 160ms ease; cursor: pointer; }
    /* [data-theme="dark"] auf IRGENDEINEM Vorfahren, nicht nur auf .up-root -- genau wie beim
       Topic-Chip weiter oben. Ein Schalter steht auch in einem Portal am Koerper (das
       Einstellungs-Popover von Opportunities zum Beispiel), und das ist keine .up-root. Solange
       hier ein TOKEN stand (--vc-border), fiel das nicht auf: Token erben ueber die
       Attributgrenze. Seit dem 07.09. steht hier ein fester Wert, und fest heisst: er erbt nicht.
       Im Portal stand der Schalter dadurch auf der hellen Schiene -- gemessen als
       rgb(214,214,212) mitten im dunklen Menue. */
    /* Die zwei Regeln bleiben als Sicherung fuer eine Stelle, an der der Token nicht ankommt --
       kosten nichts und schaden nicht. Die Arbeit macht jetzt der Token oben. */
    .up-root[data-theme="dark"] .up-switch,
    [data-theme="dark"] .up-switch { background: var(--up-switch-off, #444444); }
    /* Der Knopf: e-1 ohne Rueckfall (28.09.). In den Menues am <body> galt bisher der Rueckfall
       0 1px 2px .2 -- dreimal so kraeftig wie derselbe Schalter in einer Wurzel. Seit die Tiefen an
       :root stehen, sehen beide gleich aus. */
    .up-switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 999px; background: #fff; box-shadow: var(--up-e-1); transition: transform 160ms ease; }
    .up-switch.is-on { background: var(--up-accent); }
    /* dark base .up-switch rule out-specifies .up-switch.is-on, so the ON track needs an
       explicit dark rule — otherwise it stays the dark off-colour.
       HIER STAND #e0e0e0 FEST, und das war der gemeldete Fehler (19.09.: "die Flaeche, die da
       aktuell in Fontcolor ist, auch als Primaercolor setzen -- im Lightmode hast du es korrekt
       gemacht, im Darkmode nicht"). Der Token traegt im Dunkeln von sich aus #e0e0e0, am
       Standard aendert sich also nichts; bei einer gewaehlten Akzentfarbe folgt die Bahn ihr
       jetzt auch im dunklen Thema. */
    .up-root[data-theme="dark"] .up-switch.is-on,
    [data-theme="dark"] .up-switch.is-on { background: var(--up-accent); }
    .up-switch.is-on::after { transform: translateX(16px); }
    .up-root[data-theme="dark"] .up-switch::after,
    [data-theme="dark"] .up-switch::after { background: #e0e0e0; }
    /* Der Knopf liegt AUF der eingeschalteten Bahn, traegt also --up-accent-fg wie jede Schrift
       auf einer Akzentflaeche. Im Standard ist das #1b1b1b statt der frueheren #1f1f1b -- vier
       Stufen, nicht sichtbar; bei Indigo oder Azur ist es weiss statt dunkel, und das ist der
       Unterschied zwischen sichtbarem und unsichtbarem Knopf. */
    .up-root[data-theme="dark"] .up-switch.is-on::after,
    [data-theme="dark"] .up-switch.is-on::after { background: var(--up-accent-fg); }

    /* ---------- brand mentioned toggle ---------- */

    

    

    

    

    /* ---------- table ---------- */
    .up-box { border: 1px solid var(--vc-border); background: var(--vc-bg); border-radius: 16px; overflow: hidden; }
    /* ---------- sticky header (default on >=1000px page width, off via data-sticky="no") ----------
       Both the component head and the column header stick to the scrolling ancestor. overflow:hidden
       on .up-box would trap the column header, so in sticky mode it becomes visible and the visible
       border moves onto the header and body (see below). Backgrounds must be OPAQUE or rows bleed
       through while scrolling underneath. */
    .up-sticky .up-head {
      position: sticky; top: var(--up-sticky-top); z-index: 6;
      /* DIE FLAECHE MUSS DECKEND SEIN, sonst laufen beim Scrollen die Zeilen sichtbar hinter der
         angehefteten Kopfzeile durch. Genau dafuer ist sie da.
         Sie war vom 08.09. bis zum 07.09. auf transparent: nach der Umstellung auf Linears Leiter
         passte --up-surface nicht mehr zur Seitenfarbe, die in Bubble eingestellt ist, und der
         Streifen wurde in sieben Komponenten als "dunkler Hintergrund hinter Heading und Toolbar"
         gemeldet. Die Diagnose war richtig, die Behandlung nicht -- ich habe die Flaeche entfernt,
         statt die beiden Werte anzugleichen. Nachgereicht vom Nutzer: "das war ein Fehler, du
         haettest sie einfach nur angleichen muessen."
         Jetzt steht die Seite in Bubble auf #ffffff (hell) und #101114 (dunkel), also genau auf
         --up-surface, und der Streifen ist unsichtbar UND tut seine Arbeit.
         WER --up-surface AENDERT, MUSS DIE SEITENFARBE IN BUBBLE MITAENDERN. Laufen die beiden
         auseinander, ist der Streifen sofort wieder als Kasten hinter Ueberschrift und
         Werkzeugleiste zu sehen -- das ist kein Fehler dieser Regel, sondern die Anzeige dafuer,
         dass die zwei Werte nicht mehr zusammenpassen. */
      background: var(--up-surface);
      margin-bottom: 0; height: 48px; padding-bottom: 16px;
    }
    /* Rows scroll up past the pinned header; without this they'd show behind a transparent page
       topbar. This opaque strip covers everything from the header up to the top of the viewport. */
    /* Nur solange die Leiste wirklich klebt (.is-geklebt, 13.10., siehe klebPruefenAlle in core.js):
       in Ruhe deckte der Streifen sonst den Inhalt UEBER der Tabelle zu. */
    .up-sticky.is-geklebt .up-head::before {
      content: ""; position: absolute; left: 0; right: 0; bottom: 100%;
      /* Exactly var(--up-sticky-top), not +16px on top of it -- that extra was meant as "a little
         more painted above, just in case", but it does not scale: at the app's usual sticky-top
         (171px) 16 extra is invisible, but at a small one (16px, an embed sitting near the very top
         of its own page) it DOUBLES the painted strip -- confirmed live as an obvious oversized top
         padding that visibly extends past the component even before any scrolling happens (this
         pseudo-element is unconditionally part of .up-head's box, not scroll-triggered). If a sliver
         of a row ever peeks through above a stuck header again, fix it with a couple of px here, not
         a flat +16 that stops matching the configured offset at all. */
      /* --up-sticky-cover statt --up-sticky-top: hat der Scrollport ein Polster, klebt die Leiste
         an dessen Polsterkante, der Streifen muss aber bis zur Rahmenkante reichen. core setzt
         den Wert gemessen (syncDeckstreifen) und NUR dann; ohne Polster gibt es ihn nicht und es
         bleibt beim bisherigen Wert. */
      height: var(--up-sticky-cover, var(--up-sticky-top)); background: var(--up-surface);
    }
    /* In sticky mode the box's visible border is dropped (kept transparent so layout doesn't
       shift) and drawn on the header and body instead. Both span the full width and align exactly,
       so there's no 1px offset between a header sitting inside the box border and the box's own
       border — that offset was what left a doubled corner and a missing straight top edge. */
    .up-sticky .up-box { overflow: visible; border-color: transparent; }
    .up-sticky .up-table { background: transparent; }
    .up-sticky .up-thead {
      position: sticky; top: calc(var(--up-sticky-top) + var(--up-thead-off)); z-index: 5;
      background-image: linear-gradient(var(--vt-head-bg), var(--vt-head-bg));
      background-color: var(--vc-bg);
      border: 1px solid var(--vc-border); border-bottom: none;
      border-top-left-radius: 16px; border-top-right-radius: 16px;
    }
    /* The rounded top corners are transparent by nature, so the body's straight side border shows
       through them as it scrolls behind the pinned header (the little vertical stub). These two
       corner pieces fill ONLY the triangle outside the radius with the page background, hiding that
       border while leaving the rounded corner itself (inside the radius) untouched. Absolute takes
       them out of the header's grid flow. */
    .up-sticky .up-thead::before,
    .up-sticky .up-thead::after {
      content: ""; position: absolute; top: -1px; width: 16px; height: 16px; pointer-events: none; z-index: 2;
    }
    .up-sticky .up-thead::before { left: -1px;  background: radial-gradient(circle at bottom right, rgba(0,0,0,0) 16px, var(--up-surface) 16.5px); }
    .up-sticky .up-thead::after  { right: -1px; background: radial-gradient(circle at bottom left,  rgba(0,0,0,0) 16px, var(--up-surface) 16.5px); }
    .up-sticky .up-tbody {
      border: 1px solid var(--vc-border); border-top: none;
      border-bottom-left-radius: 16px; border-bottom-right-radius: 16px;
      /* clip, not hidden: visually identical (still clips row backgrounds to the rounded corner),
         but per spec `clip` does not establish a scroll container the way `hidden`/`auto`/`scroll`
         do -- and a scroll-container ancestor is exactly what breaks position:sticky for anything
         nested inside it (prompts-table's grouped view sticks its open group's own header here;
         with `hidden` that sticky silently failed, the header just scrolled away with the page).
         `clip` sidesteps the conflict entirely instead of working around it with corner masks. */
      overflow: clip;
    }
    /* ---------- Dark: kein Rand um die aeusseren Flaechen ----------
       Im Dunkeln traegt die FUELLUNG die Flaeche: --vc-bg (#1b1b1b) auf der Seite (--up-surface
       #121212). Der Rahmen aus --vc-border (#353535) ist dann eine zweite, hellere Linie um eine
       Form, die man ohnehin sieht -- im Hellen ist er noetig, dort liegt Weiss auf Weiss.

       DAS IST DAS KRITERIUM, und es ist der Grund fuer jede Ausnahme unten: der Rand darf nur dort
       weg, wo die Fuellung die Flaeche schon von ihrem Grund trennt.

       Erfasst sind die aeusseren Flaechen -- Tabellenkaesten, Diagrammkaesten, Karten:
         .up-box            der geteilte Tabellenkasten (Prompts, URLs, Domains, Responses,
                            prompt-research)
         .up-sticky .up-thead / .up-tbody
                            im Sticky-Modus zeichnen Kopf und Koerper den Rand SELBST, weil
                            .up-box dort overflow:visible braucht. Ohne diese zwei Zeilen bliebe
                            genau bei den grossen Tabellen die Umrandung stehen.
         .vot-box .tcd-box .ubo-box .combo-box    die vier Diagramm-/Tabellenkaesten
         .uto-table .ube-table                    Team-Protokoll und Alias-Tabelle
         .ubd-card .udd-card .urd-card            die Karten der Detailbereiche
         .urd-cite-card .urt-card                 Quellen- und Antwortkarten (auch die
                                                  Skelettfassung, sie traegt dieselbe Klasse)
         .usb-card                                die Karten der Einstellungen

       NICHT ERFASST, jede mit ihrem Grund:
         .uo-card .uo-row   Opportunities. Die Bahnen sind im Dunkeln #181818, die Karte #1b1b1b --
                            drei Werte Unterschied. Dort TRAEGT der Rand die Karte. In Mira liegen
                            dieselben Karten auf --am-card (#1b1b1b), also auf ihrer eigenen Farbe;
                            ohne Rand waeren sie unsichtbar.
         .usb-card-danger   faerbt den Rand absichtlich rot (color-mix mit --vt-down). Deshalb
                            :not(...) an .usb-card -- diese Regel ist spezifischer und haette das
                            Warnsignal sonst geschluckt.
         .unc-card          die Meldung schwebt ueber fremdem Inhalt, ihr Rand IST ihre Kante.
         Menues, Popover, Modale, Eingabefelder, Knoepfe, Chips
                            schweben oder sind Bedienelemente, keine Flaechen.
         .up-rb-table       dort ist der Rand die Trennlinie ZWISCHEN Zellen, nicht die Kante des
                            Kastens.

       border-color und nicht border: 0 -- der 1px bleibt als Platz stehen, damit keine Flaeche um
       2px waechst und kein Sticky-Kopf um einen Pixel springt. */
    .up-root[data-theme="dark"] .up-box,
    /* .up-vartable ist derselbe Kasten (Rahmen, Radius 16, eigene Flaeche) -- im Dunkeln also
       auch ohne sichtbaren Rand. Steht sie IN einer Karte, holt die Karte den Rand selbst zurueck
       (brand-detail.css). */
    .up-root[data-theme="dark"] .up-vartable,
    /* .up-sticky liegt auf DERSELBEN Wurzel, nicht darunter -- core.js toggelt die Klasse an
       root (makeSticky). Als Nachfahrenselektor geschrieben traf die Regel nichts, und genau die
       sechs grossen Tabellen behielten im Dunkeln ihre Umrandung: Prompts, Responses, Domains,
       URLs, prompt-research, Teams. Sie sind sticky, sobald die Seite 1000px breit ist, und dort
       zeichnen Kopf und Koerper den Rand selbst. */
    .up-root[data-theme="dark"].up-sticky .up-thead,
    .up-root[data-theme="dark"].up-sticky .up-tbody,
    .up-root[data-theme="dark"] .vot-box,
    .up-root[data-theme="dark"] .tcd-box,
    .up-root[data-theme="dark"] .ubo-box,
    .up-root[data-theme="dark"] .combo-box,
    .up-root[data-theme="dark"] .uto-table,
    .up-root[data-theme="dark"] .ube-table,
    .up-root[data-theme="dark"] .ubd-card,
    .up-root[data-theme="dark"] .udd-card,
    .up-root[data-theme="dark"] .urd-card,
    .up-root[data-theme="dark"] .urd-cite-card,
    .up-root[data-theme="dark"] .urt-card,
    /* Die drei Kennzahlen-Karten ueber der Prompts-Tabelle. Sie sind Chart-Karten wie .combo-box
       und .tcd-box, also gehoeren sie in DIESE Liste und nicht in eine eigene Regel: derselbe
       Wert an zwei Orten laeuft irgendwann auseinander. Gemeldet am 03.09. -- die Karten hatten
       im Dunkeln noch ihren Rand. */
    .up-root[data-theme="dark"] .upt-kpi,
    .up-root[data-theme="dark"] .usb-card:not(.usb-card-danger) { border-color: transparent; }

    /* ---------- Dark: an den SEITEN Breite 0, nicht nur farblos ----------
       Ein farbloser Rahmen ist immer noch ein 1px-Streifen, und er zeigt die Flaeche des Elements,
       das ihn traegt: bei .up-box ist das --vc-bg (#1b1b1b). Neben den Zeilen faellt das nicht auf,
       sie haben dieselbe Farbe -- neben einer FLAECHE MIT ANDERER FARBE liest er sich als feine
       helle Linie. Gemeldet am offenen Drilldown der Domains-Tabelle: das Panel ist dort #171717,
       gemessen, und links wie rechts stand 1px #1b1b1b daneben.
       Ueber das Panel legen laesst sich der Streifen nicht: .up-box hat overflow:hidden, und das
       beschneidet am POLSTERkasten -- Inhalt kommt nie in den Rahmenbereich. Also muss der
       Streifen selbst weg.
       NUR links und rechts. Oben und unten bleibt der 1px stehen (farblos), damit keine Hoehe
       springt und der Sticky-Kopf seinen Platz behaelt.
       Und ZWINGEND alle drei zusammen: im Sticky-Modus zeichnen Kopf und Koerper den Rand selbst,
       und schon 1px Unterschied zwischen Kopf und Koerper verschiebt die Spalten gegeneinander --
       davor warnt der Kommentar am Sticky-Block ausdruecklich. */
    .up-root[data-theme="dark"] .up-box,
    .up-root[data-theme="dark"] .up-vartable,
    .up-root[data-theme="dark"].up-sticky .up-thead,
    .up-root[data-theme="dark"].up-sticky .up-tbody {
      border-left-width: 0; border-right-width: 0;
    }

    /* ---------- Rang-/Indexspalte ("#") ----------
       Die feste, nie ausblendbare Spalte ganz links, die UC.makeColumns ueber cfg.leadWidth als
       eigenen Track anlegt. Stand bis eben nur in brands-overview.css; discover-brands ist der
       zweite Verbraucher, also gehoert es nach §25 hierher -- sonst haette die zweite Tabelle
       wieder ihre eigene Schriftgroesse und Farbe bekommen, und genau so entstehen die
       Unterschiede zwischen Tabellen, die gleich aussehen sollen.
       Die Kopfzelle traegt UC.HASH_ICON, nicht das Zeichen "#". */
    /* Doppelte Klasse als Spezifitaets-Anker. .up-th und .up-td setzen selbst padding
       (0 16px 0 12px) und stehen in dieser Datei WEITER UNTEN -- bei gleicher Spezifitaet
       gewinnt die spaetere Regel, die Zahl sass also 2px links neben der Mitte statt darin.
       In brands-overview.css fiel das nicht auf, weil dort .ubo-root davor stand und damit
       ohnehin die hoehere Spezifitaet hatte; beim Umzug hierher ist die weggefallen.
       Verdoppeln statt Verschieben: eine Regel, die nur durch ihre Position gewinnt, kippt
       beim naechsten Umsortieren wieder. */
    .up-th-idx.up-th-idx, .up-td-idx.up-td-idx { justify-content: center; padding: 0; }
    .up-th-idx svg { width: 14px; height: 14px; display: block; }
    .up-td-idx.up-td-idx { font-size: 12px; color: var(--vc-muted); font-variant-numeric: tabular-nums; }

    .up-table { width: 100%; display: flex; flex-direction: column; background: var(--vc-bg); }
    /* Column widths come from --up-cols, which UC.makeColumns' applyCols() writes ONCE on the
       root. Every header and row inherits it, so changing the layout is a single style write
       instead of one per row — with 100 rows and a column drag firing at up to 120 Hz, the
       per-row version was writing ~100 inline styles several times per frame, which is a large
       part of why dragging felt like it ran at a fraction of the frame rate.
       The literal below is only the pre-JS fallback (urls-table's shape, the widest of the
       tables); applyCols() replaces it on first run. The percentages are MINIMUMS; the 1fr
       maxima let the flexible columns absorb the leftover space. */
    .up-thead, .up-row {
      display: grid;
      grid-template-columns: var(--up-cols,
        minmax(30%, 1.6fr)    /* URL              */
        minmax(13%, 150px)    /* Share            */
        minmax(11%, 1fr)      /* Type             */
        minmax(112px, 0.6fr)  /* Mentioned?       */
        minmax(13%, 1fr)      /* Brands mentioned */
        minmax(104px, 0.7fr)  /* Last Seen        */
        minmax(120px, auto)   /* Actions          */);
      align-items: center;
    }
    /* Bounds layout/style invalidation to the row. Without it every one of the measure-then-write
       cycles elsewhere in this file costs a layout of the WHOLE table subtree; with 50-100 rows
       that is the difference between a resize that tracks the cursor and one that stutters. */
    .up-row { contain: layout style; }
    .up-thead { height: 45px; flex: 0 0 45px; background: var(--vt-head-bg); }
    .up-th { font-size: 12px; font-weight: 400; color: var(--vc-muted); padding: 0 16px 0 12px; height: 100%;
      white-space: nowrap; overflow: hidden;
      display: flex; align-items: center; gap: 6px; border-left: 1px solid var(--vc-border); border-bottom: 1px solid var(--vc-border); }
    /* Column separators sit on the LEFT edge, not the right, and the exception is :first-child.
       Mirror-imaging this matters: hidden columns are still DOM children, so with border-right +
       ":last-child { border-right: none }" the exception kept landing on a display:none cell while
       the genuinely last VISIBLE column still painted a right border straight against the table's
       own frame — two lines a pixel apart, which read as "the right border gets thicker whenever
       columns drop out". Anchoring on :first-child is immune to that, because the lead column is
       the one column that never hides. Same set of separators either way. */
    .up-th:first-child { border-left: none; }
    .up-th.is-sortable { cursor: pointer; user-select: none; }
    /* Der Kopftext muss SCHRUMPFEN duerfen. Fuer .up-th-txt gab es keine einzige Regel: der Text
       war damit unschrumpfbar, der ganze Inhalt lief bei enger Spalte ueber, und weil overflow
       an der POLSTERKANTE schneidet und nicht an der Inhaltskante, rutschte der Sortierchevron in
       die 16px rechts hinein und klebte am Rahmen. Genau so gemeldet fuer die Visibility-Spalte.
       min-width:0 ist der Kern -- ein Flex-Kind schrumpft ohne das nicht unter seine Inhaltsbreite.
       Der Chevron und das Info-Zeichen bleiben feste Breite, also verliert der TEXT die Pixel und
       nicht das Bedienelement. */
    .up-th-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .up-th > .up-thsort, .up-th > .up-th-info { flex: 0 0 auto; }
    /* ---------- column resizing ---------- */
    .up-th { position: relative; }
    .up-grip {
      position: absolute; top: 0; right: -4px; width: 9px; height: 100%;
      cursor: col-resize; z-index: 6; touch-action: none;
      display: flex; align-items: center; justify-content: center;
    }
    .up-grip::before {
      content: ""; width: 2px; height: 18px; border-radius: 2px;
      background: transparent; transition: background .12s ease;
    }
    .up-grip:hover::before, .up-root.is-resizing .up-grip.is-active::before { background: var(--vc-hover-border); }
    .up-th:last-child .up-grip { display: none; }
    .up-root.is-resizing { cursor: col-resize; user-select: none; }
    .up-root.is-resizing .up-row { cursor: col-resize; }
    .up-th.is-sortable:hover { color: var(--vc-text); }

    /* Kopf-Sortierer: zwei 14px-Chevrons uebereinander, je 2px zusammengezogen. Die Geometrie ist
       Lucide chevron-up / chevron-down. Der aktive zeigt in die Sortierrichtung. */
    .up-thsort { display: inline-flex; flex-direction: column; align-items: center; flex: 0 0 auto; line-height: 0; }
    .up-thsort svg { width: 14px; height: 14px; display: block; color: var(--vc-third); transition: color 120ms ease; }
    .up-thsort .up-thsort-up { margin-bottom: -2px; }
    .up-thsort .up-thsort-down { margin-top: -2px; }
    .up-thsort.is-asc .up-thsort-up { color: var(--vc-text); }
    .up-thsort.is-desc .up-thsort-down { color: var(--vc-text); }
    /* the sub-label that appears when a column is sorted by its secondary key (Share → Trend) */
    .up-th-sub { font-size: 11px; font-weight: 500; color: var(--vc-text); }

    .up-tbody { display: flex; flex-direction: column; }
    /* Soft-reload dim — see UC.makeSoftReload. The rows stay on screen because they're still
       truthful (a sort only re-orders the same result set); dimming says "working" without
       destroying the thing the user is looking at. Search/filters do NOT use this: there the
       result set itself changes, so the old rows would be a lie and the skeleton is the honest
       answer instead. */
    /* .uto-table: die Tabellen der Team-Verwaltung (12.10.) -- waehrend Resend, Revoke, Remove
       oder ein Rollenwechsel laeuft, dimmen sie genauso; vorher gab es bis zur Antwort gar keine
       Rueckmeldung. */
    .up-root.is-reloading :is(.up-tbody, .uto-table) { opacity: .42; transition: opacity 140ms ease; pointer-events: none; }
    @media (prefers-reduced-motion: reduce){
      .up-root.is-reloading :is(.up-tbody, .uto-table) { transition: none; }
    }
    /* height/flex-basis deliberately don't transition: with 50-100 rows on screen, animating them
       forces a synchronous layout reflow on every row simultaneously, which visibly stalls the
       page for a couple of seconds on Comfortable/Compact toggle before the rows "jump" to their
       final height at once. Per-row hover (background) is instant, no transition — explicit
       instruction: no ease on table-row hover, anywhere. */
    .up-row { height: 72px; flex: 0 0 72px; cursor: pointer; }
    /* Compact row mode: shorter rows, first column shows only the title (URL sub-line hidden). */
    .up-root.is-dense .up-row { height: 55px; flex: 0 0 55px; }
    
    /* Compact mode shrinks the mentioned-brand logos ~20% and scales every gap/shift to match. */

    

    

    

       /* skeleton brand logo matches compact size */
    .up-row:hover { background: var(--vt-row-hover); }
    /* While the page is actively scrolling (see the scroll listener in core.js), hover-matching
       is suspended tbody-wide — content sliding under a stationary cursor still fires real
       mouseenter/mouseleave, and .up-row:hover's background is a paint, not a compositor-only
       change, so a tall table under the cursor during a scroll was a full-width paint per row per
       frame. pointer-events:none takes every hover-driven style with it in one rule (row
       background, chip hover, the goto arrow) instead of having to override each individually. */
    html.up-is-scrolling .up-tbody { pointer-events: none; }
    /* overflow:hidden matches .up-th (see below) -- a cell's own content (a long unbroken chip
       row, e.g.) must not be able to push past the cell's actual grid-track width once that track
       has shrunk. Tooltips/popovers spawned from inside a cell are all position:fixed (mounted on
       body or fixed to the viewport), so this never clips one of those. */
    .up-td { height: 100%; display: flex; align-items: center; gap: 10px; padding: 0 16px 0 12px; min-width: 0;
      overflow: hidden; border-left: 1px solid var(--vc-border); }
    .up-td:first-child { border-left: none; }   /* see .up-th:first-child for why left, not right */
    /* The row separator lives on the ROW now, not on every individual cell. Per-cell borders broke
       in two independent ways: (1) a stale/un-restamped cell (see applyCols()'s rowsStale fix above)
       could sit display:none and silently drop its slice of the line, and (2) prompts-table's
       Dynamic row-height mode gives every .up-td its own height:auto -- if a browser's grid-stretch
       ever rounds one cell's stretched height a subpixel off from its neighbours (fractional row
       heights from wrapped text are common there), each cell's border-bottom lands at a very
       slightly different Y and the line reads as broken. One border on the row's own box, drawn
       once at the row's actual outer edge, is immune to both: it does not care how many cells are
       hidden or how their individual heights were rounded. */
    .up-row { border-bottom: 1px solid var(--vc-border); }
    /* > und nicht bloss ein Nachfahren-Selektor. Ohne den Kind-Kombinator traf die Regel JEDE
       .up-row, die letztes Kind ihres Elternteils ist -- in der Gruppenansicht also die letzte
       Zeile JEDER Gruppe, denn die stehen in .upt-grp-rows-Containern innerhalb von .up-tbody.
       Genau das gemeldete Bild: hier und da fehlt eine Trennlinie, aber nicht ueberall. */
    .up-tbody > .up-row:last-child { border-bottom: none; }

    
    /* Letter fallback: a broken/missing favicon used to leave an empty square, which reads as a
       loading glitch. The initial keeps every row visually complete. */

    /* go-to arrow, revealed on row hover with a delay — same behaviour as the other tables */

    
    
    /* URL cell: title on top, url underneath — same treatment as the Quick Actions result rows */

    

    /* Mentioned? cell */

    

    /* ---------- brand stack ----------
       Overlapping 32px circles. The 4px visual gap between them is a ring in the ROW's background
       colour, not a real gap: with a negative margin the neighbour would otherwise touch. The ring
       has to follow the row's hover colour too, hence the two overrides below. */
    /* The cell clips: with a negative-margin stack plus the hover spread, a narrow column would
       otherwise let logos spill over the column border and sit on top of the neighbouring cell. */

    
    
    /* only the gap-ring follows the row hover colour; the circle fill stays the (light-in-dark) logo bg */

    
    
    /* plain text, no circle: the overflow count is a label, not another brand */
    
    /* Hovering a logo lifts it, brings it to front, and opens a gap on both sides — created by
       shifting the hovered item and everything AFTER it to the right. Items BEFORE it never move
       (so the first brand can't drift left). Following items shift twice as far so their gaps to
       each other stay at the default: they don't squash together.
       The ::before is a hover-only, enlarged hit area: it covers the item's pre-shift footprint so
       moving right/up can't pull the item out from under the cursor — that's what caused the
       hover/un-hover flicker loop. It only exists while hovered, so it never steals a neighbour's
       hover. */

    

    .up-name { font-size: 14px; font-weight: 500; color: var(--vc-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
    .up-hl { background: rgba(255, 214, 10, 0.38); color: inherit; border-radius: 3px; padding: 0 1px; }
    .up-root[data-theme="dark"] .up-hl { background: rgba(255, 214, 10, 0.26); }
    /* Reddit fallback title (UC.redditTitleHtml) — the r/sub prefix and its separator read as a
       muted breadcrumb, same tone as everywhere else this app uses --vc-third for "secondary,
       not the point" text; the parsed slug after it stays the row's own normal text colour. */
    .up-reddit-sub, .up-reddit-sep { color: var(--vc-third); }
    .up-pages { font-size: 12px; font-weight: 400; color: var(--vc-third); white-space: nowrap; flex: 0 0 auto; }

    

    

    

    /* 2x28px buttons + 2px gap + the cell's 12/16px padding = 86px of hard content.
       ACTIONS_MIN sits above that so the cell never has to clip. */

    

    /* brief checkmark after copying — confirms the action at the button itself, not only in the toast */
    /* `.uut-actbtn svg { display:block }` is more specific than a bare `.up-ic-done`, which is
       why the checkmark showed permanently. Both states are qualified the same way now. */

    

    .up-empty { padding: 48px 24px; text-align: center; }
    .up-empty-ic { width: 28px; height: 28px; margin: 0 auto 12px; color: var(--vc-third); }
    .up-empty-ic svg { width: 28px; height: 28px; display: block; }
    .up-empty-h { font-size: 14px; font-weight: 500; color: var(--vc-text); margin-bottom: 4px; }
    .up-empty-t { font-size: 12.5px; color: var(--vc-muted); margin-bottom: 14px; }
    .up-empty-btn { height: 32px; padding: 0 12px; border: 1px solid var(--vc-border); border-radius: 8px;
      background: transparent; color: var(--vc-text); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; }
    .up-empty-btn:hover { background: var(--vc-heading-bg); }
    .up-row.up-tsk { cursor: default; }
    .up-row.up-tsk:hover { background: transparent; }
    .up-tsk-bar { display: inline-block; height: 12px; border-radius: 4px; background: var(--vc-sk); animation: uutpulse 1.2s ease-in-out infinite; }
    .up-tsk-logo { display: inline-block; width: 24px; height: 24px; border-radius: 8px; background: var(--vc-sk); animation: uutpulse 1.2s ease-in-out infinite; flex: 0 0 auto; }
    @keyframes uutpulse { 0%,100% { opacity: 1; } 50% { opacity: .45; } }
    /* SKELETTE, DIE NIEMAND SIEHT, STEHEN STILL (13.10.): beim Start baut die App alle Ansichten und
       Drawer mit Skeletten -- gemessen 1280 Endlos-Animationen, rund 1090 davon in geparkten
       Ansichten oder geschlossenen Drawern. Stil und Malen in jedem Bild: der Hauptthread war in Ruhe
       zu 100 Prozent belegt (5.1 s Arbeit in 5 s, 4x CPU), mit dieser Regel 0.5 s. Pausieren statt
       abschalten: kein Layoutwechsel, und beim Einblenden laeuft es an derselben Stelle weiter.
       html davor fuer die Spezifitaet gegen .x .y::after-Regeln der Skelette. */
    html [id^="view-"]:not(.view-on) *, html [id^="view-"]:not(.view-on) *::before, html [id^="view-"]:not(.view-on) *::after,
    html .up-kdr:not(.open) *, html .up-kdr:not(.open) *::before, html .up-kdr:not(.open) *::after { animation-play-state: paused; }

    /* Narrow layouts drop columns too, but that is handled together with the gear menu in
       applyCols() — an inline grid-template from JS would beat a class rule here, so both
       have to come from the same place. */
    /* ---------- progressive disclosure by width ----------
       Four breakpoints, dropping the least important control first. Order of sacrifice:
       brand toggle (a shortcut, reachable via the filter) → citation type label collapses to an
       icon-width control → sort (the header sorters still work) → columns gear. Export and search
       survive longest: export is the reason people come to the table, search is the only way to
       find something in a long list. */
    /* Labelled dropdowns are never squeezed into icon-sized buttons: a control that loses its
       label stops saying what it filters, which is the whole reason it is labelled. They drop out
       one at a time instead, least important first.
         w3  brand toggle      (a shortcut; the same filter exists in the dropdowns)
         w2  mentioned brands  (the broader of the two brand filters)
         w1  sort              (the header sorters still cover Share and Last Seen)
         w0  columns gear      + Export collapses to its icon
       Search and the type filter survive longest: search is the only way through a long list,
       and the type filter is the one people actually reach for. */
    
    .up-root.is-w2 .up-ment { display: none; }
    .up-root.is-w1 .up-sort { display: none; }
    .up-root.is-w0 .up-cols { display: none; }
    .up-root.is-w0 .up-export span { display: none; }        /* export shrinks to its icon */
    .up-root.is-w0 .up-export { padding: 0 8px; }
    .up-root.is-w0 .up-search.is-open .up-search-box { width: 120px; }
    .up-root.is-vnarrow .up-search.is-open .up-search-box { width: 140px; }

    /* table: same idea, dropping cell content before dropping whole columns
       (a domain name you can't read is worse than a missing trend chip) */
    
    .up-root.is-t1 .up-pages { display: none; }

    /* ---------- tooltip (same as the other components) ---------- */
    /* DER TOOLTIP LIEGT UEBER ALLEM (21.09. gemeldet: "die Tooltips in der eingeklappten Leiste
       sollen trotzdem ueber dem Dropdown liegen -- bei offenem Pinned-Menue liegt das Tooltip
       von Recent darunter"). Gemessen lagen beide als Kinder des <body> nebeneinander, es
       entschied also allein die Zahl: Tooltip 2147483000 gegen Miras Menue 2147483600. Und das
       Menue ist nicht der einzige: quick-actions steht auf 2147483601, die Benachrichtigung auf
       2147483500, der Markendialog auf 2147483200.
       Ein Tooltip ist der oberste Gegenstand der Oberflaeche -- er beschriftet, was darunter
       liegt, und kann nichts verdecken, weil er pointer-events: none traegt und beim ersten
       Klick verschwindet. Also eine Zahl ueber ALLEN anderen, mit etwas Luft unter dem
       Hoechstwert von int32 (2147483647), damit darueber noch etwas Platz bleibt. */
    .up-tip {
      position: fixed; z-index: 2147483640; box-sizing: border-box;
      height: 24px; display: inline-flex; align-items: center; padding: 0 9px;
      border-radius: 6px; background: #1f1f1b; color: #ffffff;
      font-family: Geist, system-ui, sans-serif; font-size: 12px; font-weight: 500; line-height: 1;
      white-space: nowrap; pointer-events: none;
      /* e-2, die Tiefe von allem, was an einem Ausloeser haengt -- dieselbe wie die Erklaerkarte,
         die ein grosser Tooltip ist. Vorher ein eigener Wert (0 4px 14px .18). Das Token
         erreicht den Tooltip am <body>, seit die Tiefen an :root stehen. */
      box-shadow: var(--up-e-2);
      opacity: 0; transform: translateY(4px);
      transition: opacity .14s ease, transform .14s ease;
    }
    /* wider, wrapping variant for the full URL title (which can be long) */
    .up-tip.is-wide { height: auto; white-space: normal; max-width: 340px; padding: 6px 9px; line-height: 1.35; align-items: flex-start; }
    .up-tip.is-on { opacity: 1; transform: translateY(0); }
    /* Rechts angesetzt bekommt der Chip KEINE Verschiebung: er sitzt auf der Mitte des
       Ausloesers, und die Grundregel oben wuerde ihn um 4px nach unten schieben. Den Wert setzt
       placeTip() direkt am Element -- eine Regel hier verlor gegen die Grundregel, gemessen blieb
       transform: translateY(4px) aktiv. Eingeblendet wird allein ueber die Deckkraft. */
    .up-tip[data-theme="dark"] { background: #f0f0f0; color: var(--vc-text-invert); }

    /* ---------- Der Toast (10.10., UC.toast in core.js) ----------
       Unten mittig im INHALTSbereich: links die Breite der Seitenleiste, die sidebar.js als
       --up-sidebar-w ans <html> schreibt (dieselbe Mitte wie die Massenleiste der Prompts-Tabelle).
       --up-toast-unten setzt core, wenn unten schon etwas liegt. Die Huelle laesst Klicks durch,
       nur die Toasts selbst nehmen sie an.
       z-index ueber Drawern, Menues und Dialogen, unter dem Tooltip (2147483640 -- der
       beschriftet und verdeckt nie). */
    .up-toasts {
      position: fixed; left: var(--up-sidebar-w, 0px); right: 0; bottom: var(--up-toast-unten, 24px);
      z-index: 2147483630; display: flex; flex-direction: column; align-items: center; gap: 8px;
      padding: 0 16px; pointer-events: none;
      font-family: Geist, system-ui, sans-serif; -webkit-font-smoothing: antialiased;
      transition: bottom var(--up-t-2) var(--up-ease);
    }
    /* Im Top Layer (popover, siehe core.js toastHost) bringt der Browser eigene Vorgaben mit:
       inset 0, margin auto, Rahmen, Hintergrund. Weggeraeumt wie bei .uca-portal[popover]. */
    .up-toasts[popover] {
      top: auto; width: auto; height: auto; max-width: none; max-height: none;
      margin: 0; border: 0; background: transparent; color: inherit; overflow: visible;
    }
    .up-toasts[popover]:not(:popover-open) { display: none; }
    .up-toast {
      box-sizing: border-box; pointer-events: auto;
      display: flex; align-items: flex-start; gap: 10px;
      width: fit-content; max-width: min(420px, calc(100vw - 32px)); min-height: 44px;
      padding: 12px 12px 12px 14px; border-radius: var(--up-r-xl);
      background: var(--vc-toast-bg); color: var(--vc-toast-ink); box-shadow: var(--up-e-4);
      opacity: 0; transform: translateY(24px) scale(.96);
      transition: opacity var(--up-t-2) var(--up-ease-auf), transform var(--up-t-2) var(--up-ease-auf);
    }
    /* DEUTLICHER HEREIN (12.10. gemeldet: "sieht oft aus, als wuerden die einfach aufploppen"):
       24px Weg statt 10, die Dauer des Seitenauftritts (600ms) mit easeOutExpo wie mobbin -- der
       groesste Teil der Strecke liegt in den ersten 200ms, der Rest setzt sanft auf. Dazu 120ms
       Abstand zur Handlung: der Toast antwortet auf den Klick, statt mit ihm zusammenzufallen. Die
       Deckkraft ist schneller als der Weg, sonst steht ein halb durchsichtiger Kasten lange da. */
    .up-toast.is-on { opacity: 1; transform: none;
      transition: opacity var(--up-t-3) ease var(--up-t-1), transform var(--up-t-auftritt) var(--up-ease-expo) var(--up-t-1); }
    @media (prefers-reduced-motion: reduce) {
      .up-toast, .up-toast.is-on { transform: none; transition: opacity var(--up-t-1) ease; }
    }
    /* Abgang: der Handoff nennt 160 ms -- auf die Skala gezogen (--up-t-1, 120 ms); der Unterschied
       ist nicht zu sehen, eine Dauer neben der Skala waere ein dritter Wert fuer dasselbe. */
    .up-toast.is-weg { opacity: 0; transform: scale(.97); transition: opacity var(--up-t-1) ease, transform var(--up-t-1) ease; }
    .up-toast-ic { flex: 0 0 16px; width: 16px; height: 16px; margin-top: 2px; display: inline-flex; color: var(--vc-toast-neutral); }
    .up-toast-ic svg { width: 16px; height: 16px; display: block; }
    .up-toast.is-success .up-toast-ic { color: var(--vc-toast-ok); }
    .up-toast.is-error .up-toast-ic { color: var(--vc-toast-fehler); }
    .up-toast-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; padding-right: 4px; }
    /* break-word, nicht anywhere: bei "anywhere" zaehlt jeder Buchstabe als moegliche Bruchstelle,
       und im schmalen Inhaltsbereich brach der Text Zeichen fuer Zeichen um (10.10. gemessen bei
       180px neben der Leiste). So bleibt das laengste Wort die kleinste Breite. */
    .up-toast-t { font-size: var(--up-fs-m); font-weight: var(--up-fw-m); line-height: 20px; overflow-wrap: break-word; }
    .up-toast-d { font-size: var(--up-fs-m); font-weight: var(--up-fw-n); line-height: 18px; color: var(--vc-toast-sub); overflow-wrap: break-word; }
    .up-toast-act {
      flex: 0 0 auto; height: 24px; margin-top: -2px; padding: 0 8px; border: 0; border-radius: var(--up-r-s);
      background: var(--vc-toast-flaeche); color: var(--vc-toast-ink);
      font-family: inherit; font-size: var(--up-fs-m); font-weight: var(--up-fw-m); cursor: pointer;
      transition: background var(--up-t-1) var(--up-ease);
    }
    .up-toast-act:hover { background: var(--vc-toast-flaeche-hover); }
    .up-toast-x {
      flex: 0 0 20px; width: 20px; height: 20px; padding: 0; border: 0; border-radius: var(--up-r-s);
      display: inline-flex; align-items: center; justify-content: center;
      background: transparent; color: var(--vc-toast-zu); cursor: pointer;
      transition: background var(--up-t-1) var(--up-ease), color var(--up-t-1) var(--up-ease);
    }
    .up-toast-x svg { width: 13px; height: 13px; display: block; }
    .up-toast-x:hover { color: var(--vc-toast-ink); background: var(--vc-toast-flaeche); }
    .up-toast-act:focus-visible, .up-toast-x:focus-visible { outline: var(--up-focus-w) solid var(--up-focus); outline-offset: 1px; }
    @media (prefers-reduced-motion: reduce){
      .up-toast, .up-toast.is-on, .up-toast.is-weg { transform: none; }
      .up-toasts { transition: none; }
    }
    /* Tooltips keep the same dark card in both themes: they float above the page, not inside
       the surface, so inverting them made them read as a different component. */

/* ==========================================================================================
   CHART KITS — styling for UpstreemCore.makeLine / makeTypeChart
   ==========================================================================================
   These used to live once per component (.vc-line-* + .cc-line-*, .tcl-* + .ccd-*), which is how
   the same chart drifted apart between components. Everything here is now the single source; a
   component only supplies the wrap/canvas/legend/body elements and its own box sizing. */

/* ---------- line chart ---------- */
.up-line-wrap { position: relative; flex: 1 1 auto; min-height: 0; width: 100%; }

/* ---- Wasserzeichen im Liniendiagramm --------------------------------------------------------
   Das Div setzt core.js (injectWatermark) leer ein, alles Sichtbare steht hier -- so wechselt die
   Datei beim Themenwechsel von selbst mit, sobald setUpstreemTheme data-theme umstellt.

   Zur Groesse, weil sie nicht mehr die alte ist und das erklaert werden muss: das frueher inline
   gezeichnete Zeichen hatte ein Seitenverhaeltnis von 3.84 (707 zu 184), die Lockup-Datei hat
   7.04 (760.5 zu 108) -- sie ist deutlich flacher. "Gleiche Groesse" geht damit nicht auf beiden
   Achsen. Gemessen wurde die TINTE, also der wirklich bedeckte Bereich, per Pixelabtastung:
     alt, Kasten 150 x 39.04   ->  Tinte 147 x 28
     neu, Kasten 150 x 21.31   ->  Tinte 141 x 22   (gleiche Breite, 21% flacher)
     neu, Kasten 275 x 39.04   ->  Tinte 258 x 40   (gleiche Hoehe, 75% breiter)
   Genommen ist der Kasten mit der GLEICHEN FLAECHE: 173 x 24.57 ergibt Tinte etwa 163 x 25, also
   beide Achsen innerhalb von zehn Prozent des alten Zeichens. Deckkraft (0.05) ist unveraendert.

   ZUR LAGE: top 47% und dazu 4px nach unten (08.09., in zwei Schritten -- erst "4px, oder eher
   6", dann "nochmal 2px nach oben"; das ergibt die 4 vom Anfang). Die 47% sind der alte Wert --
   das Zeichen sitzt absichtlich etwas ueber der Mitte, weil unter dem Chart die Achsenbeschriftung
   steht und die Flaeche darueber optisch groesser ist. Die 6px kommen als Verschiebung im
   transform dazu und nicht als geaenderte Prozentzahl: Prozent haengen an der HOEHE des Kastens,
   und in einem hohen Chart waere derselbe Prozentwert ein anderer Weg in Pixeln. Angefordert
   waren Pixel, also stehen dort Pixel.

   background-size: contain und nicht 100% 100%: die Datei behaelt ihr Verhaeltnis, auch wenn
   jemand den Kasten spaeter anfasst. */
.upstreem-watermark {
  position: absolute; top: 47%; left: 50%; transform: translate(-50%, calc(-50% + 4px));
  width: 173px; height: 24.57px;
  opacity: 0.05; pointer-events: none; z-index: 2;
  background-repeat: no-repeat; background-position: center; background-size: contain;
  background-image: url("https://tgdossbsevnonssyuewp.supabase.co/storage/v1/object/public/BRANDSTYLES/upstreem-lockup-1f1f1f.svg");
}
.up-root[data-theme="dark"] .upstreem-watermark {
  background-image: url("https://tgdossbsevnonssyuewp.supabase.co/storage/v1/object/public/BRANDSTYLES/upstreem-lockup-e0e0e0.svg");
}
.up-line-canvas { display: block; width: 100% !important; height: 100% !important; }
.up-line-empty { position: absolute; left: 0; right: 0; top: 58%; transform: translateY(-50%); display: flex; align-items: center; justify-content: center; font-size: 13px; color: var(--vc-muted); }

/* line skeleton: shimmering grid + a sweeping stroke along a fake series path */
.up-line-sk { position: absolute; inset: 0; pointer-events: none; --shimmer: .14; }
.up-line-sk .sk-linechart { width: 100%; height: 100%; box-sizing: border-box; display: flex; flex-direction: column; gap: 6px; padding: 32px; }
.up-line-sk .sk-lc-grid { flex: 1; position: relative; display: flex; flex-direction: column; justify-content: space-between; }
.up-line-sk .sk-lc-hline { height: 1px; background: var(--vc-sk); border-radius: 1px; position: relative; overflow: hidden; }
.up-line-sk .sk-lc-hline::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,var(--shimmer)) 50%, rgba(255,255,255,0) 100%); transform: translateX(-100%); animation: upskt 1200ms linear infinite; }
.up-line-sk .sk-lc-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.up-line-sk .sk-lc-astop0 { stop-color: var(--vc-sk); stop-opacity: .5; }
.up-line-sk .sk-lc-astop1 { stop-color: var(--vc-sk); stop-opacity: 0; }
.up-line-sk .sk-lc-stroke { fill: none; stroke: var(--vc-sk); stroke-width: 1.875; stroke-linecap: round; }
.up-line-sk .sk-lc-shimmer-path { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: upskt-path 1200ms linear infinite; stroke: rgba(255,255,255,var(--shimmer)); fill: none; stroke-width: 2.25; stroke-linecap: round; }
.up-line-sk .sk-lc-xaxis { display: flex; justify-content: space-between; flex-shrink: 0; padding: 0 1px; }
.up-line-sk .sk-lc-xlabel { height: 8px; width: 24px; background: var(--vc-sk); border-radius: 4px; position: relative; overflow: hidden; }
.up-line-sk .sk-lc-xlabel::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,var(--shimmer)) 50%, rgba(255,255,255,0) 100%); transform: translateX(-100%); animation: upskt 1200ms linear infinite; }
@keyframes upskt { 100% { transform: translateX(100%); } }
@keyframes upskt-path { 0% { stroke-dashoffset: 1000; } 100% { stroke-dashoffset: -1000; } }
@media (prefers-reduced-motion: reduce){ .up-line-sk .sk-lc-hline::after, .up-line-sk .sk-lc-xlabel::after, .up-line-sk .sk-lc-shimmer-path { animation: none; } }

/* ---------- line legend (balanced rows, hover-dims the other series) ---------- */
.up-legend { width: 100%; height: auto; box-sizing: border-box; overflow: visible; --up-column-gap: 16px; margin-top: 12px; flex: 0 0 auto; }
.up-legend.is-hidden { display: none; }
.up-legend .up-company-rows { width: 100%; height: auto; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; row-gap: 8px; box-sizing: border-box; }
.up-legend .up-company-row { width: 100%; min-height: 24px; display: flex; align-items: center; justify-content: center; column-gap: var(--up-column-gap, 16px); box-sizing: border-box; overflow: hidden; min-width: 0; }
.up-legend .up-company-item { height: 24px; padding: 2px 8px; border: 0; background: transparent; display: inline-flex; align-items: center; justify-content: flex-start; flex: 0 1 auto; min-width: 0; box-sizing: border-box; white-space: nowrap; transition: opacity 200ms ease; }
.up-legend .up-company-color { width: 10px; height: 10px; min-width: 10px; min-height: 10px; border-radius: 4px; flex: 0 0 auto; }
/* Vergleichsreihe (Event-Detail): das Feld gestrichelt umrandet statt gefuellt -- wie ihre Linie.
   Die Farbe kommt inline als border-color. */
.up-legend .up-company-color.is-dash { background: transparent; border: 2px dashed; box-sizing: border-box; }

/* ---- Event-Pins in den Liniendiagrammen (Design-Handoff "Event-Pins im Analytics-Chart", 02.10.) --
   Die Masse sind die der Vorgabe, Wert fuer Wert: Pin 20 x 20, Radius 6, Rahmen 1px, Zeichen 11px
   mit Deckkraft .72; Tooltip 20px rechts der Pin-Mitte, Polster 7/10, Radius 8; Stapel mit 14px
   Versatz und 1,5px Ring; Karte min. 190 breit, Polster 6. Die Hilfslinie zeichnet makeLine auf
   der Leinwand. Der Anker (.up-evm-ort) hat keine Groesse und sitzt auf der Pin-Mitte.
   Zwei Abweichungen: die Schrift ist Geist wie in der ganzen App (Vorgabe: System-UI), und der
   Zeitraum im Kartenkopf steht auf 11 statt 10,5px -- halbe Pixel runden je Browser anders, die
   Skala der App hat sie darum nicht (CLAUDE.md 1c). */
/* Der Kasten eines Charts ist sein eigener Stapelkontext (13.10.): z-index 9999/10000 von Tooltip
   und Pins gelten nur darin. Ohne das stachen sie durch die ganze Seite -- ueber Drawer (9905-9930),
   Menues und Popover. Jetzt liegen sie auf der Ebene der Seite, in der das Chart steht. */
.up-chart-kasten { isolation: isolate; }
.up-evm-layer { position: absolute; inset: 0; pointer-events: none;
  /* UEBER dem Tooltip von Chart.js (.up-line-tt, z-index 9999 im selben Kasten). Mit 2 lag die
     Karte eines Stapels hinter ihm (03.10. gemeldet); die Ebene selbst nimmt keinen Zeiger, nur
     Pins und eine offene Karte tun es. */
  z-index: 10000; }
.up-evm-ort { position: absolute; width: 0; height: 0; }
/* Der Knopf traegt seine Pins: einer beim einzelnen Event, bis zu drei im Stapel, je 14px versetzt
   (Vorgabe), der vorderste oben. --n und --i setzt makeLine. */
.up-evm-knopf { position: absolute; left: -10px; top: -10px; height: 20px; width: calc(20px + (var(--n, 1) - 1) * 16px);
  padding: 0; margin: 0; border: 0; background: none; cursor: pointer; pointer-events: auto; font: inherit; }
.up-evm-knopf:focus-visible { outline: 2px solid var(--up-focus); outline-offset: 2px; border-radius: var(--up-r-m); }
.up-evm { position: absolute; left: calc(var(--i, 0) * 16px); top: 0; z-index: calc(3 - var(--i, 0)); width: 20px; height: 20px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--up-evm-rand); border-radius: var(--up-r-s); background: var(--vc-bg);
  color: var(--vc-muted); box-shadow: var(--up-e-1); }
.up-evm svg { width: 11px; height: 11px; opacity: .72; flex: 0 0 auto; }
/* Das Fokus-Event im Event-Detail: dasselbe Zeichen, voll gedeckt -- der Rest ist Vorgabe. */
.up-evm.is-fokus { border-color: var(--vc-third); }
.up-evm.is-fokus svg { opacity: 1; }
/* Im Stapel ein Ring in der Flaeche des Diagramms -- er ist die sichtbare Luft zwischen den
   ueberlappenden Pins. Vorgabe 1,5px; am 03.10. zwei Pixel mehr verlangt, und zwar an DIESER Luft,
   nicht am Versatz (der kurz auf 16 stand und die Pins nur auseinanderschob). */
.up-evm-stapel .up-evm { box-shadow: 0 0 0 3.5px var(--vc-bg); }

/* DIE KARTE -- eine fuer einen Pin und fuer einen Stapel. Rechts oben am Marker: 20px rechts der
   Mitte des letzten Pins, 3px ueber seiner Oberkante; an der Kante spiegelt makeLine sie nach links
   (.is-links). Schatten wie in der Vorgabe (0 4px 14px .08) -- zwischen --up-e-1 und --up-e-2, die
   beide nicht passen. Erscheint bei Hover, Fokus oder nach einem Tipp (.is-offen), 120ms Einblenden. */
.up-evm-karte { position: absolute; z-index: 5; left: calc(20px + (var(--n, 1) - 1) * 16px); top: -13px; white-space: nowrap;
  min-width: 190px; padding: 6px; display: flex; flex-direction: column; gap: 2px;
  background: var(--vc-bg); border: 1px solid var(--vc-border); border-radius: var(--up-r-m);
  box-shadow: 0 4px 14px rgba(17,24,39,.08);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--up-t-1) ease, visibility 0s linear var(--up-t-1); }
.up-root[data-theme="dark"] .up-evm-karte { background: var(--up-menu-bg); border-color: var(--up-menu-border); box-shadow: var(--up-e-2); }
.up-evm-ort.is-links .up-evm-karte { left: auto; right: 20px; }
.up-evm-ort:hover .up-evm-karte,
.up-evm-ort:focus-within .up-evm-karte,
.up-evm-ort.is-offen .up-evm-karte { opacity: 1; visibility: visible; pointer-events: auto; transition-delay: 0s; }
/* Die Zeilen sind klickbar; die 10px Luft zwischen Pin und Karte ueberbrueckt ein unsichtbarer
   Streifen, sonst ginge der Hover auf dem Weg dorthin verloren. */
.up-evm-karte::before { content: ""; position: absolute; top: 0; left: -12px; width: 12px; height: 34px; }
.up-evm-ort.is-links .up-evm-karte::before { left: auto; right: -12px; }
.up-evm-ic { display: inline-flex; flex: 0 0 auto; color: var(--vc-muted); }
.up-evm-ic svg { width: 13px; height: 13px; opacity: .7; }
.up-evm-karte-kopf { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 2px 2px 4px; }
/* Datum links in der vierten Schriftfarbe, die Zahl rechts eine Stufe kraeftiger in der dritten
   (03.10.) -- sie ist die Auskunft, das Datum die Einordnung. */
.up-evm-zeitraum { padding-left: 4px; font-size: var(--up-fs-xs); color: var(--vc-fourth); font-variant-numeric: tabular-nums; }
.up-evm-zahl { font-size: var(--up-fs-xs); color: var(--vc-third); background: var(--up-sel-bg); border-radius: 5px; padding: 2px 6px; }
.up-evm-zeile { display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 6px; margin: 0;
  border: 0; border-radius: 5px; background: none; font: inherit; text-align: left; cursor: pointer; }
.up-evm-zeile:hover, .up-evm-zeile:focus-visible { background: var(--up-sel-bg); outline: none; }
.up-evm-zeile-name { flex: 1 1 auto; margin-right: 12px; font-size: var(--up-fs-s); font-weight: 500; color: var(--vc-text); }
/* Die Daten der Zeilen wie das Datum im Kopf: vierte Schriftfarbe statt der Farbe der Hilfslinie
   (die lag neben der Skala der Schriftfarben). */
.up-evm-zeile-datum { flex: 0 0 auto; font-size: var(--up-fs-xs); color: var(--vc-fourth); font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce){ .up-evm-karte { transition: none; } }
.up-legend .up-company-favicon { width: 16px; height: 16px; min-width: 16px; min-height: 16px; border-radius: 4px; object-fit: cover; display: block; flex: 0 0 auto; }
.up-legend .up-company-name { font-family: Geist, Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: 12px; font-weight: 500; line-height: 16px; color: var(--vc-text); white-space: nowrap; max-width: 150px; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
.up-root.is-narrow .up-legend .up-company-name { max-width: 90px; }
.up-legend .up-company-inner-gap { width: 8px; height: 1px; flex: 0 0 8px; }
/* off-screen probe row: the balanced-rows algorithm measures intrinsic item widths from this */
.up-legend .up-company-measure { position: absolute; left: -99999px; top: -99999px; visibility: hidden; pointer-events: none; display: flex; column-gap: var(--up-column-gap, 16px); width: auto; height: auto; overflow: visible; }

/* ---------- doughnut ---------- */
.up-chart-empty { font-size: 13px; color: var(--vc-muted); text-align: center; padding: 40px 0; }
.up-donut-layout { display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 28px; width: 100%; }
.up-donut-wrap { position: relative; flex: 0 0 auto; width: min(52%, 200px); aspect-ratio: 1 / 1; }
.up-donut-wrap canvas { width: 100% !important; height: 100% !important; }
/* once the legend drops BELOW the donut it no longer shares width with it — let the ring grow */
.up-donut-layout.is-collapsed .up-donut-wrap { width: min(75%, 220px); }
.up-donut-center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; }
.up-donut-center .n { font-size: 22px; font-weight: 500; color: var(--vc-text); line-height: 1; }
.up-donut-center .lbl { font-size: 14px; font-weight: 400; color: var(--vc-third); margin-top: 8px; line-height: 1; }
.up-donut-legend { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.up-donut-legend-row { display: flex; align-items: center; gap: 10px; }
.up-donut-legend-row.is-clickable { cursor: pointer; margin: -4px -6px; padding: 4px 6px; border-radius: 6px; transition: background 120ms ease; }
.up-donut-legend-row.is-clickable:hover { background: var(--vc-heading-bg); }
.up-donut-legend-chip { width: 12px; height: 12px; border-radius: 4px; flex: 0 0 auto; }
/* Das Logo in der Legende: zwischen Farbfleck und Name, mit derselben Luecke wie der Fleck sie
   zum Namen hat. 14px, damit es neben dem 10px-Fleck und dem 13px-Text nicht dominiert. */
.up-donut-legend-logo { width: 14px; height: 14px; border-radius: 4px; flex: 0 0 auto;
  object-fit: cover; display: block; }
.up-donut-legend-name { font-size: 12px; font-weight: 500; color: var(--vc-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; flex: 1 1 auto; min-width: 0; }
.up-donut-legend-pct { font-size: 12px; font-weight: 500; color: var(--vc-muted); white-space: nowrap; flex: 0 0 auto; }
.up-donut-layout.is-collapsed { flex-direction: column; gap: 18px; }
.up-donut-layout.is-collapsed .up-donut-legend { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 10px 16px; }

/* ---------- bars ---------- */
.up-bars { display: flex; flex-direction: column; gap: 0; width: 100%; }
/* Beschriftungsspalte links (UC.makeBarList mit labelCol). Nur dort, wo genug Platz ist -- die
   Entscheidung faellt an der EIGENEN Box der Komponente, nicht am Fenster. Die Spalte ist fest
   breit, damit alle Balken an derselben Kante beginnen; ein zu langer Name endet mit Punkten. */
.up-bars.has-labelcol .up-bar-label {
  flex: 0 0 148px; width: 148px; display: inline-flex; align-items: center; gap: 8px;
  min-width: 0; padding-right: 12px; box-sizing: border-box;
}
.up-bar-logo { width: 16px; height: 16px; flex: 0 0 16px; border-radius: 4px; object-fit: contain; display: block; }
.up-bar-labeltxt {
  font-size: 12px; font-weight: 500; color: var(--vc-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
/* Steht der Name links in der Spalte, ist der Prozentwert das EINZIGE Kind im Balken -- und
   space-between schiebt ein einzelnes Kind nach links, also an den Anfang des Balkens. Dort
   gehoert er nicht hin: er ist der Endwert und steht am Ende. */
.up-bars.has-labelcol .up-bar-fill { justify-content: flex-end; }
.up-bar-row { display: flex; align-items: center; padding: 5px 0; }
.up-bar-row.is-clickable { cursor: pointer; }
/* Hover reads on the FILL and the grey TRACK's own background only — never a border, never the
   row as a whole (a full-row treatment reads as "the row is a button", which it isn't outside
   is-clickable, and even there the row's own hit area is enough without a visible outline).
   filter: brightness() works directly against each bar's inline background color, so it doesn't
   need its own per-item colour math the way the doughnut's hoverBackgroundColor does (core.js) —
   same visual language (colours read "stronger" on hover), different mechanism because one is a
   canvas fill and the other a DOM element's inline style. The track darkens via background, not
   filter — filter on the track would also repaint .up-bar-fill sitting inside it, compounding
   with the fill's own brightness() instead of leaving it alone. */
.up-bar-row:hover .up-bar-fill { filter: brightness(1.12); }
/* A dimmed (filtered-out) bar's fill is already a light neutral grey — brightening THAT further
   on hover read as "turns nearly white," not "is being hovered." Darken instead, same amount the
   grey track itself darkens by, for one consistent "muted things get a bit darker on hover" rule
   rather than two different directions depending on which grey element you're looking at. */
.up-bar-row.is-dimmed:hover .up-bar-fill { filter: brightness(0.94); }
.up-bar-row:hover .up-bar-track { background: color-mix(in srgb, var(--vc-heading-bg) 100%, black 6%); }
.up-bar-track { flex: 1; height: 32px; background: var(--vc-heading-bg); border-radius: 8px; overflow: visible; position: relative; min-width: 0; transition: background 120ms ease; }
/* Einfahrt wie Linie und Ring (11.10.): dieselbe Dauer, dieselbe Kurve. Vorher .5s mit
   (.4,0,.2,1) -- ein langsam anfahrender Verlauf, der neben der Linie wie ein Ruck aussah. */
.up-bar-fill { height: 100%; border-radius: 8px; transition: width var(--up-t-auftritt) var(--up-ease-quart), filter 120ms ease; display: flex; align-items: center; justify-content: space-between; padding: 0 10px; min-width: 0; gap: 8px; overflow: hidden; }
.up-bar-name { font-size: 11px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity .18s ease; }
.up-bar-pct { font-size: 11px; font-weight: 600; white-space: nowrap; flex: 0 0 auto; transition: opacity .18s ease; }
/* shown instead of the inside labels when the bar is too short to hold them */
.up-bar-outside { position: absolute; top: 50%; transform: translateY(-50%); left: 0; pointer-events: none; display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; transition: opacity .18s ease; }
/* Das Logo im Balken sitzt vor dem Namen, in beiden Namensfeldern (innen und ausserhalb). Es
   faedt mit dem Text ein, weil es in demselben span liegt -- eine eigene Animation waere ein
   zweiter Takt in derselben Bewegung. */
.up-bar-name, .up-bar-name-out { display: inline-flex; align-items: center; gap: 6px; }
/* makeBarList zeichnet dieselben Balken noch einmal: ein Bild lang ohne Uebergang (06.10.). */
.up-bars.is-sofort :is(.up-bar-fill, .up-bar-name, .up-bar-pct, .up-bar-outside) { transition: none; }
/* DAS LOGO IM BALKEN IN EINEM KAESTCHEN (06.10. fuer alle Balkenlisten, angefordert an den
   Ads-Balken: "genau so wie ueberall, wie bei Domain Detail"). Bis hier stand das Kaestchen nur
   lokal in domain-detail.css (01.10., Modell-Logos): Rahmen, heller Grund, 2px Luft -- ueberall
   sonst lag das Bild randlos auf einer 15px-Platte, und ein Favicon, das seine Flaeche ganz
   fuellt, sah aus wie ein loses Bild. Jetzt ist das Kaestchen der Standard, fuer Logos UND
   Zeichen-Platten (Haendler in Shopping, Werbetreibende ohne Logo in Ads).
   20px aussen: 16 Bild + 2x1 Polster + 2x1 Rahmen, Radius s (6). Die Spur ist 32px hoch, es passt.
   1px Polster statt der 2 aus domain-detail (06.10. abends: "das Polster wirkt an ein paar Stellen
   zu gross"): Favicons bringen ihren eigenen Rand mit, mit 2px stand das Bild darin bei 14px und
   wirkte verloren. 1px ist das Polster jeder anderen Logo-Kachel in core (.up-logo-box, .up-ment-logo).
   Am Bild selbst und ohne Huelle: Rahmen und Polster gehen an einem img, object-fit contain
   beschneidet nichts. Im Dunkeln verschmilzt der Rahmen mit dem hellen Grund. */
.up-bar-logo { box-sizing: border-box; width: 20px; height: 20px; flex: 0 0 20px; padding: 1px;
  border: 1px solid var(--vc-border); border-radius: var(--up-r-s);
  background: var(--vt-logo-bg); object-fit: contain; display: block; }
.up-root[data-theme="dark"] .up-bar-logo { background: var(--vc-text); border-color: var(--vc-text); }
/* Die Zeichen-Platte (makeBarList, it.zeichen, 04.10.): dasselbe Kaestchen, darauf das Zeichen in
   der Tinte der Logo-Platten (--vt-logo-ink, wie der Buchstabe in .up-logo-box). */
.up-bar-logo.is-zeichen { display: inline-flex; align-items: center; justify-content: center; color: var(--vt-logo-ink, var(--vc-muted)); }
.up-bar-logo.is-zeichen svg { width: 12px; height: 12px; display: block; }
.up-bar-name-out { font-size: 11px; font-weight: 600; }
.up-bar-pct-out { font-size: 11px; font-weight: 600; }
/* Die eigene Wertspalte (cfg.pctCol). Rechts ausgerichtet und mit tabellarischen Ziffern, damit
   die Zahlen untereinander stehen -- eine Zahlenspalte, die wandert, liest sich als Fehler. */
.up-bar-pctcol { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--vc-text); font-size: 13px; font-weight: 600; }

/* ---------- doughnut skeleton ----------
   Sized to match .up-donut-wrap/.up-donut-layout exactly (200px cap, 220px once collapsed) —
   this used to be its own slightly-smaller 180px guess, which is why the skeleton circle visibly
   grew the instant the real chart replaced it. */
/* max-height ueberall (23.09. gemeldet: der Ring clippt unten in der Maerkte-Karte). Die Breite
   war gedeckelt, die HOEHE nicht: aspect-ratio 1 machte aus min(52%, 200px) eine ebenso hohe
   Box -- gemessen 192px in einem 173px-Koerper, also 19px unter der Kante abgeschnitten.
   Der Ring haengt an einem svg mit viewBox, er skaliert also von selbst mit, sobald die Box
   niedriger wird, und bleibt dabei rund. Dieselbe Ueberlegung wie beim Balken-Skelett, nur
   greift sie hier ueber die Hoehe statt ueber die Zeile. */
.up-donut-sk { display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 28px; width: 100%; max-height: 100%; pointer-events: none; }
.up-donut-sk.is-collapsed { flex-direction: column; gap: 18px; }
.up-sk-chart { flex: 0 0 auto; width: min(52%, 200px); aspect-ratio: 1; max-height: 100%; }
.up-donut-sk.is-collapsed .up-sk-chart { width: min(75%, 220px); }
/* object-fit: contain haelt den Ring rund, wenn max-height die Box flacher macht als breit --
   ohne das zoege das svg in die Breite. */
.up-sk-chart svg { width: 100%; height: 100%; display: block; object-fit: contain; }
/* Der Schimmer im Ringskelett per CSS statt SMIL (13.10.): ein <animateTransform> tickt im
   Hauptthread in jedem Bild, auch geparkt und in einer 0x0-Huelle, und animation-play-state haelt
   es nicht an. Sechs Ringe kosteten in Ruhe 1.4 s von 5 s (1x CPU). Dieselbe Strecke wie vorher
   (-60 bis 160 in 1.2 s), jetzt anhaltbar mit der Regel oben. */
.up-sk-chart .up-sk-wisch { animation: upskwisch 1.2s linear infinite; }
@keyframes upskwisch { from { transform: translateX(-60px); } to { transform: translateX(160px); } }
.up-sk-ring { stroke: var(--vc-sk); }
.up-sk-g0 { stop-color: var(--vc-sk); }
.up-sk-g1 { stop-color: #ffffff; }
.up-root[data-theme="dark"] .up-sk-g1 { stop-color: #555555; }
.up-sk-legend { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.up-sk-row { display: flex; align-items: center; gap: 10px; }
.up-sk-dot { width: 12px; height: 12px; border-radius: 4px; background: var(--vc-sk); flex: 0 0 auto; position: relative; overflow: hidden; }
.up-sk-lbl { height: 10px; background: var(--vc-sk); border-radius: 5px; position: relative; overflow: hidden; }
.up-sk-pct { height: 10px; background: var(--vc-sk); border-radius: 5px; flex: 0 0 auto; position: relative; overflow: hidden; }
.up-sk-dot::after, .up-sk-lbl::after, .up-sk-pct::after {
  content: ""; position: absolute; inset: 0;
  background: var(--up-sk-lauf);
  transform: translateX(-100%); animation: upskt 1200ms linear infinite;
}
@media (prefers-reduced-motion: reduce){ .up-sk-dot::after, .up-sk-lbl::after, .up-sk-pct::after { animation: none; } }

/* ---------- bar-mode skeleton (same shimmer, shaped like .up-bar-track rows) ---------- */
.up-bars-sk { display: flex; flex-direction: column; gap: 0; width: 100%; pointer-events: none; }
/* DIE HOEHE DER ECHTEN ZEILE, NICHT EINE EIGENE (23.09. gemeldet: "der skeleton des bar charts
   clippt unten ueber die card grenze"). Gemessen in der Topics-Karte der Prompts-Tabelle: eine
   echte Balkenzeile ist 26px hoch, fuenf davon 128px in einem 172px-Koerper. Das Skelett stand
   auf 42 je Zeile -- fuenf davon 210px, also 38 ueber den Koerper und 4 sichtbar ueber die
   Kartenkante hinaus (der Koerper zentriert mit justify-content: safe center, der Ueberlauf
   verteilt sich also nach oben UND unten).
   20 Spur + 2x3 Polster = 26, dieselbe Zeile wie daneben. Damit springt beim Wechsel vom
   Skelett zur Liste auch nichts mehr. */
.up-bar-sk-row { display: flex; align-items: center; padding: 3px 0; }
.up-bar-sk-track { display: block; height: 20px; border-radius: 6px; background: var(--vc-sk); position: relative; overflow: hidden; }
.up-bar-sk-track::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.14) 50%, rgba(255,255,255,0) 100%);
  transform: translateX(-100%); animation: upskt 1200ms linear infinite;
}
@media (prefers-reduced-motion: reduce){ .up-bar-sk-track::after { animation: none; } }

/* ==========================================================================================
   TABLE PRIMITIVES — shared cell furniture
   ==========================================================================================
   Each of these existed 3-4 times under different prefixes (.uut-/.udt-/.tct-/.vt-) with
   byte-identical rules. One copy now; components keep only their own column widths and the
   cells that are genuinely theirs. */

/* delta chip: arrow + value, green when the change is good, red when it isn't */
.up-trend { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.up-trend svg { width: 16px; height: 16px; display: block; }
.up-trend.pos { color: var(--vt-up); }
.up-trend.neg { color: var(--vt-down); }

/* favicon holder with a letter fallback — a broken favicon used to leave an empty square, which
   reads as a loading glitch; the initial keeps every row visually complete */
/* ---------- Zaehl-Badge am Icon-Button ----------
   Der kleine Kreis oben rechts an einem Trigger ("N Filter aktiv"). Lag zweimal in
   Komponenten-CSS (brands-overview, citations-combo-chart) und ist beim dritten Verbraucher
   prompt zu gross und zu schwer geraten, weil ich ihn frei nachgebaut habe statt ihn zu nehmen.
   Die beiden alten Klassennamen haengen als Aliase mit dran -- ihr Markup steht in vom Nutzer
   eingefuegten Bubble-Seiten, ein Rename wuerde die zerlegen. */
/* --vc-text-invert statt zweier Literale: der Grund traegt schon var(--vc-text), die Schrift
   darauf war von Hand gebaut -- #e0e0e0 hell und eine eigene Dunkel-Regel mit #1f1f1b. Genau die
   zwei Werte fuehrt --vc-text-invert. Die Dunkel-Regel faellt damit weg. (22.09.) */
.up-badge, .ubo-filter-badge, .ubo-cols-badge, .udb-cols-badge, .combo-filter-badge {
  position: absolute; top: -3px; right: -3px; z-index: 2;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
  background: var(--vc-text); color: var(--vc-text-invert);
  font-family: inherit; font-size: 10px; font-weight: 600; line-height: 1;
  display: none; align-items: center; justify-content: center;
  font-variant-numeric: tabular-nums; pointer-events: none;
}
.up-badge.is-visible, .ubo-filter-badge.is-visible,
.ubo-cols-badge.is-visible, .udb-cols-badge.is-visible, .combo-filter-badge.is-visible { display: inline-flex; }
/* Reine Markierung ohne Zahl. Fuer Trigger, bei denen eine Zahl nichts beantwortet -- der
   Radar-Picker hat zwei Achsen, "13" waere dort die Summe aus Aepfeln und Birnen.

   Masse und Position sind NICHT neu gewaehlt, sondern die des Grouping-Buttons der Prompts-
   Tabelle (.upt-group-dot): 6px, top/right 5px, --vc-text, per opacity eingeblendet. Ich hatte
   hier 8px auf 0/0 frei nachgebaut, obwohl die Vorgabe im Repo lag -- .upt-group-dot haengt
   deshalb jetzt als Alias mit dran, damit es EINE Sache ist und nicht wieder auseinanderlaeuft.
   Sichtbar wird der Punkt ueber .is-visible (Badge) bzw. .is-on am Wrapper (Grouping-Button). */
.up-badge.is-dot, .upt-group-dot {
  min-width: 6px; width: 6px; height: 6px; padding: 0; top: 5px; right: 5px;
  border-radius: 50%; background: var(--vc-text);
  opacity: 0; transition: opacity 120ms ease;
}
.up-badge.is-dot.is-visible { display: block; opacity: 1; }

.up-logo-box { width: 24px; height: 24px; border-radius: 8px; background: var(--vt-logo-bg); padding: 1px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; position: relative; }
.up-logo-box img { width: 22px; height: 22px; border-radius: 7px; object-fit: cover; display: block; }
.up-logo-ltr { font-size: 11px; font-weight: 600; color: var(--vt-logo-ink, var(--vc-muted)); line-height: 1; text-transform: uppercase; }
.up-logo-box.has-img .up-logo-ltr { display: none; }
/* EIN ZEICHEN STATT DES ANFANGSBUCHSTABENS (06.10., Ads: "Werbetreibende ohne Logo zeigen das
   Werbetreibende-Zeichen"). Dieselbe Stelle und Tinte wie der Buchstabe, in beiden Kacheln: der
   Logo-Kachel der Tabellen (24px, Zeichen 14) und dem 18px-Logo der Chips (Zeichen 11). Kommt
   das Bild doch, verschwindet es wie der Buchstabe. */
.up-logo-zeichen { display: inline-flex; color: var(--vt-logo-ink, var(--vc-muted)); }
.up-logo-zeichen svg { width: 14px; height: 14px; display: block; }
.up-ment-logo .up-logo-zeichen svg { width: 11px; height: 11px; }
.up-logo-box.has-img .up-logo-zeichen, .up-ment-logo.has-img .up-logo-zeichen { display: none; }

/* ---------- DER LOGO-FAECHER (UC.makeFaecher, 29.09.) ----------
   Fuenf Karten auf einem Platz, gedreht um EINEN Punkt weit unter ihnen: transform-origin 50%
   440% liegt 141px unter der Karte, so stehen sie von selbst auf einem Bogen, die aeusseren etwas
   tiefer. Die Mitte liegt vorne (z-index aus dem JS), jede Lage faehrt 40ms nach der inneren auf.
   DIE KARTE IST EINE LOGO-PLATTE: --vt-logo-bg wie .up-logo-box, hell auch im Dunkeln (29.09.
   spaet: "im Darkmode hellen Background in den Cards, wie in anderen Logo-Chips"); Buchstabe und
   Zeichen darauf in --vt-logo-ink. Der Ring in der Farbe der Flaeche dahinter trennt die
   ueberlappenden Karten -- ein Einbau auf anderem Grund setzt --up-fan-ring (Quick Actions).
   Das Logo steht mit 20px mittig und wird NICHT beschnitten (contain): cover schnitt breite Logos
   ab, gemessen an der Wolke von Salesforce.
   Der Bogen (aus Mira uebernommen): bei 12 Grad liegt die Mitte der Nachbarkarte 26px daneben,
   bei 24 Grad 51 -- die Karten ueberlappen um gut 6px, und jedes Logo bleibt ganz sichtbar (bei
   350% waren es 15px Deckung). Die aeusseren sitzen 3 und 11px tiefer; 160 x 60 umschliesst den
   ganzen Bogen. */
.up-root.up-fan-block { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.up-fan { position: relative; width: 160px; height: 60px; pointer-events: none; }
.up-fan-c {
  position: absolute; left: calc(50% - 16px); top: 4px; width: 32px; height: 32px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  border-radius: var(--up-r-l); background: var(--vt-logo-bg); color: var(--vt-logo-ink, var(--vc-muted));
  /* Der Rand der Laderkachel, aus der die Karte kommt (.am-tload-logo: 1px --am-border, und das
     ist --vc-border). Beim Umzug nach core fehlte er -- 29.09. spaet: "waren nicht vorher Borders
     um die Cards? bitte wieder hin". box-sizing border-box: die Karte bleibt 32. */
  border: 1px solid var(--vc-border);
  font-size: var(--up-fs-l); font-weight: var(--up-fw-h); line-height: 1;
  box-shadow: 0 0 0 2px var(--up-fan-ring, var(--up-menu-bg)), var(--up-e-chip);
  transform-origin: 50% 440%; transform: rotate(0deg);
  transition: transform var(--up-t-3) var(--up-ease-auf);
  transition-delay: calc(var(--n, 0) * 40ms);
}
.up-fan.is-auf .up-fan-c { transform: rotate(var(--w, 0deg)); }
/* Das Zusammenlegen vor dem Auffaechern laeuft ohne Uebergang -- Begruendung an makeFaecher. */
.up-fan.is-still .up-fan-c { transition: none; }
.up-fan-c img { width: 20px; height: 20px; object-fit: contain; border-radius: var(--up-r-xs); display: block; }
.up-fan-c.is-zeichen svg { width: 16px; height: 16px; display: block; }
/* "In der Drittfarbe und recht duenn" (29.09. spaet) -- 400 ist der duennste Schnitt der Achse. */
.up-fan-cap { font-size: var(--up-fs-m); font-weight: var(--up-fw-n); line-height: 1.4; color: var(--vc-third); white-space: nowrap; }
@media (prefers-reduced-motion: reduce){ .up-fan-c { transition: none; } }

/* ---------- Das Ladebild fuer lange Laeufe (UC.makeLadebild, zweite Fassung 01.10.) ----------
   Die Form des Ladebilds aus Explore Brands (dort als Grundlage gewaehlt): Kachel in der Mitte eines
   Kreises, darunter Titel und Statuszeile. Die Kachel steht still, der Kreis ist der Spinner --
   ein Bogen von einem Fuenftel des Umfangs, der in einer Sekunde einmal umlaeuft. Das ist die
   uebliche Drehzahl eines Spinners: langsamer liest sich als Haenger, schneller als Hektik.
   Masse: Kachel 44 (Radius xl), Zeichen 20, Kreis 72 -- 14px Luft zwischen Kachel und Kreis, genug,
   dass beides als zwei Dinge zu lesen ist. Titel 14/500 und Statuszeile 13 wie in Explore Brands.
   Die Hoehe der Statuszeile ist fest (19): der Text wechselt, und ohne feste Hoehe huepfte alles
   darunter bei jedem Wechsel. */
.up-lb { display: flex; flex-direction: column; align-items: center; text-align: center;
  width: min(100%, 420px); margin: 0 auto; animation: upLbAuf var(--up-t-3) ease both; }
@keyframes upLbAuf { from { opacity: 0; } to { opacity: 1; } }
.up-lb-mark { position: relative; width: 72px; height: 72px; display: grid; place-items: center; flex: 0 0 auto; }
.up-lb-spin { position: absolute; inset: 0; width: 100%; height: 100%; display: block; overflow: visible;
  animation: upLbDreh 1s linear infinite; }
@keyframes upLbDreh { to { transform: rotate(360deg); } }
.up-lb-spur, .up-lb-bogen { fill: none; stroke-width: 1.5; }
.up-lb-spur { stroke: var(--vc-border); }
/* Der Bogen in der Akzentfarbe -- dieselbe, die vorher der Balken trug. Das Schliessen zum vollen
   Kreis bei stop() laeuft ueber dieselbe Angabe, darum die transition. */
.up-lb-bogen { stroke: var(--up-accent); stroke-linecap: round; stroke-dasharray: 20 100;
  transition: stroke-dasharray var(--up-t-3) var(--up-ease); }
.up-lb.is-fertig .up-lb-spin { animation-play-state: paused; }
.up-lb.is-fertig .up-lb-bogen { stroke-dasharray: 100 0; }
.up-lb-tile {
  position: relative; width: 44px; height: 44px; border-radius: var(--up-r-xl); display: grid; place-items: center;
  background: linear-gradient(180deg, var(--vc-bg), var(--vc-heading-bg));
  border: 1px solid var(--vc-border); color: var(--vc-text); box-shadow: var(--up-e-1);
}
.up-lb-tile svg { width: 20px; height: 20px; display: block; }
.up-lb-name { margin-top: 16px; max-width: 100%; font-size: var(--up-fs-l); font-weight: var(--up-fw-m);
  color: var(--vc-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-lb-sub { margin-top: 2px; max-width: 100%; font-size: var(--up-fs-m); color: var(--vc-third);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-lb-loop { position: relative; height: 19px; overflow: hidden; width: 100%; margin-top: 6px; }
.up-lb-text {
  position: absolute; left: 0; right: 0; top: 0; white-space: nowrap;
  font-size: var(--up-fs-m); color: var(--vc-muted); transform: translateY(0); opacity: 1;
  transition: transform var(--up-t-3) cubic-bezier(0.22,1,0.36,1), opacity var(--up-t-2) ease-out;
}
.up-lb-text.is-out { transform: translateY(-95%); opacity: 0; }
.up-lb-text.is-in  { transform: translateY(95%);  opacity: 0; }
/* Reduziert heisst langsamer, nicht still: ein Spinner, der steht, sagt "haengt". */
@media (prefers-reduced-motion: reduce){
  .up-lb { animation: none; }
  .up-lb-spin { animation-duration: 2.4s; }
  .up-lb-bogen, .up-lb-text { transition: none; }
}

    /* ---------- Globus statt Buchstabe, wo das Bild ein FAVICON war ----------
       Ein Favicon, das nicht laedt, hinterliess den Anfangsbuchstaben der Domain -- ein "R" fuer
       reddit.com sagt nichts, und bei einer Subdomain ist es schlicht falsch. Fuer Seiten steht
       deshalb ein Globus da, genau wie im Onboarding.
       Bei MARKEN bleibt der Buchstabe: dort ist er die Marke, und "K" fuer Kestrel ist eine
       Aussage. Deshalb ein eigener Aufhaenger (.up-fav) an den Kaesten, die eine Seite zeigen,
       und keine Aenderung an .up-logo-box selbst.
       Maske und keine Bilddatei: so traegt der Globus die Textfarbe seiner Umgebung und stimmt in
       beiden Themen. */
    .up-fav:not(.has-img) .up-logo-ltr,
    .up-fav:not(.has-img) .up-stack-ltr,
    .up-fav:not(.has-img) .udt-logo-ltr,
    .up-fav:not(.has-img) .udt-sub-ltr,
    .up-fav:not(.has-img) .uut-logo-ltr { display: none; }
    /* Der Globus haengt normalerweise am KASTEN. Im Stapel-Chip nicht: dort ist der Kasten
       (.up-stack-item) mit Absicht unbeweglich -- er traegt die Trefferflaeche, und nur die
       Sichtebene .up-stack-vis darin hebt und schiebt sich beim Hover. Ein Globus am Kasten blieb
       damit stehen, waehrend Platte, Rahmen und Ring um ihn herum wegfuhren: gemeldet als
       "das Globe-Icon verschiebt sich nicht mit". Ein Buchstabe hat das Problem nie gehabt, der
       liegt schon IN der Sichtebene.
       Also zeichnet ihn im Stapel die Sichtebene, und der Kasten laesst es. Dieselben
       Deklarationen, nur ein zweiter Selektor -- kein zweiter Globusblock, der auseinanderlaufen
       koennte. Die Zahlen (62 Prozent, mittig) beziehen sich auf die Sichtebene, und die fuellt
       den Kasten zu 100 Prozent: derselbe Kreis an derselben Stelle wie vorher. */
    .up-fav:not(.has-img)::after,
    .up-stack-item.up-fav:not(.has-img) .up-stack-vis::after {
      content: ""; position: absolute; left: 50%; top: 50%;
      width: 62%; height: 62%; transform: translate(-50%, -50%);
      background: var(--vc-third);
      -webkit-mask: var(--up-globus) center / contain no-repeat;
      mask: var(--up-globus) center / contain no-repeat;
      pointer-events: none;
    }
    .up-stack-item.up-fav:not(.has-img)::after { content: none; }

    /* ---------- Globus, wo ein Favicon gescheitert ist ----------
       Zwei Faelle, beide bedient vom Zuhoerer in core.js (faviconRueckfall):
       .up-globus-an   an einem Kasten, der das Bild getragen hat und sonst nichts anzeigen wuerde
                       -- der Kasten behaelt seine Groesse, der Globus sitzt darin.
       .up-globus-frei anstelle eines Bildes, das ohne Kasten in einer Zeile stand (Tooltip,
                       Legende, Filterzeile). Die Groesse setzt der Zuhoerer aus dem Bild, das
                       dort stand -- sonst rutscht die Zeile beim Fehlschlag.
       Farbe aus --vc-third mit festem Rueckfall: diese Marken koennen ausserhalb von .up-root
       stehen. */
    .up-globus-an { position: relative; }
    .up-globus-an::after {
      content: ""; position: absolute; left: 50%; top: 50%;
      width: 62%; height: 62%; transform: translate(-50%, -50%);
      background: var(--vc-third, #9e9e9e);
      -webkit-mask: var(--up-globus) center / contain no-repeat;
      mask: var(--up-globus) center / contain no-repeat;
      pointer-events: none;
    }
    .up-globus-frei {
      display: inline-block; flex: 0 0 auto; vertical-align: middle;
      background: var(--vc-third, #9e9e9e);
      -webkit-mask: var(--up-globus) center / contain no-repeat;
      mask: var(--up-globus) center / contain no-repeat;
      pointer-events: none;
    }

/* ---------- Zeilen-Affordanz: Pfeil, der nach einer Verweildauer zur Aktion wird ----------
   Die Bauart kommt aus domains-table ("Show Pages") und steht jetzt hier, weil brands-overview
   mit seinem Edit-Knopf genau dasselbe braucht -- Buchstabe fuer Buchstabe dieselben Zeiten,
   Groessen und Positionen. Die .udt-*-Klassen stehen in jeder Regel mit, damit das Markup der
   Domains-Tabelle unangetastet bleibt (derselbe Kniff wie bei .up-seg/.cc-seg).

   Der Anker ist EIN nullbreites Flex-Kind mit margin-left:auto, gegen das beide Kinder absolut
   liegen. Vorher waren Pfeil und Aktion zwei Geschwister mit je eigenem auto-Rand: Flexbox teilt
   den freien Platz gleichmaessig auf ALLE auto-Raender einer Zeile auf, der Pfeil landete also
   irgendwo in der Mitte statt an der Kante.

   Die Uebergabe ist gestaffelt: beim Hover kommt der Pfeil nach 220ms; nach 1s Verweilen (die
   Klasse setzt UC.rowDwell) geht der Pfeil OHNE Verzoegerung in 120ms weg und die Aktion kommt
   140ms spaeter -- eins geht, dann kommt das naechste, statt dass sich beide um denselben Platz
   streiten. */
.up-rowswap, .udt-row-affordance {
  display: inline-block; margin-left: auto; position: relative; flex: 0 0 auto; width: 0; height: 32px;
}
.up-rowswap-goto, .udt-row-goto {
  position: absolute; top: 50%; right: 0; width: 16px; height: 16px; color: var(--vc-third);
  opacity: 0; transform: translateY(-50%) translateX(-3px);
  transition: opacity .18s ease, transform .18s ease; transition-delay: 0ms;
}
.up-rowswap-goto svg, .udt-row-goto svg { width: 16px; height: 16px; display: block; }
.up-row:hover .up-rowswap-goto, .up-row:hover .udt-row-goto {
  opacity: 1; transform: translateY(-50%) translateX(0); transition-delay: 220ms;
}
.up-row.is-rowswap .up-rowswap-goto, .up-row.is-showpages-hover .udt-row-goto {
  opacity: 0; transition: opacity 120ms ease; transition-delay: 0ms;
}
.up-root.is-vnarrow .up-rowswap-goto, .up-root.is-vnarrow .udt-row-goto { display: none; }
/* Die Aktion sitzt in demselben Platz, den der Pfeil hatte: kein Rahmen, keine Pille -- eine
   Beschriftung mit Zeichen, nicht ein Knopf, der aus dem Nichts auftaucht. */
.up-rowswap-act, .udt-row-showpages {
  position: absolute; top: 50%; right: 0;
  display: inline-flex; align-items: center; gap: 6px; transform: translateY(-50%) translateX(6px);
  height: 32px; padding: 0; border: 0; background: transparent; cursor: pointer; font: inherit;
  font-size: 12px; font-weight: 500; color: var(--vc-third); white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity 160ms ease, transform 160ms ease, color 120ms ease; transition-delay: 0ms;
}
.up-rowswap-act svg, .udt-row-showpages svg { width: 14px; height: 14px; flex: 0 0 14px; display: block; }
.up-rowswap-act:hover, .up-rowswap-act:focus-visible,
.udt-row-showpages:hover, .udt-row-showpages:focus-visible { color: var(--vc-text); }
.up-row.is-rowswap .up-rowswap-act, .up-row.is-showpages-hover .udt-row-showpages {
  opacity: 1; transform: translateY(-50%) translateX(0); pointer-events: auto; transition-delay: 140ms;
}
.up-root.is-vnarrow .up-rowswap, .up-root.is-vnarrow .udt-row-affordance { display: none; }

/* go-to arrow, revealed on row hover after a short delay so it doesn't flicker while the cursor
   sweeps across rows */
.up-row-goto { margin-left: auto; width: 16px; height: 16px; flex: 0 0 auto; color: var(--vc-third);
  opacity: 0; transform: translateX(-3px); transition: opacity .18s ease, transform .18s ease; transition-delay: 0ms; }
.up-row-goto svg { width: 16px; height: 16px; display: block; }
.up-row:hover .up-row-goto { opacity: 1; transform: translateX(0); transition-delay: 220ms; }
.up-root.is-vnarrow .up-row-goto { display: none; }

/* type/citation chip. Rand 1px (Farbe inline aus UC.typeStyle) innerhalb von border-box, damit Hoehe und Polster bleiben; Schnitt 500 statt 600, seit Variante D (13.10.) traegt die Farbe den Chip, nicht die Staerke. */
.up-tag { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px; border-radius: 7px;
  border: 1px solid transparent; box-sizing: border-box;
  font-size: 12px; font-weight: 500; white-space: nowrap; max-width: 100%; overflow: hidden; }
.up-tag-dot { width: 6px; height: 6px; border-radius: 999px; flex: 0 0 auto; }
.up-tag-lbl { overflow: hidden; text-overflow: ellipsis; }

/* Visibility / Rank / Sentiment metric cells — shared by visibility-chart's Top Brands table
   and prompts-table's Sentiment/Rank/Visibility columns. */
/* tabular-nums: digits keep the same advance width, so a column of numbers lines up across rows
   instead of jittering. urls-table/domains-table always had this on their own .uut-num/.udt-num;
   visibility-chart's .vt-num never did, and extracting THAT one into .up-num quietly spread the
   omission to prompts-table. Fixing it here fixes both at once. */
.up-num { font-size: 14px; font-weight: 500; color: var(--vc-text); white-space: nowrap; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
/* In einer Tabellenzelle darf die Zahl schrumpfen und kuerzt dann mit Auslassungspunkten (06.10.).
   Die Zelle schneidet erst an ihrem POLSTERRAND ab (overflow hidden), nicht am Inhalt -- ein
   langer Wert ("€19.62–€21.62" in 100px) lief deshalb bis zu 4,5px in die 16px rechts hinein, und
   das Polster ist Pflicht (gemeldet an der Preisspalte der Shopping-Produkte). */
.up-td > .up-num { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.up-rank-group { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.up-hash { width: 12px; height: 12px; color: var(--vc-third); flex: 0 0 auto; display: block; }
/* ---------- SENTIMENT OHNE KASTEN (29.09. angefordert) ----------
   "Aktuell ist das ja in so Containern, das moechte ich nicht mehr. Die Zahl soll in gleicher
   Fontsize sein wie Visibility oder Rank, der Container darum -- Border und Paddings -- weg. Und
   der Shape davor bitte 4px breit und 16px hoch." Nachgeschaerft am selben Abend: 3px breit, 12px
   hoch.
   Also kein Rahmen, kein Polster, keine feste Hoehe: ein Balken 3x12 in der Farbe des Werts
   (UC.sentColor, inline), daneben die Zahl in genau der Schrift von .up-num (14/500) -- dieselbe
   wie Visibility und Rank in derselben Zeile. Rund an den Enden: bei 4px Breite ist der volle
   Radius die halbe Breite.
   Das Markup kommt NUR aus UC.sentHtml(wert). Vorher baute jede Komponente ihren eigenen String
   (neun Stellen, eine davon mit eigenem Kasten). Wo Visibility und Rank groesser stehen
   (Kennzahlkarten), zieht der Kontext NUR .up-sent-val mit; der Balken bleibt 3x12. */
.up-sent { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.up-sent-dot { width: 3px; height: 12px; border-radius: var(--up-r-voll); flex: 0 0 auto; }
.up-sent-val { font-size: var(--up-fs-l); font-weight: 500; color: var(--vc-text); white-space: nowrap;
  font-variant-numeric: tabular-nums; }
/* DIE PILLE MIT RAHMEN bleibt -- fuer das, was kein Messwert ist: den Tarif (UC.planPilleHtml) und
   den Status "Active" in Teams. Beide nutzen dasselbe Markup wie Sentiment, sind aber Plaketten.
   Hier stehen die Werte, die .up-sent bis zum 29.09. selbst trug: 24px, Polster 6, Rahmen, Radius
   6, Punkt 6x6 mit Radius 2 (ein abgerundetes Quadrat, kein Kreis), Schrift 13. */
.up-sent.up-pille { height: var(--up-h-chip); padding: 0 6px; border: 1px solid var(--vc-border); border-radius: var(--up-r-s); }
.up-pille .up-sent-dot { width: 6px; height: 6px; border-radius: 2px; }
.up-pille .up-sent-val { font-size: var(--up-fs-m); }
/* DIE MARKE HINTER EINEM NAMEN (03.10. aus team-orga.css nach core): "You" in der Mitgliederliste
   und in der Marktbewegung der Events, "upstreem Team" in der Mitgliederliste. EINE Geometrie,
   die Farbe je Marke -- vorher lebte die Form nur in team-orga, und die Events bauten sich eine
   zweite. Masse unveraendert von dort: 20 hoch, Polster 7, Radius 7, Schrift 11/600. "You" in
   der Akzent-Tinte mit 10 Prozent Flaeche; am Standard ein leises Grau mit fast schwarzer Schrift. */
.up-marke { flex: 0 0 auto; height: 20px; padding: 0 7px; border-radius: 7px; max-width: 100%;
  font-size: var(--up-fs-xs); font-weight: var(--up-fw-h); letter-spacing: .02em; line-height: 20px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-you { background: color-mix(in srgb, var(--up-accent-ink) 10%, transparent); color: var(--up-accent-ink); }
/* Die leise Marke: eine Einordnung ohne Betonung -- der Typ eines Events auf seiner Karte (03.10.:
   "den Typ ohne Icon in einem kleinen Chip"). Die Flaeche ist die des Zaehler-Chips in der Karte
   der Event-Pins (--up-sel-bg, "3 Events"): dieselbe Art Angabe, derselbe Grund. --vc-heading-bg
   war zu nah am Kartenweiss (#f9f8f9 auf #fff), der Chip las sich als blosser Text. */
.up-marke.is-leise { background: var(--up-sel-bg); color: var(--vc-muted); }
/* Die leise Marke in der Akzentfarbe (05.10., "Beta"): dieselbe Staerke wie .is-leise, nur aus
   der Primaerfarbe statt aus der Schriftfarbe -- Text in der Akzent-Tinte, die Flaeche ein Hauch
   davon. Angefordert: "nicht Deckkraft o. Ae. aendern, nur das, was vorher Schriftfarbe war, in
   Primaerfarbe". Die Anteile sind so gewaehlt, dass am Standard-Akzent (= Schriftfarbe) der alte
   Grund herauskommt: 5 % auf Weiss ergibt #f4f4f4 (war --up-sel-bg #f4f2f4), 6 % im Dunkeln auf
   #1c1c1f ergibt #28282b (war #28282c). Eine volle Akzentflaeche war die erste Fassung -- zu laut. */
.up-marke.is-akzent { background: color-mix(in srgb, var(--up-accent-ink) 5%, var(--vc-bg)); color: var(--up-accent-ink); }
.up-root[data-theme="dark"] .up-marke.is-akzent { background: color-mix(in srgb, var(--up-accent-ink) 6%, var(--vc-bg)); }

/* DIE TABELLE, DIE WAAGERECHT SCROLLT (05.10. aus shopping.css nach core, seit Ads sie auch
   braucht): UC.makeColumns setzt im Scrollmodus (cfg.scrollen) --up-cols-min, die Summe der
   Mindestbreiten. Darunter scrollt der Kasten waagerecht, statt dass Spalten wegfallen ("ich will
   beides", ziehbare erste Spalte UND die ganze Tabelle). Die erste Spalte bleibt dabei stehen; der
   Trenner als Schatten, weil die Linie der naechsten Zelle mit wegscrollt, und der Grund wie die
   Zeile darunter, sonst scheint der Inhalt durch.
   Markup: .up-colscroll > .up-colscroll-innen > .up-thead + .up-tbody */
.up-colscroll { overflow-x: auto; overscroll-behavior-x: contain; }
.up-colscroll-innen { min-width: var(--up-cols-min, 0px); }
.up-colscroll .up-thead > .up-th:first-child, .up-colscroll .up-row > .up-td:first-child {
  position: sticky; left: 0; z-index: 1; box-shadow: 1px 0 0 var(--vc-border); }
.up-colscroll .up-thead > .up-th:first-child { background: var(--vt-head-bg); }
.up-colscroll .up-row > .up-td:first-child { background: var(--vc-bg); }
.up-colscroll .up-row:hover > .up-td:first-child { background: var(--vt-row-hover); }

/* DIE KLICKBARE KARTE (13.10.): EIN Zustand fuer Hover, Druck und Fokus jeder Karte, die man
   anklicken kann ("alle Cards sollen die gleichen Hover/Klick-States kriegen wie die im
   Response-Table Card Mode"). Vorher hatte jede Karte ihren eigenen: die Antwortkarte Rand plus
   --up-e-1, die Quellenkachel eine gefaerbte Flaeche, Produkt- und Ad Card ein Anheben um 2px mit
   --up-e-chip, die Event-Karte --vc-hover-border, die Opportunity-Karte Rand plus Flaeche.
   Die Werte sind die der Antwortkarte (.urt-card, Kartenentwurf 13.10.): der Rand eine Spur
   kraeftiger -- derselbe Ton wie der Hover der Eingabefelder --, die Tiefe einer Flaeche (--up-e-1;
   --up-e-2 war "viel zu krass", das ist der Schatten der Menues), beim Druck 1 Prozent kleiner.
   Geometrie (Radius, Polster, Rand, Flaeche) bleibt bei der Karte -- hier steht NUR der Zustand.
   Im Dunkeln nimmt die Randliste oben einigen Karten den Rand ((0,3,0) gegen (0,2,0) hier): die
   behalten ihn auch im Hover nicht und zeigen nur die Tiefe, genau wie die Antwortkarte.
   is-hl ist derselbe Zustand, von aussen gesetzt (Produktkarte und ihr Wort im Antworttext).
   Markup: die Klasse an jede klickbare Karte, NICHT an Skelett, Vorschau oder statische Fassung --
   die reagieren nicht und brauchen so auch keine Ruecknahme. Traeger am 13.10.: .urt-card,
   .urd-cite-card, .urd-pc (mit Produkt-Id), .up-adcard (adCardHtml), .uad-auf-ad, .uev-karte,
   .uo-card. */
.up-karte-klick { cursor: pointer;
  transition: box-shadow var(--up-t-2) var(--up-ease), border-color var(--up-t-2) var(--up-ease), transform var(--up-t-1) var(--up-ease); }
.up-karte-klick:hover, .up-karte-klick.is-hl { border-color: color-mix(in srgb, var(--vc-border) 86%, var(--vc-text)); box-shadow: var(--up-e-1); }
.up-karte-klick:active { transform: scale(.99); }
.up-karte-klick:focus-visible { outline: 2px solid var(--vc-text); outline-offset: 2px; }

/* DIE AD CARD (05.10., Ads V1, core.js adCardHtml): die Produktkarte des Response Detail (.urd-pc)
   als Baustein fuer Anzeigen. Polster 8, Radius 16, innen 8 am Bild (konzentrisch); Text 14/6/6,
   Fuss unten buendig (margin-top auto). Hover, Druck und Fokus kommen seit dem 13.10. aus
   .up-karte-klick (oben), die adCardHtml mitgibt -- vorher hob sich die Karte um 2px mit
   --up-e-chip. Das Raster (.up-adgrid) fuellt, was da ist, ab 240px je Karte. */
.up-adgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.up-adcard { display: flex; flex-direction: column; min-width: 0; padding: 8px; border-radius: var(--up-r-2xl);
  border: 1px solid var(--vc-border); background: var(--vc-bg); box-sizing: border-box; text-align: left;
  font: inherit; color: inherit; }
/* Das Werbemittel: 16:10 auf der Kopfflaeche, contain -- ein Banner wird nie beschnitten. Ohne
   Polster und ohne multiply (anders als ein Produktfoto steht ein Banner nicht auf Weiss frei). */
.up-adcard-media { position: relative; aspect-ratio: 16 / 10; border-radius: var(--up-r-m); background: var(--vc-heading-bg);
  overflow: hidden; display: flex; align-items: center; justify-content: center; }
.up-adcard-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; }
.up-adcard-ph { display: inline-flex; color: var(--vc-fourth); }
.up-adcard-ph svg { width: 22px; height: 22px; }
.up-adcard-media.has-img .up-adcard-ph { display: none; }
/* Das Format oben links: 22 hoch, Radius 6 wie die Position auf der Produktkarte (.urd-pc-pos). */
.up-adcard-fmt { position: absolute; top: 10px; left: 10px; height: 22px; padding: 0 7px; border-radius: var(--up-r-s);
  background: var(--vc-bg); color: var(--vc-text); font-size: var(--up-fs-xs); font-weight: 600; line-height: 22px;
  white-space: nowrap; box-shadow: var(--up-e-1); }
.up-adcard-body { display: flex; flex-direction: column; gap: 8px; padding: 14px 6px 6px; flex: 1 1 auto; min-width: 0; }
/* Der Advertiser als Marken-Chip (UC.markenChip), der Name eine Stufe kleiner und gedaempft wie
   der Absender auf der Produktkarte; die Marke (You / Competitor) dahinter. */
.up-adcard-top { display: flex; align-items: center; min-width: 0; min-height: 20px; }
.up-adcard-top .up-marken-chip { min-width: 0; max-width: 100%; }
.up-adcard-top .up-ment-name { font-size: var(--up-fs-s); font-weight: 500; color: var(--vc-muted); }
.up-adcard-title { font-size: var(--up-fs-l); font-weight: 500; line-height: 1.4; letter-spacing: -0.01em; color: var(--vc-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: calc(2 * 1.4em);
  overflow-wrap: anywhere; }
.up-adcard-title .is-empty { color: var(--vc-third); }
.up-adcard-desc { font-size: var(--up-fs-m); line-height: 1.45; color: var(--vc-muted); overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.up-adcard-foot { margin-top: auto; padding-top: 6px; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.up-adcard-price { font-size: var(--up-fs-xl); font-weight: 600; letter-spacing: -0.01em; color: var(--vc-text);
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-adcard-domain { display: flex; align-items: center; gap: 5px; min-width: 0; font-size: var(--up-fs-s); color: var(--vc-third); }
.up-adcard-domain svg { width: 12px; height: 12px; flex: 0 0 12px; display: block; }
.up-adcard-domain span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.up-adcard-meta { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: var(--up-fs-s); color: var(--vc-muted); white-space: nowrap; }
/* Modell und Markt als die Chips der Tabellen (05.10. abends: "das LLM-Logo und den Market wie in
   der Prompts-Tabelle, mit der Flagge"). Der Name des Modells in 12 wie der Werbetreibende oben in
   der Karte (.up-adcard-top), die Kachel bleibt bei 18 -- dieselbe Paarung, schon einmal hier. */
.up-adcard-model { min-width: 0; }
.up-adcard-model .up-ment-name { font-size: var(--up-fs-s); color: var(--vc-muted); }
.up-adcard-meta .up-market { flex: 0 0 auto; }
.up-adcard-date { margin-left: auto; flex: 0 0 auto; color: var(--vc-third); font-variant-numeric: tabular-nums; }

/* DER SEITEN-DRAWER (05.10., core.js makeSeitenDrawer). Der Kasten am <body> erzeugt keine Box
   (display: contents), traegt aber die Farbmarken. Zwei Klassen gegen die eine von .up-root.
   DIE DAUERN SIND DIE DER APP, NICHT DER SKALA: 180ms hinaus, 220ms herein, Schleier 160/200ms --
   die Drawer des Kopf-Skripts (project-host-app-layout) und der Opportunities-Drawer fahren genau
   so. Ein Drawer, der anders faehrt als jeder andere der App, faellt auf; darum die benannte
   Ausnahme wie --up-dd-radius. */
.up-root.up-sdrawer-host { display: contents; }
/* Der Schleier wie im Entwurf ("with a scrim"): 18 % der Schrift im Hellen, im Dunkeln 45 % der
   dunklen Gegenfarbe -- dieselben Werte wie dort (rgba 17,17,20 / .18 und 0,0,0 / .45), aus den
   Marken statt als feste Farbe. */
.up-sdrawer-scrim { position: fixed; inset: 0; z-index: 9903; display: none; opacity: 0;
  background: color-mix(in srgb, var(--vc-text) 18%, transparent);
  -webkit-backdrop-filter: blur(1.5px); backdrop-filter: blur(1.5px);
  transition: opacity 160ms ease; }
.up-sdrawer-host[data-theme="dark"] .up-sdrawer-scrim { background: color-mix(in srgb, var(--vc-text-invert) 45%, transparent); }
.up-sdrawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 9904; width: min(560px, 100vw); display: none;
  flex-direction: column; box-sizing: border-box; background: var(--vc-bg); border-left: 1px solid var(--vc-border);
  box-shadow: var(--up-e-drawer); color: var(--vc-text);
  font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;
  transform: translateX(110%); transition: transform 180ms ease-in; will-change: transform; backface-visibility: hidden; }
.up-sdrawer-host.is-open .up-sdrawer-scrim { display: block; }
.up-sdrawer-host.is-open .up-sdrawer { display: flex; }
.up-sdrawer-host.is-in .up-sdrawer-scrim { opacity: 1; transition: opacity 200ms ease; }
.up-sdrawer-host.is-in .up-sdrawer { transform: translateX(0); transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1); }
@media (prefers-reduced-motion: reduce) { .up-sdrawer, .up-sdrawer-scrim { transition: none; } }
/* Die Kopfzeile in den Massen von drawer-topbar.css: 32 hoch, 7px Polster, 12px darueber, 8
   darunter; Krume 12/600 mit Zeilenhoehe 1, Typ und Trenner in der dritten Stufe, Logo 18/r6. */
.up-sdrawer-top { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 7px; margin: 12px 0 8px; min-width: 0; }
.up-sdrawer-top .up-iconbtn { flex: 0 0 auto; }
.up-sdrawer-crumb { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  font-size: var(--up-fs-s); font-weight: 600; line-height: 1; }
.up-sdrawer-typ, .up-sdrawer-sep { flex: 0 0 auto; color: var(--vc-third); white-space: nowrap; }
/* Das Logo in der Krume ist das 18px-Logo der Marken-Chips (.up-ment-logo) -- dieselbe Groesse
   wie .utb-logo, ohne eigene Regel. */
.up-sdrawer-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-sdrawer-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-gutter: stable; padding: 4px 24px 40px;
  display: flex; flex-direction: column; gap: 28px; }

/* DIE DRAWER AUS CORE (11.10., core.js KERN_DRAWER; Response Detail als erster).
   Die Huelle traegt die Ids des Kopf-Skripts (#drawer-<art>, #backdrop-<art>), und das Kopf-
   Skript setzt Bewegung und Ebene mit !important (translateX 110 %, 220/180ms, dz/bz-Klassen).
   Was hier zu Bewegung und Ebene steht, ist darum der RUECKFALL fuer eine Seite ohne Kopf-
   Skript (Pruefstand, spaeter die Web-App) -- mit den Werten der Recherche vom 11.10.: herein
   260ms, hinaus 200ms, beide mit der Kurve von vaul, und nie ease-in.
   Form: BUENDIG (13.10., vorher schwebend 8px vom Rand mit Radius 12): volle Hoehe, rechts am
   Fensterrand, ohne Rundung -- wie die Seitenpanels von Linear, Notion, Stripe und Vercel. Bei
   88 % Breite las sich der Rand eher als Deko; die Ebene tragen jetzt Schatten (--up-e-drawer)
   und die eine Kante links. Unter 768px wird er zur ganzen Seite wie in der Mobilvorlage -- ein
   Blatt von rechts, kein Blatt von unten: die Vorlage zeigt eine volle Seite mit Kopf, und
   Wischen vom Rand kollidiert auf iOS mit Zurueck. */
.up-root.up-kdr { position: fixed; top: 0; right: 0; bottom: 0; z-index: 9920;
  width: min(var(--up-kdr-breite, 840px), 100vw); box-sizing: border-box;
  display: flex; flex-direction: column; overflow: hidden;
  /* Der Grund der Seite (--up-surface), nicht der Karten (--vc-bg): im Dunkeln stand der Drawer
     sonst heller als die Seite darunter (12.10. gemeldet). Im Hellen sind beide weiss. */
  background: var(--up-surface, var(--vc-bg)); color: var(--vc-text);
  border: 0; border-left: 1px solid var(--vc-border); border-radius: 0; box-shadow: var(--up-e-drawer);
  font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;
  transform: translateX(110%); transition: transform var(--up-t-2) cubic-bezier(.32, .72, 0, 1);
  will-change: transform; backface-visibility: hidden; outline: none; }
.up-root.up-kdr.open { transform: none; transition-duration: var(--up-t-3); }
.up-root.up-kdr-scrim { position: fixed; inset: 0; z-index: 9915; opacity: 0; pointer-events: none;
  background: color-mix(in srgb, var(--vc-text) 18%, transparent);
  transition: opacity var(--up-t-2) ease; }
/* Im Dunkeln dieselbe Rechnung wie beim Seiten-Drawer (.up-sdrawer-scrim): 45 % der dunklen
   Gegenfarbe, sonst hellt der Schleier die Seite auf statt sie zurueckzunehmen. */
.up-root.up-kdr-scrim[data-theme="dark"] { background: color-mix(in srgb, var(--vc-text-invert) 45%, transparent); }
.up-root.up-kdr-scrim.open { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .up-root.up-kdr, .up-root.up-kdr-scrim { transition: opacity var(--up-t-1) ease; } }

/* Der Kopf: 56 hoch (32er Knoepfe plus 12 darueber und darunter), steht, waehrend der Inhalt
   darunter laeuft. Die Linie kommt erst mit dem Rollen (.is-gerollt) -- oben ist der Kopf Teil
   der Flaeche. Die Knoepfe sind .up-iconbtn aus core, unveraendert. */
/* GLAS (12.10. angefordert, "wie Apple"): der Kopf liegt UEBER dem Rollbereich, der Inhalt laeuft
   unter ihm hindurch und scheint durch -- 72 % Deckkraft des Seitengrunds, dahinter 24px
   Unschaerfe mit etwas mehr Saettigung (so mischt Apple seine Materialien). Erst 92 %: "sieht
   man kaum" (12.10.). Der Rollbereich
   beginnt mit dem Abstand der Kopfhoehe (--up-kdr-kopf-h), oben steht also nichts verdeckt.
   Ohne backdrop-filter (alter Browser) bleibt es ein fast deckender Kopf -- lesbar wie vorher. */
.up-root.up-kdr { --up-kdr-kopf-h: 56px; }
.up-kdr-kopf { position: absolute; top: 0; left: 0; right: 0; z-index: 3;
  display: flex; align-items: center; gap: 8px; height: var(--up-kdr-kopf-h);
  padding: 0 12px; box-sizing: border-box; min-width: 0;
  background: color-mix(in srgb, var(--up-surface, var(--vc-bg)) 72%, transparent);
  -webkit-backdrop-filter: saturate(1.8) blur(24px); backdrop-filter: saturate(1.8) blur(24px);
  border-bottom: 1px solid transparent; transition: border-color var(--up-t-1) ease; }
.up-kdr.is-gerollt .up-kdr-kopf { border-bottom-color: var(--vc-border); }
.up-root .up-kdr-knopf[hidden] { display: none; }
/* Ohne Zurueck-Knopf beginnt die Krume auf der Kante der Inhaltsspalte nicht, aber auf der des
   Kreuzes gegenueber: 8px Luft wie die Polsterung des Knopfes, damit links und rechts gleich
   weit vom Rand stehen. */
.up-kdr-krume { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  padding-left: 8px; font-size: var(--up-fs-m); font-weight: 500; line-height: 1; }
.up-kdr.has-zurueck .up-kdr-krume { padding-left: 0; }
.up-kdr-art, .up-kdr-sep { flex: 0 0 auto; color: var(--vc-third); white-space: nowrap; }
.up-kdr-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--vc-text); }
.up-kdr-werkzeuge, .up-kdr-eigen { flex: 0 0 auto; display: flex; align-items: center; gap: 4px; }
.up-kdr-eigen:empty { display: none; }
.up-kdr-sk { display: inline-block; vertical-align: middle; width: 132px; height: 12px;
  border-radius: var(--up-r-xs); background: var(--vc-sk); animation: uutpulse 1.2s ease-in-out infinite; }

/* Der Koerper rollt, die Seite darunter nicht (overscroll-behavior: am Ende des Inhalts lief
   das Rollen sonst auf #main weiter, trotz der Sperre des Kopf-Skripts auf iOS).
   DIE SPALTE NIMMT DIE GANZE BREITE (12.10.): mit 720 in der Mitte stand der Inhalt in einem
   80-%-Drawer mit ueber 200px Luft je Seite ("padding left right zu gross"). Die Lesebreite
   begrenzt jetzt der Text selbst (.urd-body .up-rb, 720). 20px Rand: dieselbe Kante wie die
   Krume im Kopf (12 Polster + 8). */
/* overflow-x hidden: ein GESCHLOSSENES Menue (der Kalender oben rechts) steht linksbuendig an
   seinem Ausloeser und reichte 150px ueber die Kante -- gemessen auf 390px, die Flaeche liess
   sich seitlich schieben. Geoeffnet richtet es sich selbst aus (is-right) und bleibt innen. */
/* position + z-index 0: der Koerper ist eine eigene Stapelebene UNTER dem Glaskopf (z 3). Ohne sie
   legte sich ein klebender Tabellenkopf (.up-sticky .up-head, z 6, samt Deckstreifen bis oben) ueber
   den Glaskopf -- gemessen 13.10., als die Tabellen in den Drawern zu kleben begannen. */
.up-kdr-koerper { position: relative; z-index: 0; flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  scrollbar-gutter: stable; padding-top: var(--up-kdr-kopf-h); }
.up-kdr-spalte { box-sizing: border-box;
  padding: 16px 20px calc(96px + env(safe-area-inset-bottom)); }
/* Response Detail: doppelter Rand (12.10. angefordert, "padding left right verdoppeln") -- dort
   ist der Inhalt ein Lesetext, und 20px an der Kante liessen ihn an den Rand gedrueckt wirken.
   Auf dem Telefon gilt weiter der schmale Rand (unten). */
.up-kdr-response .up-kdr-spalte { padding-left: 40px; padding-right: 40px; }
.up-kdr-warte { display: flex; flex-direction: column; gap: 14px; padding-top: 8px; }
.up-kdr-warte .up-kdr-sk { display: block; height: 16px; }
.up-kdr-warte .up-kdr-sk.is-l { width: 88%; height: 28px; }
.up-kdr-warte .up-kdr-sk.is-m { width: 56%; height: 28px; }
.up-kdr-warte .up-kdr-sk.is-s { width: 32%; margin-top: 8px; }

/* DER DETAIL-KOPF (12.10.): was Response Detail und URL Detail gleich haben, und spaeter jeder
   weitere Drawer -- die Meta-Zeile mit feinen Trennstrichen, der Titel, die Leisten auf der
   Kopfflaeche (Zahl gross links, Inhalt rechts) und die Abschnittskoepfe. Stand zuerst lokal in
   response-detail.css; mit dem zweiten Nutzer gehoert es hierher (CLAUDE.md 1b, Punkt 2).
   Die Werte und ihre Begruendung: Titel 28 = --up-fs-3xl, dieselbe Stufe wie der Seitenkopf
   (die Designvorlage setzt ihn als groesste Zeile); Strich 1x12 in der Rahmenfarbe, so hoch wie
   die Versalhoehe der 13er Schrift, kein Mittelpunkt (CLAUDE.md 5); Leiste Radius 16 wie die
   Karten der App, links 20 statt 12 Polster, weil dort Text und kein Knopf an der Kante steht. */
.up-dk { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.up-dk-meta { display: flex; align-items: center; gap: 8px 12px; flex-wrap: wrap; min-height: 24px;
  font-size: var(--up-fs-m); color: var(--vc-third); }
.up-dk-meta:empty { display: none; }
.up-dk-sep { flex: 0 0 1px; width: 1px; height: 12px; background: var(--vc-border); }
.up-dk-titel { margin: 0; font-size: var(--up-fs-3xl); font-weight: 600; line-height: 1.2; letter-spacing: -0.02em;
  max-width: 820px; text-wrap: pretty; overflow-wrap: anywhere; color: var(--vc-text); }
.up-dk-leiste { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 12px 24px; min-width: 0; margin-top: 16px; padding: 12px 12px 12px 20px;
  border-radius: var(--up-r-2xl); background: var(--vc-heading-bg); }
.up-root .up-dk-leiste[hidden] { display: none; }
.up-dk-zahl { flex: 0 0 auto; display: inline-flex; align-items: baseline; gap: 8px;
  font-size: var(--up-fs-m); font-weight: 500; color: var(--vc-third); }
.up-dk-zahl-n { font-size: var(--up-fs-2xl); font-weight: 700; line-height: 1; letter-spacing: -0.02em;
  color: var(--vc-text); font-variant-numeric: tabular-nums; }
.up-dk-zahl-n:empty { display: none; }
/* Abschnittskopf: Ueberschrift ueber der Unterzeile, 16/600 und 13. */
.up-dk-h2 { font-size: var(--up-fs-xl); font-weight: 600; letter-spacing: -0.01em; color: var(--vc-text); }
/* Die Unterzeile in der DRITTEN Textstufe (12.10. angefordert): sie erklaert die Ueberschrift
   und tritt hinter den Inhalt des Abschnitts zurueck. */
.up-dk-unter { font-size: var(--up-fs-m); font-weight: 400; line-height: 1.45; color: var(--vc-third); }
/* DER KOPF MIT BILD (13.10. aus brand-drawer.css hierher, seit der Domain-Drawer ihn auch traegt):
   Logo oder Favicon links, daneben der Titel (.up-dk-titel) und darunter eine leise Zeile. Die
   Kachel ist .up-logo-box aus core in Kopfgroesse, genau die des Kopfes von performance-detail und
   Ads (.upd-held-logobox): 44, Radius 12 aussen, innen 11 -- das konzentrische Kind (CLAUDE.md 1c);
   56 war dem Nutzer zu gross. */
.up-dk-held { display: flex; align-items: center; gap: 14px; min-width: 0; }
.up-dk-held-bild { display: inline-flex; flex: 0 0 auto; }
.up-dk-logo.up-logo-box { width: 44px; height: 44px; flex: 0 0 44px; border-radius: var(--up-r-xl); }
.up-dk-logo.up-logo-box img { width: 42px; height: 42px; border-radius: 11px; }
.up-dk-logo .up-logo-ltr { font-size: var(--up-fs-xl); }
.up-dk-held-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1 1 auto; }
/* Die leise Zeile unter dem Titel: 13 in der zweiten Textfarbe; ist sie ein Knopf, wird sie beim
   Hover dunkler und unterstrichen (wie die Adresse unter dem Titel im URL-Drawer, .uud-link). */
.up-dk-held-sub { display: flex; min-height: 20px; min-width: 0; }
.up-dk-held-link { display: inline-flex; align-items: center; max-width: 100%; min-width: 0; padding: 0; border: 0;
  background: none; font: inherit; font-size: var(--up-fs-m); line-height: 20px; color: var(--vc-muted);
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-dk-held-link:hover { color: var(--vc-text); text-decoration: underline; text-underline-offset: 3px; }
/* KOPF UND FILTER IN EINER ZEILE (13.10.): links der Kopf (Bild, Name, Unterzeile), rechts Kalender
   und Filterknopf, auf der MITTE des Kopfes ("oben rechts zentriert", zweite Runde -- erst an der
   Unterkante). Heute im Domain-Drawer; Brand, Prompt und URL tragen ihre Filter in der Meta-Zeile
   darueber (.up-dk-metazeile). Reicht die Breite nicht, bricht die Zeile und die Filter stehen
   linksbuendig darunter (space-between laesst ein umgebrochenes Kind links). Der Kopf gibt bis
   280px nach -- sonst wuerde ein langer Name zerlegt, bevor die Filter weichen. */
.up-dk-heldzeile { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 16px 24px; min-width: 0; }
.up-dk-heldzeile > :first-child { flex: 1 1 auto; min-width: min(100%, 280px); }
/* META-ZEILE UND FILTER (13.10. zweite Runde: "Oben rechts zentriert", im URL-Drawer "auf Hoehe des
   zuletzt-gesehen-Datums"): die Filter stehen in der OBERSTEN Zeile des Kopfes, rechts und mittig
   zu ihr, nicht neben Name oder Titel darunter. Die Meta-Zeile darf schrumpfen und umbrechen; ist
   sie leer (:empty in core), stehen die Filter allein oben rechts. Zu schmal: darunter, links. */
.up-dk-metazeile { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 16px 24px; min-width: 0; }
.up-dk-metazeile > .up-dk-meta { flex: 1 1 auto; min-width: 0; }
/* Leere Meta-Zeile (Laden, keine Angaben): ohne sie stuende die Filterreihe als einziges Kind links. */
.up-dk-metazeile > .up-dk-meta:empty + .up-dk-filterzeile { margin-left: auto; }
/* Die Filterreihe: 8px wie jede Werkzeugreihe. Mit aktiven Filtern stehen deren Chips im
   Filterknopf-Baustein daneben (ufb bricht selbst um) -- darum darf er schrumpfen, der Kalender
   nicht. flex-start: allein unter dem Kopf UND selbst umgebrochen (sehr schmal) stuenden ihre Teile
   mit flex-end rechts gestaffelt statt linksbuendig (gemessen bei 480). */
.up-dk-filterzeile { display: flex; align-items: center; justify-content: flex-start; flex-wrap: wrap; gap: 8px;
  flex: 0 1 auto; min-width: 0; max-width: 100%; }
.up-dk-filterzeile > .udr-root { flex: 0 0 auto; }
.up-dk-filterzeile > .ufb-root { flex: 0 1 auto; width: auto; min-width: 0; }
/* DIE ABSCHNITTSFORM (13.10., Vorbild Domain Detail; "die Borders um die grossen Elemente weg, das
   sieht cleaner aus"). Ein Baustein im Drawer steht ohne Kartenrahmen, Radius und eigene Flaeche
   auf dem Grund des Drawers -- die Luft zwischen den Abschnitten trennt. Gesetzt von der
   einbettenden Stelle ueber data-form="abschnitte" an der Wurzel des Bausteins (Domain- und
   Brand-Drawer, je das grosse Chart oben); Dashboard und Landingpage setzen es nicht und behalten ihre Karten. Erst in drei
   Bausteinen einzeln gebaut, darum hier. Tabellen behalten ihren Kasten wie ueberall in der App --
   darum neben ihnen auch kein Trenner (er laege als zweite Linie neben ihrem Rand).
   NUR die grossen Charts oben (Brand Detail, Domain Detail): Visibility Chart und Top Citations
   bleiben auch im Drawer in ihren Karten (13.10. zweite Runde, "einfach core").
   (0,4,0) gegen die Kartenregeln der Bausteine und deren dunkle Randregel (0,3,0). */
.up-root[data-form="abschnitte"] :is(.ubd-card, .udd-chartcard, .udd-funnelcard) {
  border: 0; border-radius: 0; background: transparent; padding: 0; }
.up-dk-held-link:focus-visible { outline: var(--up-focus-w, 2px) solid var(--vc-text); outline-offset: 2px; border-radius: var(--up-r-xs); }
/* Skelett des Kopfes, in den Massen dessen, was gleich dasteht. */
.up-dk-sk { display: inline-block; height: 14px; border-radius: var(--up-r-xs); background: var(--vc-sk);
  animation: uutpulse 1.2s ease-in-out infinite; }
.up-dk-sk.is-logo { width: 44px; height: 44px; border-radius: var(--up-r-xl); }
.up-dk-sk.is-titel { display: block; width: 240px; max-width: 100%; height: 28px; }
.up-dk-sk.is-s { width: 120px; }

/* Tablet: die volle Breite -- 80 % von 900px liessen links einen Streifen Seite stehen, der nur
   noch nach Platzverlust aussah (so wie die Bubble-Gruppe, die dort auf 100 % ging). */
@media (max-width: 1023px) {
  .up-root.up-kdr { width: 100vw; }
}
@media (max-width: 767px) {
  .up-root.up-kdr { top: 0; right: 0; bottom: 0; width: 100vw; border: 0; border-radius: 0; }
  .up-root.up-kdr { --up-kdr-kopf-h: calc(56px + env(safe-area-inset-top)); }
  .up-kdr-kopf { padding-top: env(safe-area-inset-top); }
  /* Wie in der Mobilvorlage: der Kopf nennt nur, WAS offen ist ("Response"), mittig. Modell und
     Datum stehen darunter im Inhalt -- auf 375px haette die Krume ohnehin nur Punkte gezeigt. */
  .up-kdr-kopf { display: grid; grid-template-columns: 1fr auto 1fr; }
  .up-kdr-kopf > [data-kdr-zurueck] { grid-area: 1 / 1; justify-self: start; }
  .up-kdr-krume { grid-area: 1 / 2; justify-content: center; padding-left: 0; }
  .up-kdr-werkzeuge { grid-area: 1 / 3; justify-self: end; }
  .up-kdr-sep, .up-kdr-name { display: none; }
  .up-kdr-art { color: var(--vc-text); font-weight: 600; }
  .up-kdr-spalte, .up-kdr-response .up-kdr-spalte { padding-left: 16px; padding-right: 16px; }
}
/* Der Tarif als Pille (UC.planPilleHtml, 28.09. aus teams.css hierher, seit settings-billing sie
   auch zeigt). Der Grundton des Punktes steht HIER und nicht im JS: .up-sent-dot bringt keine
   Farbe mit (die Sentiment-Zelle setzt sie immer inline), und ein Tarif ohne Farbeintrag hatte
   in teams einen unsichtbaren Punkt (gemessen rgba(0,0,0,0) bei "Legacy Free"). Ein Tarif mit
   Eintrag ueberschreibt ihn inline; ohne laufenden Tarif bleibt es bei diesem Ton, der Name wird
   gedaempft. */
.up-planpill .up-sent-dot { background: var(--vc-third); }
.up-planpill.is-off .up-sent-val { color: var(--vc-muted); }
/* Mobil kuerzt die Pille, statt abgeschnitten zu werden (29.09., Teams: "der Tarif ist
   abgeschnitten, truncate den einfach im mobile"). .up-sent steht sonst auf flex 0 0 auto; hier
   darf sie schrumpfen, der Name endet mit Auslassungspunkten.
   NUR auf schmalen Wurzeln: am Desktop ist die Tarifzelle der Teams-Tabelle innen 137px breit und
   ">_ Developer Access" 147px -- die Pille laeuft dort unsichtbar ins Polster der Zelle, und eine
   Kuerzung waere an genau der Stelle, an der Platz ist, ein neuer Fehler (so gemessen). */
.up-root.is-narrow .up-planpill { max-width: 100%; min-width: 0; flex-shrink: 1; }
.up-root.is-narrow .up-planpill .up-sent-val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Der dauerhafte Entwicklerzugang ("Legacy Free", UC.planIstDev): kein Stufenpunkt, sondern ein
   Prompt >_ und der Name in Monospace, der Rahmen gestrichelt -- er ist kein Tarif der Leiter
   Essential/Professional/Enterprise und soll auch nicht so aussehen. Dieselbe Hoehe und dasselbe
   Polster wie jede Pille, damit die Spalte ruhig bleibt; 12 statt 13, weil Monospace je Zeichen
   breiter laeuft und die Tarifspalte der Teams-Tabelle bei 140 anfaengt. Eine Schrift wird nicht
   geladen: ui-monospace ist auf jedem System da, auf dem Mac ist es SF Mono. */
/* .up-sent davor: die Pille bekommt ihren Rahmen seit dem 29.09. aus .up-sent.up-pille (zwei Klassen),
   und die Abweichung gewinnt ueber die Spezifitaet, nicht ueber die Reihenfolge. */
.up-sent.up-planpill.is-dev { border-style: dashed; border-color: var(--vc-fourth); }
.up-planpill.is-dev .up-sent-val,
.up-planpill-prompt { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; }
.up-planpill.is-dev .up-sent-val { font-size: var(--up-fs-s); letter-spacing: -0.01em; }
.up-planpill-prompt { font-size: var(--up-fs-xs); font-weight: var(--up-fw-b); color: var(--vc-third); line-height: 1; flex: 0 0 auto; }
/* ---------- "nothing here" convention ----------
   ONE muted dash for every "no value" cell in the app — sentiment/rank/share percentages,
   brand-mention/citation stacks with zero items. Before this there were three different
   answers to "nothing here" (a bare unstyled "-" on .up-stack-empty, a plain "–" inline in
   .up-num/.up-sent-val with no color override so it rendered in full text-strength color, and
   responses-table's own SVG minus icon) — one per component, none of them muted. This is the
   single source every one of them now points at. */
/* Der leere Stapel traegt jetzt ein Minus-Icon statt eines Schriftzeichens: feste Groesse, und
   die Drittfarbe nimmt es aus dem Lesefluss -- es sagt "hier ist nichts", und das soll nicht so
   laut sein wie ein Wert. */
.up-stack-empty { display: inline-flex; align-items: center; justify-content: center;
  color: var(--vc-third); }
.up-stack-empty svg { width: 14px; height: 14px; display: block; }

.up-num.is-empty, .up-sent-val.is-empty, .up-stack-empty,
.tct-num.is-empty, .udt-sub-share.is-empty { color: var(--vc-fourth); font-weight: 500; }

/* ---------- icon segmented control ----------
   Lifted verbatim from topcitations-dashboard's doughnut/bar switch (.tcl-seg), which is the
   look every icon-pair switch in the app already had. It lives here so a new one cannot be
   "almost" it: components add .up-seg/.up-seg-btn, they do not restate the rules.
   NOTE this is a different control from .up-dense — that one is the full-width label switcher
   inside the Table Settings popover. */
/* Dieselbe Fassung wie beim Day/Week/Month-Umschalter darueber: 26px hoch, Polster 8, leisere
   Flaeche, oben ausgerichtet. Die Zeichen ziehen mit auf 14px -- 16px in einer 26er-Pille waeren
   dieselbe Enge, gegen die STYLEGUIDE 1 warnt. */
/* align-self: center -- die kleinen Umschalter stehen MITTIG in ihrer Zeile. Bei 26px Hoehe neben
   einer 32px-Zeile sah oben buendig aus wie verrutscht.
   Zwei bleiben oben buendig, und beide aus demselben Grund: sie stehen nicht in einer Zeile mit
   Knoepfen, sondern in der oberen Ecke einer Kachel, deren Kopf mehrzeilig ist -- .vc-gran im
   Visibility-Chart und .tcl-seg im Zitatteil. Ihre Ausnahme steht unten. */
/* VIER WEITERE ALIASE (27.09.), aus demselben Grund wie .cc-seg/.ubo-seg/.tcl-seg: es waren
   Nachbauten dieses Bauteils in den Komponentendateien, Wert fuer Wert abgeschrieben, und jeder
   lief auf seine Art auseinander -- .upt-status malte seine Pille aus --vc-bg/--up-sel-bg statt aus
   der Pillenmarke und stand damit nach dem Anheben der Schiene im Dunkeln UNTER ihr (gemessen:
   im Menue -6.9 Stufen). Gefunden beim Aufraeumen nach der Meldung "Switcher im Darkmode kaum zu
   erkennen", mit der Frage, ob noch Individualbauten da sind.
     .ubo-yaxis        Y-Achse der Landscape-Matrix (brands-overview)   Markup in der Bubble-Seite
     .tcd-mode         Domains/URLs in Top Citations, grosse Form unten  Markup in der Bubble-Seite
     .upt-status       Active/Inactive der Prompts-Tabelle              Kasten in der Bubble-Seite
     .udt-sub-dispseg  Title/URL im Drilldown der Domains-Tabelle       eigene 24px, siehe dort
   Alias und nicht Umbenennung: die ersten drei stehen im eingefuegten Bubble-Markup, das kein
   CDN-Pin erreicht. Der vierte darf nach STYLEGUIDE (Drilldown-Bedienelemente) die Klasse von
   core gar nicht tragen -- ein ungezieltes querySelector eines Kits wuerde ihn sonst fuer das
   eigene Element halten. In domains-table.css bleibt nur seine Groesse. */
.up-seg, .cc-seg, .ubo-seg, .tcl-seg,
.ubo-yaxis, .tcd-mode, .upt-status, .udt-sub-dispseg { display: inline-flex; padding: 0; gap: 0; background: var(--vc-switch-rail); border-radius: 8px; border: 0; height: 26px; flex: 0 0 auto; align-self: center; }
.tcl-seg { align-self: flex-start; }

/* ---- Der gleitende Umschalter --------------------------------------------------------------
   Die aktive Stufe wird nicht umgefaerbt, sie FAEHRT: ein Streifen liegt unter den Knoepfen und
   zieht in 200ms an die neue Stelle. Dasselbe Bild, das der Umschalter im Onboarding schon hatte.

   Der Streifen ist ein ::before und KEIN eingehaengtes Element -- und das ist der Kern dieser
   Fassung. Die erste hat einen span in den Umschalter gehaengt, und genau daran ist am 29.08. eine
   ganze Bubble-App stillgestanden: die Umschalter stehen dort im Markup der HTML-Elemente, Bubble
   setzt dieses Markup beim Zeichnen ueber jQuery.html() immer wieder neu, und ein fremder Knoten
   darin fuehrt zu einer Schleife, aus der die Seite nicht mehr herauskommt. In der Konsole war
   kein Fehler zu sehen -- nur eine Seite, die nie fertig wurde, und dieselbe Ladewarnung dreimal
   hintereinander aus drei verschiedenen Skripten, also dasselbe Element dreimal neu gezeichnet.
   Ein Pseudo-Element gibt es im DOM nicht. Es kann von niemandem gesehen, geerbt oder
   wegdiffed werden. core setzt nur noch vier Zahlen als Eigenschaften an den Kasten. */
.up-seg, .cc-seg, .ubo-seg, .tcl-seg, .vc-gran, .cc-gran, .up-pagesize-seg, .up-filter-dim,
.up-dense, .tcd-mode, .uca-format-seg, .ubo-yaxis, .upt-status, .udt-sub-dispseg,
.uap-tabs { position: relative; }
.up-seg::before, .cc-seg::before, .ubo-seg::before, .tcl-seg::before, .vc-gran::before,
.cc-gran::before, .up-pagesize-seg::before, .up-filter-dim::before, .up-dense::before,
.tcd-mode::before, .uca-format-seg::before, .ubo-yaxis::before, .upt-status::before,
.udt-sub-dispseg::before, .uap-tabs::before {
  content: ""; position: absolute; left: 0; top: 0; z-index: 0; box-sizing: border-box;
  width: var(--up-seg-w, 0); height: var(--up-seg-h, 100%);
  transform: translate(var(--up-seg-x, 0), var(--up-seg-y, 0));
  border-radius: 8px; background: var(--vc-switch-pill); border: 1px solid var(--vc-switch-border);
  opacity: 0; pointer-events: none;
}
/* Sichtbar und beweglich erst, wenn core gemessen hat. Die Deckkraft faehrt NICHT mit: sonst
   blendet der Streifen beim ersten Bild aus der Breite 0 heran, und das behauptet einen Wechsel,
   den es nicht gab. */
.is-gleitend::before { opacity: 1; }
.is-gleitend::before { transition: transform var(--up-seg-dauer, 200ms) var(--up-seg-kurve, ease),
  width var(--up-seg-dauer, 200ms) var(--up-seg-kurve, ease), height var(--up-seg-dauer, 200ms) var(--up-seg-kurve, ease); }
/* --up-seg-dauer und --up-seg-kurve setzt core NUR, wenn eine neu gebaute Box eine laufende Fahrt
   uebernimmt (segSchreiben, 29.09.): Restzeit statt 200ms, ease-out statt ease. Sonst fehlen sie,
   und es gilt der Rueckfall -- dieselben 200ms ease wie immer. */
/* Ein Schreibvorgang OHNE Fahrt. Gesetzt von segMessen fuer alles, was kein echter Wechsel des
   aktiven Knopfes ist: die erste Messung nach einem Neuaufbau, eine engere Werkzeugleiste, ein
   Menue, das aufgeht. Zwei Klassen gegen eine, also ueber die Spezifitaet und nicht ueber die
   Reihenfolge gewonnen. */
.is-gleitend.is-sofort::before { transition: none; }
[data-theme="dark"] .up-seg::before, [data-theme="dark"] .cc-seg::before,
[data-theme="dark"] .ubo-seg::before, [data-theme="dark"] .tcl-seg::before,
[data-theme="dark"] .vc-gran::before, [data-theme="dark"] .cc-gran::before,
[data-theme="dark"] .up-pagesize-seg::before, [data-theme="dark"] .up-filter-dim::before,
[data-theme="dark"] .up-dense::before, [data-theme="dark"] .tcd-mode::before,
[data-theme="dark"] .uca-format-seg::before, [data-theme="dark"] .ubo-yaxis::before,
[data-theme="dark"] .upt-status::before, [data-theme="dark"] .udt-sub-dispseg::before,
/* DIE GLEITENDE PILLE UND JEDER AKTIVE KNOPF EINES UMSCHALTERS liegen HOEHER als ihre Schiene,
   nicht tiefer. Nach dem Farbtausch stand hier ueberall der Grund der Seite, also die dunkelste
   Flaeche der App, mitten auf einem helleren Umschalter. Der Wert kam nicht aus einer
   Entscheidung: die alte Pille trug #121212, und der Tausch machte daraus stumpf #08090a.
   Gemeldet als "die Switcher in Dropdowns sind jetzt kaputt", und das waren sie.
   Danach bg-quaternary (#28282c) auf border-secondary, seit dem 27.09. --vc-switch-pill als
   Anhebung der Schiene: auf jedem Grund 14 bis 15 Stufen ueber ihr (siehe dort). Sieben Stellen,
   alle mit demselben Muster. */
[data-theme="dark"] .uap-tabs::before { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }
/* ---- UND DIE SCHIENE DARUNTER --------------------------------------------------------------
   Gemeldet am 07.09. zuerst fuer das Dunkle ("die grossen Switcher, die 32px hoch sind, muessen
   denselben Grund bekommen wie die kleinen, weil der Selected-State sich sonst zu wenig davon
   unterscheidet"), danach fuer alle Dropdowns ("kaum erkennbar, der Kontrast ist zu niedrig").
   Ursache war, dass die GROESSE den Grund bestimmte: die grossen trugen den vollen Ton, der
   dicht an der Pille liegt (dunkel 5.0 Stufen, hell 6.7).
   Hier stand danach eine Regel mit vierzehn Selektoren, die alle grossen Umschalter auf den Ton
   der kleinen zwang. Sie ist am 22.09. weggefallen: die Schiene haengt jetzt an EINER Marke
   (--vc-switch-rail), die Groesse aendert nur noch die Hoehe, und damit gibt es nichts mehr
   nachzuziehen. Die Regel hatte ausserdem einen stillen Preis -- sie ueberschrieb per .up-root
   die Komponentendateien, und deren eigene Grundangaben waren dadurch wirkungslos, ohne dass man
   es ihnen ansah: acht tote Zeilen, gefunden beim Messen. Jetzt nennt jede Schiene ihren Grund
   selbst, und es gibt nur einen. */
/* Die Knoepfe stehen ueber dem Streifen. */
.is-gleitend > button { position: relative; z-index: 1; }
/* Und die aktive Stufe gibt ihre eigene Flaeche ab, SOBALD der Streifen misst. Drei Stufen der
   Spezifitaet, weil die Aktivregeln der Bauteile in drei Stufen stehen: .up-seg-btn.is-active
   (0,2,0), [data-theme="dark"] ... (0,3,0) und .up-root[data-theme="dark"] .vc-gran-btn.is-active
   (0,4,0). Ueber die Spezifitaet gewonnen, nicht ueber die Ladereihenfolge. */
.is-gleitend > button.is-active,
[data-theme="dark"] .is-gleitend > button.is-active,
.up-root[data-theme="dark"] .is-gleitend > button.is-active {
  background: transparent; border-color: transparent;
}
/* KEINE Ausnahme fuer prefers-reduced-motion. Sie stand hier und war der wahrscheinlichste Grund
   dafuer, dass der Umschalter im Onboarding glitt und kein anderer: dessen eigene Marke hatte nie
   eine solche Regel. Wer die Bewegung auf seinem Rechner abgestellt hat, saehe damit genau EINEN
   Umschalter fahren und alle anderen springen -- der Unterschied waere auffaelliger als die
   Bewegung selbst. 200ms auf einer Flaeche von wenigen Pixeln ist ausserdem keine Bewegung, die
   jemanden stoert; die Regel schuetzte vor nichts. */
/* ---- Die zweite GROESSE des Umschalters ----------------------------------------------------
   is-lg ist der Umschalter, der nicht als Werkzeug in einer Kopfzeile steht, sondern allein ueber
   dem Inhalt und die Ansicht der ganzen Seite waehlt (brand-detail, domain-detail).
   Die Groesse bestimmt den GRUND nicht mehr: seit dem 22.09. tragen kleine wie grosse
   Umschalter --vc-switch-rail, und der steht in der Grundregel von .up-seg weiter oben. Hier
   bleibt nur, was die Groesse wirklich aendert -- die Hoehe. */
.up-seg.is-lg, .cc-seg.is-lg, .ubo-seg.is-lg, .tcl-seg.is-lg,
/* Der Umschalter im Topics-Dropdown (Match-Modus), der keine eigene Klasse hat und deshalb an
   seiner Beschriftung gefasst wird -- aria-label steht so in seinem Markup. Er steht in einem
   Element ausserhalb dieser Bibliothek, traegt aber ihre Klassen: ohne is-lg bekam er die
   kleine Form, obwohl er in einem Dropdown genauso 32 sein soll wie ueberall sonst. Die
   Umschalter von Models und Markets standen hier mit; die gibt es seit dem 28.09. nicht mehr. */
.up-seg[aria-label="Match mode"],
/* Domains/URLs in Top Citations ersetzt dort die Ueberschrift und war schon als Nachbau 32 hoch,
   nur mit 12px Polster statt 16 -- ohne Begruendung, also Drift. Kein is-lg im Markup (Bubble),
   deshalb an seiner Wurzel gefasst: zwei Klassen schlagen die eine der Grundregel. */
.tcd-root .tcd-mode {
  height: 32px;
}
.up-seg.is-lg .up-seg-btn, .cc-seg.is-lg .cc-seg-btn,
.ubo-seg.is-lg .ubo-seg-btn, .tcl-seg.is-lg .tcl-seg-btn,
.up-seg[aria-label="Match mode"] .up-seg-btn,
.tcd-root .tcd-mode-btn { height: 32px; padding: 0 16px; }

/* gap + type spec: this control was lifted from an ICON-ONLY switcher, so a button carrying an icon
   AND a label had its two children touching, and its text fell back to whatever the page inherited.
   Both are set here rather than per component -- gap is a no-op on the single-child icon variants
   that already use this, and the type matches .vc-gran-btn, the app's other labelled switcher
   (STYLEGUIDE §2: switcher label 12px/500). */
/* Die Knoepfe der vier Aliase vom 27.09. (siehe die Grundregel oben) stehen in jeder dieser Regeln
   mit: .ubo-yaxis-btn, .tcd-mode-btn, .upt-status-btn, .udt-sub-disp-btn. */
.up-seg-btn, .cc-seg-btn, .ubo-seg-btn, .tcl-seg-btn,
.ubo-yaxis-btn, .tcd-mode-btn, .upt-status-btn, .udt-sub-disp-btn { height: 26px; padding: 0 8px; border: 1px solid transparent; border-radius: 8px; background: transparent;
  color: var(--vc-muted); cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  gap: 7px; font-family: inherit; font-size: 12px; font-weight: 500; line-height: 1; }
.up-seg-btn svg, .cc-seg-btn svg, .ubo-seg-btn svg, .tcl-seg-btn svg,
.ubo-yaxis-btn svg, .tcd-mode-btn svg, .upt-status-btn svg, .udt-sub-disp-btn svg { width: 14px; height: 14px; color: var(--vc-muted); display: block; }
/* HIER STAND EINE DREHUNG UM 90 GRAD, und sie ist am 23.09. entfallen. Sie kam am 22.09. auf
   Wunsch dazu, weil das Zeichen damals senkrechte Saeulen zeigte, der Umschalter aber eine
   liegende Rangliste meint. Seit das Zeichen ChartBarBig ist, LIEGT es schon -- die Drehung
   stellte es also wieder auf und spiegelte es dabei sichtbar.
   Das hat vier Runden gekostet, in denen ich das ZEICHEN gewechselt habe, waehrend die Lage
   aus der CSS kam. Wer die Lage des Balken-Zeichens aendern will, aendert ab jetzt das Zeichen
   (ChartBarBig liegend, ChartColumnBig stehend) und nicht die Darstellung. */
.up-seg-btn:hover, .up-seg-btn:hover svg,
.cc-seg-btn:hover, .cc-seg-btn:hover svg,
.ubo-seg-btn:hover, .ubo-seg-btn:hover svg,
.tcl-seg-btn:hover, .tcl-seg-btn:hover svg,
.ubo-yaxis-btn:hover, .tcd-mode-btn:hover, .upt-status-btn:hover, .udt-sub-disp-btn:hover { color: var(--vc-text); }
/* Die drei Aliase sind KEIN Stil-Duplikat, sondern der Verzicht auf eine Umbenennung: das
   Markup dieser Switcher steht in der vom Nutzer eingefuegten Bubble-Seite, nicht in einer Datei
   die ein CDN-Pin erreicht. Ein Rename hier haette deren Seiten zerlegt -- citations-combo-chart
   bricht sogar seine ganze Initialisierung ab, wenn es seine Buttons nicht findet. */
.up-seg-btn.is-active, .cc-seg-btn.is-active, .ubo-seg-btn.is-active, .tcl-seg-btn.is-active,
.ubo-yaxis-btn.is-active, .tcd-mode-btn.is-active, .upt-status-btn.is-active, .udt-sub-disp-btn.is-active { background: var(--vc-switch-pill); color: var(--vc-text); border-color: var(--vc-switch-border); }
.up-seg-btn.is-active svg, .cc-seg-btn.is-active svg, .ubo-seg-btn.is-active svg, .tcl-seg-btn.is-active svg { color: var(--vc-text); }
[data-theme="dark"] .up-seg-btn.is-active,
[data-theme="dark"] .cc-seg-btn.is-active,
[data-theme="dark"] .ubo-seg-btn.is-active,
[data-theme="dark"] .tcl-seg-btn.is-active,
[data-theme="dark"] .ubo-yaxis-btn.is-active, [data-theme="dark"] .tcd-mode-btn.is-active,
[data-theme="dark"] .upt-status-btn.is-active, [data-theme="dark"] .udt-sub-disp-btn.is-active { background: var(--vc-switch-pill); border-color: var(--vc-switch-border); }

/* ---------- "X mentioned?" yes/no cell ----------
   Shared by urls-table and responses-table (both had their own byte-identical copy under a local
   prefix, which is how they drifted apart in the first place). A FILLED 14px badge carries the
   colour, the glyph inside is knocked out of it, and the label stays in the primary text colour —
   a coloured label next to a coloured icon read as two competing signals. */
/* ---- Marken-Schalter ("Brand X mentioned?") ---------------------------------------------------
   Dreistufig: aus, ja, nein. Lag als Kopie in urls-table.css; zweiter Verbraucher (response-detail),
   also hierher -- die alten .uut-*-Klassen bleiben als Alias, damit dort kein Markup anzufassen ist.
   Werte unveraendert: 32px hoch wie jeder Kopfzeilen-Knopf, Radius 8, Kaestchen 16px mit Radius 5. */
.up-brandtoggle, .uut-brand-toggle { display: none; align-items: center; justify-content: space-between;
  gap: 10px; height: 32px; padding: 0 8px; border-radius: 8px; background: transparent; border: 0;
  cursor: pointer; flex: 0 0 auto; font-family: inherit; }
.up-brandtoggle.is-visible, .uut-brand-toggle.is-visible { display: inline-flex; }
.up-brandtoggle:hover, .uut-brand-toggle:hover { background: var(--vc-heading-bg); color: var(--vc-text); }
.up-brandtoggle.is-yes, .up-brandtoggle.is-no,
.uut-brand-toggle.is-yes, .uut-brand-toggle.is-no { background: var(--vc-heading-bg); }
.up-brandtoggle-lbl, .uut-brand-toggle-lbl { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.up-brandcheck, .uut-brand-check { width: 16px; height: 16px; border-radius: 5px;
  border: 1.5px solid var(--vc-border); display: inline-flex; align-items: center;
  justify-content: center; flex: 0 0 auto; }
.up-brandcheck svg, .uut-brand-check svg { width: 11px; height: 11px; display: none; }
/* DAS ZEICHEN AUF DER AKZENTFLAECHE IST --up-accent-fg, und nur das (18.09.). Sichtbar ist
   ohnehin nur eines der beiden svg, und nur in is-yes/is-no -- dort liegt es auf --up-accent,
   und welche Farbe darauf lesbar ist, entscheidet die Akzentwahl, nicht das Thema. Hier stand
   --up-surface: das passte, solange der Akzent die Schriftfarbe des Themas war, und wird mit
   Indigo oder Azur im Dunkeln zu dunkel auf dunkel. */
.up-brandcheck, .uut-brand-check { color: var(--up-accent-fg); }
.up-brandtoggle.is-yes .up-brandcheck, .up-brandtoggle.is-no .up-brandcheck,
.uut-brand-toggle.is-yes .uut-brand-check, .uut-brand-toggle.is-no .uut-brand-check {
  background: var(--up-accent); border-color: var(--up-accent); }
/* Die dunkle Sonderregel darunter faerbt NICHT mehr das Zeichen -- das macht --up-accent-fg
   oben, in beiden Themen. Sie bleibt fuer alles andere, was hier im Dunkeln abweicht. */
.up-root[data-theme="dark"] .up-brandtoggle.is-yes .up-brandcheck,
.up-root[data-theme="dark"] .up-brandtoggle.is-no .up-brandcheck,
.up-root[data-theme="dark"] .uut-brand-toggle.is-yes .uut-brand-check,
.up-root[data-theme="dark"] .uut-brand-toggle.is-no .uut-brand-check {
  color: var(--up-surface, #1c1c1f); }
.up-brandtoggle.is-yes .up-brandcheck .up-brandcheck-yes,
.uut-brand-toggle.is-yes .uut-brand-check .uut-brand-check-yes { display: block; }
.up-brandtoggle.is-no .up-brandcheck .up-brandcheck-no,
.uut-brand-toggle.is-no .uut-brand-check .uut-brand-check-no { display: block; }
.up-brandlogo, .uut-brand-logo { width: 16px; height: 16px; border-radius: 4px; flex: 0 0 auto;
  display: block; object-fit: cover; }
.up-brandlabel, .uut-brand-label { font-size: 12px; font-weight: 500; color: var(--vc-text);
  white-space: nowrap; }

/* ---- Entitaeten-Chip -------------------------------------------------------------------------
   Ein anklickbarer Chip mit Logo und Name. Es gab in core das Logo-Name-Paar (.up-ment-logo /
   .up-ment-name) und den Themen-Chip (.up-topicchip) -- aber keinen NEUTRALEN Chip: der Themen-Chip
   traegt die Farbe seines Themas als Hintergrund, was fuer eine Marke falsch waere.
   Die Geometrie ist die des Themen-Chips (28px, Radius 8, gap 6..8), damit ein Chip mit Logo und
   einer mit Emoji in derselben Zeile gleich hoch stehen. Links weniger Polsterung als rechts, weil
   das Logo dort schon Rand mitbringt. Hover kommt aus .up-chiphover -- dieselbe Klasse, die die
   Themen-Chips tragen. */
.up-entchip { height: 28px; padding: 0 10px 0 5px; border-radius: 8px; display: inline-flex;
  align-items: center; gap: 8px; cursor: pointer; font-family: inherit;
  border: 1px solid var(--vc-border); background: var(--vc-bg);
  white-space: nowrap; overflow: hidden; min-width: 0; max-width: 100%; box-sizing: border-box; }
.up-entchip.is-static { cursor: default; }
/* is-soft: derselbe Chip mit leichtem Grund statt weissem. Der Rahmen BLEIBT im hellen Thema --
   dort ist der Grund so blass, dass ohne Rahmen kein Chip mehr zu sehen ist, nur ein Schatten von
   einem. Im Dunkeln faellt er weg: da ist der Grund heller als seine Umgebung und traegt die Form
   allein. Hover verdichtet den Grund. */
.up-entchip.is-soft { background: var(--vc-heading-bg); }
.up-root[data-theme="dark"] .up-entchip.is-soft { border-color: transparent; }
/* Nur dort, wo der Chip NICHT den geteilten Hover traegt: .up-entchip.is-soft:hover ist
   spezifischer als .up-chiphover:hover und hat ihn geschlagen -- ein Chip mit beiden Klassen zeigte
   den Hover des Chips und nicht den der App. */
.up-entchip.is-soft:not(.up-chiphover):hover { background: var(--vt-row-hover-solid, var(--vc-switch-bg)); }
.up-entchip:focus-visible { outline: 2px solid var(--vc-text); outline-offset: 1px; }
/* SPALTEN ALS CHIPS (15.10., UC.makeColumns cfg.form "chips", Display-Menue der Prompt-Tabelle nach
   Entwurf B): derselbe Chip, nur ohne Zeichen -- darum gleiches Polster links und rechts -- und als
   Schalter. An = is-soft (leichter Grund, wie oben), aus = gestrichelter Rand ohne Grund in der
   vierten Schriftfarbe: "gibt es, ist aber nicht zu sehen". Hoehe, Radius und Rand bleiben die des
   Chips. */
.up-spaltchips { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 0 4px; }
.up-entchip.up-spaltchip { padding: 0 10px; font-size: var(--up-fs-s); font-weight: var(--up-fw-m); color: var(--vc-text); }
.up-entchip.up-spaltchip.is-aus { border-style: dashed; background: transparent; color: var(--vc-fourth); }
.up-entchip.up-spaltchip.is-aus:hover { color: var(--vc-text); border-color: var(--vc-hover-border); }
.up-entchip.up-spaltchip.is-locked { cursor: default; }
/* Eine Zeile "Name ........ Bedienelement" in einem Dropdown (Row height, Grouping im Display-Menue). */
.up-pop-feld { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 40px; }
/* Der Segmentstreifen darin steht RECHTS neben einem Namen, also so breit wie sein Inhalt statt
   der ganzen Zeile, und 28 hoch (--up-h-seg) statt 34 -- Schiene UND Knoepfe zusammen, sonst
   stoesst die Pille an den Rand. Radius 8 bleibt. */
.up-pop-feld .up-dense { width: auto; flex: 0 0 auto; height: var(--up-h-seg); }
.up-pop-feld .up-dense-btn { flex: 0 0 auto; height: var(--up-h-seg); padding: 0 10px; }

/* ---- KEIN HINEINZOOMEN BEIM FOKUS AUF DEM TELEFON ----------------------------------------
   iOS-Safari zoomt die Seite, sobald ein Feld mit einer Schrift UNTER 16px den Fokus bekommt.
   Das ist kein Fehler, sondern eine Lesehilfe -- nur passt sie zu unseren Komponenten nicht:
   eine Tabelle oder ein Chart, in das hineingezoomt wird, ist danach unbedienbar, und der
   Nutzer muss von Hand zurueckzoomen. Die einzige Abhilfe, die OHNE Eingriff in das
   viewport-Meta auskommt, ist die Schriftgroesse selbst: ab 16px zoomt iOS nicht.

   DER PREIS, und er gehoert hierhin: auf dem Telefon steht in JEDEM Eingabefeld 16px statt
   der 12 bis 14, die am Schreibtisch stehen. Die Felder werden dort also etwas hoeher. Der
   Weg ueber user-scalable=no im Meta waere unsichtbar, nimmt aber der ganzen Seite das
   Zoomen -- auch dem, der es zum Lesen braucht. Deshalb dieser.

   Die Spezifitaet ist Absicht: eine nackte input-Regel (0,0,1) verliert gegen jede
   Komponentenregel wie .up-search-input (0,1,0). :not([type]) hebt sie auf 0,2,1 und schlaegt
   damit auch die zweiklassigen Regeln. Kein !important -- CLAUDE.md 1.
   Gemessen wird das, nicht angenommen: der Prueftand prueft JEDES Feld im Baum. */
@media (hover: none) and (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]),
  textarea:not([readonly]),
  select:not([multiple]) { font-size: 16px; }
}

/* ---- is-lifted: der Chip, wie ihn die Palette traegt -------------------------------------
   Die Werte sind die von .mqa-chip in quick-actions.css, Wert fuer Wert: Radius 7.6, gap 6.5,
   Polsterung 0 6.5 0 9.75, halbdurchsichtiger Grund und ZWEI Schattenlagen. Die zwei Lagen
   sind Absicht und keine Verzierung -- eine Lage kann nur eines von beiden: mit 3px Streuung
   ist die Kante weich, mit 1px fehlt das Abheben. Zusammen 2px Gesamtausdehnung.
   Im Dunkeln beide kraeftiger (24 und 16 statt 13 und 7 Prozent), weil ein Schatten auf
   dunklem Grund weniger hergibt. Alle vier Deckkraefte am 09.09. um 40 Prozent zurueck
   (22/12 und 40/26 mal 0.6) -- der Chip soll sich abheben und nicht schweben.
   WARUM HIER UND NICHT NUR DORT: der Chip ist derselbe Gedanke an zwei Orten -- die Palette
   und der Picker in Ask Mira zeigen beide Brand, Prompt, Domain, URL. Die Palette BEHAELT
   ihre eigene Geometrie, weil ihr Bubble-Loader nur quick-actions.css zieht und core dort nicht
   im Baum ist; dieselbe Begruendung wie bei ihren Farbpaletten. Diese Datei ist die Quelle.
   DER SCHATTEN IST SEIT DEM 28.09. EIN TOKEN (--up-e-chip, oben bei den Tiefen): die vier Zahlen
   stehen nur noch dort, die dunkle Fassung kommt mit dem Thema. quick-actions.css liest dasselbe
   Token und traegt die alten Werte nur noch als Rueckfall fuer Seiten ohne core. */
.up-entchip.is-lifted {
  height: 26px; gap: 6.5px; padding: 0 6.5px 0 9.75px; border-radius: 7.6px;
  font-size: 12.5px; font-weight: 500;
  background: color-mix(in srgb, var(--vc-heading-bg) 50%, transparent);
  box-shadow: var(--up-e-chip);
}
/* ---- Feld-Chip: ein gewaehlter Bezug IM Eingabefeld (14.10.) ------------------------------
   Der Chip aus Ask Mira (.am-pick-tag), als Baustein fuer jedes Feld, das Chips aufnimmt --
   zuerst Prompt Research ("genau so wie die Item-Chips, die wir bei Mira haben"). Grund, Rahmen,
   Radius und Schatten kommen aus .up-entchip.is-lifted, die Masse sind die von Mira, Wert fuer
   Wert: 28px hoch, 7 Abstand, links 5 und rechts 4 Polster, eine 18er-Kachel links, ein 19er-
   Kreuz rechts. Drei Klassen im Selektor, weil .up-entchip.is-lifted zwei hat -- Konflikt ueber
   Spezifitaet, nicht ueber die Ladereihenfolge.
   Die Kachel traegt, was der Bezug IST: bei einer Topic ihr Emoji oder ihren Farbstrich (dieselbe
   Marke wie .up-topicchip, 4x10, Radius 2), beim Intent sein Zeichen. Radius 5 = 6 minus 1
   (konzentrisch in der 7.6er-Ecke des Chips mit 5 Polster, gerundet). */
.up-feldchip.up-entchip.is-lifted { height: 28px; gap: 7px; padding: 0 4px 0 5px; color: var(--vc-text); vertical-align: middle; }
.up-feldchip-av { width: 18px; height: 18px; flex: 0 0 auto; border-radius: 5px; overflow: hidden;
  background: var(--vc-heading-bg); display: flex; align-items: center; justify-content: center; color: var(--vc-third); }
.up-feldchip-av svg { width: 12px; height: 12px; display: block; }
.up-feldchip-av .up-topicchip-e { font-size: 12px; line-height: 1; }
/* Der Farbstrich der Topic steht in der Regel von .up-topicchip::before mit (dieselbe Marke,
   dieselbe Begruendung fuer Radius 2 dort). */
.up-feldchip-lbl { min-width: 40px; max-width: 320px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-feldchip-x { width: 19px; height: 19px; flex: 0 0 auto; border: 0; border-radius: var(--up-r-s); padding: 0;
  background: transparent; color: var(--vc-muted); cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--up-t-2) ease, color var(--up-t-2) ease; }
.up-feldchip-x svg { width: 12px; height: 12px; display: block; }
.up-feldchip-x:hover { color: var(--vc-text); background: color-mix(in srgb, var(--vc-text) 6%, transparent); }

/* Die kleine Fassung (.mqa-mini): 22px statt 26, Radius 6.4. Der Schatten ist derselbe wie beim
   grossen Chip -- die Begruendung steht bei --up-e-chip. */
.up-entchip.is-lifted.is-sm {
  height: 22px; gap: 5.5px; padding: 0 5.5px 0 8.25px; border-radius: 6.4px; font-size: 11px;
}

/* ---- Mention-Liste ---------------------------------------------------------------------------
   Die Aufzaehlung der Marken, die irgendwo vorkommen. Lag als lokaler Block in response-detail;
   url-detail braucht denselben Chip, also gehoert er hierher -- zwei Kopien sind genau der Weg,
   auf dem sie auseinanderlaufen.

   ALLE Masse haengen an --up-mc, der Hoehe. Die Verhaeltnisse sind die des Grundchips
   .up-entchip (28px): Polsterung 10/5, Radius 8, Innenabstand 8, Logo 18, Bild 16, Buchstabe 10.
   Bei --up-mc: 32px ergibt das Zeichen fuer Zeichen die Zahlen, die in response-detail standen
   (11.43 / 5.71 / 9.14 / 20.57 / 18.29 / 11.43) -- der Block dort ist damit nicht geaendert,
   sondern nur umgezogen.
   Der NAME faellt bewusst aus dem Verhaeltnis: 0.45 der Hoehe statt 13.5/28 = 0.48. Er stand
   sonst zu gross neben seinem Logo, und eine Schrift fast so hoch wie das Bild daneben laesst
   den Chip nach Ueberschrift aussehen statt nach Aufzaehlung.
   is-lg ist dieselbe Liste eine Stufe groesser -- eine Zahl aendert sich, alles andere zieht mit. */
.up-mentlist { --up-mc: 32px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.up-mentlist.is-lg { --up-mc: 40px; }
/* Drei Selektorteile, weil .up-entchip.is-soft oben zwei hat und sonst gewinnt. */
.up-mentlist .up-entchip.is-soft {
  height: var(--up-mc);
  padding: 0 calc(var(--up-mc) * 0.3571) 0 calc(var(--up-mc) * 0.1786);
  border-radius: calc(var(--up-mc) * 0.2857);
  gap: calc(var(--up-mc) * 0.2857);
  max-width: 100%;
}
/* Grund und Rahmen NUR in Ruhe. Standen sie in der Regel darueber, schlugen sie mit ihren drei
   Klassen den geteilten Hover aus core (.up-chiphover:hover, zwei) -- der Chip haette dann gar
   keinen Hover. Mit :not(:hover) gilt hier die Ruhe und oben der Hover.
   Hell: KEIN Grund, nur der Rahmen, den .up-entchip ohnehin mitbringt. */
.up-mentlist .up-entchip.is-soft:not(:hover) { background: transparent; }
/* Dunkel: dort ist der Grund heller als seine Umgebung und traegt die Form allein -- ein Rahmen
   je Marke waere zu viel, ein rahmenloser Chip ohne Grund waere nichts mehr. */
.up-root[data-theme="dark"] .up-mentlist .up-entchip.is-soft:not(:hover) { background: var(--vc-field); }
.up-mentlist .up-ment-logo {
  width: calc(var(--up-mc) * 0.6429); height: calc(var(--up-mc) * 0.6429);
  border-radius: calc(var(--up-mc) * 0.1786);
}
.up-mentlist .up-ment-logo img {
  width: calc(var(--up-mc) * 0.5714); height: calc(var(--up-mc) * 0.5714);
  border-radius: calc(var(--up-mc) * 0.1429);
}
.up-mentlist .up-model-ltr { font-size: calc(var(--up-mc) * 0.3571); }
.up-mentlist .up-ment-name { font-size: calc(var(--up-mc) * 0.45); }

/* ---- Modell-Chip / Market-Chip ----------------------------------------------------------------
   Beide lagen als Kopie in responses-table bzw. prompts-table. Die alten Klassen bleiben als
   Alias erhalten, damit dort kein Markup angefasst werden muss. Die Werte sind unveraendert die
   der Vorbilder: 18px Logokachel aus .up-ment-logo, Flagge 18x14 mit Radius 3. */
.up-model-chip, .urt-model-chip { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.up-model-ltr, .urt-model-ltr { font-size: 10px; font-weight: 600; color: var(--vt-logo-ink, var(--vc-muted)); line-height: 1;
  text-transform: uppercase; }
.up-ment-logo.has-img .up-model-ltr, .up-ment-logo.has-img .urt-model-ltr { display: none; }
/* Im Dunkeln traegt die Kachel keinen Ton: die Modell-Logos sind dort selbst hell. */
.up-root[data-theme="dark"] .up-model-chip .up-ment-logo { background: transparent; padding: 0; }
/* Ohne Platte steht der Buchstabe auf der Karte -- dort die Schrift der Karte und nicht die Tinte
   der hellen Platte (--vt-logo-ink), die gilt nur, wo die Platte liegt: in den Markenchips von
   response-detail und url-detail (28.09., dort war der Buchstabe im Dunkeln unsichtbar). */
.up-root[data-theme="dark"] .up-model-chip .up-model-ltr { color: var(--vc-muted); }

.up-market, .upt-market { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.up-flag, .upt-flag { width: 18px; height: 14px; border-radius: 3px; overflow: hidden; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; line-height: 0;
  background: var(--vc-heading-bg); }
.up-flag img, .upt-flag img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Der Code ist eine Beschriftung der Flagge, kein eigener Wert -- also eine Stufe zurueck. */
.up-market-code, .upt-market-code { font-size: 12px; font-weight: 600; color: var(--vc-muted);
  letter-spacing: 0.02em; white-space: nowrap; }

.up-ment-cell { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 500; color: var(--vc-muted); }
.up-ment-badge { width: 14px; height: 14px; border-radius: 4px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; }
/* The glyph is the SURFACE colour, not a hardcoded white: on dark the badge is a light tint and
   a white glyph would vanish into it. 8px, not 9: a 14px badge minus a 9px glyph splits into a
   fractional 2.5px pad on each side, which different rows can round to 2px-left/3px-right or the
   reverse depending on the badge's own sub-pixel screen position — reads as the glyph drifting
   left/right row to row even though the flexbox centering math is exact. 8px divides the 14px
   badge into a flat, unambiguous 3px on both sides every time. */
.up-ment-badge svg { width: 8px; height: 8px; display: block; color: var(--up-surface, #fff); }
.up-ment-cell.is-yes .up-ment-badge { background: var(--vt-up); }
.up-ment-cell.is-no  .up-ment-badge { background: var(--vt-down); }
.up-root[data-theme="dark"] .up-ment-badge svg { color: var(--vc-text-invert); }

/* ---------- "<brand logo> mentioned?" column header ----------
   urls-table established this; it lives here so every table with the column gets the same thing. */
.up-th-brandlogo { width: 16px; height: 16px; border-radius: 4px; flex: 0 0 auto; object-fit: cover; margin-right: 5px; }

/* ---------- brand-mention chip stack ----------
   Overlapping 32px circles. Shared by urls-table's Brands mentioned column and prompts-table's
   Brand Mentions column. The 4px visual gap between them is a ring in the ROW's background
   colour, not a real gap: with a negative margin the neighbour would otherwise touch. The ring
   has to follow the row's hover colour too, hence the override below.
   No size/margin transition: with up to 100 rows × 4 logos on screen, animating those on the
   dense-mode toggle forces simultaneous layout reflow across hundreds of elements and visibly
   stalls the page for a couple of seconds (see .up-row for the same reasoning). The hover
   lift/spread stays on .up-stack-vis's transform, which is cheap and only ever touches one
   row's stack at a time. */
.up-stack { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
.up-stack-item {
  width: 28px; height: 28px; flex: 0 0 28px;
  display: inline-flex; align-items: center; justify-content: center;
  position: relative; cursor: default;
}
.up-stack-item + .up-stack-item { margin-left: -4px; }
/* Hit-testing lives on .up-stack-item, which never moves — only its inner .up-stack-vis animates
   on hover. With the outer box empty and static, neighbouring items' hover boundaries never move,
   so crossing from one logo to the next always fires a clean mouseout/mouseover pair — the
   border/background/ring live on .up-stack-vis instead, so the whole circle (not just the image
   inside it) lifts and shifts together on hover. */
/* padding, not flex-centering a smaller fixed-px child, is what actually guarantees the logo
   sits dead-center: 28px outer minus a 5px (odd) or 21px (also odd relative to 28) child left a
   fractional remainder that flexbox splits unevenly depending on the item's own sub-pixel screen
   position — the same class of drift already fixed once on the Mentioned-badge icon. Padding
   removes the split entirely: the image is sized 100%/100% of the PADDED content box (border-box
   is already the sitewide default, see .up-root *), so it always lands exactly 2px from every
   edge with nothing left over to round differently row to row. */
/* Der helle Rand ist EIN Pixel breit, und das ist dasselbe Mass wie an .up-logo-box in der
   Tabelle des Visibility Charts. Vorher waren es 2px Polsterung PLUS 1px Rahmen: im Dunkeln
   liegt die Platte (--vt-logo-bg, #e0e0e0) auf #1b1b1b, da ist jeder dieser drei Pixel zu sehen,
   und die Chips trugen einen weissen Ring, den dieselben Logos zwei Komponenten weiter nicht
   haben. Groesse (28), Ueberlappung (-4) und der 4px-Ring in Zeilenfarbe bleiben unberuehrt --
   der Ring ist der ABSTAND zum Nachbarn, nicht der Rand des Chips.
   Bildradius entsprechend 8 - 1 = 7 statt 8 - 2 = 6. */
/* Rahmen UND Polsterung: 1px Rahmen, dann 2px Platte, dann das Logo. Die Platte traegt die Farbe
   (--vt-logo-bg), der Rahmen die Kante -- im Hellen in --vc-border, im Dunkeln in der Plattenfarbe,
   wo er mit ihr verschmilzt und der Chip damit aussieht wie vorher, nur einen Pixel groesser
   umrandet. Eine Geometrie fuer beide Themen.
   Rechnung: 28px Chip - 2x1 Rahmen - 2x2 Polsterung = 22px Bild, Bildradius 8 - 3 = 5.
   Der 4px-Ring aus box-shadow bleibt unberuehrt: das ist der ABSTAND zum Nachbarn, nicht die
   Kante des Chips. */
.up-stack-vis {
  display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%;
  border-radius: 8px; padding: 2px; background: var(--vt-logo-bg); box-shadow: 0 0 0 4px var(--vc-bg);
  border: 1px solid var(--vc-border);
  transition: transform .16s ease;
  /* Bezug fuer den Globus des Favicon-Rueckfalls (siehe .up-fav weiter oben). In Ruhe traegt die
     Sichtebene keine transform, ohne position waere der Bezug also der Kasten -- und der bewegt
     sich nicht. Eine Zeile, damit die Rechnung in beiden Zustaenden dieselbe ist. */
  position: relative;
}
/* Im Dunkeln kein Rahmen, und die helle Platte ist nur EINEN Pixel breiter als das Logo (27.09.
   gemeldet: "die Brand-Chips haben im Darkmode eine zu dicke helle Border -- wie das Favicon in
   der URL-Tabelle"). Dort sind es 22px Bild in einer 24px-Platte, also 1px Rand, und genau das
   steht auch als Regel in performance-detail.css und quick-actions.css ("der helle Rand um ein
   Logo ist ueberall in der App genau einen Pixel breit"). Hier waren es 2px -- die ganze
   Polsterung.
   Geloest ueber die MALFLAECHE, nicht ueber die Masse: die Platte endet an der Inhaltskante
   (background-clip: content-box, also genau unter dem Bild), und den einen Pixel Rand malt der
   Schatten des Bildes darunter in derselben Farbe. Rahmen, Polsterung und Bildgroesse bleiben
   unberuehrt -- in beiden Themen dieselbe Geometrie, und die Kacheln mit eigenem Mass
   (performance-detail ohne Rahmen, die kompakte Zeile) bekommen denselben 1px-Rand, ohne dass
   ihr Bild im Dunkeln schrumpft. Der Ring folgt dem Radius des Bildes, er ist also konzentrisch. */
.up-root[data-theme="dark"] .up-stack-vis { border-color: transparent; background-clip: content-box; }
.up-root[data-theme="dark"] .up-stack-item img { box-shadow: 0 0 0 1px var(--vt-logo-bg); }
/* DER VORGAENGER SCHIEN DURCH (28.09. gemeldet: "links vor jedem Chip ist noch ein schmaler
   Bereich des Chips davor sichtbar", nur im Dunkeln). Die Regel darueber macht Rahmen und
   Polsterung durchsichtig -- 3px je Seite --, und ein Chip ueberlappt seinen Vorgaenger um 4px.
   Der 4px-Ring (box-shadow) sollte die Ueberlappung zudecken, aber ein aeusserer Schatten malt nie
   IN seinen eigenen Kasten: in diesen 3px stand also der rechte Rand des Vorgaengers, seine Platte
   und sein heller 1px-Ring. Im Hellen ist die Platte deckend bis zur Kante, dort gibt es das nicht.
   Jetzt malt ein Folgechip diese 3px in der Farbe des Rings: die Platte bleibt eine eigene Schicht
   nur unter dem Logo (content-box), die Ringfarbe liegt darunter bis zur Rahmenkante. Der Chip
   sieht damit aus wie vorher -- 1px heller Rand, dann Abstand --, nur dass hinter dem Abstand
   nichts mehr durchscheint.
   Die Ringfarbe ist eine Marke (--up-stack-ring), weil sie den Ring der Zeile begleiten muss:
   beim Hover der Zeile, im dichten Modus und in der Leiste (sidebar.css) setzen die Regeln des
   Rings sie mit. Nur Folgechips: der erste liegt ueber nichts, und die einzelnen Kacheln in
   performance-detail (ohne Ring) bleiben durchsichtig wie am 27.09. bestellt. */
.up-root[data-theme="dark"] .up-stack-item + .up-stack-item .up-stack-vis {
  background: linear-gradient(var(--vt-logo-bg), var(--vt-logo-bg)) content-box,
              var(--up-stack-ring, var(--vc-bg));
}
/* only the gap-ring follows the row hover colour; the circle fill stays the (light-in-dark) logo bg.
   Die Karte (.urt-card) gehoert seit dem Kartenentwurf vom 13.10. NICHT mehr hierher: sie wechselt
   beim Hover ihre Flaeche nicht mehr (nur Rand und Schatten). Mit der Regel bekam jeder Chip einer
   gehoverten Karte einen Ring in der Hover-Farbe der Tabellenzeile -- gemeldet als "Border um die
   Brand- und Citation-Chips". */
.up-row:hover .up-stack-vis { box-shadow: 0 0 0 4px var(--vt-row-hover-solid, var(--vc-bg));
  --up-stack-ring: var(--vt-row-hover-solid, var(--vc-bg)); }
/* 5 statt 7: der Chip hat jetzt 1px Rahmen und 2px Polsterung, das Bild sitzt also 3px innen. */
.up-stack-item img { width: 100%; height: 100%; border-radius: 5px; object-fit: contain; display: block; }
.up-stack-ltr { font-size: 10px; font-weight: 600; color: var(--vt-logo-ink, var(--vc-muted)); line-height: 1; text-transform: uppercase; }
.up-stack-item.has-img .up-stack-ltr { display: none; }
/* plain text, no circle: the overflow count is a label, not another brand */
.up-stack-more {
  margin-left: 8px; flex: 0 0 auto;
  font-size: 12px; font-weight: 500; color: var(--vc-muted); white-space: nowrap;
  transition: transform .16s ease;
}
/* Der Anhang hinter dem Stapel (brandStack opts.tailHtml). Dieselbe Uebergangszeit wie das "+N",
   damit beide zusammen gehen und nicht zwei Bewegungen zu sehen sind. Der Abstand kommt aus dem
   Fuss, der ihn vorher als eigenes Kind hatte -- 8px, wie die Luecke in .urt-card-footleft. */
.up-stack-tail {
  display: inline-flex; align-items: center; margin-left: 8px; flex: 0 0 auto;
  transition: transform .16s ease;
}
/* Hovering a logo lifts it, brings it to front, and opens a gap on both sides — created by
   shifting the hovered item's visual layer and everything AFTER it to the right. Items BEFORE
   it never move (so the first brand can't drift left). Following items shift twice as far so
   their gaps to each other stay at the default: they don't squash together. */
.up-stack-item:hover { z-index: 3; }
.up-stack-item:hover .up-stack-vis { transform: translateX(8px) translateY(-3px); }
.up-stack-item:first-child:hover .up-stack-vis { transform: translateY(-3px); }
.up-stack-item:hover ~ .up-stack-item .up-stack-vis,
.up-stack-item:hover ~ .up-stack-more,
.up-stack-item:hover ~ .up-stack-tail { transform: translateX(16px); }
.up-stack-item:first-child:hover ~ .up-stack-item .up-stack-vis,
.up-stack-item:first-child:hover ~ .up-stack-more,
.up-stack-item:first-child:hover ~ .up-stack-tail { transform: translateX(8px); }

/* Mirror image of the four rules above, for a stack pinned to the RIGHT edge of its container
   (Citations in the responses table, and the same stack in a card footer): spreading rightwards
   there pushes chips out of the cell/card instead of into free space. Same distances, same lift,
   same easing — only the axis is flipped. brandStack(..., {spread:"left"}).
   `:has(~ …)` is the previous-sibling selector CSS otherwise lacks: "an item that is followed by
   the hovered one" is the mirror of the plain `~` used above. The last CHIP carries .is-last from
   brandStack — :last-child would miss it when a "+N" badge follows, and expressing it inline would
   need :has() inside :has(), which CSS forbids (the rule is dropped as invalid). */
/* FIRST cancel the right-hand spread. The four rules above still match inside a left-spreading
   stack — they are not scoped to it — so without this the chips AFTER the hovered one kept
   sliding right while the ones before it slid left. Nothing in a left-spread stack may move
   right, so the trailing items (and the "+N" badge, which sits at the right edge) stay put. */
.up-stack.is-spread-left .up-stack-item:hover ~ .up-stack-item .up-stack-vis,
.up-stack.is-spread-left .up-stack-item:hover ~ .up-stack-more,
.up-stack.is-spread-left .up-stack-item:hover ~ .up-stack-tail,
.up-stack.is-spread-left .up-stack-item:first-child:hover ~ .up-stack-item .up-stack-vis,
.up-stack.is-spread-left .up-stack-item:first-child:hover ~ .up-stack-more,
.up-stack.is-spread-left .up-stack-item:first-child:hover ~ .up-stack-tail { transform: none; }

.up-stack.is-spread-left .up-stack-item:hover .up-stack-vis { transform: translateX(-8px) translateY(-3px); }
.up-stack.is-spread-left .up-stack-item.is-last:hover .up-stack-vis { transform: translateY(-3px); }
.up-stack.is-spread-left .up-stack-item:has(~ .up-stack-item:hover) .up-stack-vis { transform: translateX(-16px); }
.up-stack.is-spread-left .up-stack-item:has(~ .up-stack-item.is-last:hover) .up-stack-vis { transform: translateX(-8px); }
.up-root.is-dense .up-stack.is-spread-left .up-stack-item:hover .up-stack-vis { transform: translateX(-7.04px) translateY(-3px); }
.up-root.is-dense .up-stack.is-spread-left .up-stack-item.is-last:hover .up-stack-vis { transform: translateY(-3px); }
.up-root.is-dense .up-stack.is-spread-left .up-stack-item:has(~ .up-stack-item:hover) .up-stack-vis { transform: translateX(-14.08px); }
.up-root.is-dense .up-stack.is-spread-left .up-stack-item:has(~ .up-stack-item.is-last:hover) .up-stack-vis { transform: translateX(-7.04px); }
/* Compact mode shrinks the mentioned-brand logos ~20% and scales every gap/shift to match. */
.up-root.is-dense .up-stack-item { width: 24.64px; height: 24.64px; flex: 0 0 24.64px; }
/* Dichte Zeilen: Faktor 0.88 auf alles (24.64 statt 28). Polsterung 2 -> 1.76, Rahmen bleibt 1px,
   also sitzt das Bild 2.76px innen und der Bildradius ist 7.04 - 2.76 = 4.28. */
.up-root.is-dense .up-stack-vis { border-radius: 7.04px; padding: 1.76px; box-shadow: 0 0 0 3.52px var(--vc-bg); }
.up-root.is-dense .up-row:hover .up-stack-vis { box-shadow: 0 0 0 3.52px var(--vt-row-hover-solid, var(--vc-bg));
  --up-stack-ring: var(--vt-row-hover-solid, var(--vc-bg)); }
.up-root.is-dense .up-stack-item img { border-radius: 4.28px; }
.up-root.is-dense .up-stack-ltr { font-size: 8.8px; }
.up-root.is-dense .up-stack-item + .up-stack-item { margin-left: -3.52px; }
.up-root.is-dense .up-stack-more { margin-left: 7.04px; }
.up-root.is-dense .up-stack-item:hover .up-stack-vis { transform: translateX(7.04px) translateY(-3px); }
.up-root.is-dense .up-stack-item:first-child:hover .up-stack-vis { transform: translateY(-3px); }
.up-root.is-dense .up-stack-item:hover ~ .up-stack-item .up-stack-vis,
.up-root.is-dense .up-stack-item:hover ~ .up-stack-more { transform: translateX(14.08px); }
.up-root.is-dense .up-stack-item:first-child:hover ~ .up-stack-item .up-stack-vis,
.up-root.is-dense .up-stack-item:first-child:hover ~ .up-stack-more { transform: translateX(7.04px); }

/* ---------- interactive chip hover ----------
   For any coloured chip/tag that is CLICKABLE (topic tags in a table cell, topic tags in the
   bulk editor, and anything similar that follows). The chip keeps its own tint at rest; on
   hover it drops to the neutral surface with the shared hover border, so "this is a control"
   reads the same everywhere regardless of which colour the tag happens to carry.
   Applied by adding .up-chiphover next to the chip's own class. */
.up-chiphover { transition: background 120ms ease, border-color 120ms ease, color 120ms ease; }
/* Zwei Marken statt vier Literalen: #f9f8f9/#232326 ist --vt-row-hover-solid, #1f1f1b/#e0e0e0
   ist --up-accent-ink. Die eigene Dunkel-Regel faellt damit weg -- sie war nur da, weil die
   hellen Werte fest verdrahtet waren.
   Der Chip kann auf der Komponentenwurzel ODER auf einer Flaeche am body liegen (der Bulk-Leiste
   der Prompts-Tabelle). Deshalb fuehrt diese Leiste --vt-row-hover-solid seit dem 22.09. selbst
   mit; --up-accent-ink setzt ihr das JS ohnehin schon (akzentSyncen). (22.09.) */
.up-chiphover:hover { background: var(--vt-row-hover-solid); color: var(--up-accent-ink); box-shadow: none; }

/* compact "No data" line for tables that live in a fixed-height box and have no room for the
   full illustrated empty state */
.up-empty-mini { padding: 24px; text-align: center; color: var(--vc-muted); font-size: 13px; }

/* ============================================================
   Topic create/edit modal (UC.makeTopicModal) — STYLEGUIDE §27's reference implementation, moved
   here from topics-manager.css once prompts-table's "Add Topic" button became a second consumer
   of the exact same modal. Body-mounted (outside any .up-root), so — like .upt-bulkbar and every
   other document.body-mounted surface in this library — it redeclares its own --vc- and
   --up-surface tokens locally instead of relying on the cascade from a .up-root ancestor it
   doesn't have.
   ============================================================ */
.up-topicmodal-backdrop {
  position: fixed; inset: 0; z-index: 100000;   /* above the bulk-bar's 99999 */
  background: rgba(0,0,0,.30);
  backdrop-filter: blur(1.5px); -webkit-backdrop-filter: blur(1.5px);
  display: flex; align-items: center; justify-content: center; padding: 24px;
  opacity: 0; pointer-events: none;
  transition: opacity 160ms ease;
}
.up-topicmodal-backdrop.is-shown { opacity: 1; pointer-events: auto; }
/* Darker backdrop in dark mode: the same 30% black reads as noticeably lighter/washed-out over a
   dark page than over a light one, so dark mode gets its own, slightly heavier value. */
.up-topicmodal-backdrop[data-theme="dark"] { background: rgba(0,0,0,.35); }

.up-topicmodal-card {
  --vc-text: #1f1f1b; --vc-muted: #3e4146; --vc-third: #6b6f78; --vc-fourth: #80858e; --vc-border: #dcdbdd;
  --vc-heading-bg: #f9f8f9; --vc-hover-border: #6f6f6f; --up-surface: #ffffff; --vc-sk: #eeedef;
  /* Die Fokus-Tokens MUESSEN hier mit rein. Diese Karte haengt am body, nicht unter .up-root, wo
     die Tokens definiert sind -- ohne sie ist var(--up-focus) leer, und eine Deklaration mit einer
     leeren Variablen ist ungueltig. Die :focus-Regel des Namensfeldes weiter unten stand damit
     zwar in der Datei, hat aber nichts getan, und das Feld behielt seinen alten Fokuszustand.
     Wer hier eine Palette neu aufmacht, muss sie VOLLSTAENDIG aufmachen. */
  --up-focus: #4A9EFF; --up-focus-ring: rgba(74,158,255,0.20); --up-focus-w: 2px;
  width: 100%; max-width: 420px; max-height: calc(100vh - 48px); overflow-y: auto;
  background: var(--up-surface); border: 1px solid var(--vc-border);
  border-radius: 18px;
  /* Ohne Rueckfall (28.09.): die Tiefen stehen jetzt an :root, das Token loest auch hier am body
     auf. Bis dahin galt hier der Rueckfall (0 20px 48px .22) statt e-4, dunkel wie hell. */
  box-shadow: var(--up-e-4);
  color: var(--vc-text); font-family: Geist, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  opacity: 0; transform: translateY(-4px) scale(.985); pointer-events: none;
  transition: opacity 140ms ease, transform 140ms ease;
  /* One flex column, one gap value (32px) between every top-level block (header, each field,
     footer) — padding carries the OUTER 16px on all four sides, gap carries everything between. */
  position: relative;
  padding: 16px;
  display: flex; flex-direction: column; gap: 32px;
}
.up-topicmodal-backdrop.is-shown .up-topicmodal-card { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.up-topicmodal-backdrop[data-theme="dark"] .up-topicmodal-card {
  --vc-text: #e0e0e0; --vc-muted: #c9cbd0; --vc-third: #a0a0a0; --vc-fourth: #6c717a; --vc-border: #3e3e44;
  --vc-heading-bg: #232326; --vc-hover-border: #707070; --up-surface: #1c1c1f; --vc-sk: #28282c; --up-switch-off: #444444;
  --up-focus: #6DB2FF; --up-focus-ring: rgba(109,178,255,0.28);
}
@media (prefers-reduced-motion: reduce){ .up-topicmodal-backdrop, .up-topicmodal-card { transition-duration: 1ms; } }
.up-topicmodal-card * { box-sizing: border-box; }

.up-topicmodal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.up-topicmodal-heading { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.up-topicmodal-title { font-size: 22px; font-weight: 500; margin: 0; line-height: 1.25; }
/* Optional second line under the title — no current consumer uses one, but the token exists so a
   future one doesn't have to invent its own size/color/weight. */
.up-topicmodal-sub { font-size: 16px; font-weight: 400; color: var(--vc-third); margin: 0; line-height: 1.3; }
/* 32x32 hit target, pulled -8px top/right via margin so it visually sits close to the card's true
   corner despite the card's own 16px padding — a plain 16px inset read as floating, not anchored. */
/* .up-popup-close ist der allgemeine Name: JEDES Popup/Modal nimmt diesen Knopf, statt sich einen
   eigenen zu bauen. Wichtig daran ist, was NICHT drinsteht -- kein Rahmen, auch nicht im Hover.
   Ein Rahmen, der erst beim Ueberfahren erscheint, laesst den Knopf springen und ist im ganzen
   Rest der App nirgends so; der Hover ist ueberall nur Flaeche + Primaerfarbe (siehe .up-iconbtn).
   .up-topicmodal-close bleibt als Zweitname bestehen, damit bestehendes Markup weiterlaeuft. */
.up-popup-close, .up-topicmodal-close { flex: 0 0 auto; width: 32px; height: 32px; margin: -8px -8px 0 0; border: 0; border-radius: 9px;
  background: transparent; color: var(--vc-muted); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .14s ease, color .14s ease; }
.up-popup-close svg, .up-topicmodal-close svg { width: 16px; height: 16px; display: block; }
.up-popup-close:hover, .up-topicmodal-close:hover { background: var(--vc-heading-bg); color: var(--vc-text); }

.up-topicmodal-body { display: flex; flex-direction: column; gap: 32px; }
.up-topicmodal-field { display: flex; flex-direction: column; gap: 8px; }
/* Die Fehlerzeile sitzt im Feld und darf im Ruhezustand keinen Platz belegen: der gap von 8px
   kaeme sonst immer dazu, und das padding-top 5px des Satzes bleibt auch in der zugeklappten Zeile
   stehen (gemessen: Feld 65 statt 60 hoch). Also den gap aufheben und die 5px erst geben, wenn
   die Zeile sichtbar ist -- dann steht der Satz 5px unter dem Feld wie ueberall in der App. */
.up-topicmodal-field > .up-topicmodal-err { margin-top: -8px; }
.up-topicmodal-field > .up-topicmodal-err > span { padding-top: 0; }
.up-topicmodal-field > .up-topicmodal-err.is-on > span { padding-top: 5px; }
/* Each field's own heading: primary color + weight, not muted — these are content labels a user
   reads every time the modal opens, not secondary chrome. */
/* .up-topicmodal-label steht jetzt OBEN in der Sammelregel bei .up-label, nicht mehr hier.
   Der Grund ist eine gemessene Panne: als die Etikettfarbe von --vc-muted auf --vc-third ging,
   hat diese eine Regel es nicht mitbekommen -- fuenf Etiketten standen auf rgb(158,158,158),
   die sechste auf rgb(107,111,120). Eine zweite Deklaration derselben Rolle ist genau die
   Sorte Abweichung, gegen die der ganze Umbau laeuft.
   Sechs Verwender in drei Dialogen: Name, Appearance, Topics, Group name, Prompt, Topics. */
.up-topicmodal-name { height: 36px; padding: 0 12px; border: 1px solid var(--vc-border); border-radius: 9px;
  background: transparent; color: var(--vc-text); font: inherit; font-size: 14px; outline: 0; width: 100%; }
.up-topicmodal-name:focus { border-color: var(--up-focus); box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); }

/* Appearance row: emoji + color trigger buttons. */
.up-topicmodal-approw { display: flex; align-items: center; gap: 8px; }
.up-topicmodal-pickbtn { flex: 0 0 auto; width: 36px; height: 36px; padding: 0; border-radius: 9px;
  border: 1px solid var(--vc-border); background: transparent; cursor: pointer; font-size: 17px; line-height: 1;
  color: var(--vc-muted); display: inline-flex; align-items: center; justify-content: center; }
.up-topicmodal-pickbtn svg { width: 17px; height: 17px; display: block; }
.up-topicmodal-pickbtn:hover, .up-topicmodal-pickbtn.is-open { border-color: var(--vc-hover-border); background: var(--vc-heading-bg); }
.up-topicmodal-pickswatch { width: 17px; height: 17px; border-radius: 50%; display: block; border: 1px solid rgba(0,0,0,.14); }

.up-topicmodal-pickpanel { margin-top: 10px; }
.up-topicmodal-colorgrid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 6px; }
.up-topicmodal-colorcell { width: 100%; aspect-ratio: 1 / 1; padding: 0; border: 1px solid transparent; border-radius: 8px;
  background: transparent; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  transition: background 120ms ease, border-color 120ms ease; }
.up-topicmodal-colorcell:hover { background: var(--vc-heading-bg); border-color: var(--vc-hover-border); }
.up-topicmodal-colorblob { width: 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; color: #fff; }
.up-topicmodal-colorblob svg { width: 12px; height: 12px; display: block; stroke: currentColor; stroke-width: 4; }
/* Every color-swatch checkmark in the app (this modal, and prompts-table's two local pickers)
   uses one fixed ink instead of a per-swatch contrast-solved color: white in light mode, the
   theme's own primary text color in dark -- simpler, and one less thing that can drift out of
   sync between three separate render sites. */
.up-topicmodal-backdrop[data-theme="dark"] .up-topicmodal-colorblob { color: var(--vc-text); }
.up-topicmodal-emojipicker {
  width: 100%; height: 268px;
  --num-columns: 8; --emoji-size: 1.25rem; --emoji-padding: 0.35rem;
  --border-radius: 10px; --border-color: var(--vc-border); --background: transparent;
  --input-border-radius: 9px; --input-border-color: var(--vc-border); --input-font-size: 12.5px;
  --indicator-color: var(--vc-text); --outline-color: var(--vc-hover-border);
}

.up-topicmodal-foot { display: flex; align-items: center; gap: 10px; }
.up-topicmodal-delete { height: 32px; padding: 0 14px; border-radius: 9px; border: 1px solid var(--vc-border);
  background: transparent; color: var(--vc-muted); font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
/* --vt-down MIT Rueckfall, und der Rueckfall ist kein Schoenheitsfehler: der Schleier dieses
   Dialogs wird an vier Stellen gebaut und nur an zwei davon mit .up-root (preferences.js und
   ask-mira.js ja, prompts-table.js und core.js nein). Ohne .up-root ist die Marke unaufloesbar,
   und eine color-Deklaration mit unaufloesbarem var() ist als Ganzes ungueltig -- der Knopf
   verlaere dann seine Warnfarbe ganz. So bekommt er im Dunkeln das helle Rot #d25d5d, wo die
   Marke ankommt, und sonst weiter das alte #b0200c. */
.up-topicmodal-delete:hover { border-color: var(--vt-down, #b0200c); color: var(--vt-down, #b0200c); }
.up-topicmodal-delete.is-armed { border-color: var(--vt-down, #b0200c); background: var(--vt-down, #b0200c); color: #fff; }
.up-topicmodal-delete.is-armed:hover { opacity: .9; }
/* Nur noch die Lage -- Aussehen kommt aus .up-btn-pri. */
.up-topicmodal-save { margin-left: auto; }

/* ---- Das allgemeine Popup (UC.makeModal, 02.10.) -------------------------------------------
   Nur, was die Huelle oben nicht schon hat. is-breit: der Anlegen-Dialog der Events hat drei
   Schritte mit Listen -- 420 waere dort zu eng; 560 ist die Breite des Plan-Dialogs. Der Fuss
   stellt die Knoepfe rechts; ein Gefahr-Knopf (Loeschen) traegt die Warnfarbe wie
   .up-topicmodal-delete. Der Kreisel erscheint nur im Knopf, der gerade arbeitet. */
.up-modal.is-breit { max-width: 560px; }
.up-modal-foot { justify-content: flex-end; }
.up-modal-foot > button { display: inline-flex; align-items: center; gap: 6px; }
/* Ein Knopf, den ein Schritt nicht braucht (Zurueck auf Schritt 1): das hidden-Attribut allein
   verliert gegen display: inline-flex darueber. */
.up-modal-foot > button[hidden] { display: none; }
/* Der Gefahr-Knopf ist gefuellt rot mit weisser Schrift (10.10. angefordert, vorher umrandet und
   erst beim Ueberfahren rot). Mit .up-modal-foot davor: er muss .up-btn-sec samt :hover schlagen,
   und das entscheidet die Spezifitaet, nicht die Reihenfolge. Ueberfahren dunkelt ein wenig ab. */
.up-modal-foot > .up-modal-gefahr { background: var(--vc-danger); border-color: var(--vc-danger); color: var(--vc-danger-fg); }
.up-modal-foot > .up-modal-gefahr:hover { background: color-mix(in srgb, var(--vc-danger) 88%, #000); border-color: color-mix(in srgb, var(--vc-danger) 88%, #000); color: var(--vc-danger-fg); }
.up-modal-spin { display: none; width: 12px; height: 12px; border-radius: var(--up-r-voll);
  border: 2px solid currentColor; border-right-color: transparent; animation: upModalDreh 700ms linear infinite; }
.up-modal-foot > button.is-busy .up-modal-spin { display: inline-block; }
@keyframes upModalDreh { to { transform: rotate(360deg); } }
.up-modal-err:not(.is-on) { display: none; }
/* Felder im Popup, in der Form des Namensfeldes (.up-topicmodal-name): derselbe Rahmen, Radius 9,
   Polster 12, Schrift 14 und derselbe Fokusring -- die Klasse bleibt am Element, hier steht nur,
   was ein mehrzeiliges Feld und ein Auswahlknopf anders brauchen. */
.up-topicmodal-name.up-modal-text { display: block; height: auto; min-height: 76px; padding: 9px 12px; line-height: 1.45; resize: vertical; }
.up-topicmodal-name.up-modal-wahl { display: flex; align-items: center; gap: 8px; text-align: left; cursor: pointer; }
.up-modal-wahl:hover { border-color: var(--vc-hover-border); }
.up-modal-wahl-ic, .up-modal-wahl-chev { display: inline-flex; flex: 0 0 auto; color: var(--vc-third); }
.up-modal-wahl-ic svg { width: 16px; height: 16px; }
.up-modal-wahl-chev svg { width: 14px; height: 14px; }
.up-modal-wahl-txt { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-modal-wahl-txt.is-leer { color: var(--vc-fourth); }
/* Ein erklaerender Satz unter einem Etikett oder am Fuss eines Schritts: leiser als der Inhalt,
   lauter als nichts. */
.up-modal-hinweis { margin: 0; font-size: var(--up-fs-m); line-height: 1.45; color: var(--vc-third); }
/* Saetze, die zusammengehoeren (Loeschdialoge von Prompts und Brands, 11.10. nach core): 8px
   zwischen ihnen statt der 32px, die die Dialogflaeche zwischen ihre Teile legt. */
.up-modal-saetze { display: flex; flex-direction: column; gap: 8px; }

/* ---- Die Trefferzeile der Entitaets-Suche (UC.entityRow mit dem Praefix "up-es", 02.10.) -----
   entityRow traegt "up-es" seit jeher als Vorgabe, aber Regeln dazu gab es keine: Ask Mira malt
   mit .am-pick-*, die Palette mit .mqa-* (beide mit eigenen Marken, beide bleiben). Hier dieselben
   Werte wie .am-pick-row ff. in ask-mira.css, Wert fuer Wert, nur aus den Marken der App statt
   aus --am-*: --am-text = --vc-text, --am-muted = --vc-muted, --am-muted-2 = --vc-third,
   --am-border = --vc-border, --am-soft = --vc-heading-bg. Wer eine URL sucht, soll dieselbe
   Zeile sehen wie in Mira (Spezifikation der Events, 57). */
.up-es-row { display: flex; align-items: center; gap: 11px; width: 100%; padding: 8px 10px;
  border: 0; border-radius: var(--up-r-l); background: transparent; cursor: pointer;
  font-family: inherit; text-align: left; transition: background var(--up-t-2) ease; }
.up-es-row:hover, .up-es-row.is-active { background: var(--up-sel-bg, var(--vc-heading-bg)); }
.up-es-row.is-taken { opacity: 0.45; pointer-events: none; }
.up-es-av { width: 30px; height: 30px; flex: 0 0 auto; border-radius: var(--up-r-m); overflow: hidden;
  border: 1px solid var(--vc-border); background: var(--vc-heading-bg); position: relative;
  display: flex; align-items: center; justify-content: center; }
.up-es-av img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.up-es-av.is-flag { width: 24px; height: 18px; border-radius: 3px; border: 0; }
.up-es-av-fb { display: none; align-items: center; justify-content: center; width: 100%; height: 100%; color: var(--vc-muted); }
.up-es-av.is-fb .up-es-av-fb { display: flex; }
/* > svg dazu: ohne Bild legt entityAvatar den Rueckfall DIREKT in den Kasten, nicht in -av-fb
   (Treffer ohne Favicon, das "+" fuer ein neues Ziel) -- sonst stand dort ein 24er-Zeichen. */
.up-es-av-fb svg, .up-es-av > svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.up-es-av.is-fb { color: var(--vc-muted); }
.up-es-av-t { font-size: var(--up-fs-xs); font-weight: 600; color: var(--vc-muted); }
.up-es-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.up-es-primary { font-size: var(--up-fs-l); font-weight: 500; color: var(--vc-text); letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-es-secondary { font-size: var(--up-fs-s); color: var(--vc-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-es-type { flex: 0 0 auto; font-size: var(--up-fs-xs); font-weight: 500; color: var(--vc-third); letter-spacing: 0.01em; }
.up-es-hl { background: transparent; color: inherit; font-weight: 700; }

/* ---- Die Kennzahl-Kachel (UC.kpiKarte, 02.10.) ------------------------------------------------
   Masse von .upw-kpi (power-dashboard), die Zahl auf der Skalenstufe 22 statt 21. */
.up-kpi { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.up-kpi-label { font-size: var(--up-fs-m); font-weight: var(--up-fw-m); color: var(--vc-muted); }
.up-kpi-row { display: flex; align-items: baseline; flex-wrap: wrap; column-gap: 8px; row-gap: 2px; min-height: var(--up-h-seg); }
.up-kpi-val { font-size: var(--up-fs-2xl); font-weight: var(--up-fw-m); line-height: 1.15; letter-spacing: -0.4px;
  font-variant-numeric: tabular-nums; color: var(--vc-text); }
.up-kpi-vorher { font-size: var(--up-fs-xl); font-weight: var(--up-fw-m); color: var(--vc-third); font-variant-numeric: tabular-nums; }
.up-kpi-pfeil { display: inline-flex; color: var(--vc-fourth); align-self: center; }
.up-kpi-pfeil svg { width: 14px; height: 14px; }
.up-kpi-trend { display: inline-flex; align-items: center; align-self: center; }
.up-kpi-trend .up-trend { font-size: var(--up-fs-l); }
/* Fusszeile wie in der Overview des Agentic Dashboards (.upw-kpi-foot): 11px in der dritten
   Farbe, 10px unter der Zahl (5 gap + 5 hier). */
.up-kpi-foot { font-size: var(--up-fs-xs); color: var(--vc-third); margin-top: 5px; }
/* DAS KENNZAHLEN-BAND (04.10.): mehrere UC.kpiKarte in EINEM Kasten (.up-box am Behaelter),
   getrennt durch senkrechte Linien ueber die volle Hoehe -- die Bauart der Kacheln in
   performance-detail (.upd-kpi + .upd-kpi): border-left an jeder ausser der ersten, so bleibt die
   Aussenkante die des Kastens. Kein gap-Trick: der verschwindet bei Bruchteilbreiten.
   Zuerst im Event-Detail (.uev-kpis), dann viermal in Shopping -- darum hier. Spalten ueber
   --up-kpiband-n (Vorgabe 3). is-4: im schmalen Fenster zwei mal zwei, die Linien wandern mit;
   ganz schmal alle untereinander, der Trenner liegt dann oben. */
.up-kpiband { display: grid; grid-template-columns: repeat(var(--up-kpiband-n, 3), minmax(0, 1fr)); }
.up-kpiband.is-4 { --up-kpiband-n: 4; }
.up-kpiband > .up-kpi { padding: 16px; }
.up-kpiband > .up-kpi + .up-kpi { border-left: 1px solid var(--vc-border); }
.up-root.is-narrow .up-kpiband.is-4 { --up-kpiband-n: 2; }
.up-root.is-narrow .up-kpiband.is-4 > .up-kpi:nth-child(odd) { border-left: 0; }
.up-root.is-narrow .up-kpiband.is-4 > .up-kpi:nth-child(n+3) { border-top: 1px solid var(--vc-border); }
.up-root.is-vnarrow .up-kpiband, .up-root.is-vnarrow .up-kpiband.is-4 { --up-kpiband-n: 1; }
.up-root.is-vnarrow .up-kpiband > .up-kpi + .up-kpi { border-left: 0; border-top: 1px solid var(--vc-border); }
/* DIE OFFENE FASSUNG (07.10. angefordert fuer Shopping und Ads: "die Aussenborder weg, nur die
   vertikalen Separatoren"): das Band ohne .up-box -- kein Kasten, kein Grund, die Kennzahlen stehen
   auf der Seite und sind nur durch die senkrechten Linien getrennt. Ohne Kasten gibt es keine Kante,
   von der die erste Zahl Abstand halten muesste: die erste Kachel einer Zeile hat links kein
   Polster, die letzte rechts keins -- so steht die Beschriftung buendig mit den Ueberschriften
   darunter. Oben und unten 4px statt 16, damit die Linien etwas ueber die Schrift hinausreichen,
   ohne dass ein leerer Streifen ueber der Leiste steht. Die Linien selbst kommen aus den Regeln
   darueber, auch im schmalen 2 x 2 und untereinander. */
.up-kpiband.is-offen > .up-kpi { padding: 4px 16px; }
.up-kpiband.is-offen > .up-kpi:first-child { padding-left: 0; }
.up-kpiband.is-offen > .up-kpi:last-child { padding-right: 0; }
.up-root.is-narrow .up-kpiband.is-offen.is-4 > .up-kpi:nth-child(odd) { padding-left: 0; }
.up-root.is-narrow .up-kpiband.is-offen.is-4 > .up-kpi:nth-child(even) { padding-right: 0; }
/* Zwei Zeilen: die zweite haelt Abstand zur Linie zwischen ihnen. */
.up-root.is-narrow .up-kpiband.is-offen.is-4 > .up-kpi:nth-child(n+3) { padding-top: 12px; margin-top: 8px; }
.up-root.is-vnarrow .up-kpiband.is-offen > .up-kpi { padding-left: 0; padding-right: 0; }
.up-root.is-vnarrow .up-kpiband.is-offen > .up-kpi + .up-kpi { padding-top: 12px; margin-top: 8px; }
/* ---------- Beispielplatten der Variations-Erklaerkarten (UC.variationsPlatte) ----------
   13.10. aus performance-detail.css hierher: brand-detail braucht dieselben. Jede Regel traegt
   auch den alten Namen (.upd-explain-vis/-sov), den performance-detail weiter mitgibt -- so bleibt
   der Vertrag ohne Entfernung, und die Deklarationen stehen trotzdem nur einmal da.
   Die Karte wird von UC.makeExplain an den body gehaengt, also ausserhalb jeder .up-root. Dort
   loest KEIN --vc-Token auf: der Ring waere ohne Strich, das "of N" ohne Farbe. Beides bekommt
   hier feste Werte, und zwar zweimal -- die Karte ist invertiert, im hellen Theme ist die Platte
   dunkel und umgekehrt.

   Die Platte soll aussehen wie die Zeile darunter, nur eine Spur groesser: 13px statt der 12px,
   die die geteilte Regel setzt. Nur an .up-varvis, damit die Erklaerkarten der anderen Komponenten
   unveraendert bleiben. */
/* gap: 6px: derselbe Abstand zwischen Zahl und Nebentext wie in der Zelle, damit die Probe die
   Zelle trifft. */
.up-varvis .up-explain-row, .upd-explain-vis .up-explain-row { font-size: 13px; gap: 6px; }
/* Zahl und Nebentext exakt wie in der Zelle darunter: 14px / 12px. Nur die Farben sind gedreht,
   weil die Platte invertiert ist -- die Groessen sollen sich nicht unterscheiden, sonst ist es kein
   Beispiel mehr. */
.up-varvis .up-num, .upd-explain-vis .up-num { font-size: 14px; font-weight: 500; color: inherit; }
.up-varvis .up-var-of, .upd-explain-vis .up-var-of { font-size: 12px; color: rgba(31,31,27,0.55); }
/* Die Share-of-Voice-Zeile steht weiter auseinander als die Mention-Count-Zeile: dort sind Zahl
   und "of N" ein zusammenhaengender Wert, hier sind es zwei Darstellungen derselben Groesse und
   duerfen als zwei Dinge lesbar sein. */
.up-varvis .up-varvis-sov, .upd-explain-vis .upd-explain-sov { gap: 32px; }
/* Der Ring behaelt seine echte Groesse. Die geteilte Regel .up-explain-row svg setzt 14px und ist
   genauso stark wie .up-varring svg -- ohne diese Zeile entschiede die Ladereihenfolge. */
.up-varvis .up-varring svg, .upd-explain-vis .up-varring svg { width: 22.4px; height: 22.4px; }
.up-varvis .up-varring-track, .upd-explain-vis .up-varring-track { stroke: #e4e2e4; }
.up-varvis .up-varring-fill, .upd-explain-vis .up-varring-fill { stroke: #1f1f1b; }
/* Dark: die Platte ist dunkel, also drehen sich Ring und Nebentext mit. */
.up-explain[data-theme="dark"] .up-varvis .up-var-of, .up-explain[data-theme="dark"] .upd-explain-vis .up-var-of { color: rgba(240,240,240,0.55); }
.up-explain[data-theme="dark"] .up-varvis .up-varring-track, .up-explain[data-theme="dark"] .upd-explain-vis .up-varring-track { stroke: #28282c; }
.up-explain[data-theme="dark"] .up-varvis .up-varring-fill, .up-explain[data-theme="dark"] .upd-explain-vis .up-varring-fill { stroke: #f0f0f0; }

/* DIE PROTOKOLL-TABELLE (13.10. aus team-orga.css hierher; Team-Log und Marken-Log): eine
   .up-table mit eigenen --up-cols je Protokoll. Kleiner, dichter, sachlich. 34px Zeile gegen 52 in den Tabellen darueber, 12px Schrift gegen
   13.5, und ueber 14 Zeilen ein eigener Ausschnitt statt einer Seite, die ins Endlose waechst. */
/* Doppelte Klasse: die Tabelle einer Komponente setzt ihre eigenen Zeilen (.uto-table .up-row:not(...),
   .uto-table .up-td) mit derselben Staerke -- ohne die zweite Klasse entschiede die Ladereihenfolge. */
.up-logtab.up-logtab .up-thead.up-row { height: 36px; flex: 0 0 36px; }
.up-logtab.up-logtab .up-row:not(.up-thead) { height: 34px; flex: 0 0 34px; }
.up-logtab.up-logtab .up-th, .up-logtab.up-logtab .up-td { font-size: var(--up-fs-s); }
/* Ab 14 Zeilen ein eigener Ausschnitt statt einer Seite, die ins Endlose waechst. 476 = 36 Kopf
   + 13 x 34 -- also genau vierzehn Zeilen, danach scrollt es.
   Hier darf overflow stehen, im Gegensatz zum Kasten oben: im Protokoll gibt es kein Zeilenmenue,
   das herausfahren muesste. overflow-x bleibt hidden -- waagerecht wird nichts geschoben, dafuer
   fallen auf schmalen Seiten zwei Spalten weg (unten). */
.up-logtab.up-logtab { max-height: 476px; overflow-x: hidden; overflow-y: auto; }
/* Zeit und Ereignisname in Festbreitenschrift: in einem Protokoll liest man Spalten, nicht Saetze.
   Die Familie ist die des Systems -- diese App liefert keine eigene Festbreitenschrift aus, und
   eine nachgeladene waere ein Netzabruf fuer fuenf Zellen. */
/* 11 (--up-fs-xs) statt der 11.5 aus team-orga: auf die Skala gezogen, als der Baustein nach core kam. */
.up-log-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--up-fs-xs); letter-spacing: -0.01em; color: var(--vc-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-log-ev { color: var(--vc-text); }
/* Die drei Ereignisarten bekommen einen Punkt davor, damit man das Protokoll ueberfliegen kann:
   eingeladen, angenommen, zurueckgezogen. Die Toene sind die vorhandenen der App: --vt-up fuer
   angenommen, --vt-brand fuer eingeladen, die dritte Farbe fuer zurueckgezogen. Kein Rot: eine
   zurueckgezogene Einladung ist kein Fehler, sondern eine Entscheidung. */
.up-log-evwrap { display: flex; align-items: center; gap: 7px; min-width: 0; }
.up-log-dot { flex: 0 0 6px; width: 6px; height: 6px; border-radius: 999px; background: var(--vc-third); }
.up-log-dot.is-ok { background: var(--vt-up); }
.up-log-dot.is-off { background: var(--vc-third); }
.up-log-dot.is-new { background: var(--vt-brand, var(--vc-muted)); }
.up-log-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--vc-muted); }

/* DIE SPARKLINE (UC.sparkHtml, 04.10.): 72 x 24 neben dem Wert einer Kachel, Linie 1.5 in der
   Akzent-Tinte, der letzte Punkt als Marke. Keine Achse, keine Flaeche -- sie sagt nur die
   Richtung, die Zahl daneben sagt den Wert. */
.up-spark { display: block; width: 72px; height: 24px; overflow: visible; flex: 0 0 auto; }
.up-spark-linie { fill: none; stroke: var(--up-accent-ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.up-spark-punkt { fill: var(--up-accent-ink); stroke: none; }
.up-kpi-foot strong { font-weight: var(--up-fw-m); color: var(--vc-muted); }
/* DIE WERTE IN DER KACHEL SIND DIE DER TABELLENZELLEN (03.10.): .up-num, UC.sentHtml (Balken +
   Zahl) und .up-rank-group mit Raute -- nur groesser gestellt, wie in power-dashboard (.upw-kpi-val)
   und brand-detail (.ubd-kpi-val). Ohne diese Zeilen stand die Zahl in ihrer Zellgroesse (14px) in
   einer 22px-Kachel, und Rang und Sentiment sahen hier anders aus als ueberall sonst.
   Sentiment-Balken 4x16 wie in power-dashboard. Die Raute bleibt klein und in der dritten Farbe
   (03.10.: mit 20px "zu gross und zu dunkel" -- im Visibility-Chart steht sie 12px neben 14px
   Zahlen): 14px neben der 22px-Zahl, wie performance-detail sie neben 18px haelt (12). Der
   Vorher-Wert davor eine Stufe kleiner und zurueckgenommen: Raute in Zellgroesse 12. */
.up-kpi-val .up-num, .up-kpi-val .up-sent-val { font-size: inherit; font-weight: inherit; letter-spacing: inherit; }
.up-kpi-val .up-sent { gap: 8px; }
.up-kpi-val .up-sent-dot { width: 4px; height: 16px; }
.up-kpi-val .up-hash { width: 14px; height: 14px; }
.up-kpi-vorher .up-num, .up-kpi-vorher .up-sent-val { font-size: inherit; font-weight: inherit; color: inherit; }
.up-kpi-vorher .up-hash { width: 12px; height: 12px; }
/* Die Fusszeile traegt seit dem 03.10. einen Trend-Chip aus core ("vs. comparison" + Chip) statt
   fett gesetzten Texts -- dieselbe Einheit wie der Trend neben der Zahl. Chip in Fussgroesse:
   Schrift wie der Fuss, Pfeil 14 statt 16 (Faktor 11/13, gerundet). */
.up-kpi-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 6px; }
.up-kpi-foot .up-trend { font-size: inherit; }
.up-kpi-foot .up-trend svg { width: 14px; height: 14px; }
.up-kpi.is-sk { gap: 10px; }
.up-kpi-sk { display: block; height: 12px; border-radius: var(--up-r-xs); background: var(--vc-sk); }
.up-kpi-sk-l { width: 40%; } .up-kpi-sk-v { width: 70%; height: 24px; } .up-kpi-sk-f { width: 55%; }

/* ======================================================================
   Page Header Kit — shared by every page-headers/* component (first user:
   prompts-page-header.js). One dedicated component per page (see that
   file's own header comment for why), but they all share this exact
   meta/heading/description block plus the sliding-tab subpage nav below
   it, so both live here instead of being copy-pasted into each page's own
   CSS/JS. A future page header only needs its own PAGES array, heading/
   description text and event names -- everything visual is this kit.
   Markup shape a page header is expected to provide:
     <div class="up-root up-ph-root">
       <div class="up-ph-top">
         <div class="up-ph-left">
           <div class="up-ph-meta"><img class="up-ph-metalogo"/><span class="up-ph-metatxt">...</span></div>
           <h1 class="up-ph-heading">...</h1>
           <p class="up-ph-desc">...</p>
         </div>
         <button class="up-ph-addbtn up-export">...</button>
       </div>
       <div class="up-ph-nav" role="tablist"></div>  <!-- built by UC.makePageNav, see core.js -->
     </div>
   ====================================================================== */
/* Top padding is 16px, not 32: the meta row sits close to the page's own top edge. Everything that
   sits top-RIGHT (Add button, refresh/docs icons, KPI strips) used to carry a -16px margin to pull
   itself back up against the old 32px padding; with 16px there is nothing left to pull back, so
   those margins are 0 now and only the is-vnarrow variants below still offset. */
.up-ph-root { padding: 16px 0 16px; background: transparent; }
/* Die Bubble-Gruppe um den Seitenkopf (core.js kopfPolsterDurchlassen, 29.09.): ihre eigene leere
   Flaeche -- das untere Polster von 16px und die Luecken -- faengt keine Klicks mehr, sie gehen an
   das, was darunter liegt (die Toolbar der Topics-Verwaltung). Die direkten Kinder holen sich die
   Klicks zurueck, alles darin bleibt bedienbar. Nur pointer-events, keine Stapelung. */
.up-ph-gruppe { pointer-events: none; }
.up-ph-gruppe > * { pointer-events: auto; }

.up-ph-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.up-ph-left { display: flex; flex-direction: column; min-width: 0; }

/* margin-bottom 8px (not the 12px heading->desc gets) -- the meta line reads as a label sitting
   directly above the heading, not a peer paragraph, so it stays visually closer to it than the
   heading is to the description below. */
.up-ph-meta { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
/* ── Keine Meta-Zeile mehr in den SEITENKOEPFEN ──────────────────────────────────────────────
   Sie sagt nichts, was die Ueberschrift nicht schon sagt: in welchem Arbeitsbereich man steht,
   nennt die Seitenleiste. Erst am Dashboard entfernt, jetzt fuer alle sieben.
   display: none und nicht ausbauen -- der Einwand von damals ("ein LEERER Kasten hinterlaesst
   seine 8px") gilt fuer einen Kasten, der noch da ist. Ohne Kasten gibt es auch keinen
   Aussenabstand.
   Hier stand .up-ph-root als Haken, mit der Begruendung, die Meta-Zeile von teams.js sei
   ausdruecklich gewuenscht. Das war ein Missverstaendnis: gewuenscht war die Zeile UEBERALL weg.
   Und der Haken hat genau das verhindert -- prompt-research und teams bauen ihren Kopf selbst und
   tragen .up-ph-root nicht, also stand die Zeile dort weiter. Gemeldet am 01.09. fuer genau diese
   zwei Seiten. Jetzt ohne Haken. */
.up-ph-meta { display: none; }
/* grayscale(1): a brand's own logo colour would otherwise compete with this being a quiet,
   secondary line above the real heading. */
/* Tuned so pure BLACK lands on --vc-third (#808080, L=128) -- the colour of the label beside it.
   grayscale(1) alone cannot dim black at all: black is already fully desaturated, so it came
   through at full strength next to nicely muted colourful ones. contrast() below 1 pulls every
   luminance toward mid-grey, which is what lifts the black end; brightness() places the result.

   opacity() would hit the same number and leave colourful logos alone -- but it blends toward the
   BACKGROUND. Measured on #121212, a black logo drops back to L=9 with it: invisible in dark
   mode, which is the second half of the same bug. contrast/brightness works on the pixels, so the
   same value comes out on either surface. Measured through the filter engine itself
   (canvas ctx.filter), against white AND #121212:
     input        grayscale(1)   this
     black             0         127   <- the target
     #222             34         141
     brand blue      105         171
     brand red        83         163
     white           255         235

   The cost, named rather than hidden: no filter can tell a black logo from a blue one, so lifting
   the black end lifts everything with it -- colourful logos come out paler than they were. Black
   at the label's colour was the requirement; this is what it costs. */
.up-ph-metalogo {
  width: 15px; height: 15px; display: block; flex: 0 0 auto; object-fit: cover;
  /* 4px, damit das Logo nicht als scharfkantiges Quadrat neben dem runden Rest der Zeile sitzt.
     Deckt auch den eingebauten Seitenkopf in prompt-research ab -- der nutzt dieselbe Klasse. */
  border-radius: 4px;
  filter: grayscale(1) contrast(0.30) brightness(1.43);
}
.up-ph-metatxt { font-size: 12px; font-weight: 500; color: var(--vc-third); line-height: 1; }

.up-ph-heading { margin: 0 0 12px; font-size: 28px; font-weight: 500; color: var(--vc-text);
  letter-spacing: -0.5px; line-height: 1.2; }
/* 15px puts the description at ~54% of the 28px heading -- inside the ~50-57% ratio typical for a
   subtitle under an H1; 17px (61%) read a shade too close to the heading's own weight. */
/* 16/450 statt 15/400 -- erst am Dashboard erprobt, jetzt fuer JEDEN Seitenkopf. Die Zeile ist
   die einzige Erklaerung, die eine Seite ueber sich abgibt; bei 15/400 stand sie als Kleingedrucktes
   unter der Ueberschrift. Der Wert stand vorher als Sonderfall in dashboard-page-header.css und ist
   dort raus: ein Bauteil, zwei Groessen waere die naechste Meldung. */
.up-ph-desc { margin: 0; font-size: 16px; font-weight: 450; color: var(--vc-muted); line-height: 1.4; }

.up-ph-addbtn { flex: 0 0 auto; margin-top: 0; }
/* Belegt, solange der Lauf dauert, den der Knopf angestossen hat (30.09., zuerst "Look for new
   Opportunities"): gedimmt und ohne Zeiger, damit ein zweiter Klick keinen zweiten Lauf startet.
   Was gerade passiert, zeigt das Ladebild auf der Seite (UC.makeLadebild) -- der Knopf sagt nur,
   dass er gerade nichts annimmt. */
.up-ph-addbtn.is-busy { opacity: .55; pointer-events: none; cursor: default; }

/* 32x32, icon-only, primary color at rest (var(--vc-text)) with a hover background -- deliberately
   NOT .up-iconbtn (that one is muted-by-default, primary-on-hover) or .up-export (a filled button):
   this is its own third state a page header can reuse wherever it needs an icon-only utility
   action next to its Add/tool buttons. First used by dashboard-page-header.js's Refresh button,
   promoted here once prompts-page-header.js needed the identical one. */
/* Der Zeichenknopf oben rechts in JEDEM Seitenkopf (Refresh, Docs, Suche) -- und ab jetzt
   derselbe Knopf wie die Ausgaenge oben rechts im Onboarding-Kopf (.uob-link). Die Geometrie war
   dort schon woertlich diese: 32x32, Radius 8, kein Rahmen, durchsichtig, Polster 0. Verschieden
   waren nur drei Dinge, und sie sind der Grund fuer diese Aenderung:

     Farbe        --vc-text        ->  --vc-muted, und erst beim Ueberfahren --vc-text. Ein
                                      Ausgang oder ein Nachladen ist nicht die Aussage der Seite;
                                      die Ueberschrift daneben ist es.
     Zeichen      16               ->  15, dasselbe Mass wie dort
     Fokusring    fehlte           ->  wie ueberall sonst

   Die Hoverflaeche bleibt --vc-heading-bg (das Onboarding hat dafuer ein eigenes #f7f8fa): das
   ist der Ton, den jeder ruhige Zeichenknopf der App beim Ueberfahren zeigt -- .up-iconbtn und
   .up-quietbtn eingeschlossen. Zwei Hovertoene im selben Kopf waeren der schlechtere Tausch. */
/* .dph-docsbtn STEHT HIER MIT DRIN, und das ist Absicht (15.09.). Ein eingebautes Bubble-Element
   bekommt Aenderungen an bubble/*.html nicht mit -- die Vorlage ist ein Bauplan, kein Update. Der
   Docs-Knopf trug frueher ein Buch-SVG PLUS das Wort "Docs" und nicht die Klasse .up-ph-iconbtn;
   dashboard-page-header.js raeumt das beim Init auf, aber bis dahin steht dort ein nackter
   HTML-Button mit Rahmen und Beschriftung. Genau so gemeldet und im Bild belegt.
   Warum in core.css und nicht in dashboard-page-header.css: core.css laedt in der ERSTEN Welle
   des Vorlade-Schnipsels, die Seitenkopf-CSS in der zweiten. Eine Regel in der zweiten Welle
   kaeme zu spaet fuer genau die Millisekunden, um die es geht.
   Gruppiert und nicht kopiert: dieselben Werte, ein Selektor mehr -- sonst haetten wir zwei
   Zeichenknoepfe, die auseinanderlaufen koennen (CLAUDE.md 1).
   font-size: 0 gehoert nur hier hin: es loescht eine Beschriftung, die im neuen Markup gar nicht
   mehr vorkommt. Bei .up-ph-iconbtn waere es eine Regel gegen ein Problem, das es dort nicht gibt. */
.up-ph-iconbtn, .dph-docsbtn { display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 0; border-radius: 8px; background: transparent;
  color: var(--vc-muted); cursor: pointer; padding: 0; flex: 0 0 auto;
  transition: color 120ms ease, background-color 120ms ease; }
.dph-docsbtn { font-size: 0; line-height: 0; }
.up-ph-iconbtn:hover, .dph-docsbtn:hover { background: var(--vc-heading-bg); color: var(--vc-text); }
.up-ph-iconbtn:focus-visible, .dph-docsbtn:focus-visible { outline: 0; box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); }
.up-ph-iconbtn svg, .dph-docsbtn svg { width: 15px; height: 15px; display: block; }
/* One full turn, ease-in-out, on click -- triggered via UC.spinOnce(btn) (core.js), which
   toggles this class off/on with a forced reflow in between so a second click mid-spin restarts
   the animation instead of doing nothing (the class name alone wouldn't change, so a plain
   classList.add on an already-spinning button is a no-op without that reset). */
@keyframes up-ph-spin { to { transform: rotate(360deg); } }
/* Gedreht wird die Huelle (UC.spinOnce legt sie beim ersten Mal um das Zeichen): ein HTML-Element,
   dessen Transformation der Compositor uebernimmt -- am svg selbst nicht, dort ruckelte es, sobald
   die Seite beim Aktualisieren rechnete. > svg bleibt fuer Knoepfe ohne Huelle. */
.up-spinhuelle { display: block; line-height: 0; }
.up-ph-iconbtn.is-spinning > svg,
.up-ph-iconbtn.is-spinning > .up-spinhuelle { animation: up-ph-spin 1s ease-in-out; will-change: transform; }

/* ---------- subpage nav: N tabs + a sliding active indicator (built by UC.makePageNav) ---------- */
.up-ph-nav { display: flex; position: relative; margin-top: 32px; gap: 4px; }
/* The continuous 1px baseline every item's own indicator sits on top of -- ONE shared separator
   spanning the full nav width, not a copy under each item. Both this and .up-ph-navunderline
   anchor to the same bottom:0, so neither ever causes a layout shift switching between the 1px
   (unselected) and 2px (selected) look. */
.up-ph-nav::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--vc-border); }
/* Rechts 3px mehr als links (29.09. angefordert: erst 2 -- "dann sieht es symmetrischer aus mit
   den Icons" --, am Abend noch 1 dazu: "so dass der aktive Strich 1px rechts weiter geht"). Das
   Zeichen vorne hat in seinem 16er-Kasten eigene Luft, die Schrift hinten keine -- bei gleichem
   Polster stand der Strich darunter links sichtbar weiter ueber als rechts. */
.up-ph-navitem { display: flex; align-items: center; justify-content: center; gap: 6px;
  height: 32px; padding: 0 15px 0 12px; cursor: pointer; user-select: none; }
/* Die NICHT gewaehlten Reiter in der Drittfarbe, Zeichen und Schrift (29.09. angefordert, vorher
   --vc-muted). Damit steht die Reiterzeile im selben Ton wie die Brotkrumen darueber, deren
   Vorstufen auch --vc-third tragen; Hover hebt weiter auf --vc-text, gewaehlt bleibt Akzent. */
.up-ph-navicon { display: inline-flex; flex: 0 0 auto; color: var(--vc-third); transition: color 120ms ease; }
.up-ph-navicon svg { width: 16px; height: 16px; display: block; }
/* DAS MARKENLOGO ALS REITERZEICHEN (Settings: "Your Brand"). Seit dem 11.10. hier statt in
   settings-page-header.css: der alte Kopf UND die Seite Settings (settings-page.js) zeigen es, und
   zwei Kopien liefen auseinander. Groesse: .up-ph-navicon misst nur SVG, ein <img> ohne Mass kaeme
   in seiner natuerlichen Aufloesung. Der Selektor ohne Elternteil, wie er vorher in
   settings-page-header.css stand. Ohne grayscale -- das Logo ist das Erkennungszeichen des
   Reiters; nicht gewaehlt wird es ueber die Deckkraft leiser, weil color ein Bild nicht faerbt. */
:is(.sph-navlogo, .uss-navlogo) {
  width: 16px; height: 16px; display: block; flex: 0 0 auto; object-fit: cover; border-radius: var(--up-r-xs); }
.up-ph-navitem:not(.is-selected) :is(.sph-navlogo, .uss-navlogo) { opacity: .55; transition: opacity var(--up-t-1) ease; }
.up-ph-navitem:not(.is-selected):hover :is(.sph-navlogo, .uss-navlogo) { opacity: 1; }
.up-ph-navlabel { font-size: 14px; font-weight: 500; color: var(--vc-third); white-space: nowrap; transition: color 120ms ease; }
/* DER GEWAEHLTE REITER GANZ IN DER AKZENTFARBE (18.09. angefordert): Zeichen, Schrift und der
   Streifen darunter. Nur hier -- die Seitenleiste bleibt in ihrer Schriftfarbe, dort ist die
   Auswahl eine Flaeche und kein Strich.
   --up-accent-ink und nicht --up-accent: hier liegt die Farbe als SCHRIFT auf dem Seitengrund,
   nicht als Flaeche unter einer Schrift. Der Unterschied ist gemessen, siehe oben beim Token. */
.up-ph-navitem.is-selected .up-ph-navicon,
.up-ph-navitem.is-selected .up-ph-navlabel { color: var(--up-accent-ink); }
.up-ph-navitem:not(.is-selected):hover .up-ph-navicon,
.up-ph-navitem:not(.is-selected):hover .up-ph-navlabel { color: var(--vc-text); }
/* The active indicator: slides left/width to the newly selected tab on click (200ms ease, set in
   JS via inline style so it can animate to an arbitrary measured position/width -- a CSS-only
   nth-child approach can't do that for a JS-built, reorderable list). Position is set with
   transition:none on first paint (see UC.makePageNav) so it doesn't grow in from 0 on load.
   z-index:1 -- without it this paints BEHIND .up-ph-nav::after: a ::after's generated box is
   always painted after all of its element's real children, regardless of DOM/append order, so the
   grey 1px separator would silently cover the bottom half of this 2px indicator. */
.up-ph-navunderline { position: absolute; bottom: 0; left: 0; width: 0; height: 2px; background: var(--up-accent-ink);
  z-index: 1; transition: left 200ms ease, width 200ms ease; }
/* DER GEWAEHLTE REITER IN EINEM BEHAELTER (13.10. angefordert: "leichter Field-Background und
   Border, eher kleines Padding"; Rundung zweite Runde 8 statt 12). Er fuellt den Reiter in der
   Breite GENAU -- die Luft um Zeichen und Schrift ist das Polster des Reiters selbst. So ist der
   Akzentstrich darunter genau so lang wie der Behaelter, und der erste Behaelter beginnt an der
   Kante des Inhalts statt im 16px-Rand (beides 13.10. angefordert).
   Der Behaelter ist .up-ph-navpille (makePageNav): unter den Reitern, von links nach rechts so breit
   wie der gewaehlte Reiter -- dieselbe Fahrt wie der Strich (200ms ease) und wie die Pille der
   Umschalter (.is-gleitend, 200ms ease). Die Reiter liegen darueber (z-index 1). */
.up-ph-navitem { position: relative; z-index: 1; }
.up-ph-navpille { position: absolute; top: 0; bottom: 0; left: 0; width: 0; z-index: 0; pointer-events: none;
  background: var(--vc-heading-bg); border: 1px solid var(--vc-border); border-radius: var(--up-r-m);
  transition: left 200ms ease, width 200ms ease; }

/* ---------- DER KOMPAKTE SEITENKOPF MIT BROTKRUMEN (29.09. angefordert) ----------
   "Aktuell nehmen die mir zu viel Platz ein und sind groesstenteils nicht aligned mit dem Layout
   der Sidebar." Also: keine Beschreibung mehr, die Ueberschrift wird zu kleinen Brotkrumen
   (UC.makePageCrumbs), und die Kopfzeile bekommt eine Trennlinie auf GENAU der Hoehe der Linie in
   der Seitenleiste.
   Die Zahlen sind abgelesen, nicht gewaehlt: .usn-bar hat 8px Polster oben, .usn-brand ist 32px
   hoch mit 8px darunter, ihre Linie liegt also bei y 47 bis 48. Hier dasselbe: 8 oben, eine
   32px-Zeile (die Hoehe der Knoepfe rechts, --up-h-btn), 8 darunter, die Linie im letzten Pixel.
   Derselbe Abgleich stand schon am Power-Kopf des Dashboards und hat dort gepasst.
   ZUERST fuer vier Seiten (Dashboard, Prompt Insights, Citations, Brands), am 29.09. nachmittags
   die drei uebrigen eigenen Koepfe dazu (Performance, Opportunities, Settings). Die Liste im
   :is() bleibt trotzdem: .up-ph-root allein traefe auch die Vorfuehrkoepfe der Landingpage
   (landing-hero.js), die ihre eigenen Masse tragen. Teams und Prompt Research bauen ihren Kopf
   IN der Komponente -- fuer sie steht der Block weiter unten (.uts-pagehead, .upr-pagehead).
   Ueber die Wurzelklassen und nicht ueber .has-crumbs: die Geometrie soll stehen, BEVOR das JS
   laeuft, sonst springt der Kopf beim Laden von 164px auf 48. */
/* SEIT DEM 29.09. ABENDS RECHTS 8 STATT 16: "das Padding runter auf das, was bei den 32er-Knoepfen
   an Luft nach oben und unten zur Linie ist" -- 8, gemeint rechts (eine erste Fassung setzte
   links, zurueckgenommen). Links bleibt 16. Die Zahlen setzt UC.makePageCrumbs
   (KOPF_EINZUG_L / KOPF_EINZUG_R), die Rechnung darunter gilt unveraendert.
   DER INHALT DES SEITENKOPFS STEHT IMMER 16px VOM RAND -- nie mehr, nie weniger (29.09.).
   Drei Meldungen zu genau dieser Zahl: erst 32 (16 aus Bubble plus 16 hier, "16px zu viel"), dann
   0 (hier raus, Bubble-Polster auch weg: der Strich des ersten Reiters lag auf den 16px, "die
   eigentlich der duenne Separator sein sollten"). Also rechnet UC.makePageCrumbs aus, wie viel
   Polster die Bubble-Gruppe schon gibt, und legt hier nur den Rest auf 16 drauf
   (--up-ph-innen-l/-r). Die Linien laufen davon unabhaengig bis an die Kante der Gruppe. */
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) {
  padding-top: 8px; padding-left: var(--up-ph-innen-l, 0px); padding-right: var(--up-ph-innen-r, 0px);
  /* Gibt Bubble MEHR als 16 (Gruppenpolster plus Versatz des HTML-Elements), zieht sich das Element
     um den Ueberschuss nach aussen und wird um genau so viel breiter. */
  margin-left: calc(-1 * var(--up-ph-zug-l, 0px)); margin-right: calc(-1 * var(--up-ph-zug-r, 0px));
  width: calc(100% + var(--up-ph-zug-l, 0px) + var(--up-ph-zug-r, 0px)); }
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-desc,
.up-ph-top:is(.uts-pagehead, .upr-pagehead, .uev-pagehead, .ush-pagehead, .uad-pagehead, .ucs-pagehead, .uds-pagehead, .upf-pagehead, .uop-pagehead, .upi-pagehead, .ubr-pagehead, .uss-pagehead, .uvb-pagehead) .up-ph-desc { display: none; }
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-top {
  align-items: center; min-height: var(--up-h-btn); padding-bottom: 8px; position: relative; }
/* Die Linie laeuft bis an die Kante der Bubble-Gruppe um den Kopf, also so weit UEBER das Element
   hinaus, wie die Gruppe Polster hat -- 16 oder 0, je nachdem, ob es in Bubble schon raus ist.
   UC.makePageCrumbs misst das (--up-ph-rand-l/-r); ohne Messung liegt sie unter dem Element. */
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-top::after,
.up-ph-top:is(.uts-pagehead, .upr-pagehead, .uev-pagehead, .ush-pagehead, .uad-pagehead, .ucs-pagehead, .uds-pagehead, .upf-pagehead, .uop-pagehead, .upi-pagehead, .ubr-pagehead, .uss-pagehead, .uvb-pagehead)::after {
  content: ""; position: absolute; bottom: 0; height: 1px; pointer-events: none;
  left: calc(-1 * var(--up-ph-rand-l, 0px)); right: calc(-1 * var(--up-ph-rand-r, 0px));
  background: var(--vc-border); }
/* Die Ueberschrift als Krumenzeile: 14px, auf der Mitte der 32px-Zeile. Vor dem JS steht hier der
   Seitenname aus der Vorlage -- in derselben Groesse, also ohne Sprung. */
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-heading,
.up-ph-top:is(.uts-pagehead, .upr-pagehead, .uev-pagehead, .ush-pagehead, .uad-pagehead, .ucs-pagehead, .uds-pagehead, .upf-pagehead, .uop-pagehead, .upi-pagehead, .ubr-pagehead, .uss-pagehead, .uvb-pagehead) .up-ph-heading {
  display: flex; align-items: center; gap: 6px; min-width: 0; margin: 0;
  font-size: var(--up-fs-l); font-weight: 500; line-height: var(--up-h-btn); letter-spacing: 0;
  color: var(--vc-third); white-space: nowrap; }
/* DIE REITERLEISTE IST SO HOCH WIE DIE KOPFZEILE (29.09. spaet angefordert: "dass die Topbar und
   die Bar darunter mit der Subpage gleich gross sind"). Kopfzeile: 8 + 32 + 8 = 48 bis zur ersten
   Linie. Reiterleiste: von dieser Linie bis zu ihrer eigenen Grundlinie ebenfalls 48 -- ohne
   Abstand darueber, die Reiter selbst 48 hoch, ihr Text also auf der Mitte der Leiste statt am
   Boden. Der Strich des gewaehlten Reiters sitzt weiter auf der Grundlinie.
   Auch ihre Grundlinie laeuft bis an die Kante der Bubble-Gruppe, wie die erste. */
/* DAS ZEICHEN DES ERSTEN REITERS STEHT GENAU UNTER DEM ZEICHEN DER KRUMENZEILE (29.09.
   angefordert: "wenn in der Topbar ein Icon waere, dann waere dieses Icon mittig exakt ueber dem
   Icon des Subpage-Nav-Items darunter"). Beide Zeichen sind 16 breit, also muessen ihre linken
   Kanten gleich liegen: auf dem Inhaltsabstand der Kopfzeile. Mit den 12px Seitenpolster im
   Reiter lag das Zeichen darunter 12px weiter rechts. Hier hat der Reiter keins: sein Kasten IST
   Zeichen plus Beschriftung, und der Strich des gewaehlten Reiters -- UC.makePageNav misst den
   Kasten -- beginnt am Zeichen statt 12px davor. Damit bleibt er auch aus dem Randstreifen, auf
   dem nur die duenne Linie liegen soll.
   Der Abstand von einer Beschriftung zum naechsten Zeichen bleibt, was er war: 12 + 4 + 12 = 28.
   Die Klickflaeche auch -- ::before reicht je die halbe Luecke nach aussen, die zwei Haelften
   treffen sich in der Mitte.
   RECHTS 3px Polster (29.09.: nachmittags 2, "symmetrischer mit den Icons"; abends 1 mehr, "so
   dass der aktive Strich 1px rechts weiter geht"): der Strich laeuft 3px ueber das Textende
   hinaus. Die Luecke wird dafuer 25 -- vom Textende bis zum naechsten Zeichen bleiben es
   3 + 25 = 28, kein Reiter rueckt. SEIT DEM 13.10. (Behaelter um den gewaehlten Reiter): 8
   Polster links und rechts am Reiter, Luecke 12 -- Schrift zu Schrift weiter 8 + 12 + 8 = 28.
   ::before links und rechts je 6: zusammen genau die Luecke, ohne dass sich Reiter ueberlappen. */
.up-root.uvb-root .up-ph-nav,
.up-root.ucs-root .up-ph-nav,
.up-root.uad-root .up-ph-nav,
.up-root.upi-root .up-ph-nav,
.up-root.ubr-root .up-ph-nav,
.up-root.uss-root .up-ph-nav,
.up-root.ush-root .up-ph-nav,
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-nav { margin-top: 0; gap: 12px; }
.up-root.uvb-root .up-ph-navitem,
.up-root.ucs-root .up-ph-navitem,
.up-root.uad-root .up-ph-navitem,
.up-root.upi-root .up-ph-navitem,
.up-root.ubr-root .up-ph-navitem,
.up-root.uss-root .up-ph-navitem,
.up-root.ush-root .up-ph-navitem,
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-navitem {
  height: calc(var(--up-h-btn) + 16px); padding: 0 8px; position: relative; }
.up-root.ucs-root .up-ph-navitem::before,
.up-root.uad-root .up-ph-navitem::before,
.up-root.upi-root .up-ph-navitem::before,
.up-root.ubr-root .up-ph-navitem::before,
.up-root.uss-root .up-ph-navitem::before,
.up-root.ush-root .up-ph-navitem::before,
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-navitem::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: -6px; right: -6px; }
/* Der ERSTE Reiter reicht nach links hoechstens bis an die Kante der Gruppe -- bei einem Einzug
   unter 13 laege sonst ein Teil der Klickflaeche ueber dem Rand der Seitenleiste. */
.up-root.ucs-root .up-ph-navitem:first-child::before,
.up-root.uad-root .up-ph-navitem:first-child::before,
.up-root.upi-root .up-ph-navitem:first-child::before,
.up-root.ubr-root .up-ph-navitem:first-child::before,
.up-root.uss-root .up-ph-navitem:first-child::before,
.up-root.ush-root .up-ph-navitem:first-child::before,
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-navitem:first-child::before { left: calc(-1 * min(6px, var(--up-ph-rand-l, 0px))); }
/* ZWEITE RUNDE (13.10.: "Padding ganz minimal kleiner, Akzentstrich auf ganzer Laenge, nicht ueber
   die linke Grenze"): der Behaelter ist 28 hoch (--up-h-seg, die naechste Stufe unter 32) und
   genau so breit wie der Reiter, der dafuer 8 Polster links und rechts traegt (vorher 10 Luft, der
   Behaelter ragte um 10 nach links in den Rand). Der Abstand zwischen den Reitern sinkt um diese
   2 x 8 von 25 auf 12 -- von Schrift zu Schrift bleiben es 28 wie vorher.
   Folge: das erste Reiterzeichen steht 8 weiter rechts als das Krumenzeichen darueber. */
.up-root:is(.ucs-root, .uad-root, .upi-root, .ubr-root, .uss-root, .ush-root, .uvb-root) .up-ph-navpille,
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-navpille {
  top: 10px; bottom: 10px; }
/* Im SCHMALEN Kopf (is-narrow, unter 768) teilen sich die Reiter die Breite (flex 1 1 0, Inhalt
   mittig) -- dort gibt es keine linke Kante, die unter dem Krumenzeichen stehen koennte. Also die
   alte Geometrie: 4px Luecke und 12px Seitenpolster, sonst laegen die gestreckten Reiter 28px
   auseinander. Und keine verlaengerte Klickflaeche: bei 4px Luecke griffe sie 10px in den
   Nachbarn, und der spaetere Nachbar liegt oben -- ein Klick rechts in Reiter 1 waehlte Reiter 2.
   Der Platzhalter (.uvb-root) steht mit in den Listen, seit er die echten Reiter zeichnet (14.10.):
   sonst lagen seine Reiter im schmalen Fenster 12 statt 4 auseinander und sprangen beim Tausch. */
.up-root.ucs-root.is-narrow .up-ph-nav,
.up-root.uad-root.is-narrow .up-ph-nav,
.up-root.upi-root.is-narrow .up-ph-nav,
.up-root.ubr-root.is-narrow .up-ph-nav,
.up-root.uss-root.is-narrow .up-ph-nav,
.up-root.ush-root.is-narrow .up-ph-nav,
.up-root.uvb-root.is-narrow .up-ph-nav,
.up-ph-root.is-narrow:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-nav { gap: 4px; }
.up-root.ucs-root.is-narrow .up-ph-navitem,
.up-root.uad-root.is-narrow .up-ph-navitem,
.up-root.upi-root.is-narrow .up-ph-navitem,
.up-root.ubr-root.is-narrow .up-ph-navitem,
.up-root.uss-root.is-narrow .up-ph-navitem,
.up-root.ush-root.is-narrow .up-ph-navitem,
.up-root.uvb-root.is-narrow .up-ph-navitem,
.up-ph-root.is-narrow:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-navitem { padding: 0 15px 0 12px; }
/* Schmal: die Pille folgt dem gestreckten Reiter von selbst (gemessen wird sein Rechteck). */
.up-root.ucs-root.is-narrow .up-ph-navitem::before,
.up-root.uad-root.is-narrow .up-ph-navitem::before,
.up-root.upi-root.is-narrow .up-ph-navitem::before,
.up-root.ubr-root.is-narrow .up-ph-navitem::before,
.up-root.uss-root.is-narrow .up-ph-navitem::before,
.up-root.ush-root.is-narrow .up-ph-navitem::before,
.up-ph-root.is-narrow:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-navitem::before { content: none; }
.up-ph-root:is(.dph-root, .pph-root, .cph-root, .bph-root, .pfph-root, .oph-root, .sph-root) .up-ph-nav::after {
  left: calc(-1 * var(--up-ph-rand-l, 0px)); right: calc(-1 * var(--up-ph-rand-r, 0px)); }
/* Die Seiten mit eigener Wurzel (cfg.komponente): dort zieht sich nur die KOPFZEILE rechts um die
   Differenz nach aussen (--up-ph-kopfzug-r, 8), die Reiterleiste steht weiter in der Wurzel. Ihre
   Grundlinie muss also um genau diesen Zug weiter laufen -- gemessen am 13.10. in der
   Next.js-Huelle: Kopflinie bis 1024, Reiterlinie bis 1016, also 8 vor dem Rand. In Bubble
   dieselbe Rechnung. */
.up-root:is(.ucs-root, .uad-root, .upi-root, .ubr-root, .uss-root, .ush-root, .uvb-root) .up-ph-nav::after {
  left: calc(-1 * (var(--up-ph-rand-l, 0px) + var(--up-ph-kopfzug-l, 0px)));
  right: calc(-1 * (var(--up-ph-rand-r, 0px) + var(--up-ph-kopfzug-r, 0px))); }
.up-ph-crumb { display: inline-flex; align-items: center; gap: 6px; min-width: 0; color: var(--vc-third); }
.up-ph-crumb.is-akt { color: var(--vc-text); }
/* Das Zeichen der Seite vor ihrem Namen, in dessen Farbe (erbt von .up-ph-crumb). 16 breit wie
   das Reiterzeichen darunter -- die Gleichheit ist die Ausrichtung, siehe oben. */
.up-ph-crumbic { display: inline-flex; flex: 0 0 auto; }
.up-ph-crumbic svg { width: 16px; height: 16px; display: block; }
.up-ph-crumbsep { display: inline-flex; flex: 0 0 auto; color: var(--vc-third); }
.up-ph-crumbsep svg { width: 14px; height: 14px; display: block; }
.up-ph-crumbname { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Eine fruehere Stufe als Knopf (Events: "Events > Overview > Name"). Sieht aus wie die Krume,
   bekommt beim Ueberfahren die Farbe der aktuellen Stufe -- kein Unterstrich, keine Flaeche. */
button.up-ph-crumb { background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
  transition: color var(--up-t-1) var(--up-ease); }
button.up-ph-crumb:hover, button.up-ph-crumb:focus-visible { color: var(--vc-text); outline: none; }

/* Ein Kopf OHNE Reiterleiste endet an seiner Linie (29.09. nachmittags, Performance und
   Opportunities): wie der Dashboard-Kopf im Analysemodus. Den Abstand zum naechsten Block gibt
   darunter die Bubble-Gruppe (16), sonst stuenden 32 unter einer einzelnen Zeile. */
.up-ph-root:is(.pfph-root, .oph-root) { padding-bottom: 0; }

/* ---------- DER KOMPAKTE KOPF IN EINER KOMPONENTE (29.09. nachmittags) ----------
   Teams und Prompt Research bauen ihren Seitenkopf SELBST, als erstes Kind ihrer Wurzel
   (.uts-pagehead, .upr-pagehead) -- ein eigenes Kopf-Element mit eigener Bubble-Gruppe gibt es
   dort nicht. Also misst UC.makePageCrumbs die Komponente: sie steht wie ein Kopf in ihrer
   Gruppe, und dieselbe Rechnung legt ihren GANZEN Inhalt -- Kopf, Tabelle, Startfeld -- auf 16px
   vom Rand (--up-ph-innen-l/-r) oder zieht sie um Bubbles Ueberschuss hinaus (--up-ph-zug-l/-r).
   Damit ist es gleich, ob in Bubble noch 16 Polster an der Gruppe stehen oder 0: der Inhalt
   steht bei 16, die Linie unter dem Kopf laeuft bis an die Kante der Gruppe (--up-ph-rand-l/-r).
   Ueber .has-crumbs: die setzt makePageCrumbs im selben Zug, in dem die Komponente ihren Kopf
   baut -- vorher steht noch nichts auf der Seite, das springen koennte.
   Beschreibung, Linie und Ueberschrift teilen sich die Regeln des kompakten Kopfs oben. Hoehe
   und Luft der Zeile stehen in der Komponente (teams.css, prompt-research.css), weil dort ihr
   Regler data-head-top sitzt; hier nur, was keinen Regler braucht. */
.up-root:is(.uts-root, .upr-root, .uev-root, .ush-root, .uad-root, .ucs-root, .uds-root, .upf-root, .uop-root, .upi-root, .ubr-root, .uss-root, .uvb-root).has-crumbs {
  padding-left: var(--up-ph-innen-l, 0px); padding-right: var(--up-ph-innen-r, 0px);
  margin-left: calc(-1 * var(--up-ph-zug-l, 0px)); margin-right: calc(-1 * var(--up-ph-zug-r, 0px));
  width: calc(100% + var(--up-ph-zug-l, 0px) + var(--up-ph-zug-r, 0px)); }
/* Die Kopfzeile steht rechts bei 8 wie in jedem Seitenkopf, der Inhalt der Komponente darunter
   bei 16: also zieht sie sich um die Differenz nach aussen (--up-ph-kopfzug-l/-r aus
   UC.makePageCrumbs, ohne JS 0; links ist die Differenz heute 0). */
.up-ph-top:is(.uts-pagehead, .upr-pagehead, .uev-pagehead, .ush-pagehead, .uad-pagehead, .ucs-pagehead, .uds-pagehead, .upf-pagehead, .uop-pagehead, .upi-pagehead, .ubr-pagehead, .uss-pagehead, .uvb-pagehead) { align-items: center; min-height: var(--up-h-btn); position: relative;
  margin-left: calc(-1 * var(--up-ph-kopfzug-l, 0px)); margin-right: calc(-1 * var(--up-ph-kopfzug-r, 0px)); }

/* ---------- DER PLATZHALTER EINER ANSICHT (.uvb-*, core.js seitenVorab, 13.10. abends) ----------
   Steht, solange die Dateien einer Ansicht beim ersten Besuch laden (Next.js-App), statt einer
   leeren Flaeche. Der Kopf steht oben in den Listen der Seitenkoepfe -- dieselbe Geometrie wie
   jede Seite, 8 ueber und unter der 32er-Zeile wie .ucs-pagehead und Geschwister. Darunter ein
   ruhiges Skelett in den Massen, mit denen jede Seite beginnt: Filterzeile in Knopfhoehe, Karten
   im Rahmen und Radius der Diagrammkarten (16, combo-box-left).
   Einblenden wie der Auftritt der Seiten (makeAuftritt: Deckkraft und 10px Weg, easeOutExpo,
   50ms Staffel), damit die Seite die Bewegung an derselben Stelle uebernehmen kann. */
.uvb-root { display: block; }
.uvb-root .uvb-pagehead { padding: 8px 0; animation: uvb-auf var(--up-t-auftritt) var(--up-ease-expo) both; }
/* Die Reiterleiste der Seiten mit Reitern (Prompts, Citations, Brands, Shopping, Ads, Settings):
   Hoehe und Grundlinie aus den Listen oben (.up-root.uvb-root .up-ph-nav/-navitem), die Reiter
   seit dem 14.10. die echten (core.js VORAB_REITER) statt grauer Balken. Ohne Klick: bis die Seite
   steht, hat ein Reiter nichts zu oeffnen. */
.uvb-root .uvb-nav { animation: uvb-auf var(--up-t-auftritt) var(--up-ease-expo) 50ms both; pointer-events: none; }
.uvb-main { display: flex; flex-direction: column; gap: 16px; padding-top: 16px;
  animation: uvb-auf var(--up-t-auftritt) var(--up-ease-expo) 100ms both; }
.uvb-zeile { display: flex; gap: 8px; }
.uvb-sk { display: block; background: var(--vc-sk); border-radius: var(--up-r-m); animation: uutpulse 1.2s ease-in-out infinite; }
.uvb-sk.is-knopf { width: 112px; height: var(--up-h-btn); }
.uvb-sk.is-knopf.is-kurz { width: 88px; }
.uvb-sk.is-titel { width: 132px; height: 12px; border-radius: var(--up-r-xs); }
.uvb-karte { box-sizing: border-box; height: 240px; padding: 20px; border: 1px solid var(--vc-border); border-radius: var(--up-r-2xl); }
.uvb-karte.is-gross { height: 360px; }
@keyframes uvb-auf { from { opacity: 0; transform: translateY(10px); } }
@media (prefers-reduced-motion: reduce) {
  .uvb-root .uvb-pagehead, .uvb-root .uvb-nav, .uvb-main { animation-name: uvb-auf-weich; }
  .uvb-sk { animation: none; }
}
@keyframes uvb-auf-weich { from { opacity: 0; } }

/* ---------- DIE KOMPONENTEN TRAGEN IHR SEITENPOLSTER SELBST (29.09. angefordert) ----------
   "Gib den ganzen Komponenten einfach 16px padding left und right, nur nicht den Topbars."
   Bisher gab die Bubble-Gruppe der Ansicht allen Bloecken das Polster -- auch dem Seitenkopf, und
   dessen Linien liefen deshalb nicht bis an den Rand. Jetzt tragen es die Bloecke selbst, und in
   Bubble kommt es raus.
   NUR in den vier Ansichten mit dem neuen Seitenkopf (die #view-Behaelter des Kopfskripts) --
   dieselben Komponenten in einem Drawer behalten ihr Mass. NUR die grossen Bloecke: Kalender,
   Filterknoepfe und Umschalter stehen nebeneinander in einer Zeile; 16px an jedem waeren Luecken
   zwischen ihnen. Keiner dieser Wurzeln hatte bisher ein eigenes Seitenpolster.
   Die Filterleiste "More Filters" (.ufb-root) stand anfangs mit in der Liste und ist am selben
   Tag wieder raus (gemeldet): sie steht in der Zeile neben dem Kalender, ist also genau so ein
   Knopf und kein Block.
   Ebenso .upw-root (das Agentic Dashboard, am selben Tag gefunden): seine Spalte traegt ihr
   Polster selbst (--upw-pad in power-dashboard.css), im schmalen Fenster ausdruecklich 16
   ("ueberall 16px im mobile Dashboard", 15.09.). Die 16 hier kamen DAZU -- 32 statt 16, genau
   die Verdopplung, vor der der Kommentar dort warnt. */
:is(#view-dashboard, #view-prompts, #view-citations, #view-brands)
  .up-root:is(.vot-root, .tcd-root, .combo-root, .upt-root, .urt-root, .utm-root,
              .uut-root, .udt-root, .ubo-root, .udb-root) { padding-left: 16px; padding-right: 16px; }
/* IN DER CITATIONS-SEITE (citations-page.js, 08.10.) stehen Chart und Tabellen im Inhalt der
   Komponente, und den legt UC.makePageCrumbs schon auf 16 vom Rand (--up-ph-innen-l/-r). Die 16
   oben kaemen dazu -- 32 statt 16. Mit der Kennung, sonst verliert die Regel gegen die obere. */
:is(#view-dashboard, #view-prompts, #view-citations, #view-brands) .ucs-root
  .up-root:is(.combo-root, .uut-root, .udt-root) { padding-left: 0; padding-right: 0; }
/* Ebenso in der Dashboard-Seite (dashboard-page.js, 08.10.): Visibility-Chart, Top Citations und
   Responses stehen im Inhalt der Komponente, der schon 16 vom Rand steht. */
:is(#view-dashboard, #view-prompts, #view-citations, #view-brands) .uds-root
  .up-root:is(.vot-root, .tcd-root, .urt-root) { padding-left: 0; padding-right: 0; }

/* ---------- DIESELBEN 16px IN DEN FUENF UEBRIGEN ANSICHTEN (29.09. nachmittags) ----------
   Mit den Koepfen von Performance, Opportunities, Prompt Research, Teams und Settings muss auch
   dort das Seitenpolster aus Bubble raus -- sonst enden die Linien der Koepfe wieder 16px vor dem
   Rand. Die Bloecke tragen es dann selbst, wie in den ersten vier Ansichten.
   Ueber [id^="view-"] und nicht ueber die Namen: bestaetigt sind nur #view-opportunities und
   #view-settings, die uebrigen setzt das Kopf-Skript der App (view-<name>), und ein falsch
   geratener Name liesse die Regel still ins Leere laufen. Drawer liegen in keiner Ansicht.
   Nicht dabei: .uo-root bringt seine 16px seit jeher selbst mit; .uts-root und .upr-root tragen
   ihren Kopf in sich und rechnen ihren Randabstand mit ihm (Block darueber). .upd-root war eine
   Karte mit Rahmen und trug darum Aussenabstand; seit dem 07.10. steht der Bereich offen wie die
   Detailbereiche von Events, Shopping und Ads und polstert sich wie der Radar darueber. */
[id^="view-"] .up-root:is(.uhm-root, .usb-root, .uto-root, .ubl-root, .upd-root) { padding-left: 16px; padding-right: 16px; }
/* IN DER PERFORMANCE-SEITE (performance-page.js, 09.10.) stehen Radar, Detailbereich und
   URL-Tabelle im Inhalt der Komponente, und den legt UC.makePageCrumbs schon auf 16 vom Rand. Die
   16 oben kaemen dazu -- 32 statt 16, wie bei Citations und Dashboard. Ueber die Seitenkennung, sonst
   verliert die Regel gegen die obere. */
[id^="view-"] .upf-root .up-root:is(.uhm-root, .upd-root, .uut-root) { padding-left: 0; padding-right: 0; }
/* IN DER SEITE PROMPT INSIGHTS (prompts-page.js, 10.10.): Prompt-Tabelle, Response-Tabelle und
   Topic-Verwaltung stehen im Inhalt der Komponente, der schon 16 vom Rand steht -- wie bei
   Citations und Dashboard. */
:is(#view-dashboard, #view-prompts, #view-citations, #view-brands) .upi-root
  .up-root:is(.upt-root, .urt-root, .utm-root) { padding-left: 0; padding-right: 0; }
/* IN DER SEITE BRANDS (brands-page.js, 11.10.): Brands Overview und Explore Brands stehen im
   Inhalt der Komponente, der schon 16 vom Rand steht -- wie bei Prompt Insights. */
:is(#view-dashboard, #view-prompts, #view-citations, #view-brands) .ubr-root
  .up-root:is(.ubo-root, .udb-root) { padding-left: 0; padding-right: 0; }
/* IN DER SEITE SETTINGS (settings-page.js, 11.10.): Your Brand, Team Organisation und Billing
   stehen im Inhalt der Komponente, der schon 16 vom Rand steht -- die 16 aus dem Block "DIESELBEN
   16px" kaemen dazu. Ueber die Seitenkennung, sonst verliert die Regel gegen die dortige. */
[id^="view-"] .uss-root .up-root:is(.usb-root, .uto-root, .ubl-root) { padding-left: 0; padding-right: 0; }

/* ---------- responsive (classes set by UC.makePageNav via UC.onResize) ---------- */
/* is-narrow (<768px default): tabs go from auto-width/left-packed to even thirds, still centered
   internally (.up-ph-navitem already centers its own icon+label). A page header's own "Add X"
   button can drop its trailing word here by wrapping it in a child with class up-ph-addbtn-full. */
.up-root.is-narrow .up-ph-navitem { flex: 1 1 0; }
/* NIE UEBER DEN RAND (06.10. abends angefordert, fuer jeden Seitenkopf). makePageNav misst, ob die
   Reiterzeile ueberlaeuft, und setzt dann zwei Stufen nacheinander:
   1. is-ohne-zeichen: die Zeichen fallen weg, Polster 8 (die Fassung, die Shopping und Ads bis
      hier je fuer sich unter 500px hatten -- gemessen bei 400: 108px Ueberlauf mit Zeichen);
   2. is-kuerzen, nur wenn es auch ohne Zeichen nicht passt: Reiter, die breiter sind als ihr
      Anteil, werden auf die Kappe --up-nav-kappe begrenzt (makePageNav rechnet sie aus, kurze
      Namen bleiben ganz) und der Name endet mit "…".
   Gemessen und nicht an einer Breite festgemacht: wie viele Reiter mit welchen Namen in welcher
   Sprache passen, sagt keine feste Stufe. (0,5,0), damit es die schmalen Reiter der kompakten
   Koepfe weiter unten ((0,4,0), Polster 0 15 0 12) schlaegt. */
.up-root .up-ph-nav.is-ohne-zeichen .up-ph-navicon { display: none; }
.up-root .up-ph-nav.is-ohne-zeichen .up-ph-navitem.up-ph-navitem { padding: 0 8px; }
.up-root .up-ph-nav.is-kuerzen .up-ph-navitem.up-ph-navitem { flex: 1 1 auto; min-width: 0; max-width: var(--up-nav-kappe, none); }
.up-root .up-ph-nav.is-kuerzen .up-ph-navlabel { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.up-root.is-narrow .up-ph-addbtn-full { display: none; }
/* Die schmale Fassung des Etiketts. Sie ist ein eigener Satz aus dem Katalog und nicht der Rest
   eines abgeschnittenen -- im Deutschen steht das Verb hinten, ein abgeschnittener Satz waere
   dort das falsche Wort. knopfEtikett() in core.js baut beide Spans. */
.up-ph-addbtn-kurz { display: none; }
.up-root.is-narrow .up-ph-addbtn-kurz { display: inline; }
/* Hier stand bis 18.08. eine Zusatzpolsterung: unter 500px bekamen Seitenkoepfe (.up-ph-root) und
   Komponenten mit eigener Kopfzeile (.up-sidebar-clear -- ask-mira, prompt-research) 32px Luft
   nach oben, damit Bubbles mobiler Sidebar-Schalter oben links nicht auf der Ueberschrift lag.
   Diesen Schalter gibt es nicht mehr: die Leiste ist unsere eigene (usn-bar) und bringt ihren
   Knopf selbst mit. Die Polsterung war damit nur noch ein Sprung in der Hoehe, sobald das Fenster
   schmal wurde -- ersatzlos raus, auf Ansage.

   Mit ihr gehen VIER Gegenrechnungen: .up-ph-addbtn hier und die -16px an .bph-topright,
   .pph-topright und .cph-refreshbtn in den Seitenkopf-Dateien. Sie existierten ausschliesslich,
   um diese Polsterung wieder abzuziehen; allein stehengelassen haetten sie die Knoepfe im
   schmalen Fenster 16px nach oben gezogen.

   .up-sidebar-clear wird von beiden Komponenten weiter gesetzt und traegt jetzt keine Regel mehr.
   Absichtlich: die Klasse steht im Vertrag, und der naechste Kopf, der oben links etwas zeichnet,
   haengt sich wieder daran. */

/* ======================================================================
   Dropdown-Panel-Kante — EIN Ort, wie die Panel-Farbe daneben.

   Gemessen auf der Live-Seite: das Panel ist #151515, der Drawer darunter #121212.
   Drei Stufen von 255 -- die Flaeche verschwindet darin, und das liest sich als
   "das Dropdown ist durchsichtig". Auf der Seite sind es sechs Stufen gegen
   #1b1b1b, gerade genug, dass es dort nicht auffiel.

   Die Panel-FARBE bleibt, wo sie ist: sie ist eine bewusste, app-weite
   Entscheidung, und sie wegen eines Kontexts zu verschieben waere der falsche
   Hebel. Was fehlte, ist die Kante. core hatte diese Aufhellung schon -- aber
   nur fuer .up-filter-menu, und die Filter-Dropdowns, date-range und
   add-prompts bringen eigene Menue-Klassen mit und wurden nie mitgenommen.
   Deshalb hier alle an einer Stelle, statt die Regel ein siebtes Mal zu kopieren.
   ====================================================================== */
.up-root .up-filter-menu, .up-root .up-menu, .up-root .up-cols-menu,
.up-root .up-sort-menu, .up-root .up-ment-menu, .up-root .up-pop,
/* ---- Hover IN einem Menue: der Auswahlton, nicht der Kopfzeilenton -----------------------------
   Gemeldet am 07.09. fuer die Type-Dropdowns in den Tabellenkoepfen: "entweder haben die Hover
   Effekte exakt die gleiche Farbe wie der BG, oder sie sind schlicht nicht sichtbar."

   Genau so war es, und zwar rechnerisch: die Zeilen holen ihren Hover aus --vc-heading-bg
   (#232326 im Dunkeln), und der Grund JEDES dieser Menues ist --up-menu-bg -- ebenfalls #232326.
   Gemessen in _h_dd_hover.html: Abstand 0.0. Auf einer Karte oder in einer Werkzeugleiste ist
   derselbe Ton richtig und gut sichtbar (dort liegt er auf --vc-bg, #1c1c1f); nur AUF einem
   Menue faellt er mit dem Grund zusammen. Die Dropdowns der Topics-, Markets- und Models-Filter
   traf es genauso -- dort fiel es nur nicht auf, weil ihre Zeilen meist angehakt sind und dann
   den Auswahlton tragen.

   Darum wird der Ton nicht in zwanzig :hover-Regeln getauscht, sondern EINMAL an der Quelle: in
   einem Menue IST der leise Ton der Auswahlton. Damit gilt hier die Hausregel, die .up-optrow
   schon beschreibt -- "eine Zeile, auf die man zeigt, und eine Zeile, die gewaehlt ist, sind
   beide 'diese hier', und zwei verschiedene Graus nebeneinander lesen sich als zwei
   Bedeutungen". Alles, was in einem Menue liegt, zieht mit: Zeilen, Schalterzeilen, die
   Icon-Knoepfe der Kopfzeile, das X im Suchfeld.

   Die Schiene eines Umschalters folgte diesem Muster bis zum 22.09. ebenfalls; sie braucht es
   seit ihrem deckenden Ton nicht mehr. Die Trigger sind NICHT betroffen: sie sind Geschwister des Menues, keine
   Kinder, und ihr geoeffneter Zustand bleibt der Kopfzeilenton auf der Seitenflaeche. */
.up-root .up-filter-menu, .up-root .up-menu, .up-root .up-cols-menu,
.up-root .up-sort-menu, .up-root .up-ment-menu, .up-root .up-pop,
.up-root .up-submenu,
.up-root .utf-menu, .up-root .umf-menu, .up-root .umk-menu,
.up-root .udr-menu, .up-root .uap-menu {
  --vc-heading-bg: var(--up-sel-bg);
}

.up-root .utf-menu, .up-root .umf-menu, .up-root .umk-menu,
.up-root .udr-menu, .up-root .uap-menu {
  border-color: var(--up-menu-border);
  /* Ganz nach oben, direkt unter die Bulk-Bar (99999) und den Modal-Shell (100000), die beide
     weiter gewinnen muessen. 9940 reichte nicht: Teile des Drawers bauen hoeher als das
     dokumentierte 9905-9930-Band, und eine Zahl, die man gegen einen fremden Host schaetzt, ist
     immer die naechste Runde. Ueber --up-menu-z pro Placement nachjustierbar. */
  z-index: var(--up-menu-z, 99998);
}

/* ---- .up-scroll ------------------------------------------------------------------------------
   Die schmale, dezente Scrollbar des Hauses. Stand in fuenf Dateien fast gleich -- die
   Unterschiede waren zufaellig, die FARBEN aber nicht: drei Komponenten ziehen sie aus der
   Haus-Palette (--vc-*), ask-mira und quick-actions aus ihrer eigenen (--am-*). Eine Klasse mit
   festen Farben haette die beiden anders aussehen lassen.

   Darum: Geometrie fest, Farbe ueber zwei Variablen mit Rueckfall. Wer nichts setzt, bekommt die
   Haus-Toene; wer eine eigene Palette fuehrt, setzt --up-scroll-thumb und --up-scroll-track auf
   dem eigenen Wurzelelement und behaelt sein Aussehen.

   Firefox kennt ::-webkit-scrollbar nicht und nimmt scrollbar-width/scrollbar-color -- beide
   Zeilen gehoeren zusammen, sonst ist es in einem der beiden Browser unveraendert breit. */
.up-scroll { scrollbar-width: thin;
  scrollbar-color: var(--up-scroll-thumb, var(--vc-border)) var(--up-scroll-track, transparent); }
.up-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
.up-scroll::-webkit-scrollbar-track { background: var(--up-scroll-track, transparent); }
.up-scroll::-webkit-scrollbar-thumb { background: var(--up-scroll-thumb, var(--vc-border));
  border-radius: 999px; }
.up-scroll::-webkit-scrollbar-thumb:hover { background: var(--up-scroll-thumb-hover, var(--vc-hover-border)); }
.up-scroll::-webkit-scrollbar-corner { background: transparent; }

/* ---- Variantentabelle (UC.variationRows) -----------------------------------------------------
   Die Klassen, die das Kit ausgibt. Standen als .upd-* in performance-detail und gelten jetzt fuer
   beide Verbraucher -- der Ring hat keine Farbe im SVG, er holt sie von hier; ohne diese Regeln
   waere er unsichtbar.

   Kein Farbton fuer den Bogen: Share of Voice ist keine Wertung, sondern ein Anteil. Ein gruener
   oder roter Ring wuerde eine Aussage treffen, die die Zahl nicht hergibt. */
.up-varname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-varring { display: inline-flex; align-items: center; flex: 0 0 auto; line-height: 0; }
/* 40 Prozent groesser: 16 x 1.4 = 22.4. Die Groesse steht hier und nicht im SVG -- die viewBox
   bleibt 16x16, alles darin skaliert proportional mit, und Radius und Strichstaerke muessen nicht
   nachgerechnet werden. */
.up-varring svg { display: block; width: 22.4px; height: 22.4px; }
.up-varring-track { stroke: var(--vc-heading-bg); }
.up-varring-fill { stroke: var(--up-accent); transition: stroke-dasharray 240ms ease; }
.up-var-of { font-size: 12px; font-weight: 400; color: var(--vc-fourth); white-space: nowrap; }

/* ══ Custom Groupings ═══════════════════════════════════════════════════════════════════════════
   Diese Regeln standen in prompts-table.css und sind hierher GEWANDERT, Werte unveraendert. Grund:
   die Verwaltung der Gruppierungen gibt es an zwei Orten (Dropdown der prompts-table, Abschnitt im
   topics-manager), und "1:1 dasselbe" ist nur zu halten, wenn es EINE Regel ist.

   Jede Regel traegt beide Selektoren: den alten .upt-*-Namen und den Kit-Namen .up-cgm-*. Das
   Markup aus UC.makeGroupingModal setzt beide Klassen. Damit bleibt die Tabelle pixelgleich, der
   Manager bekommt dasselbe Aussehen ohne zweite Kopie, und kein Klassenname verschwindet aus dem
   Vertrag -- .contract_diff zaehlt entfernte Klassen als Alarm, und zu Recht: an einem
   .upt-gm-*-Namen kann fremdes CSS in der Bubble-App haengen.
   Das Farbraster (.upt-colorgrid und Geschwister) ist mitgewandert, weil der Themen-Ersteller der
   prompts-table es ebenfalls benutzt: es war schon vor dieser Runde an zwei Stellen im Einsatz und
   stand trotzdem in einer Komponentendatei. */
/* ---------- "New grouping" popup: shell + chips are core's, only the layout here is local ---------- */
.upt-gm-backdrop .up-topicmodal-card, .up-cgm-backdrop .up-topicmodal-card { max-width: 560px; }
.upt-gm-labelrow, .up-cgm-labelrow { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.upt-gm-count, .up-cgm-count { font-size: 12px; font-weight: 500; color: var(--vc-muted); font-variant-numeric: tabular-nums; }
/* Hidden until the magnifier is clicked — with a handful of topics a permanent search field is
   just a field nobody needs. */
.upt-gm-search, .up-cgm-search { position: relative; display: none; align-items: center; }
.upt-gm-search.is-open, .up-cgm-search.is-open { display: flex; }
.upt-gm-right, .up-cgm-right { display: inline-flex; align-items: center; gap: 8px; }
.upt-gm-searchbtn, .up-cgm-searchbtn { width: 26px; height: 26px; border: 0; border-radius: 7px; padding: 0;
  background: transparent; color: var(--vc-muted); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; }
.upt-gm-searchbtn svg, .up-cgm-searchbtn svg { width: 14px; height: 14px; display: block; }
.upt-gm-searchbtn:hover, .up-cgm-searchbtn:hover, .upt-gm-searchbtn.is-open, .up-cgm-searchbtn.is-open { color: var(--vc-text); background: var(--vc-heading-bg); }
/* Expanded list scrolls instead of growing without limit — 100 topics must not push the footer
   off screen. */
.upt-gm-list.is-scroll, .up-cgm-list.is-scroll { max-height: 240px; overflow-y: auto; }
.upt-gm-more, .up-cgm-more { width: 100%; height: 30px; border: 0; border-radius: 8px; background: transparent;
  color: var(--vc-muted); font-family: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
.upt-gm-more:hover, .up-cgm-more:hover { color: var(--vc-text); background: var(--vc-heading-bg); }
.upt-gm-search-in, .up-cgm-search-in { flex: 1 1 auto; height: 36px; padding: 0 34px 0 12px; border: 1px solid var(--vc-border);
  border-radius: 9px; background: transparent; color: var(--vc-text); font: inherit; font-size: 14px; outline: 0; }
.upt-gm-search-in:focus, .up-cgm-search-in:focus { border-color: var(--up-focus); box-shadow: 0 0 0 var(--up-focus-w) var(--up-focus-ring); outline: 0; }
.upt-gm-clear, .up-cgm-clear { position: absolute; right: 6px; width: 20px; height: 20px; border: 0; border-radius: 6px;
  padding: 0; background: transparent; color: var(--vc-muted); cursor: pointer;
  display: none; align-items: center; justify-content: center; }
.upt-gm-clear.is-on, .up-cgm-clear.is-on { display: inline-flex; }
.upt-gm-clear svg, .up-cgm-clear svg { width: 13px; height: 13px; display: block; }
.upt-gm-clear:hover, .up-cgm-clear:hover { color: var(--vc-text); background: var(--vc-heading-bg); }
.upt-gm-list, .up-cgm-list { display: flex; flex-wrap: wrap; gap: 8px; }

.upt-gm-namerow, .up-cgm-namerow { display: flex; align-items: center; gap: 8px; }
.upt-gm-colorwrap, .up-cgm-colorwrap { flex: 0 0 auto; }
.upt-gm-dotbtn, .up-cgm-dotbtn { width: 36px; height: 36px; padding: 0; border-radius: 9px; border: 1px solid var(--vc-border);
  background: transparent; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.upt-gm-dotbtn:hover, .up-cgm-dotbtn:hover, .upt-gm-colorwrap.is-open .upt-gm-dotbtn, .up-cgm-colorwrap.is-open .up-cgm-dotbtn { border-color: var(--vc-hover-border); background: var(--vc-heading-bg); }
.upt-gm-dot, .up-cgm-dot { width: 16px; height: 16px; border-radius: 50%; display: block; border: 1px solid rgba(0,0,0,.14); }
/* Centered (same reasoning as .upt-topicpickpanel above: a left-aligned grid under a full-width
   row looks like it fell over) and smooth: max-height/opacity animate together on open, driven by
   .is-open added a frame after mount (see openGroupModal's data-gm-colorbtn handler) so there is
   an actual "before" state to transition FROM. Because the panel node itself is never recreated
   (only its innerHTML), the rest of the popup -- everything below it in the modal -- reflows in
   step with the same 200ms ease instead of snapping to the new height. */
.upt-gm-colorpanel, .up-cgm-colorpanel { display: flex; justify-content: center;
  max-height: 0; opacity: 0; overflow: hidden; margin-top: 0;
  transition: max-height 200ms ease, opacity 200ms ease, margin-top 200ms ease; }
.upt-gm-colorpanel.is-open, .up-cgm-colorpanel.is-open { max-height: 160px; opacity: 1; margin-top: 10px; }
/* Rows are tone scales (vibrant / muted / deep), columns are hues — read down for a mood,
   across for a color. Centred, because a left-aligned 8-wide grid under a full-width row above
   it looked like it had fallen over. */
.upt-colorgrid, .up-cgm-colorgrid { display: grid; grid-template-columns: repeat(10, 34px); gap: 6px; }
/* The 34x34 cell is the hit target and carries the hover, the blob inside is just the colour —
   splitting them is what lets the swatch stay a fixed size while the cell gets a proper
   button-sized surface, instead of a small dot you have to aim at.
   Transparent border reserved at rest (not added on hover) so the hover state doesn't shift
   the swatch by the border's own width. */
.upt-colorcell, .up-cgm-colorcell { width: 34px; height: 34px; padding: 0; border: 1px solid transparent; border-radius: 8px;
  background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 120ms ease, border-color 120ms ease; }
.upt-colorcell:hover, .up-cgm-colorcell:hover { background: var(--vc-heading-bg); border-color: var(--vc-hover-border); }
/* No inset ring and no fractional-px spread: an inset shadow on a 20px circle plus a 3.5px
   outer ring is what made these look soft/smeared rather than crisp. The blob is now a plain
   filled circle at whole-pixel size, and selection is shown INSIDE it. */
.upt-colorblob, .up-cgm-colorblob { width: 22px; height: 22px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; flex: 0 0 auto; color: #fff; }
.upt-colorblob svg, .up-cgm-colorblob svg { width: 12px; height: 12px; display: block; stroke: currentColor; stroke-width: 4; }
/* Fixed ink, not per-swatch contrast-solved: white in light mode, primary text in dark. Same rule
   as core's canonical picker (up-topicmodal-colorblob) -- two selectors because this cell shows up
   both inside .up-root (the inline topic creator) and inside the body-mounted group-modal
   backdrop, which carries its own data-theme rather than inheriting .up-root's. */
.up-root[data-theme="dark"] .upt-colorblob,
.up-root[data-theme="dark"] .up-cgm-colorblob,
.upt-gm-backdrop[data-theme="dark"] .upt-colorblob, .up-cgm-backdrop[data-theme="dark"] .up-cgm-colorblob { color: var(--vc-text); }
.upt-topicmenu-empty, .up-cgm-empty { padding: 14px 8px; text-align: center; font-size: 12.5px; color: var(--vc-muted); }

/* ---------- Zeile einer Gruppierung: Punkt, Auge, Dreipunktmenue, Zieh-Platzhalter ------------
   Auch das gab es nur in prompts-table.css und wird jetzt an zwei Orten gebraucht. Gewandert mit
   den Werten, die dort erprobt sind, und mit beiden Klassennamen je Regel.
   Neu ist EINE Klasse: .up-cg-row. Sie markiert "das ist eine Gruppierungs-Zeile" und traegt die
   Regeln, die vom Zustand der ZEILE abhaengen (Hover deckt Auge und Menue auf, das Ziehen macht die
   Zeile zum Platzhalter). Vorher hingen die an .upt-group-item, also am Zeilentyp des Popovers --
   im topics-manager ist die Zeile eine andere, die Regeln sind dieselben. Beide Orte setzen die
   Klasse zusaetzlich zu ihrer eigenen. */
.upt-grp-cdot, .up-cg-dot { width: 8px; height: 8px; border-radius: 4px; flex: 0 0 auto; display: block; }

/* Auge und Kebab: 24x24, gedaempft, erscheinen erst beim Hover der Zeile -- eine Aktionszeile, in
   der bei Ruhe alle Symbole stehen, liest sich unruhiger als der Rest. Ein verstecktes Grouping
   bleibt trotzdem als versteckt erkennbar (gedimmter Name und Punkt weiter unten). */
.upt-group-eye, .up-cg-eye,
.upt-group-more, .up-cg-more { width: 24px; height: 24px; flex: 0 0 auto; border: 0;
  border-radius: 6px; padding: 0; background: transparent; color: var(--vc-muted); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 120ms ease, color 120ms ease, background 120ms ease; }
.upt-group-eye svg, .up-cg-eye svg,
.upt-group-more svg, .up-cg-more svg { width: 14px; height: 14px; display: block; }
.up-cg-row:hover .up-cg-eye, .up-cg-row.is-menuopen .up-cg-eye,
.up-cg-row:hover .up-cg-more, .up-cg-row.is-menuopen .up-cg-more { opacity: 1; }
.up-cg-eye:hover, .up-cg-more:hover,
.up-cg-row.is-menuopen .up-cg-more { color: var(--vc-text); background: var(--vc-heading-bg); }
.upt-group-eye.is-off, .up-cg-eye.is-off { color: var(--vc-fourth); }
/* Ein verstecktes Grouping liest sich auch in der Liste als versteckt. */
.up-cg-row:has(.up-cg-eye.is-off) .up-pop-label,
.up-cg-row:has(.up-cg-eye.is-off) .up-cg-dot { opacity: .45; }

/* core's .up-menu ist die Huelle -- gleicher Rahmen, Schatten, Radius und Uebergang wie jedes
   andere Dropdown. Nur die Verankerung ist hier. Oeffnet nach LINKS, weil die Zeile am rechten
   Rand ihres Behaelters sitzt. */
.upt-group-rowmenu, .up-cg-rowmenu { top: calc(100% - 2px); right: 4px; left: auto; width: 128px; padding: 6px;
  display: flex; flex-direction: column; }
/* KEIN eigener gap mehr (war 2px). Die Zeilen darin sind .up-pop-opt, und deren Abstand kommt
   seit dem 21.09. aus der einen Regel weiter oben -- 4px, wie in jedem Dropdown. Ein gap hier
   haette sich mit ihr ADDIERT: gemessen waeren es 6px geworden, also ausgerechnet in dem Menue,
   das am kompaktesten sein soll, der groesste Abstand der App. */
/* 32px, flach, nicht aus Polsterung gerechnet -- jede Menuezeile dieser App ist 32px hoch. */
.upt-group-rowmenu .up-pop-opt, .up-cg-rowmenu .up-pop-opt { justify-content: flex-start; height: 32px; padding: 0 10px; font-size: 13px; }
/* Gefahrenrot ueber den Token, wie in prompts-table.css. Dort steht die Kopie dieses Menues
   ("Byte-for-byte the Grouping popover's own row menu"), und die ist am 21.09. umgestellt
   worden -- die beiden waeren im dunklen Thema sonst auseinandergelaufen: hier #b0200c, dort
   das hellere #d25d5d von --vt-down. */
.upt-group-rowmenu .up-pop-opt.is-danger, .up-cg-rowmenu .up-pop-opt.is-danger, .ubo-actmenu .up-pop-opt.is-danger { color: var(--vt-down); }
.upt-group-rowmenu .up-pop-opt.is-danger:hover, .up-cg-rowmenu .up-pop-opt.is-danger:hover, .ubo-actmenu .up-pop-opt.is-danger:hover { background: color-mix(in srgb, var(--vt-down) 8%, transparent); }

/* Beim Ziehen wird die Zeile ihr eigener Platzhalter, statt nur zu dimmen: der echte Inhalt
   verschwindet, zwei graue Bloecke treten an seine Stelle, ein Rahmen markiert den Platz. Reiner
   Klassenwechsel, kein innerHTML-Tausch -- ein abgebrochenes Ziehen (ausserhalb losgelassen,
   Escape) kann die Zeile also nicht halb umgebaut hinterlassen. NICHT animiert: ein pulsendes
   Skelett liest sich als "laedt", und das ist es nicht. */
.up-cg-row.is-draggable { cursor: grab; }
.up-cg-row.is-dragging { border: 1px solid var(--vc-border); border-radius: 8px; cursor: grabbing; }
.up-cg-row.is-dragging > .up-cg-dot,
.up-cg-row.is-dragging > .up-pop-label,
.up-cg-row.is-dragging > .up-cg-eye,
.up-cg-row.is-dragging > .up-cg-more { display: none; }
.upt-group-sk-dot, .up-cg-sk-dot,
.upt-group-sk-text, .up-cg-sk-text { display: none; background: var(--vc-border); border-radius: 4px; }
.up-cg-row.is-dragging > .up-cg-sk-dot { display: block; width: 8px; height: 8px; border-radius: 4px; flex: 0 0 auto; }
.up-cg-row.is-dragging > .up-cg-sk-text { display: block; width: 45%; height: 12px; }
/* Greifhand und keine Textauswahl, solange irgendwo gezogen wird: ohne draggable-Attribut faellt
   auch die Auswahlsperre des Browsers weg, und der Zeiger markiert die Namen. */
.up-cg-list:has(.is-dragging) .up-cg-row { cursor: grabbing; }
.up-cg-list:has(.is-dragging), .up-cg-list:has(.is-dragging) * { user-select: none; -webkit-user-select: none; }

/* ---- Response Body ---------------------------------------------------------------------------
   Der Text einer Modellantwort. Ersetzt die drei zenith-Elemente, deren Werte aus einer Zeit
   stammen, in der es die Tokens noch nicht gab: 16px Grundschrift bei 1.8 Zeilenhoehe, 18px fuer
   JEDES <strong> (also auch mitten im Satz), und ueber 40 !important-Regeln gegen Bubbles
   Vorfahren. Hier gilt das Mass der App: 14px Text wie in jeder Karte, 1.65 Zeilenhoehe fuer
   Lauftext (Tabellen und Listen sind dichter), Betonung ueber das Gewicht statt ueber die Groesse.

   Die Tabelle ist die aus ask-mira, Wert fuer Wert: Rahmen mit Radius 10, 13.5px, Kopf in
   --vc-heading-bg, Zellen 9px/12px, jede zweite Zeile minimal getoent. Sie ist das Vorbild, weil
   sie die einzige Tabelle der App ist, die in einem Textfluss steht und dort funktioniert. */
/* 15px (Vorgabe: 1px groesser) bei Zeilenhoehe 1.75. Die 1.75 sind kein Geschmack, sondern eine
   Rechnung: 15 * 1.75 = 26.25px, und genau so hoch ist der Marken-Chip (26px). Damit passt er in
   die Zeilenbox, ohne sie zu dehnen -- vorher war die Zeile 23px und der Chip 26px, also wuchs
   JEDE Zeile mit einer Marke um 3px plus den Versatz, was wie ein halber Absatz aussah. */
.up-rb { font-size: 15px; line-height: 1.75; color: var(--vc-text); overflow-wrap: break-word; }
.up-rb-p { margin: 0 0 12px; }
.up-rb-p:last-child { margin-bottom: 0; }
/* Betonung ueber das Gewicht, nicht ueber die Groesse: <strong> steht mitten in Saetzen, und
   16px inmitten von 14px liess ganze Absaetze springen. */
.up-rb strong { font-weight: 600; }
.up-rb em { font-style: italic; }
/* Zwischentitel: eine Stufe ueber dem Text, mit Luft davor. 24px oben statt 16, weil der Titel
   einen neuen Abschnitt beginnt und nicht zum Absatz darueber gehoert. */
.up-rb-h { font-size: 15px; font-weight: 600; line-height: 1.4; letter-spacing: -0.01em;
  color: var(--vc-text); margin: 24px 0 8px; }
.up-rb-h:first-child { margin-top: 0; }
.up-rb-hr { border: 0; border-top: 1px solid var(--vc-border); margin: 20px 0; }
.up-rb-list { margin: 0 0 12px; padding-left: 22px; }
.up-rb-list li { margin: 0 0 6px; }
.up-rb-list li:last-child { margin-bottom: 0; }
.up-rb-list li::marker { color: var(--vc-third); }

/* ---- Tabellen (Werte 1:1 aus ask-mira) ---- */
.up-rb-tablewrap { display: block; width: 100%; max-width: 100%; overflow-x: auto; margin: 20px 0;
  border: 1px solid var(--vc-border); border-radius: 10px;
  -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
.up-rb-table { border-collapse: collapse; width: 100%; font-size: 13.5px; table-layout: auto; }
.up-rb-table thead th { text-align: left; font-weight: 600; color: var(--vc-muted);
  background: var(--vc-heading-bg); padding: 9px 12px; white-space: normal;
  overflow-wrap: break-word; border-bottom: 1px solid var(--vc-border); }
.up-rb-table tbody td { padding: 10px 12px; border-bottom: 1px solid var(--vc-border);
  vertical-align: top; white-space: normal; overflow-wrap: break-word; line-height: 1.6; }
.up-rb-table tbody tr:last-child td { border-bottom: 0; }
/* Die getoenten Zeilen tragen DIESELBE Farbe wie die Kopfzeile, nur mit 50% Deckkraft. Vorher war
   es --vt-row-hover, und das ist in beiden Themen praktisch derselbe Ton wie --vc-heading-bg: die
   Kopfzeile war damit von jeder zweiten Datenzeile nicht zu unterscheiden und las sich nicht mehr
   als Kopf. Ueber dieselbe Farbe gebaut und nicht ueber eine zweite, damit der Zusammenhang bleibt
   -- die Kopfzeile ist der Vollton, die Zebra-Zeilen sind sein Abklang. */
.up-rb-table tbody tr:nth-child(even) td {
  background: color-mix(in srgb, var(--vc-heading-bg) 50%, transparent); }
.up-rb-table th:first-child, .up-rb-table td:first-child { padding-left: 14px; }
.up-rb-table th:last-child,  .up-rb-table td:last-child  { padding-right: 14px; }

/* ---- Zitat-Chip ----
   Das alte Element gab ihm 24px Hoehe bei 11px Text und 12px Abstand zwischen Favicon und Text --
   ein Loch mitten im Chip. Hier: 22px Hoehe, 12px Text, 6px Abstand, und ein Rahmen statt einer
   Hintergrundfarbe, damit er im Lauftext nicht als Block liegt. */
/* EIN Mittel je Thema, nicht zwei: im Hellen der Rahmen ohne Grund, im Dunkeln der Grund ohne
   Rahmen. Der Grund liegt hell so nah am Kartenton, dass er nichts hinzufuegt, und der Rahmen liegt
   dunkel so nah am Kartenrand, dass er als zweite Kante liest. Also je Thema das, was traegt.
   Der Grund steht in der Dunkel-Regel weiter unten, hier ist er durchsichtig. */
.up-rb-cite { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 7px;
  margin: 0 1px; border: 1px solid var(--vc-border); border-radius: 7px; background: transparent;
  vertical-align: middle; cursor: pointer; max-width: 100%; box-sizing: border-box; line-height: 1;
  transition: background 120ms ease, border-color 120ms ease; }
.up-rb-cite:hover { background: var(--vc-heading-bg); border-color: var(--vc-hover-border); }
/* Im Dunkeln umgekehrt: Grund in der Feldfarbe, kein Rahmen. Der Hover verdichtet den Grund. */
.up-root[data-theme="dark"] .up-rb-cite { background: var(--vc-field); border-color: transparent; }
.up-root[data-theme="dark"] .up-rb-cite:hover { background: var(--vc-heading-bg); }
.up-rb-cite:focus-visible { outline: 2px solid var(--vc-text); outline-offset: 1px; }
/* 14 statt 13, und der Radius zieht im selben Verhaeltnis mit (3 * 14/13 = 3.23). Der Chip ist
   24px hoch, das Bild passt also weiter hinein, ohne an den Rahmen zu stossen. */
.up-rb-cite-fav { width: 14px; height: 14px; border-radius: 3.23px; flex: 0 0 auto;
  object-fit: cover; display: block; }
/* Die Nummer der Quelle (respBody cfg.nummern, 05.10.): dieselbe Zahl wie an der Kachel im
   Abschnitt "Citations". 16px im 24px-Chip, Radius 4 -- konzentrisch zu den 7 des Chips bei 3px
   Luft. Grund wie die Seite, damit sie sich vom Chip abhebt. */
.up-rb-cite-nr { flex: 0 0 auto; min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
  border-radius: var(--up-r-xs); background: var(--vc-bg); color: var(--vc-muted);
  font-size: var(--up-fs-xs); font-weight: 600; line-height: 16px; text-align: center;
  font-variant-numeric: tabular-nums; }
.up-rb-cite-txt { font-size: 12px; font-weight: 500; color: var(--vc-muted); line-height: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-rb-cite:hover .up-rb-cite-txt { color: var(--vc-text); }

/* ---- Zitat-Gruppe (mehrere Quellen hintereinander) -------------------------------------------
   Die Favicons ineinandergeschoben wie ein Stapel, dahinter "N Sources" -- dasselbe Bild, das die
   KI-Produkte von Google benutzen, und dasselbe Prinzip wie .up-stack in den Tabellen: der Stapel
   sagt "mehrere", die Zahl sagt "wie viele". Masse wie der einzelne Chip, damit beide in einer
   Zeile gleich hoch stehen. */
/* Grund und Rahmen wie beim einzelnen Zitations-Chip -- beide gehoeren zur selben Familie. */
.up-rb-cgroup { display: inline-flex; align-items: center; gap: 7px; height: 24px; padding: 0 8px 0 6px;
  margin: 0 1px; border: 1px solid var(--vc-border); border-radius: 7px; background: transparent;
  vertical-align: middle; cursor: pointer; box-sizing: border-box; line-height: 1; white-space: nowrap;
  transition: background 120ms ease, border-color 120ms ease; }
.up-rb-cgroup:hover { background: var(--vc-heading-bg); border-color: var(--vc-hover-border); }
/* Dieselbe Entscheidung wie beim einzelnen Chip: dunkel der Grund, hell der Rahmen. */
.up-root[data-theme="dark"] .up-rb-cgroup { background: var(--vc-field); border-color: transparent; }
.up-root[data-theme="dark"] .up-rb-cgroup:hover { background: var(--vc-heading-bg); }
.up-rb-cgroup:focus-visible { outline: 2px solid var(--vc-text); outline-offset: 1px; }
.up-rb-cg-favs { display: inline-flex; align-items: center; flex: 0 0 auto; }
/* 4px Ueberlappung, genau wie .up-stack-item -- ein Stapel sieht in der ganzen App gleich aus.
   Der Rahmen in Hintergrundfarbe trennt die Bilder, sonst verschmelzen zwei aehnliche Favicons. */
/* 15 statt 14, Radius mit (4 * 15/14 = 4.29). Der Rahmen in Hintergrundfarbe bleibt bei 1.5px --
   er trennt die gestapelten Bilder und haengt nicht an ihrer Groesse. */
.up-rb-cg-fav { width: 15px; height: 15px; border-radius: 4.29px; object-fit: cover; display: block;
  box-shadow: 0 0 0 1.5px var(--vc-bg); background: var(--vt-logo-bg); }
.up-rb-cg-fav + .up-rb-cg-fav { margin-left: -4px; }
.up-rb-cgroup:hover .up-rb-cg-fav { box-shadow: 0 0 0 1.5px var(--vc-heading-bg); }
.up-rb-cg-txt { font-size: 12px; font-weight: 500; color: var(--vc-muted); }
.up-rb-cgroup:hover .up-rb-cg-txt { color: var(--vc-text); }

/* Der Kasten, der die Quellen einer Gruppe auflistet. Kein Tooltip aus core: der ist einzeiliger
   Text, hier stehen Favicon und Titel je Zeile und man muss sie anklicken koennen. */
.up-rb-glist { position: absolute; z-index: 40; min-width: 240px; max-width: 380px;
  background: var(--up-surface, var(--vc-bg)); border: 1px solid var(--vc-border);
  border-radius: 11px; box-shadow: var(--up-e-2); padding: 5px;
  display: none; flex-direction: column; gap: 1px; }
.up-rb-glist.is-open { display: flex; }
.up-rb-glist button { display: flex; align-items: center; gap: 9px; width: 100%; border: 0;
  background: transparent; font: inherit; font-size: 13px; text-align: left; padding: 7px 8px;
  border-radius: 8px; cursor: pointer; color: var(--vc-text); min-width: 0; }
.up-rb-glist button:hover { background: var(--vc-heading-bg); }
.up-rb-glist img { width: 16px; height: 16px; border-radius: 4px; flex: 0 0 16px; object-fit: cover;
  display: block; background: var(--vt-logo-bg); }
.up-rb-glist span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/* ---- Markenauszeichnung ----
   Das alte Element zeichnete einen gestrichelten Rahmen als eingebettetes SVG, dessen Groesse ein
   ResizeObserver und ein MutationObserver nachfuehrten -- weil ein CSS-Rahmen am umbrechenden
   Inline-Element unten abgeschnitten wurde. Der Chip hier bricht nicht um (white-space: nowrap),
   damit ist der Rahmen wieder CSS und die zwei Beobachter entfallen. */
/* 26px statt 22 (Vorgabe: 4px hoeher) und ein 2px gestrichelter Rahmen -- die Strichelung ist die
   Idee der alten Elemente, dort noch als eingebettetes SVG mit zwei Beobachtern, weil ein
   CSS-Rahmen am umbrechenden Inline-Element abgeschnitten wurde. Der Chip hier bricht nicht um
   (white-space: nowrap), damit genuegt border-style: dashed.
   vertical-align: -6px statt -4: der Chip ist 4px hoeher geworden und saesse sonst zu hoch. */
/* vertical-align: middle statt eines Versatzes in Pixeln: der Chip wird an der Mitte des
   Fliesstextes ausgerichtet, also sitzt seine Schrift auf derselben Linie wie der Text drumherum.
   Der Pixelversatz vorher war fuer EINE Schriftgroesse gerechnet und verrutschte bei jeder anderen.
   line-height: 1 im Chip, damit seine eigene Schrift die Chiphoehe nicht noch einmal aufblaeht. */
.up-rb-brand { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 8px;
  margin: 0 1px; border: 2px dashed var(--vt-brand-border); border-radius: 8px;
  background: var(--vt-brand-bg); vertical-align: middle; cursor: pointer; white-space: nowrap;
  line-height: 1; box-sizing: border-box;
  transition: background 120ms ease, border-color 120ms ease; }
.up-rb-brand:hover { background: var(--vt-brand-bg-hover); border-color: var(--vt-brand-border-hover); }
.up-rb-brand:focus-visible { outline: 2px solid var(--vc-text); outline-offset: 1px; }
.up-rb-brand-ic { width: 14px; height: 14px; border-radius: 4px; flex: 0 0 auto; object-fit: cover;
  display: block; }
.up-rb-brand-txt { font-size: 13.5px; font-weight: 600; color: var(--vc-text); line-height: 1; }

/* ---- Schmal ----
   Gemessen an der eigenen Box. Die alten Elemente hatten min-width: 680px an der Tabelle: auf dem
   Telefon schob das die ganze Seite zur Seite, weil der Rahmen um die Tabelle mitwuchs. Hier
   scrollt nur der Rahmen, und die Zellen duerfen enger werden statt die Seite zu sprengen. */
.up-root.is-narrow .up-rb { font-size: 13.5px; }
.up-root.is-narrow .up-rb-table { font-size: 13px; }
.up-root.is-narrow .up-rb-table thead th { white-space: nowrap; padding: 8px 10px; }
.up-root.is-narrow .up-rb-table tbody td { padding: 8px 10px; min-width: 84px; }
.up-root.is-narrow .up-rb-tablewrap { margin: 16px 0; }
.up-root.is-narrow .up-rb-h { margin-top: 20px; font-size: 14.5px; }
/* Der Chip darf schmal werden, aber nicht verschwinden: 132px zeigen Favicon plus Domain. */
.up-root.is-narrow .up-rb-cite { max-width: 132px; }
.up-root.is-vnarrow .up-rb-cite { max-width: 108px; }

/* ======================================================================
   Hintergrundbild: Raster, Bogen, vier Kreuzmarken, vier Lichter
   ======================================================================
   Vier Ebenen und nicht eine. Die Vorlage (onboarding-bg.svg) traegt vier Gruppen mit ganz
   verschiedenem Gewicht, und das Gewicht muss je THEMA anders sein: das Raster ist auf hellem
   Grund fast unsichtbar (gezeichnet mit 2% Schwarz auf Weiss), die Marken sind auf dunklem Grund
   zu hell. Mit EINER Ebene haengen alle am selben Wert, und wer die eine hebt, hebt die anderen
   mit. Beides so gemeldet am 26.08.

   Es kommt als MASKE und nicht als Bild: die Vorlage ist fuer DUNKEL gezeichnet -- weisse Striche
   bei 2% Deckkraft, ein weisser Bogen. Auf hellem Grund waere davon nichts zu sehen. Als Maske
   bleibt die Struktur im Alphakanal erhalten, und die FARBE kommt von hier, also je Thema die
   richtige.

   Die Lichter sind KEINE Maske, sondern vier Verlaeufe. Leise heisst auf dunklem Grund eine Spanne
   von vier Graustufen fuer einen Verlauf ueber achthundert Pixel; ein Bitmap zerfaellt dabei in
   drei harte Hoehenlinien, und weil Chrome grosse Weichzeichner auf einem verkleinerten Puffer
   rechnet, sind diese Linien eckig -- gemeldet als "vage viele Vierecke". Verlaeufe dithert Skia,
   dieselbe Spanne bleibt glatt. Feldvergleich: _h_uob_band.html.

   HIER stehen nur Bild und Gewicht. Lage im Stapel, Ein- und Ausblenden und ob es auf dem Telefon
   ueberhaupt erscheint, entscheidet die Komponente an ihrem eigenen Huellelement -- die zwei
   Nutzer wollen davon Verschiedenes (Onboarding blendet beim Laden ein, Mira blendet aus, sobald
   ein Chat offen ist).

   Die vier Werte sind ERZEUGT. Quelle ist onboarding-bg.svg, Werkzeug .bg_split.py; wer das Bild
   aendert, aendert die Datei und laesst das Skript laufen. Von Hand tippt hier niemand.
   Als Adresse im Stylesheet und nicht als eigene Datei, weil sonst eine weitere Anfrage in der
   Kette haengt: Lader-JS, dann CSS, dann Maske entdecken, dann SVG holen -- auf der echten Seite
   waren das 1-2 Sekunden, in denen das Bild nachtraeglich aufpoppte. */
/* An .up-root und NICHT an :root. Das war ein Versuch und ein Fehler: die Werte sehen themenfrei
   aus, aber --up-bgart-licht enthaelt color-mix(... var(--vc-muted) ...), und ein var() INNERHALB
   einer eigenen Eigenschaft wird dort aufgeloest, wo sie DEKLARIERT ist. Auf :root ist --vc-muted
   nicht definiert, also wurde die ganze Eigenschaft ungueltig und verworfen -- gemessen:
   getComputedStyle(html).getPropertyValue("--up-bgart-licht") kam leer zurueck, waehrend bogen,
   raster und marken (reine Adressen, kein var) sauber ansprachen. Die Lichter waeren damit auch im
   Onboarding und hinter Miras Startschirm verschwunden.
   Wer die Grafik ausserhalb einer Komponente benutzen will, gibt seiner Huelle die Klasse
   .up-root -- so macht es die Landingpage. */
.up-root {
  --up-bgart-licht:
    radial-gradient(ellipse 61.53% 73.26% at 94.44% 0.56%, color-mix(in srgb, var(--vc-muted) 6.160%, transparent), color-mix(in srgb, var(--vc-muted) 0%, transparent)),
    radial-gradient(ellipse 61.53% 73.26% at 20.49% 85.39%, color-mix(in srgb, var(--vc-muted) 6.160%, transparent), color-mix(in srgb, var(--vc-muted) 0%, transparent)),
    radial-gradient(ellipse 56.04% 75.84% at -3.12% 19.44%, color-mix(in srgb, var(--vc-muted) 10.368%, transparent), color-mix(in srgb, var(--vc-muted) 0%, transparent)),
    radial-gradient(ellipse 56.04% 75.84% at 94.44% 74.16%, color-mix(in srgb, var(--vc-muted) 10.368%, transparent), color-mix(in srgb, var(--vc-muted) 0%, transparent));
  --up-bgart-bogen: url("data:image/svg+xml,%3Csvg viewBox='0 0 1440 890' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M719.5 760.5C1018.92 760.5 1289.98 814.385 1486.15 901.485C1584.24 945.036 1663.59 996.88 1718.41 1054.42C1773.23 1111.97 1803.5 1175.18 1803.5 1241.5C1803.5 1307.82 1773.23 1371.03 1718.41 1428.58C1663.59 1486.12 1584.24 1537.96 1486.15 1581.51C1289.98 1668.61 1018.92 1722.5 719.5 1722.5C420.077 1722.5 149.024 1668.61 -47.154 1581.51C-145.244 1537.96 -224.593 1486.12 -279.412 1428.58C-334.229 1371.03 -364.5 1307.82 -364.5 1241.5C-364.5 1175.18 -334.229 1111.97 -279.412 1054.42C-224.593 996.88 -145.244 945.036 -47.154 901.485C149.024 814.385 420.077 760.5 719.5 760.5Z' stroke='white' stroke-opacity='0.11'/%3E%3C/svg%3E");
  --up-bgart-raster: url("data:image/svg+xml,%3Csvg viewBox='0 0 1440 890' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='0.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='80.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='160.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='240.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='320.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='400.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='480.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='560.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='640.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='720.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='800.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='880.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='960.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1040.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1120.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1200.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1280.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='0.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='80.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='160.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='240.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='320.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='400.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='480.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='560.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='640.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3Crect x='1360.25' y='720.25' width='79.5' height='79.5' stroke='white' stroke-opacity='0.06' stroke-width='0.5'/%3E%3C/svg%3E");
  --up-bgart-marken: url("data:image/svg+xml,%3Csvg viewBox='0 0 1440 890' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg opacity='0.31'%3E%3Cpath d='M160 237L160 170' stroke='url(%23paint0_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M160 241L160 308' stroke='url(%23paint1_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M158 240L91 240' stroke='url(%23paint2_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M163 240L230 240' stroke='url(%23paint3_linear_0_9475)' stroke-linecap='round'/%3E%3C/g%3E%3Ccircle cx='160' cy='240' r='3' fill='%23B9B9B9'/%3E%3Cg opacity='0.31'%3E%3Cpath d='M400 398L400 331' stroke='url(%23paint4_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M400 402L400 469' stroke='url(%23paint5_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M398 401L331 401' stroke='url(%23paint6_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M403 401L470 401' stroke='url(%23paint7_linear_0_9475)' stroke-linecap='round'/%3E%3C/g%3E%3Ccircle cx='400' cy='401' r='3' fill='%23B9B9B9'/%3E%3Cg opacity='0.31'%3E%3Cpath d='M1119 157L1119 90' stroke='url(%23paint8_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M1119 161L1119 228' stroke='url(%23paint9_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M1117 160L1050 160' stroke='url(%23paint10_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M1122 160L1189 160' stroke='url(%23paint11_linear_0_9475)' stroke-linecap='round'/%3E%3C/g%3E%3Ccircle cx='1119' cy='160' r='3' fill='%23B9B9B9'/%3E%3Cg opacity='0.31'%3E%3Cpath d='M1279 478L1279 411' stroke='url(%23paint12_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M1279 482L1279 549' stroke='url(%23paint13_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M1277 481L1210 481' stroke='url(%23paint14_linear_0_9475)' stroke-linecap='round'/%3E%3Cpath d='M1282 481L1349 481' stroke='url(%23paint15_linear_0_9475)' stroke-linecap='round'/%3E%3C/g%3E%3Ccircle cx='1279' cy='481' r='3' fill='%23B9B9B9'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_0_9475' x1='160.498' y1='170' x2='160.498' y2='237' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint1_linear_0_9475' x1='160.498' y1='308' x2='160.498' y2='241' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint2_linear_0_9475' x1='91' y1='240.498' x2='158' y2='240.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint3_linear_0_9475' x1='230' y1='240.498' x2='163' y2='240.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint4_linear_0_9475' x1='400.498' y1='331' x2='400.498' y2='398' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint5_linear_0_9475' x1='400.498' y1='469' x2='400.498' y2='402' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint6_linear_0_9475' x1='331' y1='401.498' x2='398' y2='401.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint7_linear_0_9475' x1='470' y1='401.498' x2='403' y2='401.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint8_linear_0_9475' x1='1119.5' y1='90' x2='1119.5' y2='157' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint9_linear_0_9475' x1='1119.5' y1='228' x2='1119.5' y2='161' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint10_linear_0_9475' x1='1050' y1='160.498' x2='1117' y2='160.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint11_linear_0_9475' x1='1189' y1='160.498' x2='1122' y2='160.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint12_linear_0_9475' x1='1279.5' y1='411' x2='1279.5' y2='478' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint13_linear_0_9475' x1='1279.5' y1='549' x2='1279.5' y2='482' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint14_linear_0_9475' x1='1210' y1='481.498' x2='1277' y2='481.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint15_linear_0_9475' x1='1349' y1='481.498' x2='1282' y2='481.498' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='white' stop-opacity='0'/%3E%3Cstop offset='0.377' stop-color='white' stop-opacity='0.255'/%3E%3Cstop offset='0.675' stop-color='white' stop-opacity='0.511'/%3E%3Cstop offset='0.925' stop-color='white' stop-opacity='0.705'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E");
}
.up-bgart-licht, .up-bgart-bogen, .up-bgart-raster, .up-bgart-marken,
.up-bgart-flimmer { position: absolute; inset: 0; }
/* Nur die drei Masken. Die Lichter haben keine, und background-color wuerde ihre Flaeche voll
   ausmalen, weil nichts sie beschneidet. */
.up-bgart-bogen, .up-bgart-raster, .up-bgart-marken {
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  /* Dieselbe Groesse und Lage fuer alle drei: nur so liegen sie deckungsgleich. */
  -webkit-mask-size: cover; mask-size: cover;
  /* --vc-text dreht sich mit dem Thema: dunkel ist es hell, hell ist es dunkel. Genau das braucht
     eine Vorlage, die fuer weiss auf dunkel gezeichnet wurde. */
  background-color: var(--vc-text);
}
.up-bgart-bogen  { -webkit-mask-image: var(--up-bgart-bogen);  mask-image: var(--up-bgart-bogen); }
.up-bgart-raster { -webkit-mask-image: var(--up-bgart-raster); mask-image: var(--up-bgart-raster); }
.up-bgart-marken { -webkit-mask-image: var(--up-bgart-marken); mask-image: var(--up-bgart-marken); }
/* Die Lichter in --vc-muted und leise. Sie sind in der Vorlage mit mittlerem Grau gemalt; mit
   nahezu Weiss gefuellt hoben sie den ganzen Grund an, und weil sie fast die ganze Flaeche decken,
   sah man beim Wechsel auf einen Schritt ohne Bild einen Sprung. Gerechnet in L* auf dunklem
   Grund, mit dem staerksten Licht der Vorlage:
     --vc-text, Ebene 1.0     +16.6   viel zu hell
     --vc-muted, Ebene 1.0    +11.5   immer noch ein Sprung
     --vc-muted, Ebene 0.2     +1.3   so leise wie das Raster (dL* 1.9)
   Auf #121212 statt #1b1b1b gerechnet ergibt dasselbe: Raster 1.83, Lichter 1.29 -- der Grund von
   Mira braucht also keine eigenen Werte. */
.up-bgart-licht { background-image: var(--up-bgart-licht); opacity: .2; }
.up-root[data-theme="dark"] .up-bgart-licht { opacity: .2; }
/* Das Raster soll im Hellen GENAUSO wirken wie im Dunkeln -- nicht denselben Alphawert haben,
   sondern denselben wahrgenommenen Abstand zum Grund. Gerechnet in L*, weil der Augenabstand dort
   linear ist:
     dunkel  Strich #e0e0e0 auf #1b1b1b, wirksames Alpha 0.06 x 0.33 = 0.0198  ->  dL* 1.94
     hell    Strich #1f1f1b auf #ffffff, dafuer noetiges Alpha 0.025           ->  Ebene 0.42
   Mit voller Ebene waere es dL* 4.67, also zweieinhalbmal so kraeftig -- genau das war gemeldet. */
.up-bgart-bogen  { opacity: .45; }
.up-bgart-raster { opacity: .42; }
/* Die vier Sternmarken sind NICHT MEHR EINGESETZT -- an ihrer Stelle liegt das Flimmerraster
   darunter (07.09. angefordert). Bild, Klasse und Gewicht bleiben trotzdem stehen: der Name ist
   Teil des Vertrags nach aussen (an einem .up-bgart-*-Namen kann CSS in der Bubble-App haengen),
   und wer die Marken zurueckwill, haengt das Element wieder ein, ohne dass hier etwas
   nachgezogen werden muss. */
.up-bgart-marken { opacity: .45; }

/* ---------- Flimmerraster ----------
   Das Bild malt UC.makeFlickerGrid in ein Canvas (siehe dort: warum ein Canvas, was gegen die
   Vorlage von magicui geaendert ist, und was fuer die Leistung getan ist). HIER stehen nur die
   zwei Dinge, die zur Kaskade gehoeren: die Farbe und die Maske.

   DIE FARBE ueber color und nicht als Angabe im Aufruf: das Canvas liest sie von seinem
   Elternteil und noch einmal bei jedem Themenwechsel. Der angeforderte Aufruf traegt
   color="rgb(0, 0, 0)" -- das ist die richtige Farbe fuer HELL und im Dunkeln unsichtbar. Ueber
   --vc-text ist es im Hellen genau dieses Schwarz (#1f1f1b) und im Dunkeln die Textfarbe des
   dunklen Themas, und es gibt keinen zweiten Ort, an dem jemand eine Farbe pflegen muesste.

   KEINE DAEMPFUNG MEHR UEBER DIE EBENE. Hier stand opacity .388 / .32, damit die Deckkraft des
   Kits (damals 0.25) in beiden Themen auf denselben wahrgenommenen Abstand kommt. Angefordert
   ist jetzt maxOpacity 0.4, und zwar als die Zahl, die magicui auch benutzt -- dort gibt es
   keine Ebene darueber. Also ist die Deckkraft des Kits die ganze Wahrheit: 0.4, wie bestellt.
   Die Punkte sind dafuer viel kleiner und weiter auseinander (3px auf 23px Abstand statt 5 auf
   18), sie bedecken also 1.7 Prozent der Flaeche statt 7.7 -- deutlich weniger Farbe insgesamt,
   bei kraeftigeren einzelnen Punkten. Wird es im Dunkeln zu laut, ist es EINE Zeile hier.

   DIE MASKE HAELT DIE MITTE FREI. Dort steht der Inhalt der Seite, und ein Raster hinter einer
   Ueberschrift ist kein Hintergrund mehr, sondern Unruhe. Aussen voll, innen nichts, mit einem
   langen Verlauf dazwischen -- eine harte Kante waere als Kreis zu sehen.

   ZWEIMAL GEAENDERT, und die Zahlen sagen warum. Zuerst stand hier
       ellipse 58% 54%, transparent 0%, #000 100%
   also ein Verlauf, der SOFORT beim Mittelpunkt anfaengt. Auf 1440x890 heisst das: am Rand des
   Textblocks (240px von der Mitte) liegt die Maske schon bei 29 Prozent, es stehen also Punkte
   mit knapp einem Drittel Staerke direkt hinter der Ueberschrift. Im Dunkeln fallen die kaum
   auf, im Hellen sind es dunkle Punkte auf Weiss -- gemeldet als "wirkt gerade im Lightmode
   noch etwas unruhig mittig im Bild".
   Jetzt
       ellipse 74% 70%, transparent 0%, transparent 30%, #000 100%
   Der erste Teil ist groesser (Radien 1066 x 623 statt 835 x 481), und der zweite ist neu: die
   ersten 30 Prozent sind VOLLSTAENDIG frei. Auf 1440x890 sind das 320px zu den Seiten und 187px
   nach oben und unten -- der Textblock (480px breit) liegt damit ganz in der freien Zone, und
   der Verlauf beginnt erst dahinter. Am Rand der Karte (480px von der Mitte) steht die Maske
   bei 21 Prozent statt bei 57.

   EINE Geometrie fuer beide Themen und nicht zwei. Gemeldet war das Helle -- das ist der
   strengere Fall, weil dunkle Punkte auf Weiss mehr auffallen als helle auf Dunkel. Wer nach
   dem strengeren Fall bemisst, hat den leichteren mit erledigt; zwei Masken waeren zwei
   Wahrheiten fuer dieselbe Frage. */
.up-bgart-flimmer {
  color: var(--vc-text);
  -webkit-mask-image: radial-gradient(ellipse 74% 70% at 50% 46%,
                        transparent 0%, transparent 30%, #000 100%);
          mask-image: radial-gradient(ellipse 74% 70% at 50% 46%,
                        transparent 0%, transparent 30%, #000 100%);
}
/* Dunkel: das Raster auf ein Drittel -- die Vorlage zeichnet es mit 6%, gemeint waren 2%, und
   genau die kommen so heraus. Die Marken auf die Haelfte, sie waren zu hell. Der Bogen voll, er
   ist die einzige Linie, die tragen soll. */
.up-root[data-theme="dark"] .up-bgart-bogen  { opacity: 1; }
.up-root[data-theme="dark"] .up-bgart-raster { opacity: .33; }
.up-root[data-theme="dark"] .up-bgart-marken { opacity: .5; }

/* ══ Tarifkarten (UC.makePlans, 28.09.) ═════════════════════════════════════════════════════════
   Die Karte der Landingpage (landing-hero.css, die ulh-preis-Regeln), in die App geholt. Gleich
   geblieben sind der Aufbau, die Abstaende (Polster 28/26/30, Luecke 20, Name 6 ueber der
   Unterzeile, Betrag 18 darunter, Knopf 20, Liste 22 mit 9 zwischen den Zeilen), das Etikett auf
   der mittleren Karte, der Ring und die aufklappende Ersparnis.

   AUF DIE SKALA GEZOGEN hat sich, was die Landingpage mit eigener Typoskala fuehren darf und die
   App nicht (CLAUDE.md 1c):
       Radius 18        -> 16  (--up-r-2xl)
       Name 17          -> 16  (--up-fs-xl)
       Betrag 38        -> var(--up-fs-display), also weiter 38: das ist die Stufe fuer genau
                           diese Rolle, "die EINE grosse Zahl einer Karte"
       Merkmale 13.5    -> 13  (--up-fs-m)
       Etikett 11.5     -> 11  (--up-fs-xs)
       Uebergang 220ms  -> --up-t-2 (200ms)
       Knopf            -> .up-btn-pri.is-lg aus core (40 hoch, Radius 12, 14/600) statt des
                           Landing-Knopfs (40, Radius 10, 14/500) -- ein zweiter Hauptknopf
                           neben dem der App waere genau der Zwilling, den core verhindern soll
   Die Onboarding-Fassung (15/26, dichter gesetzt) ist KEIN Vorbild hier: dort muss der ganze
   Ablauf auf 760px Hoehe passen.

   FARBEN nur aus Marken: die Karte steht in der App auf .up-root, und in einem Fenster am body
   (settings-billing) haengt die Marke an dessen .up-root-Klasse. Die Ersparnis traegt dasselbe
   Gruen wie Landingpage und Onboarding -- mit demselben Rueckfall wie dort, weil --vc-pos in
   core nie gesetzt wurde. */
.up-plans { display: flex; flex-direction: column; min-width: 0; }
.up-plans-switch { display: flex; justify-content: center; }
.up-plans-row {
  margin-top: 26px;
  display: grid; grid-template-columns: repeat(var(--up-plan-cols, 3), minmax(0, 1fr)); gap: 20px;
  align-items: start;
}
/* Untereinander, sobald die REIHE schmal ist (widthTiers am .up-plans, siehe makePlans). Die
   Unterzeile braucht dann keine Mindesthoehe mehr -- die haelt nur drei Koepfe nebeneinander auf
   einer Linie. */
.up-plans.is-narrow .up-plans-row { grid-template-columns: minmax(0, 1fr); gap: 22px; }
.up-plans.is-narrow .up-plan-desc { min-height: 0; }
.up-plan {
  position: relative; display: flex; flex-direction: column; text-align: left; min-width: 0;
  padding: 28px 26px 30px; border: 1px solid var(--vc-border); border-radius: 16px;
  background: var(--vc-bg); color: var(--vc-text); box-shadow: var(--up-e-1);
}
/* Zweiter Ring statt 2px Rahmen, sonst spraenge der Inhalt um einen Pixel gegen die Nachbarn
   (derselbe Kniff wie auf der Landingpage und im Onboarding). Welche Karte ihn traegt, entscheidet
   makePlans: der Tarif des Nutzers, sonst die mittlere. */
.up-plan.is-ring { border-color: var(--up-accent); box-shadow: 0 0 0 1px var(--up-accent), var(--up-e-1); }
.up-plan-tag {
  position: absolute; top: -11px; left: 26px; height: 22px; padding: 0 10px; border-radius: 999px;
  background: var(--up-accent); color: var(--up-accent-fg);
  font-size: 11px; font-weight: 600; letter-spacing: .02em; white-space: nowrap;
  display: inline-flex; align-items: center;
}
.up-plan-name { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: var(--vc-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Die Unterzeile sagt, fuer WEN der Tarif ist -- sie gehoert vor die Zahl. Mindesthoehe fuer zwei
   Zeilen, damit die drei Betraege nebeneinander auf einer Hoehe stehen. */
.up-plan-desc { margin: 6px 0 0; font-size: 14px; line-height: 1.45; color: var(--vc-third);
  text-wrap: pretty; min-height: 40px; }
.up-plan-price { margin-top: 18px; display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.up-plan-amt { font-size: var(--up-fs-display); font-weight: 600; letter-spacing: -.035em;
  line-height: 1.05; font-variant-numeric: tabular-nums; color: var(--vc-text); white-space: nowrap; }
.up-plan-per { font-size: 14px; color: var(--vc-third); white-space: nowrap; }
/* Klappt auf und zu (0fr -> 1fr) statt zu erscheinen: so schiebt sie die Kartenhoehe weich mit. */
.up-plan-note { display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows var(--up-t-2) var(--up-ease), opacity var(--up-t-2) var(--up-ease); }
.up-plan-note > span { overflow: hidden; min-height: 0; display: block; padding-top: 6px;
  font-size: 13px; font-weight: 500; color: var(--vc-pos, #2e9e63); }
.up-plan-note.is-on { grid-template-rows: 1fr; opacity: 1; }
.up-plan-cta { margin-top: 20px; display: flex; }
.up-plan-cta > .up-btn-pri { width: 100%; }
/* "Current plan" an der Stelle des Knopfes: dieselbe Geometrie wie .up-btn-pri.is-lg (40 hoch,
   Radius 12, Polster 20, Abstand 9, 14/600), damit die Karte nicht kuerzer wird -- aber als
   ruhige Flaeche und ohne Zeiger, weil es hier nichts zu kaufen gibt. Der Haken in der Tinte der
   Akzentfarbe, wie die You-Marke in team-orga. */
.up-plan-mine {
  width: 100%; height: 40px; padding: 0 20px; border-radius: 12px; gap: 9px;
  border: 1px solid var(--vc-border); background: var(--vc-heading-bg); color: var(--vc-text);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap;
  cursor: default; user-select: none;
}
.up-plan-mine svg { width: 15px; height: 15px; flex: 0 0 auto; display: block; color: var(--up-accent-ink); }
.up-plan-feats { margin: 22px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; }
.up-plan-feat { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; line-height: 1.4; color: var(--vc-third); }
.up-plan-feat b { font-weight: 600; color: var(--vc-text); }
.up-plan-ic { display: inline-flex; flex: 0 0 auto; margin-top: 2px; color: var(--vc-text); }
.up-plan-ic svg { width: 15px; height: 15px; display: block; }
.up-plans-foot { margin: 30px 0 0; font-size: 13px; line-height: 1.45; color: var(--vc-third); }
/* Das Skelett: Polster, Rahmen und Radius der echten Karte, damit das Raster beim Eintreffen der
   Tarife stehen bleibt. Kein Schatten -- eine Huelle soll nicht wie eine fertige Karte wirken. */
.up-plan.is-skel { gap: 10px; box-shadow: none; }
/* Hoehen und Abstaende nach den Zeilen der Karte: Name 16, Unterzeile 2 x 12, Betrag 38 (18
   darueber), Knopf 40 (20 darueber), Liste mit 22 darueber. Die Balken sind Platzhalter, keine
   Nachbildung -- es geht darum, dass die Kartenhoehe beim Eintreffen stehen bleibt. */
.up-plan-sk-name { width: 44%; height: 16px; }
.up-plan-sk-desc { width: 86%; height: 12px; }
.up-plan-sk-desc.is-kurz { width: 58%; }
.up-plan-sk-amt  { width: 52%; height: 38px; border-radius: 8px; margin-top: 16px; }
.up-plan-sk-note { width: 46%; height: 12px; }
.up-plan-sk-btn  { width: 100%; height: 40px; border-radius: 12px; margin-top: 14px; }
.up-plan-sk-z    { width: 88%; height: 12px; }
.up-plan-sk-btn + .up-plan-sk-z { margin-top: 16px; }
.up-plan-sk-z.is-kurz { width: 64%; }

/* ---------- Das Tarif-Fenster (UC.makePlanDialog, 28.09.) ----------
   Stand bis zum 28.09. als .ubl-* in settings-billing.css, dieselben Werte. Die Schale ist core's
   Modal; hier steht nur, was dieses Fenster anders macht.
   z-index 100002: ueber dem Access Gate (100001), das es als seine Tarifliste oeffnet, und ueber
   core's anderen Fenstern (100000). Zwei Klassen gegen die eine von .up-topicmodal-backdrop. */
.up-plandlg.up-topicmodal-backdrop { z-index: 100002; }
/* 1160 (28.09. angefordert: "etwas mehr max width"; vorher 1000): drei Karten zu gut 350 plus
   zwei Luecken zu 20 plus das Polster der Karte. Bei 1000 standen sie knapp 300 breit, die Karten
   der Landingpage bei 1280 Fensterbreite knapp 390 -- darunter wird ihre Merkmalsliste eng.
   Zwei Klassen, weil .up-topicmodal-card eine eigene Breite (420) setzt. */
.up-plandlg .up-plandlg-card { width: 100%; max-width: 1160px; }
.up-plandlg-card:focus { outline: 0; }
.up-plandlg-body { min-width: 0; }
/* Der Fehlersatz unter den Karten steht nur da, wenn ein Klick keinen Empfaenger fand; zu ist er
   eine Zeile der Hoehe 0 und naehme trotzdem die Luecke der Karte (32) mit. */
.up-plandlg-card > .up-plandlg-err:not(.is-on) { display: none; }
.up-plandlg-card > .up-plandlg-err.is-on { margin-top: 0; }
@media (prefers-reduced-motion: reduce){ .up-plan-note { transition: none; } }
