

/* Bootstrap setzt beim Oeffnen eines Modals overflow:hidden auf <body> (die
   Scrollbar verschwindet), was den sichtbaren Inhalt um die Scrollbar-Breite
   springen laesst - stoerender Layout-Shift bei jedem Modal (u.a.
   #sendShipmentModal, index.html). scrollbar-gutter:stable reserviert den
   Platz permanent, unabhaengig davon ob die Scrollbar gerade sichtbar ist,
   dadurch bleibt die Breite beim Oeffnen/Schliessen konstant. */
html {
  scrollbar-gutter: stable;
}

/* Font size */

.r2fs-25 {
  font-size: 0.25rem !important;
}

.r2fs-30 {
  font-size: 0.3rem !important;
}

.r2fs-40 {
  font-size: 0.4rem !important;
}

.r2fs-50 {
  font-size: 0.5rem !important;
}

.r2fs-55 {
  font-size: 0.55rem !important;
}

.r2fs-60 {
  font-size: 0.6rem !important;
}

.r2fs-70 {
  font-size: 0.7rem !important;
}

.r2fs-75 {
  font-size: 0.75rem !important;
}

.r2fs-80 {
  font-size: 0.8rem !important;
}

.r2fs-90 {
  font-size: 0.9rem !important;
}

.r2fs-100 {
  font-size: 1rem !important;
}

.r2fs-110 {
  font-size: 1.1rem !important;
}

.r2fs-120 {
  font-size: 1.2rem !important;
}

.r2fs-130 {
  font-size: 1.3rem !important;
}

.r2fs-140 {
  font-size: 1.4rem !important;
}

.r2fs-150 {
  font-size: 1.5rem !important;
}

.r2fs-160 {
  font-size: 1.6rem !important;
}

.r2fs-170 {
  font-size: 1.7rem !important;
}

.r2fs-180 {
  font-size: 1.8rem !important;
}

.r2fs-190 {
  font-size: 1.9rem !important;
}

.r2fs-200 {
  font-size: 2rem !important;
}



/* Sidebar Footer */

.sidebar-clock {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--sidebar-muted-color);
  text-align: center;
  white-space: nowrap;
}

.sidebar-collapsed .sidebar-clock {
  opacity: 0;
  width: 0;
  overflow: hidden;
}



/* Table Font Size */

table.table-summary td {
  font-size: 0.5rem;
}



/* Table Borderless Override (main.css border-bottom Regel aufheben) */

.table-borderless > tbody > tr > td {
  border-bottom: 0;
}



/* Table Hover Text Color Fix (main.css setzt --bs-table-hover-color nicht,
   Bootstrap-Default --bs-emphasis-color bleibt im Dark Mode schwarz) */

.table {
  --bs-table-hover-color: var(--default-color);
}



/* Zeilen-Hover für div-basierte Zusammenfassungs-Tabellen (Shipment/calculation.html) -
   ersetzt table-hover, seit die Zusammenfassung von <table> auf <div>/flex umgestellt
   wurde (Tabellen-Layout brach mit text-nowrap-Zellen in der schmalen Sidebar um) */

.r2-row-hover {
  border-radius: var(--bs-border-radius-sm, .25rem);
}

.r2-row-hover:hover {
  background-color: var(--bs-table-hover-bg);
  color: var(--bs-table-hover-color);
}



/* Dashboard Welcome Banner Text Width (main.css begrenzt widget-banner-text auf
   max-width: 400px, wodurch links vom Profilbild unnötig Platz frei bleibt) */

.widget-banner-welcome .widget-banner-text {
  max-width: none;
}



/* Draggable Modals */

.modal-dialog.r2-draggable {
  position: absolute;
  top: 0;
  left: 0;
  margin: 0;
  transform: none !important;
}

.modal-dialog.r2-draggable .modal-header {
  cursor: move;
  user-select: none;
}


/* Statisch angezeigtes Modal (ohne JS-Trigger, z.B. calculation_edit.html):
   erzwingt sichtbaren Backdrop unabhaengig von konkurrierenden
   opacity/background-color-Regeln aus bootstrap.min.css und main.css */

.modal-backdrop.r2-backdrop-solid {
  opacity: 1 !important;
  background-color: #fff !important;
}

/* Backdrop von modal_add_freight in der Farbe der Zustellung-Karte (bg-light)
   statt im Standard-Schwarz von Bootstrap; Klasse wird per JS (r2.js) beim
   Oeffnen des Modals gesetzt, siehe "Modal Frachtgut hinzufuegen".
   var(--background-color) statt fixem Hex-Wert, damit Light- und Dark-Mode
   automatisch mitgehen (main.css ueberschreibt .bg-light ebenfalls damit). */

.modal-backdrop.r2-backdrop-light {
  opacity: 1 !important;
  background-color: var(--background-color) !important;
}

/* Sichtbarer Schatten um das statisch angezeigte Modal (weisser Backdrop
   sonst ohne Kontrast zum Modal-Content) */

.modal-content.r2-modal-shadow {
  box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.3) !important;
}

/* modal-fullscreen erzwingt sonst border-radius:0 (main.css) */

.modal-content.modal-rounded {
  border-radius: 0.65rem !important;
}

/* Ueberschriften im Frachtgut-bearbeiten-Modal (z.B. "Beschaffung",
   "Zustellung", "Gewicht") verkleinern. Scope auf #kt_modal_edit_freight
   und #modal_add_freight (Frachtgut hinzufuegen, an dasselbe Design
   angeglichen), da .fs-3 global in anderen Templates verwendet wird. */

#kt_modal_edit_freight .fs-3,
#modal_add_freight .fs-3 {
  font-size: 1rem !important;
}

/* "Optionen"-Ueberschrift im Bereich Zustellung (delivery_options) an
   die Groesse der anderen Modal-Ueberschriften angleichen */

#delivery_options .fs-5 {
  font-size: 1rem !important;
}



/* Transfer-Upload: Sofort-Vorschau der hochgeladenen Datei(en) (Rendering
   via pdf.js/UTIF.js, tabellarisch mit einer Zeile je Seite/Bild) */

.transfer-file-preview {
  margin-top: 0.75rem;
  padding: 0.75rem;
  border: 1px solid var(--border-color);
  border-radius: var(--bs-border-radius-lg);
}

.transfer-file-preview-summary {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-bottom: 0.75rem;
}

.transfer-file-preview-name {
  font-weight: 500;
  color: var(--default-color);
  word-break: break-all;
}

.transfer-file-preview-size {
  font-size: 0.8125rem;
  color: var(--muted-color);
}

/* Container fuer die Datei-Tabellen: stapelt sie untereinander - z.B. wenn
   ein Archiv mehrere unterschiedliche Dateien enthaelt oder mehrere Dateien
   gleichzeitig hochgeladen wurden (je Datei eine eigene Tabelle, siehe
   buildFileTable()/buildTableRow() in r2.js). */
.transfer-file-preview-pages {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.transfer-file-preview-table-heading {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--default-color);
  margin-bottom: 0.35rem;
}

.transfer-file-preview-table td,
.transfer-file-preview-table th {
  vertical-align: middle;
}

/* Format-Spalte: einzelne Kennwerte (Endformat/Medienrahmen/...) je auf
   einer eigenen Zeile, als zweispaltiges Grid statt Fliesstext (siehe
   setFormatLines() in r2.js) - Label und Wert sind eigene Grid-Zellen, die
   Wert-Spalte beginnt dadurch unabhaengig von der jeweiligen Label-Laenge
   ("Endformat" vs. "Medienrahmen") an derselben Horizontalposition, wie in
   einer Tabelle. Erste Spalte per max-content genau so breit wie das
   laengste Label, keine feste Breite noetig. */
.transfer-file-preview-format-cell {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 0.4rem;
  row-gap: 0.15rem;
}

.transfer-file-preview-format-label,
.transfer-file-preview-format-value {
  white-space: nowrap;
}

.transfer-file-preview-thumb {
  width: 96px;
  height: 128px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-color);
  border-radius: var(--bs-border-radius);
  background: var(--body-bg, transparent);
  overflow: hidden;
  cursor: zoom-in;
}

/* Schwebende Hover-Vorschau (5-fache Groesse, siehe THUMB_HOVER_FAKTOR/
   zeigeHoverZoom() in r2.js) - EIN gemeinsames, per position:fixed
   ausserhalb jeder Tabelle liegendes Element statt eines CSS-transforms am
   Thumbnail selbst. Ein reines transform:scale() dort hatte zwei Probleme:
   1) Browser skalieren dabei typischerweise die bereits in Normalgroesse
   gerasterte Compositor-Ebene hoch (unscharf, da die hoehere Backing-
   Aufloesung der Canvas beim Scale-Transform nicht automatisch neu
   ausgewertet wird), 2) .table-responsive (noetig fuer die Tabellen selbst)
   schneidet per overflow-x:auto unweigerlich auch vertikal ab (CSS-Regel:
   overflow-x ungleich visible erzwingt overflow-y ebenfalls ungleich
   visible), wodurch die Vergroesserung teils unsichtbar/abgeschnitten war.
   Deutlicher Schatten, damit sich die Vergroesserung sichtbar vom Rest der
   Seite abhebt (schwebt sichtlich "darueber"). */
