/* ========= elbephant shared styles (Bootstrap-first) ========= */

/* Fonts */
@font-face {
  font-family: 'KoHo';
  src: url('../fonts/KoHo/KoHo-Regular.woff2') format('woff2'),
       url('../fonts/KoHo/KoHo-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'KoHo';
  src: url('../fonts/KoHo/KoHo-Bold.woff2') format('woff2'),
       url('../fonts/KoHo/KoHo-Bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Base */
html{
  overflow-y: scroll; /* Scrollbar-Platz immer reservieren, damit die Containerbreite zwischen Seiten mit/ohne Scrollbalken nicht springt */
}
body{
  font-family: 'KoHo', sans-serif;
  background:#f7fafd;
}

/* Headline */
.page-header{
  font-weight:700;
  letter-spacing:-1px;
  margin-bottom:38px;
  text-align:left;
  color:#111;
}

/* Cards (Bootstrap look + eure Rundung/Schatten) */
.dev-card{
  border-radius:12px;
  box-shadow:0 4px 18px 0 rgba(49,130,206,0.07);
  border:2px solid #fff;
  transition:border-color .2s;
  height:100%;
  display:flex;
  flex-direction:column;
  background:#fff;
  position:relative;
  padding:1.25rem;
  overflow:hidden;
}

/* Title in cards */
.card-title{
  font-weight:600;
  display:flex;
  align-items:center;
  gap:6px;
  z-index:1;
  position:relative;
  color:#111;
}

/* Lists */
.dev-list{
  list-style:none;
  padding:0;
  margin-bottom:0;
  z-index:1;
  position:relative;
}
.dev-list li{ margin-bottom:.6rem; font-size:1.07rem; }
.dev-list li:last-child{ margin-bottom:0; }

.pfeil-icon{ color:#222; margin-right:8px; }
.icon-plus{ color:#bbb; margin-right:8px; transition:color .15s; }

/* Link style */
.dev-link{
  color:#222;
  text-decoration:none;
  border-bottom:2px solid transparent;
  transition:border-color .15s;
}

/* Theme favicons */
.theme-favicon{
  width:15px; height:15px;
  vertical-align:middle;
  margin-left:2px;
  margin-bottom:3px;
  display:inline;
  background:none !important;
  padding:0;
}
.theme-favicon-default{
  color:#8995a2;
  font-size:13px;
  margin-bottom:0;
}

/* Background icons in cards */
.has-wp-bg::before{
  content:"\f19a";
  font-family:"Font Awesome 6 Brands";
  font-weight:400;
  font-size:6.2rem;
  color:#b8c1ce33;
  position:absolute;
  right:20px;
  bottom:0;
  transform:translateY(20px);
  z-index:0;
  pointer-events:none;
  user-select:none;
}
.has-multisite-bg::before{
  content:"\f6ff";
  font-family:"Font Awesome 5 Free";
  font-weight:900;
  font-size:6.2rem;
  color:#b8c1ce33;
  position:absolute;
  right:20px;
  bottom:0;
  transform:translateY(20px);
  z-index:0;
  pointer-events:none;
  user-select:none;
}

/* Hover border colors per section */
.color-projekte .dev-card:hover{ border-color:#006f83 !important; }
.color-play     .dev-card:hover{ border-color:#8f58af !important; }
.color-bbc      .dev-card:hover{ border-color:#912c5d !important; }
.blueprints     .dev-card:hover{ border-color:#3453b2 !important; }
.projekte       .dev-card:hover{ border-color:#0092AE !important; }

@media (hover:none){
  .dev-card:hover{ border-color:inherit; }
}

/* Link hover underline per section */
.color-projekte .dev-link:hover,
.color-projekte .dev-link:focus{ border-bottom-color:#006f83; }

.color-play .dev-link:hover,
.color-play .dev-link:focus{ border-bottom-color:#8f58af; }

.color-bbc .dev-link:hover,
.color-bbc .dev-link:focus{ border-bottom-color:#912c5d; }

.blueprints .dev-link:hover,
.blueprints .dev-link:focus{ border-bottom-color:#3453b2; }

.projekte .dev-link:hover,
.projekte .dev-link:focus{ border-bottom-color:#0092AE; }

/* Icon colors (wie bei dir) */
.color-projekte .icon-projekte,
.color-projekte .fa-user-tie{ color:#006f83; }

.projekte .icon-eigeneprojects,
.projekte .fa-rocket{ color:#0092AE; }

.color-play .icon-play,
.color-play .fa-flask{ color:#8f58af; }

.color-bbc .icon-bbc{ color:#912c5d; }

.blueprints .icon-blueprints{ color:#3453b2; }

/* Einzelne Utility-Icons */
.icon-dev-add{
  color:#0092AE;
}

/* Header icon row (InfiniteWP / All-Inkl) */
.icon-row{
  display:flex;
  align-items:center;
  gap:10px;
}
.icon-img{
  height:24px;
  width:auto;
  display:block;
  object-fit:contain;
  margin:0;
  padding:0;
  background:none;
  box-shadow:none;
  border:none;
}
.all-inkl-link{
  display:inline-flex;
  align-items:center;
}
.all-inkl-link:hover .icon-img{
  filter:brightness(0.8) drop-shadow(0 1px 3px rgba(0,0,0,0.17));
}

/* Forms – Bootstrap feel, nur leichte Rundung */
.form-control, .form-select{ border-radius:12px !important; }
.btn{ border-radius:12px !important; font-weight:600; }

/* Damit Form/Alerts über dem Background-Icon liegen */
.content-layer{ position:relative; z-index:1; }
.alert{ position:relative; z-index:2; }

.trello-link, .drive-link {
    text-decoration: none;
}

.trello-link i, .drive-link i {
    text-decoration: none;
}

/* ========= Utilities (Bootstrap-Erweiterungen) ========= */
.fs-14{ font-size:14px; }
.text-break-all{ word-break:break-all; }
.tooltip-icon{ font-size:12px; cursor:help; }
.icon-md{ font-size:.95rem; }
.icon-wp{ font-size:.95rem; color:#21759b; }
.make-favicon{ width:18px; height:18px; vertical-align:middle; }
.site-footer{ font-size:.8rem; }

/* ========= Login (index.php) ========= */
body.login-page{
  min-height:100vh;
  display:flex;
  align-items:center;
  background:#f5f5f7;
}
.login-card{
  max-width:360px;
  width:100%;
  margin:0 auto;
  background:#fff;
  border:1px solid rgba(0,0,0,.08);
  border-radius:16px;
  padding:2rem;
}

/* ========= Automatisierungen (automatisierungen.php) ========= */
.page-subtitle{ font-size:.95rem; }
.section-card{ background:#fff; border:1px solid rgba(0,0,0,.08); border-radius:16px; padding:12px 14px; }
.is-loading{ pointer-events:none; opacity:.9; }
.form-check-input{
  border-radius:12px !important;
  margin-top:0 !important;
  margin-bottom:0 !important;
  flex:0 0 auto;
}
.rounded-3, .rounded-4, .section-card{ border-radius:12px !important; }
.form-check{ display:flex; align-items:center; gap:.5rem; margin:.45rem 0; }
.form-check-input:checked{ background-color:#212529; border-color:#212529; }
.form-check-input:focus{ border-color:#212529; box-shadow:0 0 0 .25rem rgba(33,37,41,.25); }
.form-check-label{ margin:0; display:inline-flex; align-items:center; gap:.5rem; }
.form-check-label a.no-underline{ text-decoration:none; color:inherit; display:inline-flex; align-items:center; gap:.25rem; }
.form-check-label a.no-underline:hover{ text-decoration:none; }
#kategorie_select{ color:#6c757d; }
#kategorie_select.has-value{ color:#000; }
.step-badge{
  display:inline-flex; align-items:center; justify-content:center;
  width:24px; height:24px; flex:0 0 auto;
  border-radius:50%;
  font-size:.8rem; font-weight:600; margin-right:.55rem;
  transition:background-color .15s ease;
}
.kv-wrap{
  background:#fff; border-radius:0; border:1px solid rgba(0,0,0,.08);
  max-height:420px; overflow:auto;
}
.kv-table{ width:100%; border-collapse:collapse; font-size:11px; line-height:1.35; }
.kv-table th, .kv-table td{ border:1px solid rgba(0,0,0,.08); padding:.4em .6em; text-align:left; vertical-align:top; }
.kv-table th{ width:1%; white-space:nowrap; font-weight:600; background:#f8f9fa; }
.kv-table table{ margin:-1px; width:calc(100% + 2px); }
.kv-value{ white-space:pre-wrap; word-break:break-word; }
.results-table{ width:100%; border-collapse:collapse; font-size:11px; line-height:1.35; }
.results-table th, .results-table td{ border:1px solid rgba(0,0,0,.08); padding:.4em .6em; text-align:left; vertical-align:top; }
.results-table thead th{ background:#f8f9fa; font-weight:600; white-space:nowrap; }
/* ERGAENZT 17.08.2026, auf Wunsch von Peer: lange, ungebrochene Texte
   (Domains, Plugin-Namen) liefen bei table-layout:auto ueber die Spalte
   hinaus optisch in die naechste – overflow ist bei Tabellenzellen ohne
   diese beiden Zeilen NICHT automatisch abgeschnitten. Volltext jetzt per
   Hover (title-Attribut, siehe swTableScript() in lib/ui.php). */
.results-table td:first-child{
    white-space:nowrap;
    font-weight:600;
    overflow:hidden;
    text-overflow:ellipsis;
    max-width:1px;
}
/* --- Umrandete Etiketten: EIN Satz Klassen fuer alle drei Seiten -----------
   Bis zum 09.08.2026 gab es zwei parallele Systeme mit demselben Zweck:
   .badge-outline auf automatisierungen.php und .sw-tag in stackwatch.php.
   Verschiedene Farbwerte, verschiedene Schriftgewichte, und .sw-tag wirkte
   nur innerhalb von .results-table. Wer ein Etikett ausserhalb einer Tabelle
   setzte, bekam einen unsichtbaren Rahmen.

   Es gilt jetzt .badge-outline, ueberall. .sw-tag ist entfallen. */
/* EIGENE TEXTFARBE, AUCH OHNE MODIFIER.
   Bootstrap setzt .badge{ color: var(--bs-badge-color) } mit #fff. Ein
   .badge-outline ohne gueltigen Modifier – Tippfehler, alte Klasse, neuer
   Variantenname – waere damit weisse Schrift auf weissem Grund: ein leeres
   Etikett. Genau so gemeldet am 10.08.2026. Eine eigene Grundfarbe macht
   diesen Fehlerfall unmoeglich; im schlimmsten Fall steht der Text dann
   grau statt farbig da, aber er steht da.

   border: 1px solid currentColor ausgeschrieben, damit der Rahmen auch dann
   die Textfarbe traegt, wenn kein Modifier greift. */
.badge-outline{
    background:#fff;
    border:1px solid currentColor;
    color:#495057;
    font-weight:600;
    /* Gleiche Rundung wie .btn/.form-control weiter oben (12px !important) –
       sonst wirkt ein Badge neben einem Button wie aus einem anderen System,
       wie am 16.08.2026 direkt sichtbar geworden (SLA-Badge neben "Jetzt
       prüfen"). */
    border-radius:12px !important;
}
.badge-outline-success{ color:#198754; border-color:#198754; }
.badge-outline-warning{ color:#997404; border-color:#ffc107; }
.badge-outline-danger{ color:#dc3545; border-color:#dc3545; }
.badge-outline-secondary{ color:#6c757d; border-color:#6c757d; }

/* Zwei neutrale Abstufungen fuer Angaben, die KEINE Wertung tragen:
   Gattung (Core/Theme/Plugin) und Einsatz (Standard/Optional). Vorher waren
   das gefuellte Bootstrap-Badges (text-bg-dark, -secondary, -light) und damit
   die letzten farbigen Flaechen in den Tabellen. Dunkel bis hell liest sich
   als Rangfolge, ohne eine Ampelfarbe zu belegen. */
.badge-outline-dark { color:#212529; border-color:#212529; }
.badge-outline-muted{ color:#8a8f94; border-color:#ced4da; }

/* SLA-Badge im Seitenkopf (stackwatch.php, seit 16.08.2026 neben "Jetzt
   prüfen"): .badge-outline ist für Fließtext bemessen (font-size .75em,
   Bootstrap-Standard) und wirkt neben einem .btn-sm winzig. Padding/Schrift
   hier auf .btn-sm-Maß gebracht, damit beide gleich hoch wirken. */
.sw-badge-header{
    font-size:.875rem;
    padding:.25rem .5rem;
    line-height:1.5;
}

/* In Tabellen wird die Rahmenfarbe weiter unten zurueckgeholt – siehe den
   Block bei `.results-table .badge`. Das Schriftgewicht bleibt dort bei 400. */
.pre-response{
  background:#fff; padding:.65em; border-radius:8px; border:1px solid rgba(0,0,0,.08);
  max-height:420px; overflow:auto; font-size:11px; line-height:1.35;
}
.card-timestamp{ font-size:.75rem; line-height:1; }
.route-badges{ display:inline-flex; gap:10px; flex-wrap:wrap; margin-left:10px; vertical-align:middle; }
/* ===============================
   StackWatch / Dashboard Wartung
   Ergänzt am 06.08.2026 – Präfix sw- damit nichts Bestehendes kollidiert.
   =============================== */

.sw-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    margin-right: 7px;
    vertical-align: 1px;
    flex: 0 0 auto;
}
.sw-dot-green  { background: var(--bs-success, #198754); }
.sw-dot-yellow { background: var(--bs-warning, #ffc107); }
.sw-dot-red    { background: var(--bs-danger,  #dc3545); }
.sw-dot-grey   { background: var(--bs-secondary, #6c757d); }

/* --- Prüfpunkt: derselbe Ort, andere Aussage --------------------------
   Der Punkt vor "39 / 39 geprüft" sagt NICHT, ob etwas gefunden wurde,
   sondern ob VOLLSTÄNDIG geprüft wurde. Zwei verschiedene Aussagen mit
   demselben Zeichen wären beim Überfliegen nicht auseinanderzuhalten –
   deshalb ein Ring statt einer Scheibe. */
.sw-dot-ring { background: transparent !important; border: 2px solid currentColor; }
.sw-ring-green  { color: var(--bs-success, #198754); }
.sw-ring-yellow { color: #b8860b; }
.sw-ring-red    { color: var(--bs-danger, #dc3545); }

/* --- Tags: umrandet statt gefüllt -------------------------------------
   Gefüllte Badges in jeder Zeile ergeben eine Wand aus Farbe, in der die
   eine Zeile, um die es geht, untergeht. Die Umrandung trägt dieselbe
   Information, drängt sich aber nicht vor den Inhalt.
   Farbwerte dunkel genug für Kontrast auf Weiß. */
/* .sw-tag ist am 09.08.2026 entfallen und durch .badge-outline ersetzt –
   siehe oben. Zwei Klassensaetze fuer dasselbe Aussehen hatten zwangslaeufig
   auseinanderlaufende Farbwerte. */

/* Der Kasten macht die Ampelzeile zum ersten Element der Seite. */
.sw-kpis-box {
    /* Reichlich Abstand nach oben und unten: der Kasten soll fuer sich
       stehen und nicht Teil des Textflusses wirken. */
    margin: 2rem 0 2.5rem;
    background: #fff;
    border: 1px solid rgba(0,0,0,.08);
    border-radius: 12px;
    box-shadow: 0 4px 18px 0 rgba(49,130,206,.07);
    padding: .5rem;
}
.sw-kpis {
    display: grid;
    /* auto-fit statt fester Spaltenzahl: bei 5 Kacheln bleibt sonst eine
       allein in der zweiten Reihe stehen. */
    grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
    gap: .25rem;
}
/* Kacheln im Kasten brauchen keinen eigenen Rahmen mehr – sie werden
   durch feine Trenner geteilt, das beruhigt die Flaeche deutlich. */
.sw-kpis-box .sw-kpi {
    border: 0 !important;
    background: transparent !important;
    padding: .65rem .9rem !important;
    border-radius: 8px !important;
    position: relative;
}
/* „heute" mittig ueber allen Kacheln. Eine Angabe fuer vier Zahlen, die
   denselben Tag beschreiben – viermal danebengeschrieben waere es Wiederholung.
   Sehr klein und grau: Es ist eine Einordnung, keine Ueberschrift. */
.sw-kpis-heute {
    text-align: center;
    /* Deutlich groesser als eine Fussnote: Das Wort beantwortet die erste
       Frage, die man an vier Zahlen hat – welcher Zeitraum? Zu klein gesetzt
       wird es ueberlesen, und dann liest man die Zahlen falsch. */
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #6c757d;
    margin: .25rem 0 .5rem;
    cursor: help;
}

/* Eckbeschriftung oben links in einer einzelnen Kachel. Bleibt als Werkzeug
   erhalten, wird aber seit dem 11.08.2026 nirgends benutzt: „heute" steht
   jetzt einmal ueber allen. */
.sw-kpi-ecke {
    position: absolute;
    top: .3rem;
    left: .9rem;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .02em;
    line-height: 1;
    pointer-events: none;
}
.sw-kpis-box .sw-kpi-mit-ecke { padding-top: 1.15rem !important; }

/* --- Zeilen von HEUTE ----------------------------------------------------
   Zweites Merkmal neben der Ampel, und bewusst ohne eigene Farbe: Die Ampel
   sagt „wie schlimm", der Balken sagt „wie neu". Zwei Fragen, zwei Kanaele –
   ein farbiger Balken waere wieder eine Aussage im selben Kanal wie die Ampel.

   box-shadow statt border-left: Ein Rahmen verschoebe den Zellinhalt um seine
   Breite und braeche damit das feste Spaltenraster; der Schatten liegt innen
   und kostet keinen Platz. */
/* EINE TRENNLINIE STATT EINES BALKENS AN JEDER ZEILE.
   
   Erst bekam jede heutige Zeile einen dunklen Balken links. Das markierte
   richtig und sah falsch aus: ein senkrechter Strich neben jeder zweiten Zeile
   zerlegt die Tabelle optisch in Spalten, die es nicht gibt.
   
   Jetzt trennt eine einzige kraeftigere Linie das Heutige vom Aelteren. Sie
   entsteht dort, wo auf eine heutige Zeile eine nicht-heutige folgt – das
   braucht kein Wissen darueber, welche Zeile die letzte ist, und stimmt
   deshalb auch nach einem Klick auf eine andere Spalte, sofern die Zeilen
   noch zusammenstehen.
   
   Die zarte Toenung bleibt: Sie sagt „von heute", ohne eine Linie zu ziehen. */
.results-table tr.sw-row-heute > td {
    background-color: rgba(0, 0, 0, .022);
}
.results-table tr.sw-row-heute.sw-row-crit > td {
    background-color: inherit;
}
.results-table tr.sw-row-heute + tr:not(.sw-row-heute) > td {
    border-top: 2px solid rgba(0, 0, 0, .16);
}

/* Ein Tag, der ein Knopf ist. Die Spalte „Handlungsbedarf" zeigt zwei
   Zustaende derselben Sache – offen und erledigt –, und die muessen dieselbe
   Form haben. Der offene ist anklickbar; die Knopf-Voreinstellungen des
   Browsers werden deshalb zurueckgenommen, damit er wie sein gruenes
   Gegenstueck aussieht und nicht wie ein Formularelement. */
.sw-tag-btn {
    border-width: 1px;
    border-style: solid;
    font-family: inherit;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.sw-tag-btn:hover { background: #fff5f5; }
.sw-tag-btn:focus-visible { outline: 2px solid #dc3545; outline-offset: 2px; }

/* --- Pulsierende rote Ampel, bis sie angeklickt wurde -------------------
   Ein roter Punkt unter zwanzig Zeilen wird uebersehen; ein pulsierender
   nicht. Der Klick ist die Bestaetigung "gesehen" und beendet die Bewegung.

   Das ist NICHT dasselbe wie der Haken bei den Sicherheitsupdates: „gesehen"
   ist eine persoenliche Anzeigefrage und steht im Browser, „erledigt" ist
   eine Tatsache ueber die Arbeit und steht auf dem Server. Zwei Begriffe,
   zwei Ablagen. */
@keyframes swPuls {
    0%, 100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, .55); }
    50%      { box-shadow: 0 0 0 5px rgba(220, 53, 69, 0); }
}
.sw-dot-puls {
    animation: swPuls 1.8s ease-in-out infinite;
    cursor: pointer;
}
.sw-dot-puls:focus-visible {
    outline: 2px solid #dc3545;
    outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
    /* Wer Bewegung abgewaehlt hat, bekommt einen ruhigen Ring statt Puls –
       die Hervorhebung bleibt, die Animation entfaellt. */
    .sw-dot-puls {
        animation: none;
        box-shadow: 0 0 0 3px rgba(220, 53, 69, .25);
    }
}

.sw-kpis-box .sw-kpi + .sw-kpi::before {
    content: "";
    position: absolute;
    left: -.125rem; top: 20%; bottom: 20%;
    border-left: 1px solid rgba(0,0,0,.07);
}
.sw-kpis-box .sw-kpi-link:hover,
.sw-kpis-box .sw-kpi-link:focus-visible {
    background: rgba(0,0,0,.035) !important;
    box-shadow: none;
    transform: none;
}
.sw-kpi-num {
    font-size: 2rem;
    letter-spacing: -.02em;
}
.sw-kpi-label {
    font-size: .8125rem;
}

.results-table tr.sw-row-crit > td {
    background: rgba(var(--bs-danger-rgb, 220, 53, 69), .06);
}

/* ZWEITE, KRAEFTIGERE ROTSTUFE (ergaenzt 15.08.2026, auf Wunsch von Peer:
   "dunkelrot" bedeutet AUSGEFUELLT, nicht nur eine andere Farbe – siehe
   $rowKlasse in stackwatch.php). Nur fuer Zeilen mit einem Kadence-Mail-Fund
   (unabhaengig/per Patchstack bestaetigt), waehrend 'sw-row-crit' die helle
   Toenung fuer einen blossen Changelog-Stichwort-Treffer bleibt. Bewusst
   NICHT vollflaechig deckend (kein reines bg-danger) – die Badges und Links
   in der Zeile bleiben sonst gegen die eigene Hintergrundfarbe unlesbar. */
.results-table tr.sw-row-crit-fill > td {
    background: rgba(var(--bs-danger-rgb, 220, 53, 69), .35);
}
.results-table tr.sw-row-heute.sw-row-crit-fill > td {
    background-color: inherit;
}

/* ZEILENFARBE NACH PRIORITAET (P2/P3) – seit 15.08.2026, siehe
   $prioZeilenKlasse in stackwatch.php. 'sw-row-crit' oben deckt P1 bereits
   ab (dieselbe rote Toenung); hier kommen Orange fuer P2 und Gruen fuer P3
   dazu, in derselben Staerke wie das Rot, damit keine der drei Farben
   dominanter wirkt als die anderen. */
.results-table tr.sw-row-warn > td {
    background: rgba(var(--bs-warning-rgb, 255, 193, 7), .10);
}
.results-table tr.sw-row-ok > td {
    background: rgba(var(--bs-success-rgb, 25, 135, 84), .06);
}
.results-table tr.sw-row-heute.sw-row-warn > td,
.results-table tr.sw-row-heute.sw-row-ok > td {
    background-color: inherit;
}

/* QUELLEN-ICON VOR DER QUELLE IN „CHANGELOG-UPDATES" – WIEDER ENTFERNT
   (19.08.2026, auf Wunsch von Peer: "dann kann vorne das icon weg, das
   sieht eh niemand"). Das Icon (seit 15.08.2026, ersetzte den frueheren
   Ampelpunkt) zeigte dieselbe Herkunft-Einordnung, die jetzt lesbar in
   der eigenen Spalte "Herkunft" steht ($herkunftLabel() in
   stackwatch.php) – die Regel hier (`.sw-quelle-icon`) ist damit
   ungenutzt und entfernt. */

@media (max-width: 720px) {
    .sw-kpis { grid-template-columns: repeat(2, 1fr); }
    .sw-kpi-num { font-size: 1.6rem; }
    .sw-kpis-box .sw-kpi + .sw-kpi::before { display: none; }
}

/* --- StackWatch: klickbare KPI-Kacheln (Drilldown) --- */
.sw-kpi-link {
    color: inherit;
    transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.sw-kpi-link:hover,
.sw-kpi-link:focus-visible {
    border-color: rgba(0, 0, 0, .28) !important;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .08);
    transform: translateY(-1px);
}
.sw-kpi-chevron {
    font-size: .7rem;
    opacity: .35;
}
.sw-kpi-link:hover .sw-kpi-chevron { opacity: .8; }

/* Luft ueber dem Sprungziel, sonst klebt die Ueberschrift am Fensterrand */
/* Grosszuegig: die Ueberschrift soll mit Luft darueber beginnen,
   nicht am Fensterrand kleben. */
.sw-anchor { scroll-margin-top: 7.5rem; }

/* Bereiche optisch voneinander absetzen */
.sw-section + .sw-section { border-top: 1px solid rgba(0,0,0,.07); padding-top: 1.25rem; }
.sw-section-meta, .sw-section-meta a { text-transform: none; letter-spacing: 0; }
.sw-section-detail > summary { cursor: pointer; padding: .25rem 0; }
.sw-section-detail > summary:hover { color: #212529 !important; }

/* Kurzes Aufblinken, damit klar ist, wo man gelandet ist */
@keyframes swFlash {
    0%   { background: rgba(var(--bs-primary-rgb, 13, 110, 253), .14); }
    100% { background: transparent; }
}
.sw-flash { animation: swFlash 1.1s ease-out; border-radius: .5rem; }

@media (prefers-reduced-motion: reduce) {
    .sw-kpi-link, .sw-flash { transition: none; animation: none; }
}

/* --- StackWatch: Bereichsüberschriften und sortierbare Quellenliste --- */
.sw-section-title { color: #1d1d1b; letter-spacing: .6px; }
.sw-section-title .sw-section-meta { font-weight: 400; }

.results-table thead th[data-sort="asc"]::after  { content: " ▲"; opacity: .5; font-size: .7em; }
.results-table thead th[data-sort="desc"]::after { content: " ▼"; opacity: .5; font-size: .7em; }
#swSourceFilter { max-width: 420px; }

/* --- StackWatch: Schrift-Rangfolge ---------------------------------------
   1. Ampelzahl        gross, fett      -> zuerst gelesen
   2. Ampelbeschriftung fett, klein     -> sagt, worum es geht
   3. Bereichstitel     fett, versal    -> Gliederung
   4. Kartentitel       normal          -> Inhalt, tritt bewusst zurueck
   Vorher konkurrierten Karten- und Ampeltext, weil beide halbfett waren. */
.sw-kpi-label { font-weight: 600; color: #495057 !important; }

.sw-section .rounded-4 > .d-flex > .fw-semibold {
    font-weight: 400 !important;
}

/* Grüner "success"-Badge auf OK-Karten ausblenden.
   Er wiederholt nur, was schon im Kartentitel steht, und konkurriert bei
   fünf Bereichen mit fünfmal "alles in Ordnung" um Aufmerksamkeit.
   Warnung und Fehler behalten ihren Badge – dort trägt er Information.

   NUR .sw-card-badge, nicht jeder grüne Badge. Vorher stand hier
   `.sw-section .badge.text-bg-success` – und traf damit auch den Statuswert
   "Resolved" in der Incident-Tabelle, der dadurch unsichtbar war. Eine Regel,
   die nach FARBE statt nach ORT ausblendet, trifft irgendwann das Falsche. */
.sw-section .sw-card-badge.text-bg-success { display: none; }

/* Statuszeile, seit 30.08.2026 UNTER dem "heute"/Kacheln-Bereich statt
   darüber (auf Wunsch von Peer) und als natives <details>/<summary> statt
   eines einfachen <div> – zugeklappt per Default, siehe Docblock in
   stackwatch.php und die .sw-kpis-status-*-Regeln unten. Dieselbe
   <details>-Bauform wie .sw-section-detail (siehe dort), nur eine Ebene
   höher im Kasten. Vorher stand hier "Kopf des Ampelkastens" – seit dem
   Umzug ans Ende ist "padding-right: 9.5rem" (Platz für einen absolut
   positionierten SLA-Badge) entfallen: dieser Badge steht laut Kommentar
   in stackwatch.php ohnehin schon seit 16.08.2026 nicht mehr hier, sondern
   neben "Jetzt prüfen" – die Reservierung war seitdem toter Leerraum. */
.sw-kpis-status {
    position: relative;
    font-size: .8125rem;
    padding: .4rem .9rem .55rem;
    border-top: 1px solid rgba(0,0,0,.07);
    margin-top: .35rem;
    display: flex;
    flex-direction: column;
    gap: .25rem;
}
/* summary IMMER SICHTBAR, auch zugeklappt – trägt den Gesamtstatus über
   alle Ampeln (siehe $gesamtStufe in stackwatch.php). Bei "success" bewusst
   unauffällig (gedämpfte Farbe, kein Fett): "wenn alles grün ist
   interessiert mich das ja gar nicht" (Peer). Bei "warning"/"error" bewusst
   auffällig (Farbe + fett), damit ein Blick reicht, ohne aufzuklappen. */
.sw-kpis-status > summary {
    cursor: pointer;
    display: flex;
    align-items: baseline;
    gap: .5rem;
    padding: .1rem 0;
}
.sw-kpis-status > summary:hover { color: #212529; }
/* Basis wie Bootstraps .text-muted, aber ohne dessen !important – die
   Farbe hier MUSS sich per Status ueberschreiben lassen (s.u.), ein
   !important auf der Bootstrap-Klasse haette das verhindert. Deshalb
   bewusst KEINE Bootstrap-Klasse an dieser Stelle, nur diese eigene. */
.sw-kpis-status-summary-text { color: #6c757d; }
.sw-kpis-status-success > summary { color: #6c757d; }
.sw-kpis-status-success > summary strong { font-weight: 400; }
.sw-kpis-status-warning > summary,
.sw-kpis-status-error > summary { font-weight: 700; }
.sw-kpis-status-warning > summary .sw-kpis-status-summary-text { color: #997404; }
.sw-kpis-status-error > summary .sw-kpis-status-summary-text { color: #dc3545; }
/* Aufgeklappt: sichtbare Trennung zwischen summary und den drei Zeilen. */
.sw-kpis-status[open] > summary {
    border-bottom: 1px solid rgba(0,0,0,.07);
    padding-bottom: .35rem;
    margin-bottom: .35rem;
}
/* DREI FESTE ZEILEN STATT EINEM GEMEINSAMEN UMBRUCH (ergänzt 30.08.2026,
   auf Wunsch von Peer: "oben haben wir den lauf, darunter haben wir die
   systeme postfach und smtp, darunter haben wir die rückmeldungen
   betterstack, pagescreen, rybbit"). Jede Zeile ist ihr eigener,
   weiterhin umbrechender Flex-Container: Zeile 1 Tageslauf/Schneller Lauf,
   Zeile 2 die Systeme Postfach/SMTP, Zeile 3 die Rückmeldungen
   BetterStack/Pagescreen/Rybbit. */
.sw-kpis-status-row {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1.5rem;
}
/* Jede Laufampel bleibt als Einheit zusammen und bricht nicht mittendrin um. */
.sw-lauf { white-space: nowrap; }
@media (max-width: 560px) {
    .sw-lauf { white-space: normal; }
}

/* Aktueller SLA: FEST IN DER RECHTEN OBEREN ECKE von .sw-kpis-status
   (ergänzt 15.08.2026, auf Wunsch von Peer – margin-left: auto reichte nicht,
   „bitte in die rechte Ecke verschieben"). position: absolute nagelt den
   Badge an die Ecke, unabhaengig davon, wie breit „Tageslauf ..." und
   „Schneller Lauf ..." gerade sind – kein Umbruch-Sonderfall mehr noetig.
   Ab 560px (siehe .sw-lauf oben) faellt die Statuszeile ohnehin in den
   normalen Textfluss zurueck: dort stoert eine absolute Ecke mehr, als sie
   nuetzt, deshalb dort wieder normaler Fluss.

   !important zur Absicherung (noch am selben Tag ergaenzt, nachdem der Badge
   trotz dieser Regel weiter inline hinter „Schneller Lauf" auftauchte): Ohne
   erkennbaren Grund im Code selbst – wahrscheinlichste Ursache ist eine im
   Browser oder auf dem Server zwischengespeicherte alte Fassung dieser
   Datei, nicht die Regel selbst. !important schliesst zumindest eine stille
   Ueberschreibung durch eine andere, spezifischere Regel aus. */
.sw-fenster-aktuell {
    position: absolute !important;
    top: .4rem !important;
    right: .9rem !important;
    margin-left: 0 !important;
}
@media (max-width: 560px) {
    .sw-fenster-aktuell {
        position: static !important;
        display: block;
        margin-top: .35rem;
    }
}

/* --- Gefuellte Badges etwas zurueckgenommen --------------------------------
   Bootstraps Volltoene (#198754, #dc3545) sind fuer Knoepfe gedacht. Als
   Statusbadge oben rechts auf einer Karte wirken sie hart; 85 Prozent
   Deckkraft nehmen genau so viel weg, dass die Farbe eindeutig bleibt.
   Aus #198754 wird optisch ein #3c996e.

   Betrifft seit dem 10.08.2026 praktisch nur noch statusBadge() – in Tabellen
   sind die Etiketten umrandet, nicht gefuellt. Gilt fuer alle drei Seiten.

   Zurueckdrehen: diesen Block loeschen. .text-bg-light bleibt aussen vor, es
   ist ohnehin fast weiss und wuerde bei weniger Deckkraft verschwinden. */
.badge.text-bg-success,
.badge.text-bg-danger,
.badge.text-bg-warning,
.badge.text-bg-secondary,
.badge.text-bg-dark { --bs-bg-opacity: .85; }

/* Badges in Tabellen immer gleich gross: ohne diesen transparenten Rahmen
   ist ein Badge ohne .border 2 px kleiner als eines mit.

   Und NICHT fett: In jeder Zeile soll genau eine Angabe hervorstechen –
   die, um die es in der Meldung geht (Version, Ursache, Änderung). Badges
   sind Einordnung, nicht Aussage. Bootstrap setzt sie auf 700; wären sie
   fett, konkurrierten in einer Zeile drei bis vier Elemente um den Blick
   und keines gewänne. */
.results-table .badge { border: 1px solid transparent; font-weight: 400; }

/* --- UND DIE UMRANDETEN HOLEN IHREN RAHMEN ZURUECK -------------------------
   Die Regel darueber macht JEDEN Rahmen in einer Tabelle durchsichtig, auch
   den, der die Form eines .badge-outline ausmacht. Sie ist mit (0,2,0) genauso
   spezifisch wie diese hier – deshalb muss dieser Block DANACH stehen, sonst
   gewinnt der transparente Rahmen. Am 10.08.2026 genau so passiert: Aus den
   Etiketten wurde farbiger Text ohne erkennbare Form.

   border-color: currentColor statt fester Werte. Bei success, danger und
   secondary ist das dieselbe Farbe wie im Klassensatz. Bei warning ist es
   #997404 statt #ffc107 – also der dunkle Text- statt der hellen Randfarbe.
   Ein 1-px-Rahmen in #ffc107 ist auf Weiss kaum zu sehen; in einer Tabelle mit
   12-px-Schrift verschwindet die Form dann wieder. */
/* !important, weil die transparente Regel darueber dieselbe Spezifitaet hat
   und schon zweimal gewonnen hat, sobald jemand die Reihenfolge im Stylesheet
   verschoben hat. Bei einem Rahmen, der die Form des Etiketts ausmacht, ist
   das die Stelle, an der sich der Nachdruck lohnt. */
.results-table .badge-outline {
    border: 1px solid currentColor !important;
    background: #fff;
}
.results-table .badge-outline-muted { border-color: #adb5bd !important; }

/* --- Kopfzeilen-Menü (swNav) ---------------------------------------------
   Bewusst ohne .sw-page-Vorsatz: das Menü ist von Anfang an für alle Seiten
   gedacht, die es einbinden. */
/* Gleiche Höhe wie der Aktionsknopf daneben. Vorher stand hier
   line-height:1 – das machte den Knopf um die Differenz zur Bootstrap-
   Zeilenhöhe (1.5) flacher als „Jetzt prüfen" direkt daneben. Die Höhe eines
   Buttons ergibt sich aus Polsterung PLUS Zeilenhöhe; wer nur eines von
   beiden angleicht, bekommt zwei verschieden hohe Knöpfe. */
.sw-nav-toggle {
    --bs-btn-padding-x: .55rem;
    min-width: 2.1rem;
    text-align: center;
}
/* Auslöser UND Menü über allem, was darunter folgt.
   Ohne eigenen Stapelkontext kann eine nachfolgende Karte den Knopf
   überdecken: er bleibt sichtbar, ist aber nicht mehr klickbar, weil die
   Karte die Klicks abfängt. Genau dieses Verhalten sah aus wie ein
   kaputtes Menü. Karten im Projekt arbeiten mit z-index 0 bis 2. */
.sw-nav { position: relative; z-index: 1040; }

.sw-nav .dropdown-menu {
    --bs-dropdown-min-width: 13.5rem;
    --bs-dropdown-font-size: 14px;
    --bs-dropdown-zindex: 1045;
    border-radius: 8px;
}
.sw-nav .dropdown-item { display: flex; align-items: center; }
.sw-nav form { margin: 0; }

/* Der aktive Eintrag markiert "du bist hier", er ist keine Auswahl.
   Bootstraps blauer Standard sähe aus, als hätte man gerade geklickt. */
.sw-nav .dropdown-item.active,
.sw-nav .dropdown-item:active {
    background: rgba(0,0,0,.05);
    color: #1d1d1b;
    font-weight: 600;
}

/* =========================================================================
   NACH OBEN – schwebende Schaltfläche unten rechts
   ========================================================================= */

/* z-index unter dem Menü (1040), aber über allen Karten: das Menü klappt nach
   unten auf und darf nie hinter diesem Knopf verschwinden.
   Rechts/unten 1rem statt fester Pixel, damit auf dem Telefon nichts am
   Bildschirmrand klebt. 2.6rem sind knapp über den 44 px, die als kleinste
   sinnvolle Tippfläche gelten. */
.sw-top {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1030;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid rgba(0,0,0,.1);
    border-radius: 50%;
    background: #fff;
    color: #1d1d1b;
    font-size: 14px;
    line-height: 1;
    box-shadow: 0 2px 10px rgba(0,0,0,.12);
    transition: background .15s ease, box-shadow .15s ease;
}
.sw-top:hover {
    background: #f4f4f4;
    box-shadow: 0 4px 14px rgba(0,0,0,.18);
}
.sw-top:focus-visible {
    outline: 2px solid #1d1d1b;
    outline-offset: 2px;
}
/* [hidden] setzt display:none – gegen Bootstraps Button-Regeln muss das
   ausdrücklich gewinnen, sonst bleibt der Knopf immer sichtbar. */
.sw-top[hidden] { display: none !important; }

/* --- Meldung „neuer Prüflauf" --------------------------------------------
   ENTFALLEN am 17.08.2026: Die Leiste fragte nur nach, ob neu geladen werden
   soll. Peer wollte das nicht mehr manuell anstoßen – swNeuerStand() in
   lib/ui.php lädt jetzt automatisch neu, sobald ein neuer Lauf da ist, und
   stellt Scrollposition/aufgeklappte Bereiche/Sortierung danach wieder her.
   Keine Leiste mehr, also auch kein .sw-neu mehr. */

/* =========================================================================
   TABELLEN – PROJEKTWEITER STANDARD
   Festgelegt am 07.08.2026, gilt für dev-sites.php, automatisierungen.php
   und stackwatch.php gleichermassen. Wer eine Tabelle baut, baut sie so.
   ========================================================================= */

/* --- Schriftgrösse: zwei Werte, und nur diese zwei -----------------------
   Vorher trafen in einer einzigen Zeile drei Grössen aufeinander:
   .results-table stand auf 11 px, einzelne Zellen trugen .fs-14 (14 px), und
   Bootstrap-Badges rechnen mit .75em – im 11-px-Kontext also 8,25 px. Das
   sah nicht nach Hierarchie aus, sondern nach Zufall.

     13 px   alles in der Zelle
     12 px   Badge und Nebenzeile unter einem Eintrag

   Die letzte Regel ist das Sicherheitsnetz: Grössenklassen im Markup werden
   in Tabellen wirkungslos. Wer eine Ausnahme braucht, ändert sie hier. */
.results-table                { font-size: 13px; }
.results-table .badge         { font-size: 12px; }
.results-table .small,
.results-table small          { font-size: 12px; }
.results-table .fs-14         { font-size: inherit; }

/* --- Rundung: 8 px ------------------------------------------------------
   8 statt 12: die Tabelle sitzt in einer .dev-card mit 12 px. Bei
   verschachtelten Rundungen muss die innere kleiner sein als die äussere,
   sonst konkurrieren zwei gleich starke Ecken auf 20 px Abstand.

   border-collapse muss dafür von collapse auf separate wechseln – auf eine
   zusammengelegte Tabelle wirkt border-radius nicht. Der Preis: benachbarte
   Zellen zeichnen ihre Ränder doppelt. Deshalb trägt jede Zelle nur rechts
   und unten einen Rand, die erste Spalte zusätzlich links, der Kopf oben. */
.results-table {
    border-collapse: separate;
    border-spacing: 0;
    border-radius: 8px;
}
.results-table th,
.results-table td {
    border: 0;
    border-bottom: 1px solid rgba(0,0,0,.08);
    border-right:  1px solid rgba(0,0,0,.08);
}
.results-table th:first-child,
.results-table td:first-child { border-left: 1px solid rgba(0,0,0,.08); }
.results-table thead th       { border-top:  1px solid rgba(0,0,0,.08); }

.results-table thead th:first-child { border-top-left-radius: 8px; }
.results-table thead th:last-child  { border-top-right-radius: 8px; }

/* --- Einheitliche Randspalten ------------------------------------------
   Erste und letzte Spalte sind in allen Bereichen dieselben Rollen: links
   der Name, rechts der Absprung nach draussen. Wenn jede Tabelle sie anders
   breit macht, wirken fuenf untereinanderstehende Bereiche wie fuenf
   verschiedene Seiten.

   width auf Tabellenzellen ist bei automatischem Layout ein Vorschlag, kein
   Befehl – der Browser darf laengeren Inhalt breiter machen. Genau das ist
   hier gewollt: gleiche Breite, solange es passt, keine Abschneiderei. */
.results-table th:first-child,
.results-table td:first-child { width: 20rem; }

/* Lange Aufzählungen müssen umbrechen dürfen. Ohne das schiebt eine Zelle
   die Tabelle über den Rand hinaus, und was rechts steht, sieht niemand. */
.results-table td { overflow-wrap: anywhere; }

/* Die feste Breite und das Umbruchverbot gelten NUR fuer Tabellen, deren
   letzte Spalte tatsächlich ein Link nach draußen ist – also die mit fünf bis
   sieben Spalten. Bei einer dreispaltigen Aufstellung wie der Schwellenliste
   steht dort eine lange Aufzählung; die lief mit nowrap rechts aus dem Bild
   heraus und war damit unlesbar. */
.results-table.sw-cols-5 th:last-child, .results-table.sw-cols-5 td:last-child,
.results-table.sw-cols-6 th:last-child, .results-table.sw-cols-6 td:last-child,
.results-table.sw-cols-7 th:last-child, .results-table.sw-cols-7 td:last-child {
    width: 8.5rem;
    white-space: nowrap;
}
/* In den Haupttabellen steht dort nur „Öffnen" – halb so breit reicht. */
.results-table.sw-grid-sla th:last-child, .results-table.sw-grid-sla td:last-child {
    width: 4.5rem;
}

/* --- EIN RASTER FÜR VIER, FÜNF UND SECHS SPALTEN -------------------------
   Stand 09.08.2026. Vorher standen hier feste rem-Breiten. Die waren nur ein
   VORSCHLAG: bei table-layout auto verteilt der Browser die Breiten nach dem
   Inhalt und weicht ab, sobald eine Zelle mehr braucht. Ein einziger langer
   Site-Name („Grundlagen IT-Providermanagement – amendos gmbh") zog die erste
   Spalte der Pagescreen-Tabelle weit auf, während dieselbe Spalte bei
   BetterStack schmal blieb. Genau das war der schiefe Eindruck.

   Deshalb: table-layout FIXED und Prozente. Fest heißt, der Browser liest
   nur die Kopfzeile und hält sich daran, egal was in den Zellen steht. Die
   Spalten stehen damit über alle drei Bereiche auf derselben Linie.

   Prozente statt rem, weil die Tabellen unterschiedlich breite Container
   haben; feste Maße würden bei jedem Container anders aufgehen.

   Die Aufteilung ist so gewählt, dass die ERSTE und die LETZTE Spalte überall
   gleich sind – der Name links und der Link nach draußen rechts sind die
   beiden Anker, an denen das Auge beim Überfliegen entlangläuft. */
.results-table.sw-cols-4,
.results-table.sw-cols-5,
.results-table.sw-cols-6,
.results-table.sw-grid-sla { table-layout: fixed; }

/* Seit dem 11.08.2026 tragen alle drei Haupttabellen dieselbe Ordnung:
   Quelle · Sache · Zeitpunkt · SLA/Prio · Reaktion bis · Verweis.

   Die letzte Spalte ist auf die Haelfte geschrumpft: Sie traegt nur noch das
   Wort „Öffnen", nicht mehr „Changelog" oder „Incident". Der gewonnene Platz
   geht an „Reaktion bis" – dort stehen zwei Zeilen, Tag und Zeitpunkt. */
/*                             Quelle  Sache  Zeit  SLA/Prio  Frist  Öffnen */
.results-table.sw-cols-6 th:nth-child(1) { width: 17%; }
.results-table.sw-cols-6 th:nth-child(2) { width: 18%; }
.results-table.sw-cols-6 th:nth-child(3) { width: 14%; }
.results-table.sw-cols-6 th:nth-child(4) { width: 15%; }
.results-table.sw-cols-6 th:nth-child(5) { width: 29%; }
.results-table.sw-cols-6 th:nth-child(6) { width: 7%; }

/* DAS RASTER DER DREI HAUPTTABELLEN – eigene Klasse, nicht ueber die
   Spaltenzahl. Changelog-Updates, BetterStack und Pagescreen haben sieben
   Spalten; die Quellenliste im Aufklapper hat ebenfalls sieben, aber voellig
   andere Inhalte. Ueber sw-cols-7 zu steuern hiesse, beide in dasselbe Raster
   zu zwingen – und das eine dabei zu zerlegen.

   Quelle · Sache · Zeitpunkt · SLA · Prio · Reaktion bis · Öffnen

   SLA und Prio stehen getrennt, damit sich jede fuer sich sortieren laesst.
   Beide sind schmal: Dort steht ein einzelnes Tag, „Extended" oder „P1".
                                Quelle Sache  Zeit  SLA  Prio  Frist  Öffnen */
.results-table.sw-grid-sla th:nth-child(1) { width: 17%; }
.results-table.sw-grid-sla th:nth-child(2) { width: 18%; }
.results-table.sw-grid-sla th:nth-child(3) { width: 14%; }
.results-table.sw-grid-sla th:nth-child(4) { width: 10%; }
.results-table.sw-grid-sla th:nth-child(5) { width:  8%; }
.results-table.sw-grid-sla th:nth-child(6) { width: 26%; }
.results-table.sw-grid-sla th:nth-child(7) { width:  7%; }

/* EIGENES RASTER FÜR DIE CHANGELOG-UPDATES-TABELLE, seit 15.08.2026.
   Sechs statt sieben Spalten (weder 'sw-grid-sla' noch die zwischenzeitliche
   achtspaltige Fassung passen mehr): Die SLA-Spalte ist ganz entfallen (ein
   Release hat keine SLA-Zusage), und die alte Prio-Spalte zeigt jetzt die
   Patchstack-Einstufung statt dauerhaft "No value" – eine eigene
   Patchstack-Spalte gibt es dafür nicht mehr, siehe stackwatch.php.

   Quelle · Update · Erschienen am · Patchstack · Risk · Reaktion bis · Changelog

   ANGEGLICHEN AN 'sw-grid-sla' (ergänzt 15.08.2026, auf Wunsch von Peer:
   "das Tabellen-Grid ist jetzt nicht mehr wie bei den anderen"). Seit der
   "Risk"-Spalte hat diese Tabelle wieder sieben Spalten wie BetterStack/
   Pagescreen – jetzt auch mit EXAKT denselben Prozentbreiten je Spalte, statt
   einer eigenen Aufteilung. Spalte 4 (Patchstack ~ SLA) und Spalte 5
   (Risk ~ Prio) übernehmen bewusst dieselben Breiten wie ihre Entsprechungen
   bei 'sw-grid-sla' – beide sind dort wie hier ein einzelnes kurzes Tag. Drei
   Tabellen untereinander stehen dadurch wieder auf derselben Spaltenlinie. */
.results-table.sw-grid-updates { table-layout: fixed; }
.results-table.sw-grid-updates th:nth-child(1) { width: 17%; }
.results-table.sw-grid-updates th:nth-child(2) { width: 18%; }
.results-table.sw-grid-updates th:nth-child(3) { width: 14%; }
.results-table.sw-grid-updates th:nth-child(4) { width: 10%; }
.results-table.sw-grid-updates th:nth-child(5) { width: 8%; }
.results-table.sw-grid-updates th:nth-child(6) { width: 26%; }
.results-table.sw-grid-updates th:last-child, .results-table.sw-grid-updates td:last-child {
    width: 7%;
}

/* Spalte 3 hat am 09.08.2026 von 10 auf 14 % zugelegt, Spalte 1 hat sie
   abgegeben. Anlass war die neue Spalte „Seite" bei Pagescreen: dort steht
   ein Pfad wie /grundlagen-it-providermanagement/, und der braucht mehr als
   ein Typ-Etikett. Spalte 1 verkraftet es – dort stehen Domains und
   Quellennamen, beides kurz. Spalte 5 bleibt bei 25 %: sie trägt bis zu drei
   Kennzeichen nebeneinander und würde darunter umbrechen. */

/* Fünf Spalten: die vierte trägt, was bei sechs Spalten auf Zeit und Zustand
   verteilt ist. */
.results-table.sw-cols-5 th:nth-child(1) { width: 17%; }
.results-table.sw-cols-5 th:nth-child(2) { width: 18%; }
.results-table.sw-cols-5 th:nth-child(3) { width: 14%; }
.results-table.sw-cols-5 th:nth-child(4) { width: 37%; }
.results-table.sw-cols-5 th:nth-child(5) { width: 14%; }

/* „PATCHSTACK VULNERABILITY" – JETZT SIEBEN STATT SECHS SPALTEN (ergänzt
   19.08.2026, auf Wunsch von Peer: "die spalte risk eins nach rechts und
   eine spalte einfügen dann sind wir im gleichen grid wie bei den anderen
   [beiden]" – gemeint sind BetterStack/Pagescreen ('sw-grid-sla') und
   Changelog Updates ('sw-grid-updates'), die alle DASSELBE
   17/18/14/10/8/26/7-Raster tragen. Eigene Klasse statt 'sw-cols-7' bzw.
   Wiederverwendung von 'sw-grid-sla', genau wie 'sw-grid-updates' schon
   eine eigene Klasse mit identischen Werten hat (siehe Kommentar dort,
   "Ueber sw-cols-7 zu steuern hiesse, beide in dasselbe Raster zu zwingen"
   weiter oben) – vier Bereiche mit identischer Optik, aber je eigener,
   unabhaengig aenderbarer Klasse.

   Quelle · Bis Version · Email erhalten · Dauer · Risk · Sites · Patchstack

   table-layout hier explizit gesetzt, weil 'sw-cols-7' (anders als
   'sw-cols-4/5/6') KEIN table-layout:fixed mitbringt (siehe die Liste
   weiter oben) – ohne diese Zeile würde der Browser die Breiten wieder
   selbst bestimmen. */
.results-table.sw-grid-patchstack { table-layout: fixed; }
.results-table.sw-grid-patchstack th:nth-child(1) { width: 17%; }
.results-table.sw-grid-patchstack th:nth-child(2) { width: 18%; }
.results-table.sw-grid-patchstack th:nth-child(3) { width: 14%; }
.results-table.sw-grid-patchstack th:nth-child(4) { width: 10%; }
.results-table.sw-grid-patchstack th:nth-child(5) { width: 8%; }
.results-table.sw-grid-patchstack th:nth-child(6) { width: 26%; }
.results-table.sw-grid-patchstack th:last-child, .results-table.sw-grid-patchstack td:last-child {
    width: 7%;
}

/* Vier Spalten (Rybbit, Laufhistorie): die dritte nimmt den Rest auf. */
.results-table.sw-cols-4 th:nth-child(1) { width: 17%; }
.results-table.sw-cols-4 th:nth-child(2) { width: 18%; }
.results-table.sw-cols-4 th:nth-child(3) { width: 51%; }
.results-table.sw-cols-4 th:nth-child(4) { width: 14%; }

/* Kennzeichen und Links brechen nicht mitten im Wort. */
.results-table.sw-cols-4 td:last-child,
.results-table.sw-cols-5 td:last-child,
.results-table.sw-cols-6 td:last-child,
.results-table.sw-cols-6 td:nth-child(5) .badge { white-space: nowrap; }

/* --- Namen in der ersten Spalte kürzen statt umbrechen -------------------
   Ein Site-Name kann beliebig lang sein; die Spalte kann es nicht. Umbrechen
   macht eine Zeile doppelt so hoch und zerreißt das Raster, das gerade erst
   festgezurrt wurde. Also: eine Zeile, Rest als „…". Der vollständige Name
   steht im title-Attribut (siehe swTable-Aufrufe) und damit im Tooltip.

   Nur die erste Spalte – überall sonst stehen kurze Werte oder Text, der
   umbrechen darf und soll. */
.results-table.sw-cols-4 td:first-child,
.results-table.sw-cols-5 td:first-child,
.results-table.sw-cols-6 td:first-child {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* .sw-last setzt das JavaScript auf die letzte SICHTBARE Zeile. Ohne das
   fehlt der Quellenliste unten die Rundung, sobald der Filter greift –
   :last-child meint die letzte Zeile im Dokument, nicht die letzte im Bild. */
.results-table tbody tr:last-child > td:first-child,
.results-table tbody tr.sw-last    > td:first-child { border-bottom-left-radius: 8px; }
.results-table tbody tr:last-child > td:last-child,
.results-table tbody tr.sw-last    > td:last-child  { border-bottom-right-radius: 8px; }

/* --- Tabellen auf schmalen Bildschirmen ----------------------------------
   Unter 640 px wird aus jeder Zeile ein Block, aus jeder Zelle eine Zeile
   mit ihrem Spaltentitel davor (data-label aus swTable()).

   WARUM NICHT WAAGERECHT SCROLLEN: Die Tabellen hier sind fünf bis sieben
   Spalten breit. Auf einem Telefon sieht man davon zwei. Für JEDE Zeile
   müsste man hin und her wischen und dabei im Kopf behalten, in welcher
   Zeile man war – und genau dafür schaut man nicht aufs Handy. Gestapelt
   ist zwar länger, aber am Stück lesbar.

   Die Kopfzeile entfällt dabei; sortieren geht auf dem Telefon also nicht
   mehr. Das ist verkraftbar: unterwegs liest man, sortiert wird am Rechner. */
@media (max-width: 640px) {
    .results-table,
    .results-table tbody,
    .results-table tr,
    .results-table td { display: block; width: 100%; }

    .results-table thead { display: none; }

    .results-table { border-collapse: collapse; border-radius: 0; }

    .results-table tbody tr {
        border: 1px solid rgba(0,0,0,.12);
        border-radius: 8px;
        padding: .5rem .75rem;
        margin-bottom: .6rem;
        background: #fff;
    }
    .results-table tbody tr:last-child { margin-bottom: 0; }

    /* Erste Zelle ist die Überschrift des Blocks: kein Label, größer.
       Das Kürzen auf eine Zeile gilt hier NICHT: Es ist ein Zugeständnis an
       ein festes Spaltenraster, und ein Raster gibt es auf dem Telefon nicht.
       Der Name steht über die volle Breite und darf umbrechen.

       max-width:none HIER ERGÄNZT (28.08.2026, Bugfix – Peer schickte
       Screenshots, auf denen z. B. "WordPress Modern Events Calendar
       plugin" auf dem Handy einzeln, Buchstabe für Buchstabe untereinander
       stand). URSACHE: die Ellipsis-Kürzung der ersten Spalte auf dem
       Desktop (weiter oben, ".results-table td:first-child") setzt
       `max-width:1px` – ein bekannter CSS-Kniff, der bei table-layout:auto
       zusammen mit `overflow:hidden`/`text-overflow:ellipsis` funktioniert.
       Diese Regel hier setzte weiße-space/overflow/text-overflow zwar
       zurück, nie aber max-width – das 1px blieb per Spezifität bestehen,
       traf hier auf `display:flex` (siehe ".results-table tbody td" gleich
       darunter) UND `overflow-wrap:anywhere` (weiter oben, ungated), und
       zusammen erzwang das exakt einen Umbruch nach jedem einzelnen
       Zeichen. Nachgestellt und bestätigt mit den echten Produktionsstilen
       in einem Headless-Browser bei 390px Breite, vor UND nach diesem
       Fix. */
    .results-table tbody td:first-child {
        border: 0;
        padding: .1rem 0 .4rem;
        font-weight: 600;
        font-size: 14px;
        border-bottom: 1px solid rgba(0,0,0,.08);
        margin-bottom: .35rem;
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
    }
    /* ALLE DESKTOP-SPALTENBREITEN UNWIRKSAM MACHEN, PAUSCHAL UND MIT
       !important (28.08.2026, Bugfix Nr. 2 desselben Tages – derselbe
       Screenshot-Fehler tauchte bei "Changelog Updates" erneut auf, diesmal
       in der LETZTEN Spalte: ".sw-grid-updates td:last-child" (und ihre
       Geschwister ".sw-grid-sla"/".sw-grid-patchstack"/".sw-cols-4..7")
       setzen fuer das feste Desktop-Raster `width: 7%` o. ä. UNGATED, also
       auch auf dem Handy – dort wurde die Zelle "Changelog: Öffnen" auf
       rund 23px (7 % von ~330px) zusammengequetscht. Vorher hatte schon die
       erste Spalte dasselbe Problem mit `max-width:1px` (siehe Fix weiter
       oben). Statt jede einzelne dieser Compound-Klassen hier einzeln zu
       kontern (und bei der naechsten neuen ".sw-grid-*"-Variante denselben
       Fehler zu wiederholen), gilt jetzt EIN Reset mit !important fuer JEDE
       Zelle: keine Desktop-Spaltenbreite darf auf dem Handy je wirksam
       werden, unabhaengig davon, wie spezifisch sie dort war. Mit den
       echten Produktionsstilen nachgestellt und bestaetigt (390px breit,
       "Changelog Updates", Zeile "WordPress Modern Events Calendar plugin
       < 7.35.0", Zellen "Update" und "Changelog"). */
    .results-table tbody td {
        border: 0;
        padding: .18rem 0;
        display: flex;
        flex-wrap: wrap;
        gap: .25rem .6rem;
        align-items: baseline;
        width: 100% !important;
        max-width: none !important;
        white-space: normal !important;
    }
    .results-table tbody td::before {
        content: attr(data-label);
        flex: 0 0 7.5rem;
        color: #6c757d;
        font-size: 12px;
    }
    .results-table tbody td:first-child::before { content: none; }

    /* Leere Zellen erzeugen sonst eine Zeile, die nur aus dem Titel besteht. */
    .results-table tbody td:empty { display: none; }

    /* Die Rundung der Eckzellen gilt nur für die echte Tabellenform. */
    .results-table thead th:first-child,
    .results-table thead th:last-child,
    .results-table tbody tr:last-child > td:first-child,
    .results-table tbody tr:last-child > td:last-child,
    .results-table tbody tr.sw-last > td:first-child,
    .results-table tbody tr.sw-last > td:last-child { border-radius: 0; }

    /* DIE "HEUTE"-TRENNLINIE (oben, .sw-row-heute + tr:not(.sw-row-heute))
       IST NUR FUER DIE FLACHE DESKTOP-TABELLE GEDACHT: dort trennt sie
       optisch EINE zusammenhaengende Gruppe "heute" von der Gruppe
       "aelter". Hier wird jede Zeile zu einer eigenen, bereits gerandeten
       und abgerundeten Karte (siehe oben) – die 2px-Linie landete darum
       mitten in der ersten "gestern"-Karte nach einem "heute"-Eintrag und
       sah wie ein Darstellungsfehler aus (06.09.2026, Peer: "da ist auf
       einmal mitten drin ein ding mit strichen"). Auf Mobile ersetzt der
       Kartenrand die Trennlinie bereits, sie wird hier einfach abgeschaltet. */
    .results-table tr.sw-row-heute + tr:not(.sw-row-heute) > td {
        border-top: 0;
    }
}

/* --- kv-table: dieselbe Form, eine Stufe kleiner -------------------------
   Bewusste Ausnahme bei der Schrift. Die kv-Tabelle zeigt rohe API-Antworten
   in aufklappbaren Blöcken; eine verschachtelte JSON-Struktur in 13 px füllt
   schnell einen halben Bildschirm. Rundung und Randführung sind identisch,
   damit sie trotzdem zur Familie gehört. */
.kv-table {
    border-collapse: separate;
    border-spacing: 0;
    border-radius: 8px;
    font-size: 12px;
}
.kv-table th,
.kv-table td {
    border: 0;
    border-bottom: 1px solid rgba(0,0,0,.08);
    border-right:  1px solid rgba(0,0,0,.08);
}
.kv-table th:first-child,
.kv-table td:first-child { border-left: 1px solid rgba(0,0,0,.08); }
/* Kein thead: den oberen Rand trägt die erste Zeile des tbody. */
.kv-table tbody tr:first-child > th,
.kv-table tbody tr:first-child > td { border-top: 1px solid rgba(0,0,0,.08); }
.kv-table tbody tr:first-child > th:first-child { border-top-left-radius: 8px; }
.kv-table tbody tr:first-child > td:last-child  { border-top-right-radius: 8px; }
.kv-table tbody tr:last-child  > th:first-child { border-bottom-left-radius: 8px; }
.kv-table tbody tr:last-child  > td:last-child  { border-bottom-right-radius: 8px; }
/* Verschachtelte kv-Tabellen behalten die Ecken scharf – sonst entstehen
   Rundungen mitten in einer Zelle. */
.kv-table .kv-table { border-radius: 0; }

/* --- StackWatch: Release-Historie ----------------------------------------
   Die Farben tragen hier Bedeutung und stehen deshalb im Stylesheet, nicht
   als Attribute im erzeugten SVG. Wer die Ampel anpasst, passt beides an
   einer Stelle an. */
.sw-chart-wrap { margin: 0; }
/* Keine max-height: das SVG hat ein festes Seitenverhältnis und würde bei
   gedeckelter Höhe seitlich Luft lassen, statt kleiner zu werden. */
.sw-chart {
    width: 100%;
    height: auto;
    display: block;
    overflow: visible;
}
.sw-chart text { font-family: inherit; }
.sw-chart-achse,
.sw-chart-label      { font-size: 9px; fill: #6c757d; }
.sw-chart-wert       { font-size: 9px; fill: #495057; font-weight: 600; }
.sw-chart-grid       { stroke: rgba(0,0,0,.07); stroke-width: 1; }
.sw-chart-basis      { stroke: rgba(0,0,0,.25); stroke-width: 1; }

.sw-chart-uebrige    { fill: #adb5bd; }
.sw-chart-security   { fill: var(--bs-danger, #dc3545); }

/* Gemessene Null: sichtbarer Strich, nicht Abwesenheit. Der Unterschied zur
   Datenluecke daneben ist die eigentliche Aussage des Diagramms. */
.sw-chart-null       { fill: #6c757d; }

.sw-chart-luecke     { stroke: rgba(0,0,0,.12); stroke-width: 1; }
.sw-chart-luecke-strich { stroke: rgba(0,0,0,.16); stroke-width: 2.5; }
.sw-chart-fragezeichen  { font-size: 12px; fill: #adb5bd; font-weight: 700; }
.sw-chart-teil       { fill: #6c757d; opacity: .55; }

.sw-chart-legende {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1.1rem;
    margin-top: .5rem;
}
.sw-chart-key {
    display: inline-block;
    width: 10px; height: 10px;
    margin-right: .4rem;
    vertical-align: -1px;
    border-radius: 2px;
}
.sw-chart-key-sec    { background: var(--bs-danger, #dc3545); }
.sw-chart-key-rest   { background: #adb5bd; }
.sw-chart-key-null   { background: #6c757d; height: 3px; vertical-align: 2px; }
.sw-chart-key-luecke {
    background: repeating-linear-gradient(45deg,
        rgba(0,0,0,.18) 0 2px, transparent 2px 5px);
    border: 1px solid rgba(0,0,0,.12);
}

/* Zeitraumwahl über dem Diagramm */
.sw-chart-zeitraum .btn { --bs-btn-padding-y: .15rem; --bs-btn-padding-x: .5rem; font-size: .8125rem; }