.transfer-file-preview-hover-zoom {
  position: fixed;
  z-index: 2000;
  display: none;
  pointer-events: none;
  padding: 2px;
  border: 1px solid var(--border-color);
  border-radius: var(--bs-border-radius);
  background: var(--body-bg, #fff);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.45), 0 0.25rem 0.75rem rgba(0, 0, 0, 0.3);
}

.transfer-file-preview-hover-zoom.is-visible {
  display: block;
}

.transfer-file-preview-hover-zoom canvas {
  display: block;
}

/* Canvas wird per pdf.js gerendert (r2.js), Groesse dort bereits exakt per
   "contain"-Skalierung auf die Box berechnet - hier nur Begrenzung als
   Sicherheitsnetz und zentrierte Darstellung via Flex am Elternelement. */
.transfer-file-preview-pdf {
  max-width: 100%;
  max-height: 100%;
}

/* Ladezustand (Rendering/Server-Analyse/Preflight noch ausstehend) - siehe
   buildSpinner()/addSpinnerLine() in r2.js. In der
   Thumb-Box zentriert die bereits vorhandene Flex-Ausrichtung des
   Elternelements automatisch; in Tabellenzellen/Info-Zeilen nur ein
   dezenter Abstand zum folgenden Text. */
.transfer-file-preview-spinner {
  vertical-align: -0.15em;
  margin-right: 0.25rem;
  opacity: 0.7;
}

/* Preflight-Check-Ergebnis je Seite (Farbraum/Aufloesung/Endformat/
   Beschnitt) - kompakte verschachtelte Tabelle innerhalb der Preflight-
   Spalte, ein Badge (Kriterium, farbig nach Status) + Wert je Zeile, siehe
   renderPreflightInto() in r2.js. */
.transfer-file-preview-preflight-table td {
  padding: 0.15rem 0.4rem 0.15rem 0;
  font-size: 0.75rem;
  white-space: nowrap;
}

.transfer-file-preview-preflight-table .badge {
  font-weight: 500;
}

/* PDF-Metadaten je Seite (Endformat/Medienrahmen, Farbraum, Renderaufloesung),
   von r2.js per addInfoLine() befuellt */

.transfer-file-preview-info-line {
  margin: 0.2rem 0 0;
  font-size: 0.75rem;
  color: var(--muted-color);
}

.transfer-file-preview-info-label {
  color: var(--default-color);
}

/* Zusatzbericht-Zeile je Seite (regelbasiert, siehe renderZusatzberichtInto()
   in r2.js): eigene <tr> direkt unter der Format-/Preflight-Zeile (siehe
   buildTableRow() in r2.js), statt einer eigenen Tabellenspalte daneben -
   dezent per oberer Trennlinie abgesetzt, damit trotz colspan optisch klar
   bleibt, dass sie noch zur selben Seite gehoert. */
.transfer-file-preview-ai-row td {
  padding-top: 0.1rem;
  border-top: 0;
}




/* Fortschritt (Circular Progress) je Datei - siehe buildProgressRing()/
   uploadMitProgress() in r2.js. EIN Ring pro in showPreview() ausgewaehlter
   Datei (bzw. pro aus einem Archiv entpackter Einzeldatei), der
   NACHEINANDER mehrere Arbeitsschritte darstellt statt fuer jeden Schritt
   einen eigenen, gleichzeitig sichtbaren Ring zu zeigen: zunaechst echte
   0-100%-Byte-Messung waehrend des Uploads, danach - an derselben Stelle,
   kein neues Element - Umschalten auf einen zeitbasierten Status fuer die
   anschliessende Verarbeitung (Rendering + Server-Analyse/Preflight/
   Zusatzbericht). So bleibt zu jedem Zeitpunkt klar erkennbar, dass genau EIN
   Schritt gerade laeuft, und die Tabelle selbst braucht keine zusaetzlichen
   Status-Ringe je Seite. */

.transfer-upload-progress-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.transfer-upload-progress-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.transfer-upload-progress-ring-wrap {
  position: relative;
  width: 40px;
  height: 40px;
  flex: none;
}

.transfer-upload-progress-ring {
  width: 40px;
  height: 40px;
  /* Start der Fuellung oben (12 Uhr) statt am Standard-Nullpunkt (3 Uhr) */
  transform: rotate(-90deg);
}

.transfer-upload-progress-ring-track {
  fill: none;
  stroke: var(--border-color);
  stroke-width: 4;
}

.transfer-upload-progress-ring-bar {
  fill: none;
  stroke: var(--accent-color);
  stroke-width: 4;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.15s linear;
}

.transfer-upload-progress-ring-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  font-weight: 600;
  color: var(--default-color);
}

/* Textblock aus Art-Label ("Upload"/"Verarbeitung") + Dateiname (siehe
   buildProgressRing() in r2.js) - kennzeichnet, welchen der nacheinander
   im selben Ring dargestellten Schritte die Datei gerade durchlaeuft. */
.transfer-upload-progress-info {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  min-width: 0;
}

.transfer-upload-progress-kind {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted-color);
}

.transfer-upload-progress-filename {
  font-size: 0.8125rem;
  color: var(--default-color);
  word-break: break-all;
}

/* Zustands-Varianten fuer den Verarbeitungs-Schritt (siehe
   buildProgressRing().beginProcessing()/finish()) - ohne echte 0-100%-
   Messung (Rendering/Server-Analyse melden erst beim Abschluss
   ein Ergebnis): dreht sich waehrend der Verarbeitung, wird bei Abschluss
   gruen (mit Dauer) bzw. rot (Fehler). Waehrend der Upload-Phase bleibt der
   Ring ohne diese Klassen (dort faerbt .transfer-upload-progress-ring-bar
   oben konstant per --accent-color). */
.transfer-upload-progress-ring-wrap.is-running .transfer-upload-progress-ring {
  animation: transfer-processing-ring-spin 0.9s linear infinite;
}

.transfer-upload-progress-ring-wrap.is-done .transfer-upload-progress-ring-bar {
  stroke: #2e7d32;
}

.transfer-upload-progress-ring-wrap.is-done .transfer-upload-progress-ring-text {
  color: #2e7d32;
}

.transfer-upload-progress-ring-wrap.is-error .transfer-upload-progress-ring-bar {
  stroke: #c62828;
}

.transfer-upload-progress-ring-wrap.is-error .transfer-upload-progress-ring-text {
  color: #c62828;
}

@keyframes transfer-processing-ring-spin {
  from { transform: rotate(-90deg); }
  to { transform: rotate(270deg); }
}

/* Capzo-Workflow-Fortschritt (4 Schritte, siehe index.html): bewusst OHNE
   umgebende Card (kein Titel, kein Hintergrund/Rahmen/Schatten) - nur die
   Fortschrittsanzeige selbst direkt auf dem Seitenhintergrund. Gegenueber den
   schlichten EliteAdmin-Standard-Wizard-Steps (dort nur duenne Linien mit
   kleinen, unauffaelligen Kreisen ohne jede Bewegung) bleiben grosszuegigere
   Kreise mit Farbverlauf fuer den aktiven Schritt, eine weiche Eintritts-
   Animation beim Laden der Seite sowie ein rundum pulsierender Ring um den
   aktiven Schritt, damit auf einen Blick erkennbar ist, wo im Ablauf sich
   der Nutzer gerade befindet. Rein darstellend (kein Klick/Navigation) -
   der aktive/erledigte Zustand wird direkt im Template per
   is-active/is-done/is-upcoming-Klasse gesetzt. */
.capzo-steps {
  display: flex;
  align-items: flex-start;
  gap: 0.25rem;
  animation: capzo-steps-eintritt 0.5s ease both;
}

@keyframes capzo-steps-eintritt {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

.capzo-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
}

.capzo-step-circle {
  position: relative;
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  background: var(--surface-color);
  border: 2px solid var(--border-color);
  color: var(--light-color);
  transition: background 0.35s ease, border-color 0.35s ease, color 0.35s ease, transform 0.35s ease, box-shadow 0.35s ease;
}

.capzo-step-label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--muted-color);
  transition: color 0.35s ease;
}

/* Verbindungslinie zwischen zwei Kreisen - Hoehe passend zur Kreismitte
   (26px = halbe Kreis-Hoehe) per margin-top ausgerichtet, damit sie exakt
   mittig durch die Kreise laeuft statt an deren Oberkante. */
.capzo-step-connector {
  flex: 1 1 24px;
  align-self: flex-start;
  margin-top: 26px;
  height: 2px;
  background: var(--border-color);
  transition: background 0.35s ease;
}

.capzo-step.is-active .capzo-step-circle {
  background: linear-gradient(135deg, var(--accent-color), var(--info-color));
  border-color: transparent;
  color: var(--contrast-color, #fff);
  transform: scale(1.08);
  box-shadow: 0 6px 16px -4px rgba(0, 0, 0, 0.35);
}

/* Pulsierender Ring nur beim aktiven Schritt - ein wiederholt nach aussen
   auslaufender, verblassender Kreisrand (klassische "Ripple"-Animation),
   deutlich das auffaelligste Element der ganzen Fortschrittsanzeige. */
.capzo-step.is-active .capzo-step-circle::after {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 2px solid var(--accent-color);
  animation: capzo-step-pulse 1.8s ease-out infinite;
}

@keyframes capzo-step-pulse {
  0% { transform: scale(0.85); opacity: 0.7; }
  100% { transform: scale(1.35); opacity: 0; }
}

.capzo-step.is-active .capzo-step-label {
  color: var(--default-color);
  font-weight: 600;
}

.capzo-step.is-done .capzo-step-circle {
  background: var(--success-color);
  border-color: var(--success-color);
  color: var(--contrast-color, #fff);
}

.capzo-step.is-done .capzo-step-label {
  color: var(--default-color);
}

.capzo-step.is-done + .capzo-step-connector {
  background: var(--success-color);
}

@media (max-width: 640px) {
  .capzo-steps {
    flex-direction: column;
    align-items: stretch;
  }

  .capzo-step {
    flex-direction: row;
    text-align: left;
    gap: 0.75rem;
  }

  .capzo-step-label {
    align-self: center;
  }

  /* Verbindungslinien werden zwischen senkrecht gestapelten Schritten
     ebenfalls senkrecht - links buendig zur Kreismitte (25px = halbe
     Kreisbreite) statt zwischen zwei Kreisen in einer Zeile. */
  .capzo-step-connector {
    width: 2px;
    height: 20px;
    margin: 0 0 0 25px;
    flex: none;
  }
}

/* Notifications: dedizierte Listenseite (R2notifications) statt Dropdown -
   dort soll die Liste nicht wie im Header auf 340px Höhe begrenzt sein */
.notification-list-full {
  max-height: none;
}

/* Notification Detail Modal: .modal-dialog-centered macht den Dialog selbst
   zum Flex-Container (row-Achse); ohne expliziten width bleibt sein
   width:auto bei eher kurzem Inhalt (Titel + ein Absatz statt Formular) je
   nach Browser bei der Inhaltsbreite hängen statt bis max-width (modal-lg)
   zu wachsen. Bewusst nur auf dieses eine Modal gescoped, um die anderen
   modal-lg/-xl-Dialoge im Projekt nicht zu beeinflussen. */
#notificationDetailModal .modal-dialog {
  width: 100%;
}

/* Gleicher Fix für die Bild-Lightbox - modal-xl statt -lg, damit die
   Vor-/Zurück-Buttons neben dem Bild Platz haben statt es zu stauchen */
#notificationImageLightbox .modal-dialog {
  width: 100%;
}

/* Notification-Bilder: kleine runde Thumbnails (Header-Dropdown, Listenseite,
   Detail-Modal), die sich per Hover zu einer schwebenden Vorschau
   vergrößern (siehe r2.js) - Grundform an .notification-avatar angelehnt,
   nur kleiner, da mehrere davon in einer Reihe stehen können. */
.notification-images {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

.notification-image-thumb {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  object-fit: cover;
  cursor: zoom-in;
  border: 1px solid var(--border-color);
}

.notification-image-hover-zoom {
  display: none;
  position: fixed;
  z-index: 2000;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  border: 2px solid var(--surface-color);
  background: var(--surface-color);
  object-fit: contain;
  pointer-events: none;
}

.notification-image-lightbox-img {
  max-width: 100%;
  max-height: 75vh;
  border-radius: var(--radius-lg);
}

/* Vor-/Zurück-Buttons stehen als Flex-Geschwister neben dem Bild (nicht
   darüber), damit sie den Bildinhalt nicht verdecken */
.notification-image-lightbox-body {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-md);
}

/* Standardmäßig ausgeblendet, r2.js zeigt sie nur an, wenn die
   Notification mehr als ein Bild hat */
.notification-image-lightbox-nav {
  display: none;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-color);
  background: var(--surface-color);
  color: var(--default-color);
  box-shadow: var(--shadow-md);
  cursor: pointer;
}

/* Ungelesen-Counter im Header: langsam pulsierendes rotes Glimmen statt
   statischem Badge, um auf ungelesene Notifications aufmerksam zu machen */
@keyframes notification-badge-pulse {
  0%, 100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger-color), transparent 40%);
  }
  50% {
    box-shadow: 0 0 10px 4px color-mix(in srgb, var(--danger-color), transparent 15%);
  }
}

.header-badge {
  animation: notification-badge-pulse 2.5s ease-in-out infinite;
}

/* Ungelesene Notifications deutlicher kennzeichnen: kräftiger Akzent-Rahmen
   statt nur des dezenten Hintergrund-Tints aus main.css, dazu fetterer
   Titel. Der kleine Punkt (.notification-dot, kaum sichtbar) wird dafür
   ausgeblendet, damit nicht zwei schwache Signale statt eines klaren
   nebeneinander stehen.

   Rahmenfarbe richtet sich nach der Wichtigkeit (notification_type, aus
   data-notification-type) statt immer Rot zu sein - info/warning/success
   nutzen dieselben Farben wie .notification-icon, danger fällt weiterhin
   auf Rot zurück. */
.notification-item[data-notification-type="info"] { --notification-accent: var(--info-color); }
.notification-item[data-notification-type="warning"] { --notification-accent: var(--warning-color); }
.notification-item[data-notification-type="success"] { --notification-accent: var(--success-color); }
.notification-item[data-notification-type="danger"] { --notification-accent: var(--danger-color); }

.notification-item.unread {
  border-left: 3px solid var(--notification-accent, var(--danger-color));
  padding-left: calc(var(--spacing-lg) - 3px);
  background-color: color-mix(in srgb, var(--notification-accent, var(--danger-color)), transparent 95%);
}

.notification-item.unread:hover {
  background-color: color-mix(in srgb, var(--notification-accent, var(--danger-color)), transparent 90%);
}

.notification-item.unread .notification-title {
  font-weight: 700;
}

.notification-item.unread .notification-dot {
  display: none;
}

/* Vermerk "Gelesen" bei bereits gelesenen Notifications auf der Listenseite
   (im Header-Dropdown stehen ohnehin nur noch ungelesene) */
.notification-read-label {
  margin-left: 8px;
  color: var(--success-color);
  font-size: 0.6875rem;
  font-weight: 500;
}

.notification-read-label i {
  margin-right: 2px;
}

/* Gleicher Stil wie .notification-read-label, nur in Rot - Vermerk
   "Ungelesen" im Header-Dropdown und auf der Listenseite */
.notification-unread-label {
  margin-left: 8px;
  color: var(--danger-color);
  font-size: 0.6875rem;
  font-weight: 500;
}

.notification-unread-label i {
  margin-right: 2px;
  font-size: 0.5rem;
}

/* "Nudge"-Sprechblase am Glocken-Icon: erscheint, sobald mind. eine
   ungelesene Notification seit über einem Tag unbeachtet ist (siehe
   header_notification_nudge im Context Processor). Wird per r2.js
   entfernt, sobald das Dropdown geöffnet wird. .notification-dropdown ist
   bereits ein Bootstrap .dropdown (position: relative), daher genügt hier
   position: absolute ohne eigenen Anker. */
@keyframes notification-nudge-in {
  from { opacity: 0; transform: translate(-50%, -6px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

@keyframes notification-nudge-bounce {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 5px); }
}

.notification-nudge {
  position: absolute;
  top: calc(100% + 12px);
  left: 50%;
  width: 220px;
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--danger-color);
  color: var(--contrast-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.4;
  text-align: center;
  cursor: pointer;
  z-index: 1000;
  animation: notification-nudge-in 0.4s ease-out,
             notification-nudge-bounce 1.8s ease-in-out 0.4s infinite;
}

.notification-nudge::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-bottom-color: var(--danger-color);
}

/* Filterleiste (Alle/Ungelesen/Gelesen + Prioritäts-Dropdown) auf der
   Notification-Listenseite */
.notification-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-lg);
  border-bottom: 1px solid var(--border-color);
}

.notification-priority-filter .dropdown-toggle i {
  margin-right: 4px;
}

/* r2_glass.css setzt .page-dashboard .card auf overflow:hidden (rundet
   Ecken von Hintergründen/Bildern ab) - dadurch wurde das Dropdown-Menü des
   Prioritäts-Filters am Kartenrand abgeschnitten, da es als position:absolute
   über den Kartenrand hinausragt. Höhere Spezifität nötig, um die Regel aus
   r2_glass.css zu überstimmen; bewusst nur für diese eine Karte (Klasse
   notification-list-card), damit andere Dashboard-Karten ihren
   Rundungs-Clip behalten. */
.page-dashboard .card.notification-list-card {
  overflow: visible;
}

/* Gleicher Grund/gleiches Muster wie bei .notification-list-card oben:
   das Choices.js-Dropdown der "Text eingeben"-Auswahl (Capzo-Upload-Seite)
   ragt als position:absolute ueber den Kartenrand hinaus und wurde vom
   overflow:hidden aus r2_glass.css abgeschnitten. */
.page-dashboard .card.capzo-text-input-card {
  overflow: visible;
}

/* Nutzereinstellung "Hover-Effekt bei Karten deaktivieren" (Account > Einstellungen):
   überstimmt den Hover-Transform/Shadow aus r2_glass.css per body-Klasse. */
body.no-card-hover .page-dashboard .card:hover,
body.no-card-hover .page-dashboard .dash-stat-card:hover {
  transform: none;
  box-shadow:
    inset 0 1px 0 var(--glass-highlight),
    var(--glass-shadow);
}

.ticket-screenshots {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-sm);
}

.ticket-screenshot-thumb {
  max-width: 400px;
  width: 100%;
  border-radius: var(--border-radius-sm, 4px);
}

/* Kleine runde Mini-Vorschau (Ticketliste, Ergaenzungen-Verlauf) statt der
   grossen Rechteck-Ansicht von der Detailseite (.ticket-screenshot-thumb).
   Klick oeffnet weiterhin die Originalgroesse in einem neuen Tab. */
.ticket-screenshot-thumb-round {
  width: 32px;
  height: 32px;
  object-fit: cover;
  border-radius: 50%;
  border: 1px solid var(--border-color);
  cursor: zoom-in;
}

/* Hover-Zoom: gleicher Ansatz wie bei .notification-image-thumb/
   .notification-image-hover-zoom (siehe r2.js) - EIN schwebendes,
   position:fixed liegendes <img> statt CSS-transform am Thumbnail selbst.
   Ein reines transform:scale() dort skaliert nur die bereits kleine
   Rasterung des Thumbnails hoch (wirkt unscharf) und kann von
   overflow-Regeln umliegender Container abgeschnitten werden. */
.ticket-screenshot-hover-zoom {
  display: none;
  position: fixed;
  z-index: 2000;
  border-radius: var(--bs-border-radius, 4px);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.45), 0 0.25rem 0.75rem rgba(0, 0, 0, 0.3);
  border: 2px solid var(--body-bg, #fff);
  background: var(--body-bg, #fff);
  object-fit: contain;
  pointer-events: none;
}

/* Das "Neues Ticket"-Modal umschliesst modal-body UND modal-footer mit einem
   gemeinsamen <form>, damit beide Bereiche zusammen abgeschickt werden.
   Bootstraps .modal-dialog-scrollable erwartet aber, dass modal-body als
   direktes Flex-Kind von modal-content sein Wachstum/Scrollen bekommt -
   durch das dazwischenliegende <form> (ein normaler Block, kein Flex-Item)
   wird dieser Ketten-Effekt unterbrochen: der Footer mit den Buttons wurde
   dadurch aus dem sichtbaren Bereich geschnitten (modal-content hat
   overflow:hidden), und das gesamte Modal wuchs stattdessen ueber die
   Seite hinaus. Fix: das <form> selbst wird zum Flex-Container, dann greift
   das bestehende flex:1 1 auto/overflow-y:auto von .modal-body wieder wie
   vorgesehen. */
.modal-dialog-scrollable .modal-content > form {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.priority-criteria-list {
  list-style: none;
  display: grid;
  gap: 0.35rem;
  margin: 0.5rem 0 0;
  padding: 0.75rem 1rem;
  background: var(--card-bg, rgba(0, 0, 0, 0.03));
  border-radius: var(--border-radius-sm, 4px);
  font-size: 0.8125rem;
  color: var(--muted-color);
}

.priority-criteria-list strong {
  color: var(--default-color);
}

.page-subtitle {
  margin: 0;
  font-size: 0.875rem;
  color: var(--muted-color);
}

#newTicketModal .modal-header {
  align-items: flex-start;
}

.modal-subtitle {
  margin: 0.25rem 0 0;
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--muted-color);
}

.icon-calc-plus {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
}

.collapse-chevron {
  display: inline-block;
  transition: transform var(--transition-fast, 0.2s);
}

[data-bs-toggle="collapse"][aria-expanded="true"] .collapse-chevron {
  transform: rotate(90deg);
}

@media (min-width: 992px) {
  .calc-summary-sticky {
    position: sticky;
    top: var(--spacing-lg, 1rem);
  }
}

.calc-summary-sticky .card-header {
  border-bottom: 0;
}

/* Hover-Effekt fuer die Bearbeiten/Duplizieren/Loeschen-Icons einer Capzo-
   Positions-Card: gleiche Farben wie .btn-outline-primary/-warning/-danger
   :hover in calculation.html (main.css), aber ohne deren dauerhaften Rahmen
   im Ruhezustand - die Buttons sind hier bewusst rahmenlos (siehe vorherige
   Anpassung, .btn-icon ohne btn-outline-*). */
.capzo-icon-action.text-primary:hover {
  color: var(--contrast-color);
  background-color: var(--accent-color);
}

.capzo-icon-action.text-warning:hover {
  color: var(--heading-color);
  background-color: var(--warning-color);
}

.capzo-icon-action.text-danger:hover {
  color: var(--contrast-color);
  background-color: var(--danger-color);
}

.capzo-icon-action.text-info:hover {
  color: var(--contrast-color);
  background-color: var(--info-color);
}

/* Aktionen-Raster einer Capzo-Position: das Info-Icon (Preflight +
   Zusatzbericht zusammengefasst) sitzt direkt UNTER Bearbeiten+Duplizieren
   (spannt beide Spalten, zweite Zeile) statt in derselben Zeile daneben.
   Chevron (Details auf-/zuklappen) spannt links beide Zeilen und bleibt
   dadurch vertikal zentriert stehen; Loeschen bleibt dagegen bewusst NUR in
   der oberen Zeile (".") - auf gleicher Hoehe wie Bearbeiten/Duplizieren,
   statt ueber beide Zeilen vertikal zentriert zu wirken. */
.capzo-position-actions {
  display: grid;
  grid-template-columns: auto auto auto auto;
  grid-template-areas:
    "toggle edit duplicate delete"
    "toggle info info .";
  align-items: center;
}

.capzo-position-action-toggle {
  grid-area: toggle;
}

.capzo-position-action-edit {
  grid-area: edit;
}

.capzo-position-action-duplicate {
  grid-area: duplicate;
}

.capzo-position-action-delete {
  grid-area: delete;
}

.capzo-position-info-icon {
  grid-area: info;
}

/* Das "i" von bi-info ist als Glyphe deutlich kleiner gezeichnet als die
   uebrigen Icons der Aktionen-Gruppe (bi-info fuellt die Icon-Flaeche nicht
   aus, anders als z.B. bi-chevron-down oder die lucide-Icons) - daher hier
   groesser skaliert als die 1rem aus .btn-icon i (main.css), damit es in der
   Reihe optisch gleichwertig wirkt. */
.capzo-position-info-icon i {
  font-size: 1.5rem;
}

/* Popover des Info-Icons (per data-bs-custom-class am Button vergeben, wird
   von Bootstrap auf das .popover-Element selbst gesetzt): Bootstraps
   Standard von 276px (--bs-popover-max-width) ist fuer das 3-spaltige
   Preflight-Kachel-Raster (3 x min. 140px + Abstaende + Body-Padding) zu
   schmal - der Inhalt ragte ueber die Sprechblase hinaus. Ueber die CSS-
   Variable statt max-width direkt, damit die uebrige Popover-Berechnung
   von Bootstrap (Pfeil, Rand) unveraendert bleibt; auf schmalen Viewports
   auf die Viewportbreite abzueglich Rand begrenzt, dort greift zusaetzlich
   der responsive Spaltenabbau von .capzo-preflight-grid. */
.capzo-position-info-popover {
  --bs-popover-max-width: min(600px, calc(100vw - 2rem));
}

/* Grosses, dezentes "+" unter einer Capzo-Positions-Card zum Hinzufuegen
   einer weiteren Position - bewusst ohne Rahmen/Box (anders als
   .kanban-add-card), nur das Symbol selbst tritt hervor, per Hover-Farbe. */
.capzo-add-position {
  width: 48px;
  height: 48px;
  padding: 0;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--muted-color);
  border: none;
  background: none;
}

.capzo-add-position:hover {
  color: var(--accent-color);
}

/* Kompakte Vorschau, die nach dem Upload die Dropzone einer Capzo-Position
   ersetzt (siehe applyPositionPreview() in r2.js) - gleiche Rahmen-/Radius-
   Sprache wie .transfer-file-preview-thumb (untere "Daten hochladen"-Card),
   aber auf halbe Groesse skaliert, da hier nur ein Thumbnail + Dateiname
   statt der vollen Preflight-Tabelle/Zusatzbericht angezeigt wird. */
.capzo-position-preview {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.capzo-position-preview-thumb {
  width: 56px;
  height: 72px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-color);
  border-radius: var(--bs-border-radius);
  background: var(--body-bg, transparent);
  overflow: hidden;
  cursor: zoom-in;
}

.capzo-position-preview-canvas {
  max-width: 100%;
  max-height: 100%;
}

.capzo-position-preview-info {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}

.capzo-position-preview-name {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--default-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.capzo-position-preview-page,
.capzo-position-preview-format-line {
  font-size: 0.75rem;
  color: var(--muted-color);
}

/* Visuell versteckt, aber weiterhin layoutfaehig/messbar (bewusst KEIN
   display:none): traegt die untere Vorschau-Tabelle/Fortschrittsliste, an
   der r2.js weiterhin fest verdrahtet haengt (siehe Template-Kommentar bei
   #transfer_file_preview), seit die sichtbare "Daten hochladen"-Card
   entfernt wurde. display:none wuerde getBoundingClientRect() der
   enthaltenen Canvas-Thumbnails auf 0x0 zusammenfallen lassen - das wuerde
   den Hover-Zoom der Capzo-Positions-Vorschauen kaputt machen
   (_hoverSourceCanvas in zeigeHoverZoom(), r2.js), da diese Original-
   Canvas dort liegen. position:absolute nimmt es zusaetzlich aus dem
   normalen Fluss, damit es keinen sichtbaren Leerraum hinterlaesst. */
.capzo-upload-plumbing-hidden {
  position: absolute;
  top: 0;
  left: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: -1;
}

/* Fortschritts-Slot einer Capzo-Position (siehe .capzo-position-progress-
   mount/applyPositionPreview() in r2.js): Ring+Info nebst Abbrechen-Button
   nebeneinander, gleiche Ausrichtung wie die uebrige Positions-Zeile. */
.capzo-position-progress {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Aufgeklappter Details-Bereich einer Capzo-Position: in die Card
   integriert (kein eigener Rahmen/Hintergrund wie bei einem Bootstrap-
   Accordion-Element) - nur eine Trennlinie zur Kopfzeile darueber, damit
   sich die Details trotzdem optisch vom Kopf absetzen. Aufgeklappt per
   Chevron-Icon in der Buttonreihe, siehe .capzo-position-accordion-toggle
   in r2.js. */
.capzo-position-details {
  margin-top: var(--spacing-md, 1rem);
  padding-top: var(--spacing-md, 1rem);
  border-top: 1px solid var(--border-color);
}

/* Unterbereiche innerhalb der Details (Format/Preflight/Zusatzbericht,
   siehe Template) - durch je eine kleine Kopfzeile plus Trennlinie (ausser
   vor dem ersten Unterbereich) klar voneinander abgesetzt, obwohl alle drei
   ueber dasselbe Auf-/Zuklappen erreichbar sind. */
.capzo-position-detail-section + .capzo-position-detail-section {
  margin-top: var(--spacing-md, 1rem);
  padding-top: var(--spacing-md, 1rem);
  border-top: 1px solid var(--border-color);
}

.capzo-position-detail-heading {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-color);
  margin-bottom: 0.5rem;
}

/* Feste Thumbnail-Groesse fuer die Platzhalter-Positionen im Capzo-Warenkorb
   (Offcanvas), da hier kein echtes Produktbild wie in der EliteAdmin-Pro-
   Vorlage existiert, sondern ein zentriertes Bootstrap-Icon. */
.capzo-cart-thumb {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
}

.calc-summary-body {
  padding-top: var(--spacing-sm, 0.5rem);
}

.margin-individual-field {
  width: 7rem;
}

#accordionSummary {
  --bs-accordion-border-radius: 0.75rem;
  --bs-accordion-inner-border-radius: 0.7rem;
}


/* Auth-Seiten (Login + Passwort-vergessen-Flow), EliteAdmin-Design mit Hintergrundbild */

/* Flaggen-Icons im Sprachumschalter (ersetzt die weggefallenen
   Metronic-Utility-Klassen w-20px/h-20px) */
.login-flag {
  width: 20px;
  height: 20px;
  object-fit: cover;
}

/* Hintergrundbild ganzseitig hinter Marken- und Formular-Spalte.
   Bildwahl folgt data-theme (von theme.js gesetzt), nicht der
   OS-Einstellung direkt, damit ein manuell gewähltes Theme aus dem
   Dashboard auch auf der Login-Seite konsistent bleibt. */
.page-auth {
  background-image: url("../../images/bg23a.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

[data-theme="dark"] .page-auth {
  background-image: url("../../images/bg06.jpg");
}

/* Formular-Spalte durchsichtig, damit das Hintergrundbild
   durchscheint (main.css füllt sie sonst deckend mit --background-color) */
.page-auth .auth-split-form {
  background: transparent;
}

/* Marken-Spalte als dunkel getöntes Glas über dem Foto, unabhängig
   vom Theme, damit der weiße Markentext immer lesbar bleibt */
.page-auth .auth-split-brand {
  background: linear-gradient(135deg, rgba(0, 3, 20, 0.55), rgba(0, 3, 15, 0.35));
  -webkit-backdrop-filter: blur(15px);
  backdrop-filter: blur(15px);
  border-right: 1px solid rgba(255, 255, 255, 0.15);
}

/* Zentriertes Auth-Layout (Passwort vergessen/zurücksetzen etc.)
   ebenfalls durchsichtig, damit das Hintergrundbild durchscheint */
.page-auth .auth-layout {
  background: transparent;
}

/* Login-/Auth-Card als helles/dunkles Glas, analog zum Dashboard-Glass-Skin */
.page-auth .auth-card {
  background: var(--glass-bg, var(--surface-color));
  -webkit-backdrop-filter: blur(var(--glass-blur, 20px)) saturate(var(--glass-saturate, 160%));
  backdrop-filter: blur(var(--glass-blur, 20px)) saturate(var(--glass-saturate, 160%));
  border: 1px solid var(--glass-border, var(--border-color));
  box-shadow: inset 0 1px 0 var(--glass-highlight, transparent), var(--glass-shadow, var(--shadow-lg));
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .page-auth .auth-card {
    background: var(--surface-color);
  }
}

/* Presence-Dropdown ("Wer ist online"): Geometrie/Listen-Layout kommt bewusst
   aus den bestehenden .notification-menu/.notification-list/.notification-item/
   .notification-avatar/.header-badge-Klassen (main.css) - hier nur, was fuer
   Online/Offline wirklich neu ist. */
.presence-status {
  font-size: 0.75rem;
  color: var(--muted-color);
}

.presence-status.is-connected {
  color: var(--success-color);
}

/* Online-Kollegen immer oben: die Reihenfolge im DOM bleibt alphabetisch
   (siehe RnSoftware.context_processors.presence), CSS order sortiert beim
   Rendern nur die online-Nutzer nach vorne - r2.js muss dafuer nichts weiter
   tun, das Umschalten von is-online/is-offline reicht. */
/* Bootstraps Standard-Dropdown-Pfeil neben dem Icon entfernen, analog zum
   Notification-Dropdown (main.css: .notification-dropdown .dropdown-toggle::after). */
.presence-dropdown .dropdown-toggle::after {
  display: none;
}

.presence-list {
  display: flex;
  flex-direction: column;
}

.presence-item {
  order: 1;
  flex-wrap: wrap;
}

/* Wrapper um Avatar+Name (fixe Hoehe), damit .presence-status-label sich
   daran positioniert statt an .presence-item selbst - dessen Hoehe waechst,
   sobald der Composer aufklappt, wodurch der Online/Offline-Text sonst immer
   naeher an den Senden-Button ruecken wuerde. */
.presence-item-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  flex: 1;
  min-width: 0;
  position: relative;
}

.presence-item.is-online {
  order: 0;
}

/* Accordion-Nachrichtenfeld unter einem Presence-Eintrag: .presence-item hat
   flex-wrap:wrap, Avatar (flex-shrink:0) + .notification-content (flex:1)
   fuellen zusammen bereits die volle Breite - ein dritter Child mit
   flex-basis:100% rutscht dadurch automatisch in eine eigene Zeile, ohne dass
   ein zusaetzlicher Wrapper in der order-sortierten Liste noetig waere.
   padding liegt bewusst auf .presence-compose-inner, nicht direkt auf
   .presence-compose - sonst wuerde es bei max-height:0 trotzdem sichtbar
   bleiben (padding wird von max-height nicht mit auf 0 gestaucht). */
.presence-compose {
  flex-basis: 100%;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease;
}

.presence-compose.is-open {
  max-height: 96px;
}

.presence-compose-inner {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 8px 0 4px;
}

.presence-compose-input {
  flex: 1;
  resize: none;
  border: 1px solid var(--border-color);
  border-radius: var(--bs-border-radius);
  background: var(--background-color);
  color: var(--default-color);
  font-size: 0.8125rem;
  padding: 0.5rem 0.625rem;
  max-height: 72px;
}

.presence-compose-input:focus {
  outline: none;
  border-color: var(--accent-color);
  background: var(--surface-color);
}

.presence-compose-send {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--accent-color);
  color: var(--contrast-color);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 0.875rem;
  transition: opacity 0.2s;
}

.presence-compose-send:hover {
  opacity: 0.85;
}

/* bi-send ist geometrisch nahezu exakt zentriert (per Font-Metriken
   geprueft, Abweichung < 1%) - die spitz nach oben rechts zeigende
   Flugzeug-Form wirkt trotzdem optisch leicht Richtung oben rechts verschoben,
   ein bekannter Effekt bei gerichteten/spitzen Icons. Minimaler Nudge nach
   unten links gleicht das rein optisch aus. */
.chat-send-btn i,
.presence-compose-send i {
  transform: translate(-1px, 1px);
}

/* "Gelesen"/doppeltes Haeckchen (main.css faerbt .message-status.seen mit
   var(--accent-color)) in Gruen statt Akzentfarbe, passend zum Gruen-Muster
   fuer "online"/gelesen im restlichen Presence-/Chat-System. */
.message-status.seen {
  color: var(--success-color);
}

/* Info-Bubble fuer eine per Chat geteilte Sendung (chat.html,
   message_type='shipment_share') - liegt als <a> statt <div> im DOM (Klick
   oeffnet/dupliziert die Sendung), braucht deshalb die Link-Resets on top
   von .message-bubble aus main.css. */
.message-bubble.message-bubble-info {
  display: block;
  text-decoration: none;
  cursor: pointer;
  border-left: 3px solid var(--info-color, #0dcaf0);
}

.message-bubble.message-bubble-info:hover {
  opacity: 0.85;
}

/* Kreuz-Button zum Leeren der Team-Suche, rechts im Suchfeld. */
.presence-search-wrap {
  position: relative;
  margin-top: .5em;
}

.presence-search-wrap .form-control {
  padding-right: 2rem;
}

.presence-search-clear {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border: none;
  background: none;
  color: var(--muted-color);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6875rem;
  border-radius: 50%;
  cursor: pointer;
}

.presence-search-clear:hover {
  background: var(--border-color);
  color: var(--default-color);
}

.presence-compose-emoji {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: none;
  color: var(--muted-color);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 1rem;
  transition: all 0.2s;
}

.presence-compose-emoji:hover {
  background: var(--border-color);
  color: var(--default-color);
}

/* Emoji-Picker: eigenstaendig (keine externe Bibliothek/CDN), wird von r2.js
   per position:fixed direkt an document.body gehaengt - andernfalls wuerde
   .notification-menu/.presence-menu (overflow:hidden) das Popover
   abschneiden, sobald es ausserhalb des Dropdown-Rahmens reicht. */
.emoji-picker-popover {
  position: fixed;
  z-index: 3000;
  width: 260px;
  max-height: 220px;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 2px;
  padding: 8px;
  background: var(--surface-color);
  border: 1px solid var(--border-color);
  border-radius: var(--bs-border-radius-lg);
  box-shadow: var(--shadow-lg);
}

.emoji-picker-btn {
  border: none;
  background: none;
  font-size: 1.25rem;
  line-height: 1.6;
  border-radius: var(--bs-border-radius);
  cursor: pointer;
  padding: 2px 0;
}

.emoji-picker-btn:hover {
  background: var(--background-color);
}

/* Grenze zwischen Online- und Offline-Gruppe: da die Sortierung rein ueber
   CSS order laeuft (DOM-Reihenfolge bleibt alphabetisch, siehe oben), kann
   nur JS wissen, welches Item visuell das letzte online ist - r2.js setzt
   dafuer .presence-item--last-online exklusiv auf genau dieses Element. */
.presence-item.presence-item--last-online {
  border-bottom-width: .5em;
}

/* Hover fuer Online-Eintraege deutlich abheben vom neutralen Standard-Hover
   (.notification-item:hover aus main.css) - dezenter gruener Tint statt Grau,
   passend zum gruenen Online-Punkt. */
.presence-item.is-online:hover {
  background-color: color-mix(in srgb, var(--success-color), transparent 92%);
}

/* Hover fuer Offline-Eintraege passend zum grauen Offline-Punkt (.presence-dot
   Default-Farbe var(--muted-color)), statt des neutralen Standard-Hovers. */
.presence-item.is-offline:hover {
  background-color: color-mix(in srgb, var(--muted-color), transparent 92%);
}

/* Online/Offline-Info unten rechts im Eintrag - nur bei Hover sichtbar (an
   den Hover-Effekt gekoppelt), Farbe passend zu Punkt/Hover-Tint. Position
   relative dafuer kommt bereits von .notification-item (main.css). */
/* Name und Online/Offline-Status in einer Zeile, Status rechtsbuendig. */
.notification-title-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.notification-title-row .notification-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.presence-status-label {
  flex-shrink: 0;
  font-size: 0.625rem;
  font-weight: 600;
  text-transform: lowercase;
  letter-spacing: .02em;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast, .15s ease);
}

.presence-item:hover .presence-status-label {
  opacity: 1;
}

.presence-status-label-online {
  color: var(--success-color);
}

.presence-status-label-offline {
  color: var(--muted-color);
}

.presence-item.is-online .presence-status-label-offline,
.presence-item.is-offline .presence-status-label-online {
  display: none;
}

.presence-avatar-wrap {
  position: relative;
  flex-shrink: 0;
}

.presence-dot {
  display: block;
  position: absolute;
  right: -1px;
  bottom: -1px;
  box-sizing: border-box;
  width: 10px;
  height: 10px;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  border: 2px solid var(--surface-color);
  background: var(--muted-color);
}

/* .header-badge bringt von main.css/r2.css aus der Notification-Ecke eine rote
   Puls-Animation mit (Dringlichkeit bei ungelesenen Nachrichten) - fuer die
   Anzahl online Kollegen ist das unpassend, daher hier gezielt auf gruen ohne
   Animation zurueckgesetzt. */
.presence-online-count {
  animation: none;
  box-shadow: none;
  background: var(--success-color);
}

/* Ungelesen-Badge am Chat-Icon in der Sidebar - gleiches Prinzip wie
   .presence-online-count (gruen, keine Puls-Animation), nur auf die
   kleinere 32x32-Nav-Icon-Box positioniert statt auf den groesseren
   Header-Action-Button. */
.nav-icon-has-badge {
  position: relative;
}

.chat-unread-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  animation: none;
  box-shadow: none;
  background: var(--success-color);
}

.presence-item.is-online .presence-dot {
  background: var(--success-color);
}

/* Nur Avatar/Name dimmen, nicht das ganze .presence-item - sonst wuerde der
   Composer (Senden-/Emoji-Icon) als Kind-Element automatisch mitgedimmt
   (Opacity eines Vorfahren laesst sich von einem Kind aus nicht per eigenem
   opacity:1 aufheben, die sichtbare Deckkraft multipliziert sich). Das
   Senden-Icon soll bei Offline-Nutzern genauso aussehen wie bei Online. */
.presence-item.is-offline .presence-avatar-wrap,
.presence-item.is-offline .notification-content {
  opacity: 0.6;
}

/* Capzo-Seitentitel: Logo links neben dem h1, da .page-title von Haus aus nur
   Text erwartet - Inline-Flex haelt Icon und Text auf gleicher Grundlinie
   unabhaengig von der jeweiligen Ueberschriftengroesse im Theme. */
.page-title-with-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-sm);
}

.page-title-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: currentColor;
  flex-shrink: 0;
}

/* Capzo-Startseite: Marken-Intro (Icon + Claim), solange die Seite noch keine
   eigene Funktionalitaet hat - zentriert in der Karte statt am linken Rand,
   da hier (noch) kein Formular/keine Liste folgt, an der man sich orientieren
   koennte. */
.capzo-intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-lg) 0;
  text-align: center;
}

.capzo-intro-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--accent-color), transparent 88%);
  color: var(--accent-color);
}

.capzo-intro-tagline {
  margin: 0;
  font-style: italic;
  color: var(--muted-color);
}

.capzo-intro-text {
  margin: 0;
  color: var(--muted-color);
}

/* Capzo-Preflight-Ergebnis: 3-spaltiges Kachel-Raster statt der
   Transfer-Tabelle (siehe renderPreflightInto() in r2.js) - passt zu genau
   4 festen Kriterien deutlich besser als eine schmale Zwei-Spalten-Tabelle
   mit viel Leerraum. Responsiv auf 2 bzw. 1 Spalte(n) reduziert, sobald der
   Viewport (und damit die volle Breite der Capzo-Positions-Card) fuer 3
   Kacheln von je mindestens 140px nicht mehr ausreicht. */
.capzo-preflight-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(140px, 1fr));
  gap: var(--spacing-sm);
}

@media (max-width: 767.98px) {
  .capzo-preflight-grid {
    grid-template-columns: repeat(2, minmax(140px, 1fr));
  }
}

@media (max-width: 575.98px) {
  .capzo-preflight-grid {
    grid-template-columns: 1fr;
  }
}

.capzo-preflight-item {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--surface-color);
}

.capzo-preflight-dot {
  width: 10px;
  height: 10px;
  margin-top: 5px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}

.capzo-preflight-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-color);
}

.capzo-preflight-value {
  font-weight: 600;
}

/* Chat: main.css faerbt .chat-header-info span fest gruen (fuer den
   Mockup-Zustand "immer online" der Referenzvorlage) - hier auf den
   tatsaechlichen Presence-Zustand umgestellt. */
.chat-header-info span.is-offline {
  color: var(--muted-color);
}

.chat-header-info span.is-online {
  color: var(--success-color);
}

/* main.css kennt fuer .chat-item-status nur .online/.away/.offline/.busy
   (Mockup-Klassen der Referenzvorlage) - das Presence-System liefert aber
   konsequent is-online/is-offline, siehe .presence-dot weiter oben. */
.chat-item-status.is-online {
  background: var(--success-color);
}

.chat-item-status.is-offline {
  background: var(--muted-color);
}

/* Eingabefeld steht jetzt ueber dem Verlauf (main.css geht von "Input unten"
   aus und setzt border-top zur Abgrenzung nach oben zu den Nachrichten) -
   hier umgedreht: Trennlinie jetzt nach unten, zu den darunter liegenden
   Nachrichten. */
.chat-input {
  border-top: none;
  border-bottom: 1px solid var(--border-color);
}

/* main.css setzt fuer .chat-container nur min-height, dadurch waechst der
   Container unbegrenzt mit dem Inhalt und das dort bereits vorhandene
   overflow-y:auto an .chat-list/.chat-messages greift nie (die Eltern-Hoehe
   ist ja nie begrenzt). Feste Hoehe macht daraus zwei unabhaengig
   scrollende Bereiche: Kontaktliste links, Nachrichtenverlauf rechts. */
.chat-container {
  height: calc(100vh - 200px);
}

/* Datei-Anhaenge: main.css bringt .message-file/.message-file-download
   bereits aus der Referenzvorlage mit (bisher ungenutzt) - hier nur, was
   fuer den nicht mehr verfuegbaren Zustand und den Upload-Fortschritt
   zusaetzlich noetig ist. */
.message-file-download.disabled {
  cursor: not-allowed;
  opacity: 0.5;
  pointer-events: none;
}

.chat-attachment-progress {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 0.75rem;
  color: var(--muted-color);
}

/* .chat-attachment-progress-bar ist die Schiene (Hintergrund), der
   gefuellte Teil ist ein ::after mit Breite ueber die --progress-Custom-
   Property, die r2.js setzt (bar.style.setProperty('--progress', pct+'%')). */
.chat-attachment-progress-bar {
  flex: 1;
  height: 4px;
  border-radius: var(--radius-full, 4px);
  background: var(--border-color);
  position: relative;
  overflow: hidden;
}

.chat-attachment-progress-bar::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--progress, 0%);
  border-radius: var(--radius-full, 4px);
  background: var(--accent-color);
  transition: width 0.15s ease;
}

/* Team-Dropdown etwas breiter als das Standard-Notification-Menu (360px aus
   main.css) - mehr Platz fuer Such- und Compose-Textfeld. Gezielt nur auf
   .presence-menu, damit das normale Notification-Dropdown unveraendert bleibt. */
.presence-menu {
  width: 420px;
}

/* Aktions-Button in Toasts (z.B. "Chat öffnen" bei neuer Chat-Nachricht) -
   dezent unten rechts, fast transparenter Hintergrund statt eines
   vollflächigen Buttons. */
.toast-action-row {
  display: flex;
  justify-content: flex-end;
  padding: 0 0.75rem 0.5rem;
}

.toast-action-link {
  background: rgba(255, 255, 255, 0.15);
  color: inherit;
  font-size: 0.75rem;
  font-weight: 500;
  padding: 0.25rem 0.75rem;
  border-radius: var(--bs-border-radius-pill, 999px);
  text-decoration: none;
  transition: background 0.15s ease;
}

.toast-action-link:hover {
  background: rgba(255, 255, 255, 0.3);
  color: inherit;
}

/* Schliessen-Button fest oben rechts statt vertikal zum Text zentriert -
   bleibt an der Ecke, unabhaengig von Textlaenge oder der zusaetzlichen
   Aktions-Zeile darunter. */
.toast {
  position: relative;
}

.toast .toast-body {
  padding-right: 2rem;
}

.toast-close-top-right {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  margin: 0;
}

/* Ladeindikator fuer Formulare mit langlaufenden Server-Aufrufen (z.B. KI
   Textgenerator - der Mistral-Aufruf kann mehrere Sekunden dauern). Deckt
   die Seite ab, solange der volle POST/Reload-Zyklus laeuft, siehe
   [data-loading-form] in r2.js. */
.form-loading-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 4000;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 1rem;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  text-align: center;
}

.form-loading-overlay.show {
  display: flex;
}

.form-loading-overlay .spinner-border {
  width: 3rem;
  height: 3rem;
}

/* Benutzerverwaltung (users.html): Farbvarianten fuer die vorhandenen
   .dash-stat-card-Akzente/Icons (main.css), damit dafuer keine Inline-Styles
   im Template noetig sind. */
.users-stat-accent-total { background: var(--accent-color); }
.users-stat-icon-total {
  background: color-mix(in srgb, var(--accent-color), transparent 88%);
  color: var(--accent-color);
}
.users-stat-accent-active { background: var(--success-color); }
.users-stat-icon-active { background: var(--success-color-light); color: var(--success-color); }
.users-stat-accent-admin { background: var(--info-color); }
.users-stat-icon-admin { background: var(--info-color-light); color: var(--info-color); }
.users-stat-accent-inactive { background: var(--danger-color); }
.users-stat-icon-inactive { background: var(--danger-color-light); color: var(--danger-color); }

/* Benutzerverwaltung (users.html): Stat-Cards dienen zugleich als
   Filter-Buttons fuer die Tabelle (ersetzen die frueheren Filter-Tabs). */
.page-dashboard .dash-stat-card.users-stat-filter {
  width: 100%;
  border: 1px solid var(--card-border);
  margin: 0;
  padding: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.page-dashboard .dash-stat-card.users-stat-filter:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--input-focus-ring);
}

.page-dashboard .dash-stat-card.users-stat-filter.active {
  border-color: var(--accent-color);
  box-shadow: 0 0 0 1px var(--accent-color);
}

/* Benutzerverwaltung (users.html): Toolbar oberhalb der Tabelle
   (nur noch Suchfeld, Filter erfolgt ueber die Stat-Cards). */
.users-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--spacing-sm);
  padding: var(--spacing-md) var(--spacing-lg);
  border-bottom: 1px solid var(--border-color);
}

.users-toolbar-right {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}

.users-search {
  position: relative;
  display: flex;
  align-items: center;
}

@media (max-width: 640px) {
  .users-toolbar-right {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  .users-search input {
    width: 100%;
    min-width: 0;
  }
}

.users-search i {
  position: absolute;
  left: 0.75rem;
  color: var(--muted-color);
  font-size: 0.875rem;
  pointer-events: none;
}

.users-search input {
  padding: 0.375rem 0.75rem 0.375rem 2rem;
  border: 1px solid var(--input-border);
  border-radius: var(--radius-md);
  background: var(--input-bg);
  color: var(--default-color);
  font-size: 0.875rem;
  min-width: 280px;
}

.users-search input:focus {
  outline: none;
  border-color: var(--input-focus-border);
  box-shadow: 0 0 0 3px var(--input-focus-ring);
}

/* Benutzerverwaltung (users.html): Avatar + Name/E-Mail in der ersten Spalte */
.users-user {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}

.users-avatar {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  object-fit: cover;
  flex-shrink: 0;
}

.users-user-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.users-user-name {
  font-weight: 600;
  color: var(--heading-color);
}

.users-user-email {
  font-size: 0.8125rem;
  color: var(--muted-color);
}

.users-meta {
  color: var(--muted-color);
  font-size: 0.875rem;
  white-space: nowrap;
}

.users-th-actions {
  text-align: right;
}

/* Benutzerverwaltung (users.html): Rollen-/Status-Badges in der Tabelle */
.users-role-badge,
.users-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.625rem;
  border-radius: var(--radius-full);
  font-size: 0.8125rem;
  font-weight: 500;
  white-space: nowrap;
}

.users-role-badge-admin {
  background: var(--info-color-light);
  color: var(--info-color);
}

.users-role-badge-standard {
  background: var(--table-hover-bg);
  color: var(--muted-color);
}

.users-status-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: currentColor;
}

.users-status-badge-active {
  background: var(--success-color-light);
  color: var(--success-color);
}

.users-status-badge-inactive {
  background: var(--danger-color-light);
  color: var(--danger-color);
}

.users-status-badge-limited {
  background: var(--warning-color-light);
  color: var(--warning-color);
}

/* Benutzerverwaltung (users.html): Leerzustand, wenn Suche/Filter keine
   Treffer liefert (siehe r2.js) */
.users-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-2xl) var(--spacing-lg);
  color: var(--muted-color);
  text-align: center;
}

.users-empty-state i {
  font-size: 2rem;
}

/* Benutzerverwaltung (users.html): Detail-Modal (rein informativ) */
.users-detail-header {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-lg);
}

.users-detail-avatar {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  object-fit: cover;
}

.users-detail-name {
  font-weight: 600;
  font-size: 1.0625rem;
  color: var(--heading-color);
}

.users-detail-email {
  color: var(--muted-color);
  font-size: 0.875rem;
}

.users-detail-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--spacing-md);
  margin: 0;
}

.users-detail-list dt {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-color);
  margin-bottom: 2px;
}

.users-detail-list dd {
  margin: 0;
  color: var(--default-color);
}

.users-detail-about {
  margin-top: var(--spacing-md);
}

.users-detail-about dt {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-color);
  margin-bottom: 2px;
}

.users-detail-about dd {
  margin: 0;
  color: var(--default-color);
  white-space: pre-line;
}

/* Open-Tabs-Leiste (open_tabs.html): zeigt die zuletzt geoeffneten "Apps"
   des Nutzers als horizontal scrollbare Reiter direkt unter dem Header.
   Liegt als erstes Kind in <main class="main"> und ist "position: sticky"
   statt fixed, damit sie ohne Anpassungen an .main/.sidebar (main.css)
   direkt unterhalb des fixed Headers einrastet, sobald man scrollt. */
.open-tabs-bar {
  position: sticky;
  top: var(--header-height);
  z-index: 900;
  background: var(--header-bg);
  border-bottom: 1px solid var(--header-border);
}

.open-tabs-scroll {
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  padding: 6px var(--spacing-md);
  scrollbar-width: thin;
}

/* Gleiches responsives Seiten-Padding wie .header (main.css) - sonst
   wandert der erste Tab ab 576px relativ zum Sidebar-Toggle/Logo nach
   links, da der Header dort auf --spacing-lg wechselt. */
@media (min-width: 576px) {
  .open-tabs-scroll {
    padding-left: var(--spacing-lg);
    padding-right: var(--spacing-lg);
  }
}

.open-tab {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  max-width: 220px;
  padding: 6px 12px 6px 12px;
  border-radius: var(--radius-md);
  /* Gegen --header-bg gemischt statt gegen transparent: ergibt eine
     blickdichte Farbe, damit sich ueberlappende Tabs (.grouped-with-
     previous) nicht durchsichtig ineinander mischen. */
  background: color-mix(in srgb, var(--default-color) 5%, var(--header-bg) 95%);
  white-space: nowrap;
  transition: background var(--transition-fast);
  /* Ring in Leisten-Hintergrundfarbe (statt border, damit sich die
     Tab-Groesse nicht aendert) - trennt bei den leicht ueberlappenden
     Tabs derselben App (.grouped-with-previous) die Kanten sauber
     voneinander, aehnlich gestapelten Avatar-Chips. */
  box-shadow: 0 0 0 2px var(--header-bg);
}

.open-tab.active {
  z-index: 2;
}

/* Muss ueber .active liegen (nicht gleichauf) - sonst gewinnt bei
   gleichem z-index die DOM-Reihenfolge, und ein gehoverter Tab links vom
   aktiven (meist rechtesten) Tab derselben Familie kommt trotz Hover nie
   in den Vordergrund. */
.open-tab:hover {
  z-index: 3;
}

/* Tabs derselben App ruecken ab der zweiten Instanz leicht uebereinander
   (siehe grouped_with_previous im open_tabs-Context-Processor), statt mit
   dem normalen .open-tabs-scroll-Abstand (gap: 4px) nebeneinander zu
   stehen. Ueberlappen darf dabei aber nur die Tab-Flaeche selbst, nie den
   Text des linken Nachbarn: die effektive Ueberlappungstiefe (margin-left
   minus dem 4px-Gap) bleibt daher kleiner als dessen rechtes Padding
   (12px, siehe .open-tab) - der verbleibende Puffer sorgt fuer sichtbaren
   Abstand zwischen Textende und dem ueberlappenden Nachbarn. */
.open-tab.grouped-with-previous {
  margin-left: -10px;
}

/* Grenze zu einer anderen App-Familie (open_tabs.html) - zusaetzlich zum
   normalen .open-tabs-scroll-Abstand (gap: 4px), damit Familien optisch
   klarer voneinander getrennt sind als die ueberlappenden Tabs innerhalb
   einer Familie. */
.open-tab.new-family {
  margin-left: 12px;
}

.open-tab:hover {
  background: color-mix(in srgb, var(--default-color) 10%, var(--header-bg) 90%);
}

.open-tab.active {
  background: color-mix(in srgb, var(--accent-color) 15%, var(--header-bg) 85%);
}

.open-tab-link {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  font-size: 0.8125rem;
  color: var(--default-color);
}

/* Feste Groesse, auch ohne Icon (leerer Span) - damit der Schliessen-Button
   beim Hovern (siehe unten) immer exakt an derselben Stelle einblendet,
   unabhaengig davon ob die App ein Icon hinterlegt hat. */
.open-tab-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  font-size: 14px;
  line-height: 1;
}

.open-tab-icon svg {
  width: 14px;
  height: 14px;
}

.open-tab-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.open-tab.active .open-tab-link {
  color: var(--accent-color);
  font-weight: 500;
}

/* Schliessen-Button: liegt (absolut positioniert, deckungsgleich mit dem
   linken Padding von .open-tab) genau auf der Position des Icons und wird
   erst beim Hovern des Tabs sichtbar - ersetzt dann optisch das Icon
   (siehe .open-tab:hover .open-tab-icon), statt permanent daneben zu
   stehen. */
.open-tab-close {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted-color);
  line-height: 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-fast);
}

.open-tab:hover .open-tab-close {
  opacity: 1;
  visibility: visible;
}

.open-tab:hover .open-tab-icon {
  visibility: hidden;
}

.open-tab-close:hover {
  background: color-mix(in srgb, var(--danger-color), transparent 85%);
  color: var(--danger-color);
}

/* "+"-Tab am Ende der Leiste: oeffnet das Dashboard als Ausgangspunkt fuer
   die naechste App (siehe open_tabs.html) - bewusst kein OpenAppTabModel-
   Eintrag, sondern ein fixer Button ausserhalb der Tab-Schleife. */
.open-tab-add {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  margin-left: 2px;
  border-radius: var(--radius-md);
  color: var(--muted-color);
  font-size: 0.875rem;
}

.open-tab-add:hover {
  background: color-mix(in srgb, var(--default-color), transparent 90%);
  color: var(--default-color);
}

/* new_tab.html: bewusst leere Seite hinter dem "+"-Tab */
.new-tab-empty-state {
  color: var(--muted-color);
  padding: var(--spacing-lg) 0;
}

/* Drag & Drop zum Umsortieren der Tabs (r2.js) */
.open-tab {
  cursor: grab;
}

.open-tab.dragging {
  opacity: 0.4;
  cursor: grabbing;
}

/* Seitentitel im Header rechts neben dem Sidebar-Toggle (header.html),
   ueber RnSoftware.context_processors.page_title fuer alle Nutzer befuellt */
.header-page-title {
  margin: 0;
  font-weight: 600;
  font-size: 1.5rem;
  line-height: 1.3;
  color: var(--heading-color);
  white-space: nowrap;
}

@media (max-width: 575.98px) {
  .header-page-title {
    display: none;
  }
}


