/*
 * webe Media CI-Overlay – wird NACH core.css eingebunden und überschreibt nur
 * Bootstrap-CSS-Custom-Properties. Die 800-KB-Vendor-Datei bleibt unangetastet
 * (Update-Pfad bleibt offen).
 *
 * Palette:
 *   Primär Grün   #C2D107   (Akzent/Buttons – Text darauf DUNKEL)
 *   Secondary     #061223   (Sidebar/Topbar/dunkle Flächen)
 *   Third         #020911   (Tiefen/Hintergrund)
 *   Off-White     #F5F5F5
 *   Pink          #E6007E   (optionaler Zweit-Akzent)
 */

:root {
  --wm-green: #c2d107;
  --wm-green-600: #aab700;
  --wm-green-700: #909b00;
  --wm-navy: #061223;
  --wm-navy-900: #020911;
  --wm-offwhite: #f5f5f5;
  --wm-pink: #e6007e;

  /* Hausschrift: Poppins überall */
  --bs-body-font-family: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --bs-body-font-weight: 400;

  /* Bootstrap-Primary auf webe-Grün mappen */
  --bs-primary: #c2d107;
  --bs-primary-rgb: 194, 209, 7;
  --bs-link-color: #7a8400;
  --bs-link-color-rgb: 122, 132, 0;
  --bs-link-hover-color: #5f6800;
}

/* ---------------------------------------------------------------------------
 * Dunkelmodus auf die Hausfarben ziehen.
 *
 * Vuexy bringt eine eigene dunkle Palette mit (Hintergrund #25293c, Karten
 * #2f3349) - ein blaustichiges Grau, das mit dem Navy der Seitenleiste nicht
 * zusammengeht. Die Seitenleiste stand schon auf #061223, der Rest daneben auf
 * Vuexy-Lila: genau daher der unruhige Eindruck.
 *
 * Aus dem Corporate Design kommen drei Werte: Gruen #C2D107, Navy #061223,
 * Tiefschwarz #020911. Daraus die Schichtung:
 *   Seite     #020911  (das Tiefste)
 *   Flaechen  #061223  (Karten, Seitenleiste, Kopfzeile)
 *   Kanten    aufgehellter Navy statt Grau, damit nichts ins Blaugraue kippt
 *
 * Die Graustufen fuer Text stammen aus der Navy-Reihe des CD (Seite 2), nicht
 * aus neutralem Grau - sonst wirkt der Text kalt vor dem warmen Navy.
 *
 * Bewusst nur die Farbtoken: Abstaende, Radien und Schriftgroessen bleiben wie
 * im hellen Modus, der ist abgenommen.
 * ------------------------------------------------------------------------- */
[data-bs-theme="dark"] {
  --wm-dark-surface: #061223;
  --wm-dark-surface-2: #0c1c30;
  --wm-dark-line: #1b2a3e;

  --bs-body-bg: #020911;
  --bs-body-bg-rgb: 2, 9, 17;
  --bs-body-color: #d7dde4;
  --bs-body-color-rgb: 215, 221, 228;
  --bs-secondary-color: #93a1b2;
  --bs-secondary-bg: var(--wm-dark-surface-2);
  --bs-tertiary-bg: var(--wm-dark-surface-2);
  --bs-emphasis-color: #ffffff;

  --bs-border-color: var(--wm-dark-line);
  --bs-border-color-translucent: rgba(255, 255, 255, .10);

  /* Auf dunklem Grund traegt das Gruen selbst - das dunkle Oliv aus dem hellen
     Modus (#7a8400) waere auf Navy praktisch unlesbar. */
  --bs-link-color: #c2d107;
  --bs-link-color-rgb: 194, 209, 7;
  --bs-link-hover-color: #d4e32b;

  --bs-card-bg: var(--wm-dark-surface);
  --bs-card-cap-bg: var(--wm-dark-surface);
  --bs-modal-bg: var(--wm-dark-surface);
  --bs-dropdown-bg: var(--wm-dark-surface);
  --bs-dropdown-link-hover-bg: var(--wm-dark-surface-2);
}

/* Vuexy setzt diese Flaechen als feste Farbe, nicht ueber eine Variable -
   deshalb hier namentlich. */
[data-bs-theme="dark"] body,
[data-bs-theme="dark"] .layout-page,
[data-bs-theme="dark"] .content-wrapper { background-color: #020911; }

[data-bs-theme="dark"] .card,
[data-bs-theme="dark"] .modal-content,
[data-bs-theme="dark"] .dropdown-menu,
[data-bs-theme="dark"] .offcanvas {
  background-color: var(--wm-dark-surface);
  border-color: var(--wm-dark-line);
}
[data-bs-theme="dark"] .card .card-header,
[data-bs-theme="dark"] .card .card-footer { border-color: var(--wm-dark-line); }

[data-bs-theme="dark"] .layout-menu,
[data-bs-theme="dark"] .layout-navbar .navbar,
[data-bs-theme="dark"] .layout-navbar { background-color: var(--wm-dark-surface); }

/* Tabellen: Zebra und Hover aus dem Navy heraus, nicht aus Vuexy-Grau. */
[data-bs-theme="dark"] .table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--bs-body-color);
  --bs-table-border-color: var(--wm-dark-line);
  --bs-table-striped-bg: rgba(255, 255, 255, .03);
  --bs-table-hover-bg: rgba(194, 209, 7, .07);
}

/* Eingabefelder heben sich sonst kaum vom Kartengrund ab. */
[data-bs-theme="dark"] .form-control,
[data-bs-theme="dark"] .form-select,
[data-bs-theme="dark"] .input-group-text {
  background-color: #020911;
  border-color: var(--wm-dark-line);
  color: var(--bs-body-color);
}
[data-bs-theme="dark"] .form-control::placeholder { color: #6b7a8d; }
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-select:focus {
  border-color: var(--wm-green);
  box-shadow: 0 0 0 .2rem rgba(194, 209, 7, .22);
}

/* Der gruene Knopf traegt dunkle Schrift - auf Navy wie auf Weiss. */
[data-bs-theme="dark"] .btn-primary { color: var(--wm-navy-900); }

/* Trennlinien und gedaempfter Text. */
[data-bs-theme="dark"] hr,
[data-bs-theme="dark"] .dropdown-divider { border-color: var(--wm-dark-line); }
[data-bs-theme="dark"] .text-body-secondary { color: #93a1b2 !important; }

/* Logo-Paar fuer Flaechen, deren Helligkeit vom Design abhaengt. Das CD haelt
   dafuer zwei Fassungen bereit (Logo Light / Logo Dark); welche gilt, steht
   erst im Browser fest, deshalb beide ausliefern und hier umschalten. */
.wm-logo-on-dark { display: none; }
[data-bs-theme="dark"] .wm-logo-on-light { display: none; }
[data-bs-theme="dark"] .wm-logo-on-dark { display: inline-block; }

/* Der Beta-Hinweis am Dunkelmodus-Eintrag soll nicht schreien. */
.wm-theme-beta { font-size: .625rem; letter-spacing: .04em; text-transform: uppercase; }

/* Poppins konsequent durchsetzen (Vuexy setzt sonst Public Sans) */
body,
.menu,
.navbar,
.btn,
.form-control,
.form-select,
.dropdown-menu,
.card,
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-family: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* Verschiedene Schriftstärken gezielt einsetzen */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-weight: 600;
  letter-spacing: -0.01em;
}
.fw-bold { font-weight: 700 !important; }
.fw-semibold { font-weight: 600 !important; }
.fw-medium { font-weight: 500 !important; }
.menu-link { font-weight: 500; }
.btn { font-weight: 500; }
.app-brand-text { font-weight: 700; }

/* Vuexy nutzt eigene --bs-primary-Ableitungen; wichtigste nachziehen */
:root {
  --bs-primary-text-emphasis: #5f6800;
  --bs-primary-bg-subtle: #f6f8d5;
  --bs-primary-border-subtle: #e3ec86;
}

/*
 * Grün ist hell -> Text/Icons auf primären Buttons MUSS dunkel sein,
 * sonst unlesbar (weißer Default von Bootstrap).
 */
.btn-primary {
  --bs-btn-color: #061223;
  --bs-btn-bg: #c2d107;
  --bs-btn-border-color: #c2d107;
  --bs-btn-hover-color: #061223;
  --bs-btn-hover-bg: #aab700;
  --bs-btn-hover-border-color: #aab700;
  --bs-btn-active-color: #061223;
  --bs-btn-active-bg: #909b00;
  --bs-btn-active-border-color: #909b00;
  --bs-btn-focus-shadow-rgb: 194, 209, 7;
  --bs-btn-disabled-color: #061223;
  --bs-btn-disabled-bg: #e3ec86;
  --bs-btn-disabled-border-color: #e3ec86;
}

.btn-outline-primary {
  --bs-btn-color: #7a8400;
  --bs-btn-border-color: #c2d107;
  --bs-btn-hover-color: #061223;
  --bs-btn-hover-bg: #c2d107;
  --bs-btn-hover-border-color: #c2d107;
  --bs-btn-active-color: #061223;
  --bs-btn-active-bg: #c2d107;
  --bs-btn-active-border-color: #c2d107;
}

.text-primary {
  color: #7a8400 !important;
}

.bg-primary {
  background-color: #c2d107 !important;
  color: #061223 !important;
}

/* Badge/Label-Lesbarkeit */
.badge.bg-primary,
.badge.text-bg-primary {
  color: #061223 !important;
  background-color: #c2d107 !important;
}

/* --- Dunkle Sidebar im webe-Navy --- */
#layout-menu.menu.bg-menu-theme {
  --bs-menu-bg: var(--wm-navy);
  background-color: var(--wm-navy) !important;
  border-right: 1px solid rgba(255, 255, 255, 0.06);
}

#layout-menu .menu-inner > .menu-item.active > .menu-link {
  background: linear-gradient(90deg, rgba(194, 209, 7, 0.18), rgba(194, 209, 7, 0.02));
  color: var(--wm-green);
}
#layout-menu .menu-inner > .menu-item.active > .menu-link .menu-icon {
  color: var(--wm-green);
}
#layout-menu .menu-inner .menu-link {
  color: rgba(245, 245, 245, 0.75);
}
#layout-menu .menu-inner .menu-link:hover {
  color: var(--wm-green);
}
#layout-menu .menu-inner .menu-link:hover .menu-icon {
  color: var(--wm-green);
}
#layout-menu .app-brand .app-brand-text {
  color: var(--wm-offwhite);
}
#layout-menu .menu-header {
  color: rgba(245, 245, 245, 0.4);
}

/* Aktiver Menüpunkt-Marker in Grün */
#layout-menu .menu-inner > .menu-item.active > .menu-link::before {
  background-color: var(--wm-green);
}

/* Sidebar: Logo fix oben, Menüliste scrollt sauber (PerfectScrollbar wie im Vuexy-Original).
   Nur die Höhe begrenzen (Flex-Spalte) – das Scrollen/Scrollbar übernimmt PerfectScrollbar. */
#layout-menu {
  display: flex;
  flex-direction: column;
  height: 100vh;
}
#layout-menu .app-brand.demo {
  flex: 0 0 auto;
  position: relative;
  z-index: 3; /* Logo bleibt über den scrollenden Menüpunkten */
  background-color: var(--wm-navy); /* deckt scrollende Inhalte sauber ab */
}
#layout-menu .menu-inner {
  flex: 1 1 auto;
  min-height: 0; /* erlaubt dem Flex-Item zu schrumpfen -> PerfectScrollbar kann scrollen */
}

/* Marken-Logo in der Sidebar – groß, mit Luft, nicht gequetscht */
#layout-menu .app-brand.demo {
  height: auto;
  min-height: auto;
  align-items: flex-start;
  padding-top: 1.5rem;
  padding-bottom: 1.25rem;
}
#layout-menu .app-brand .app-brand-link {
  width: 100%;
}
.app-brand-logo.wm-logo {
  display: block;
  width: 100%;
}
.app-brand-logo.wm-logo img {
  height: 80px;
  width: auto;
  max-width: 100%;
  display: block;
  object-fit: contain;
}
/* Der Menü-Toggle (X) auf Mobile oben ausrichten, nicht mittig zum großen Logo */
#layout-menu .app-brand .layout-menu-toggle {
  align-self: flex-start;
  margin-top: 0.25rem;
}

/* Fokus-Ringe in Markenfarbe */
.form-control:focus,
.form-select:focus {
  border-color: var(--wm-green);
  box-shadow: 0 0 0 0.15rem rgba(194, 209, 7, 0.25);
}

/* Login-/Auth-Seite (Fallback, einspaltig) */
.wm-auth-wrapper {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(1200px 500px at 20% -10%, rgba(194, 209, 7, 0.08), transparent 60%),
    linear-gradient(160deg, var(--wm-navy) 0%, var(--wm-navy-900) 100%);
  padding: 1.5rem;
}
.wm-auth-card {
  width: 100%;
  max-width: 420px;
}
.wm-auth-logo {
  height: 106px;
  width: auto;
  max-width: 100%;
}

/* --- Cover-Auth: linke Marken-Hero-Seite --- */
.wm-auth-cover {
  min-height: 100vh;
  background:
    radial-gradient(1000px 500px at 80% -10%, rgba(194, 209, 7, 0.12), transparent 60%),
    linear-gradient(160deg, var(--wm-navy) 0%, var(--wm-navy-900) 100%);
}
.wm-auth-cover-logo {
  height: 120px;
  width: auto;
  max-width: 70%;
}
.wm-auth-cover-tagline {
  color: rgba(245, 245, 245, 0.72);
  font-size: 1.05rem;
  max-width: 460px;
}
.wm-auth-cover-illustration {
  max-width: 480px;
  width: 70%;
  height: auto;
  filter: drop-shadow(0 20px 40px rgba(0, 0, 0, 0.35));
}
/* Rechte Formularseite immer hell halten */
.authentication-cover .authentication-bg {
  background-color: #fff;
  min-height: 100vh;
}

/* --- Topbar-Stoppuhr --- */
.wm-timer-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--bs-danger, #dc3545);
  display: inline-block;
  animation: wm-pulse 1.2s ease-in-out infinite;
}
@keyframes wm-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.7); }
}
.wm-timer-clock {
  font-variant-numeric: tabular-nums;
  min-width: 62px;
  text-align: center;
}

/* --- Rich-Text (Quill) --- */
.wm-quill .ql-editor {
  min-height: 120px;
}
/* Kompakte Darstellung gerenderter Beschreibungen in Listen */
.wm-rte p { margin: 0; }
.wm-rte ul, .wm-rte ol { margin: 0; padding-left: 1.1rem; }
/* Tab-Leisten in Card-Headern auf die Card-Breite begrenzen — sonst
   zwingen lange Labels (Ansprechpartner, E-Mail-Domänen, …) bei
   <lg-Viewports die ganze Seite in den horizontalen Scroll. */
.card-header-tabs { flex-wrap: wrap; }
/* Gerenderte Mail-/Editor-HTMLs an das App-Look angleichen — sonst
   bekommen <a> den Browser-Default-Unterstrich und <button> native Button-Optik. */
.wm-rte a { color: var(--wm-green-700, #5d6f0c); text-decoration: none; }
.wm-rte a:hover { text-decoration: underline; }
.wm-rte button,
.wm-rte input[type="button"],
.wm-rte input[type="submit"] {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: text;
}
.wm-rte img { max-width: 100%; height: auto; }
.wm-rte table { max-width: 100%; }
/* Render-time Hardening: Mail-Body-Container bleibt immer in Spaltenbreite. */
.wm-rte { overflow-wrap: break-word; word-break: break-word; overflow-x: auto; }
.wm-rte table { max-width: 100% !important; width: auto !important; }
.wm-rte img { max-width: 100% !important; height: auto !important; }
.wm-rte blockquote {
  border-left: 3px solid var(--wm-green-700, #5d6f0c);
  margin: .25rem 0;
  padding: .1rem .75rem;
  color: var(--bs-secondary-color, #6c757d);
}

/* --- Größere Icons (näher an der Vuexy-Demo) --- */
:root {
  --bs-icon-size: 1.5rem;
  --bs-icon-size-xs: 1.125rem;
  --bs-icon-size-sm: 1.25rem;
  --bs-icon-size-md: 1.5rem;
  --bs-icon-size-lg: 1.75rem;
  --bs-icon-size-xl: 2.25rem;
}
/* Aktions-Icons in Buttons (Tabellen etc.) mitwachsen lassen */
.btn-icon .icon-base {
  block-size: 1.5rem;
  font-size: 1.5rem;
  inline-size: 1.5rem;
}
/* Menü-Icons in der Seitenleiste */
.menu-icon {
  font-size: 1.375rem;
  block-size: 1.375rem;
  inline-size: 1.375rem;
}

/* --- Aktions-Icons in Tabellen (btn-sm/btn-xs) waren zu klein (13px) --- */
.btn-icon.btn-sm .icon-base,
.btn-group-sm > .btn-icon.btn .icon-base,
.btn-icon.btn-xs .icon-base,
.btn-group-xs > .btn-icon.btn .icon-base {
  font-size: 1.5rem;
  block-size: 1.5rem;
  inline-size: 1.5rem;
}
/* Klickfläche der kleinen Icon-Buttons auf ~40px anheben */
.btn-icon.btn-sm,
.btn-group-sm > .btn-icon.btn {
  width: 2.5rem;
  height: 2.5rem;
}

/* --- Privacy-Modus: alle €-Beträge (.wm-money) auf Wunsch verwischen --- */
.wm-money { transition: filter .12s ease; }
.wm-privacy .wm-money {
  filter: blur(6px);
  -webkit-user-select: none;
  user-select: none;
  cursor: default;
}
/* Auge-Toggle aktiv einfärben */
.wm-privacy #wmPrivacyToggle { color: var(--wm-green); }

/* --- Artikel-Lesbarkeit (Schwarzes Brett) – mehr Zeilenabstand --- */
.wm-article { line-height: 1.8; font-size: 1.02rem; }
.wm-article p { margin: 0 0 1rem; }
.wm-article ul, .wm-article ol { margin: 0 0 1rem; padding-left: 1.4rem; }
.wm-article li { margin-bottom: .4rem; }
.wm-article h1, .wm-article h2, .wm-article h3, .wm-article h4 { margin: 1.5rem 0 .6rem; line-height: 1.3; }
.wm-article > :first-child { margin-top: 0; }
.wm-article > :last-child { margin-bottom: 0; }

/* --- Tagify an das webe/Vuexy-Theme angleichen --- */
.tagify {
  --tags-border-color: var(--bs-border-color);
  --tags-focus-border-color: var(--wm-green);
  --tag-bg: var(--bs-primary-bg-subtle, #f6f8d5);
  --tag-hover: #eef2c2;
  --tag-text-color: var(--bs-primary-text-emphasis, #5f6800);
  --tag-remove-btn-color: var(--bs-primary-text-emphasis, #5f6800);
  --tag-remove-bg: rgba(194, 209, 7, .18);
  --tag-remove-btn-bg--hover: var(--wm-green);
  --placeholder-color: var(--bs-secondary-color);
  --tag-pad: .28em .5em;
  --tag-border-radius: 6px;
  border-color: var(--bs-border-color);
  border-radius: var(--bs-border-radius, .375rem);
  background: var(--bs-body-bg);
}
.tagify.form-control-sm { padding: .1rem .25rem; min-height: calc(1.5em + .5rem + 2px); }
.tagify:focus-within { border-color: var(--wm-green); box-shadow: 0 0 0 .15rem rgba(194, 209, 7, .2); }
.tagify__tag { font-size: .8125rem; }
.tagify__tag > div { border-radius: 6px; }
.tagify__input { margin: .28em; }
.tagify__dropdown {
  border-color: var(--bs-border-color);
  border-radius: var(--bs-border-radius, .375rem);
  box-shadow: 0 .5rem 1.25rem rgba(0,0,0,.12);
  overflow: hidden;
}
.tagify__dropdown__wrapper { border: 0; }
.tagify__dropdown__item { border-radius: .375rem; margin: 2px 4px; padding: .4rem .6rem; font-size: .85rem; }
.tagify__dropdown__item--active { background: var(--wm-green); color: var(--wm-navy); }
.tagify__dropdown__item--active .text-body-secondary,
.tagify__dropdown__item--active .badge { color: var(--wm-navy) !important; }

/* ---------------------------------------------------------------------------
 * FilePond – große, gebrandete Dropzone (Vuexy-Look)
 * ------------------------------------------------------------------------- */
.filepond--root {
  min-height: 11rem; margin-bottom: 0; font-family: inherit;
  border: 2px dashed var(--bs-border-color);
  border-radius: .6rem;
  background: var(--bs-body-bg);
  transition: border-color .15s ease-in-out;
}
.filepond--root:hover { border-color: var(--wm-green); }
.filepond--panel-root { background: transparent !important; border: 0 !important; }
.filepond--drop-label { min-height: 11rem; color: var(--bs-secondary-color); }
.filepond--drop-label label { font-size: 1.05rem; }
.fp-idle { display: flex; flex-direction: column; align-items: center; gap: .6rem; }
.fp-idle-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3rem; height: 3rem; border-radius: .6rem; background: var(--bs-secondary-bg);
  color: var(--wm-green-700);
}
.fp-idle-icon i { font-size: 1.7rem; }
.fp-idle-text { font-size: 1.05rem; color: var(--bs-body-color); }
.filepond--label-action { text-decoration-color: var(--wm-green-700); color: var(--wm-green-700); font-weight: 600; }
.filepond--item-panel { background-color: var(--wm-green-600); }

/* Dashboard-Benachrichtigungen: sanftes Ausblenden des 3. Eintrags („da wäre noch mehr") */
.wm-notif-fade {
  -webkit-mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
}

/* ---------------------------------------------------------------------------
 * Responsive Topbar (mobil): Suche als aufklappbares Overlay
 * ------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  #layout-navbar { position: relative; }
  #wmSearch.wm-search { display: none; }
  /* Beim Suchen die übrigen Topbar-Elemente ausblenden – die Suche übernimmt die Leiste */
  #layout-navbar.wm-searching .navbar-nav { display: none !important; }
  #wmSearch.wm-search.wm-search-open {
    display: flex !important; align-items: center; gap: .25rem;
    position: absolute !important;
    inset: 0 !important;
    max-width: none; margin: 0 !important; padding: 0 .75rem; z-index: 1055;
    background: var(--bs-body-bg);
  }
  #wmSearch.wm-search.wm-search-open .form-control { flex: 1 1 auto; }
}

/* ---------------------------------------------------------------------------
 * Mobile: übermäßigen Abstand über der (nicht-fixen) Topbar reduzieren
 * ------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  .layout-navbar-fixed .layout-wrapper:not(.layout-horizontal):not(.layout-without-menu) .layout-page,
  .layout-navbar-fixed .layout-wrapper:not(.layout-without-menu) .layout-page { padding-top: 0 !important; }
  .layout-navbar.navbar-detached { margin-top: 0.85rem !important; }
}

/* ---------------------------------------------------------------------------
 * Mobile: Abstand für Notch/Dynamic Island (Nutzeranfrage V3.8, "Responsive":
 * "Navigation braucht margin-top, weil sie hinter der Notch sitzt"). Greift
 * nur auf Geräten mit echter Notch (env() liefert sonst 0) und nur, wenn
 * viewport-fit=cover gesetzt ist (siehe layouts/app.php, auth-cover.php) -
 * ohne das bleibt env(safe-area-inset-*) immer 0, harmlos auf alten Browsern.
 * ------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  #layout-navbar { padding-top: env(safe-area-inset-top); }
  #layout-menu { padding-top: env(safe-area-inset-top); }
}
.wm-chat-fab { bottom: calc(4.5rem + env(safe-area-inset-bottom) + var(--wm-bottom-bar, 0px)); }
.wm-chat-panel { bottom: calc(8.5rem + env(safe-area-inset-bottom) + var(--wm-bottom-bar, 0px)); }

/* Rotierendes Icon (z. B. „PDF wird generiert") */
.icon-spin { animation: wm-spin 0.8s linear infinite; display: inline-block; }
@keyframes wm-spin { to { transform: rotate(360deg); } }

/* Deadline-Badge (deadline_flag()) – kompakter in Tabellen */
.wm-deadline-badge { font-size: 0.64rem; }

/* Kleinere Status-Badges für dichte Tabellen (z. B. Projekt-Zeiteinträge) */
.wm-badge-sm { font-size: 0.68rem; padding: 0.28em 0.5em; }

/* Ticket-Verlauf: dezente Farbcodierung je Nachrichtenart – unsere Antworten
   (grau + grüner Rand) vs. interne Notizen (oranger Rand), minimalistisch
   als Akzent-Rand statt vollflächiger Einfärbung. */
.wm-msg-out { background-color: var(--wm-offwhite); border-left: 3px solid var(--wm-green-700); }
.wm-msg-note { border-left: 3px solid var(--bs-warning); }
/* Automatisch erzeugte Zeiterfassungs-/Abrechnungs-Notizen (system_reason
   time_entry/invoice_run) - blau statt orange, damit sie sich von manuell
   getippten internen Notizen abheben. */
.wm-msg-system-log { border-left: 3px solid var(--bs-info); }

/* Antworten-/Notiz-Tabs im Ticket: aktiver Tab bekommt die passende Akzentfarbe,
   damit auf einen Blick klar ist, in welchem Modus man gerade schreibt. */
.wm-tab-note.active { color: var(--bs-warning) !important; border-bottom-color: var(--bs-warning) !important; }
.wm-tab-reply.active { color: var(--wm-green-700) !important; border-bottom-color: var(--wm-green-700) !important; }

/* Read-only Vorschau der Mitarbeiter-Signatur im Antwortformular */
.wm-signature-preview {
  border: 1px dashed var(--bs-border-color);
  border-radius: 0.375rem;
  padding: 0.5rem 0.75rem;
  background-color: var(--wm-offwhite);
  font-size: 0.8rem;
  max-height: 220px;
  overflow: auto;
}
.wm-signature-preview table { font-size: inherit; }

/* ---------------------------------------------------------------------------
 * "Top Dienstleistungen" (Dashboard): gestapelter Fortschrittsbalken +
 * Farbpunkt-Liste, nach Vuexy-"Vehicles Overview"-Vorbild statt Einzelbalken
 * pro Zeile. Sechs-Ton-Palette rund um Marken-Grün/Navy, absteigend nach
 * Gewicht, da die Liste bereits nach erfasster Zeit sortiert ist.
 * ------------------------------------------------------------------------- */
.wm-pvs-labels { gap: 0; }
.wm-pvs-label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--bs-body-color);
  padding-inline-end: 0.5rem;
}
.wm-pvs-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }

/* Höhere Spezifität nötig: core.css' ".progress .progress-bar" (2 Klassen)
   setzt sonst Hintergrund/Farbe jedes Segments auf --bs-primary/weiß zurück. */
.wm-pvs-bar.progress .progress-bar.wm-pvs-1, .wm-pvs-dot.wm-pvs-1 { background-color: var(--wm-green); color: var(--wm-navy); }
.wm-pvs-bar.progress .progress-bar.wm-pvs-2, .wm-pvs-dot.wm-pvs-2 { background-color: var(--wm-navy); color: #fff; }
.wm-pvs-bar.progress .progress-bar.wm-pvs-3, .wm-pvs-dot.wm-pvs-3 { background-color: var(--wm-green-700); color: #fff; }
.wm-pvs-bar.progress .progress-bar.wm-pvs-4, .wm-pvs-dot.wm-pvs-4 { background-color: #5b6b78; color: #fff; }
.wm-pvs-bar.progress .progress-bar.wm-pvs-5, .wm-pvs-dot.wm-pvs-5 { background-color: var(--bs-primary-border-subtle, #e3ec86); color: var(--wm-navy); }
.wm-pvs-bar.progress .progress-bar.wm-pvs-6, .wm-pvs-dot.wm-pvs-6 { background-color: #d7dbdf; color: var(--wm-navy); }

/* ---------------------------------------------------------------------------
 * Personalisierbares Dashboard: freier Layout-Editor auf GridStack.js-Basis
 * (12-Spalten-Raster, Kacheln per Drag & Resize frei platzierbar). Live-
 * Ansicht nutzt dieselbe .wm-dash-grid-Klasse im staticGrid-Modus (siehe
 * dashboard-layout.js), damit Editor und Anzeige exakt gleich aussehen.
 * ------------------------------------------------------------------------- */
/* Abstand zwischen Kacheln kommt aus GRID_OPTS.margin (dashboard-layout.js) -
   GridStack setzt dafür selbst eine Inset-Regel per Instanz-Stylesheet mit
   höherer Spezifität, ein eigenes inset hier würde damit kollidieren/nichts
   bewirken. */
/* Ladezustand: Skelett statt gestapelter Kacheln.
 *
 * GridStack positioniert die Kacheln absolut - vorher liegen sie als normale
 * Blöcke untereinander und die Seite zeigt für einen Moment ein völlig anderes
 * Layout. Solange .is-loading steht, wird das echte Raster aus dem Fluss
 * genommen und ausgeblendet, sichtbar ist nur das Skelett. dashboard-layout.js
 * nimmt die Klasse nach GridStack.init() wieder weg.
 *
 * absolute + inset:0 statt display:none: GridStack misst beim Init die Breite
 * des Containers. Ein ausgeblendetes Element hat Breite 0 und das Raster käme
 * einspaltig heraus. */
.wm-dash-shell { position: relative; }
.wm-dash-shell.is-loading > .wm-dash-grid { position: absolute; inset: 0; visibility: hidden; }
.wm-dash-shell:not(.is-loading) > .wm-dash-skeleton { display: none; }

/* Dieselben Zahlen wie GRID_OPTS in dashboard-layout.js: 12 Spalten,
   cellHeight 70, margin 12. Die Kacheln bekommen ihre Position direkt aus dem
   gespeicherten Layout, das Skelett steht also genau dort, wo gleich die
   echten Karten stehen - der Wechsel fällt dadurch nicht auf.

   Kein gap: GridStack legt seinen margin NACH INNEN, jede Zelle ist volle
   cellHeight hoch und die Karte darin eingerückt. Ein gap im Raster würde die
   Kacheln stattdessen auseinanderschieben und das Skelett liefe mit jeder
   Zeile weiter nach unten weg. Der Abstand entsteht deshalb unten über
   inset am Platzhalter, genau wie bei GridStack. */
.wm-dash-skeleton {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-auto-rows: 70px;
  gap: 0;
}
/* Position und Groesse kommen als Variablen aus dem gespeicherten Layout -
   als Variablen und nicht direkt als grid-column/grid-row, damit die
   einspaltige Ansicht unten nur die Spalte ueberschreiben kann und die Hoehe
   der Kachel erhalten bleibt. */
.wm-dash-skeleton > span {
  display: block;
  position: relative;
  grid-column: var(--wm-x) / span var(--wm-w);
  grid-row: var(--wm-y) / span var(--wm-h);
}
/* Der sichtbare Kasten sitzt eingerückt in seiner Zelle - dieselben 12px, die
   GridStack als margin auf .grid-stack-item-content legt. Der Lichtstreifen
   läuft als wandernder Verlauf durch, damit erkennbar ist, dass etwas lädt,
   statt dass das Dashboard kaputt aussieht. */
.wm-dash-skeleton > span::before {
  content: "";
  position: absolute;
  inset: 12px;
  border-radius: .5rem;
  /* Dezent und in beiden Themes brauchbar: ein Hauch der Textfarbe auf dem
     jeweiligen Untergrund. Feste Grautoene wirken im Dunkelmodus wie massive
     Bloecke. Die erste Zeile ist der Rueckfall fuer Browser ohne color-mix. */
  background-color: rgba(128, 138, 157, .16);
  background-color: color-mix(in srgb, var(--bs-body-color) 9%, transparent);
  background-image: linear-gradient(90deg, transparent, rgba(255, 255, 255, .14), transparent);
  background-repeat: no-repeat;
  background-size: 200% 100%;
  animation: wm-dash-shimmer 1.4s ease-in-out infinite;
}
@keyframes wm-dash-shimmer { from { background-position: -100% 0; } to { background-position: 200% 0; } }

/* Unter 768px schaltet GridStack auf eine Spalte um (oneColumnSize) - das
   Skelett muss demselben Sprung folgen, sonst zeigt es ein zweispaltiges
   Layout, das gleich darauf einspaltig wird. */
@media (max-width: 767.98px) {
  .wm-dash-skeleton { grid-template-columns: 1fr; }
  /* Volle Breite, Reihenfolge aus dem Dokument - aber die Kachelhoehe bleibt.
     GridStack behaelt sie im Einspalten-Modus ebenfalls bei; wuerde hier jede
     Kachel auf eine Zeile schrumpfen, waere das Skelett viel zu kurz und die
     Seite spraenge beim Umschalten. */
  .wm-dash-skeleton > span {
    grid-column: 1 / -1;
    grid-row: auto / span var(--wm-h);
  }
}

@media (prefers-reduced-motion: reduce) {
  .wm-dash-skeleton > span::before { animation: none; }
}

/* Karten füllen ihre GridStack-Zelle komplett aus statt an fixer Höhe zu kleben. */
.wm-dash-grid .grid-stack-item-content > .card,
.wm-dash-grid .grid-stack-item-content > div { height: 100%; }
.wm-dash-grid .grid-stack-item-content .card { margin-bottom: 0; }
/* Scroll-Inhalt innerhalb des Card-Body statt per zoom schrumpfen. Wer
   lange Listen rendert, MUSS .wm-dash-scroll-body + .wm-dash-scroll
   verwenden (siehe top_services.php / activity_feed.php). GridStack setzt
   overflow-y:auto auf .grid-stack-item-content als letzte Fallback-Ebene. */

/* Scrollbare Widget-Listen (z. B. "Was grade läuft", "Top-Leistungen"):
   Scrollbar MUSS innerhalb der weißen .card-body-Fläche bleiben statt auf
   .grid-stack-item-content (GridStack-Fallback, siehe Kommentar oben) - der
   umschließt auch den Card-Header/-Rand und sieht dort abgeschnitten/unsauber
   aus. .card-body wird dafür zum Flex-Container, damit die Liste den
   verbleibenden Platz einnimmt und intern scrollt, unabhängig von der frei
   wählbaren Kachel-Höhe im Grid-Builder. */
/* min-height:0 ist der entscheidende Teil: Bootstraps .card-body ist selbst
   ein Flex-Item (flex:1 1 auto) mit dem Flexbox-Standard min-height:auto -
   das verhindert, dass es UNTER die eigene Inhaltsgröße schrumpft, egal wie
   klein die GridStack-Kachel ist. .wm-dash-scroll darunter bekommt dadurch
   nie eine begrenzte Höhe und kann selbst nicht scrollen - der Overflow
   platzt aus der Card und landet stattdessen auf .grid-stack-item-content
   (GridStack-Fallback), sichtbar als Scrollbar, die über die Card-Grenze
   hinausragt bzw. mit der Nachbar-Kachel verschmilzt. */
.wm-dash-scroll-body { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.wm-dash-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: .25rem; }

/* Generelles Sicherheitsnetz für Widgets OHNE .wm-dash-scroll-body (z. B.
   project_vs_support.php - fester Chart + Legende ohne Liste): min-height:0
   auf jeder .card-body innerhalb einer Dashboard-Kachel, egal welches
   Widget - verhindert genau denselben "aus der Card geplatzten Inhalt"-Bug
   auch für Widgets, die (noch) nicht aktiv auf den Scroll-Wrapper
   umgestellt wurden. overflow-y:auto NUR wenn kein eigener Scroll-Wrapper
   drin ist (:not), sonst gäbe es zwei verschachtelte Scrollbars. */
.wm-dash-grid .grid-stack-item-content > .card > .card-body { min-height: 0; }
.wm-dash-grid .grid-stack-item-content > .card > .card-body:not(.wm-dash-scroll-body) { overflow-y: auto; }
.wm-dash-scroll::-webkit-scrollbar { width: 6px; }
.wm-dash-scroll::-webkit-scrollbar-thumb { background: var(--bs-border-color); border-radius: 3px; }

/* Editor: Platzhalter-Kachel statt echtem Widget-Inhalt (Titel + Icon +
   Entfernen-Button), bis das echte Dashboard gespeichert & neu geladen wird. */
.wm-dash-edit-item {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .4rem;
  height: 100%; border: 2px dashed var(--bs-border-color); border-radius: .6rem;
  background: var(--bs-body-bg); color: var(--bs-body-color); text-align: center; padding: .5rem;
}
.wm-dash-edit-item i:not(.tabler-x) { font-size: 1.6rem; color: var(--wm-green-700, #5d6f0c); }
.wm-dash-edit-item span { font-size: .8125rem; font-weight: 500; }
.wm-dash-remove {
  position: absolute; top: .35rem; right: .35rem; width: 1.75rem; height: 1.75rem;
  background: var(--bs-secondary-bg); border-radius: 50%;
}
.grid-stack-item-content { position: relative; }

/* GridStack-Platzhalter (während des Ziehens) in Markenfarbe statt Standard-Grau. */
.grid-stack-placeholder > .placeholder-content { background-color: rgba(194, 209, 7, .18) !important; border-radius: .6rem; }

/* ---------------------------------------------------------------------------
 * Schwarzes Brett: angepinnte Beiträge als große Hero-Karten (Bild links,
 * Text rechts) - auf Mobile stapelt die Karte (flex-column im Markup),
 * das Bild muss dann die volle Breite statt der festen 220px annehmen.
 * ------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .wm-board-hero-img { width: 100% !important; height: 160px; min-height: 0 !important; }
}

/* ---------------------------------------------------------------------------
 * v3.5 Touch-Pass: Vuexy setzt die Icon-Links der Topbar (Menü-Toggle, Suche,
 * Privacy-Toggle, Glocke) standardmäßig ohne horizontales Padding (px-0) -
 * am Desktop mit der Maus unproblematisch, am Touchscreen aber ein knapp
 * 24px schmales Ziel. Nur mobil etwas Luft dazugeben, Desktop unverändert.
 * ------------------------------------------------------------------------- */
@media (max-width: 575.98px) {
  .layout-navbar .navbar-nav > .nav-item > .nav-link {
    padding-left: .5rem !important;
    padding-right: .5rem !important;
  }
}

/* ---------------------------------------------------------------------------
 * Ticket-Übersicht: Stat-Kacheln (2x2) links neben dem Stunden-Chart rechts.
 * Verschachteltes Bootstrap-Grid (.row > .col > .row > .col) stretcht die
 * äußeren Spalten trotz align-items-stretch nicht zuverlässig auf gleiche
 * Höhe, weil die innere .row dem Chart-Card keine feste Referenzhöhe gibt -
 * echtes CSS-Grid mit einer gemeinsamen Reihe erzwingt das direkt.
 * ------------------------------------------------------------------------- */
.wm-ticket-stats-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  align-items: stretch;
}
.wm-ticket-stats-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 1rem;
}
.wm-ticket-stats-chart,
.wm-ticket-stats-chart > .card {
  height: 100%;
}
@media (max-width: 991.98px) {
  .wm-ticket-stats-grid { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------------
 * "Timeline Outlined" (Vuexy) — hohle Ring-Marker statt gefüllter Icon-Badges,
 * wiederverwendbare Basis für alle Historie-/Aktivitäts-Listen (Kunde, Chat
 * u. a.). Ersetzt die zuvor nur inline in Customer/Views/show.php lebende
 * Variante mit gefüllten Icon-Badges.
 * ------------------------------------------------------------------------- */
.wm-timeline { position: relative; padding-left: .25rem; }
.wm-timeline-item { position: relative; display: flex; gap: .9rem; padding-bottom: 1.25rem; }
.wm-timeline-item:not(:last-child)::before {
  content: ''; position: absolute; left: 7px; top: 20px; bottom: -2px; width: 2px;
  background: var(--bs-border-color);
}
.wm-timeline-dot {
  flex-shrink: 0; width: 16px; height: 16px; margin-top: 2px; border-radius: 50%;
  border: 2px solid var(--wm-dot-color, var(--bs-secondary-color));
  background: var(--bs-body-bg);
  z-index: 1;
}
.wm-timeline-dot.wm-dot-primary { --wm-dot-color: var(--bs-primary); }
.wm-timeline-dot.wm-dot-success { --wm-dot-color: var(--bs-success); }
.wm-timeline-dot.wm-dot-danger  { --wm-dot-color: var(--bs-danger); }
.wm-timeline-dot.wm-dot-warning { --wm-dot-color: var(--bs-warning); }
.wm-timeline-dot.wm-dot-info    { --wm-dot-color: var(--bs-info); }
.wm-timeline-dot.wm-dot-secondary { --wm-dot-color: var(--bs-secondary-color); }
.wm-timeline-scroll {
  max-height: 400px; overflow-y: auto; padding-right: .5rem;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
}
.wm-timeline-scroll::-webkit-scrollbar { width: 6px; }
.wm-timeline-scroll::-webkit-scrollbar-thumb { background: var(--bs-border-color); border-radius: 3px; }

/* ---------------------------------------------------------------------------
 * Datei-Chip: kompakte Anhang-Vorschau (Name + Größe, farbiges Typ-Icon),
 * nach Vuexy-"Timeline Outlined"-Vorbild ("invoices.pdf"). Für Chat-Anhänge,
 * Ticket-Anhänge und überall sonst, wo eine Datei als Chip dargestellt wird.
 * ------------------------------------------------------------------------- */
.wm-file-chip {
  display: inline-flex; align-items: center; gap: .6rem;
  max-width: 260px; padding: .5rem .75rem;
  border: 1px solid var(--bs-border-color); border-radius: .5rem;
  background: var(--bs-body-bg);
  text-decoration: none; color: inherit;
  transition: border-color .12s ease, background-color .12s ease;
}
.wm-file-chip:hover { border-color: var(--wm-green); background: var(--bs-secondary-bg); color: inherit; }
.wm-file-chip-icon {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: .4rem;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-size: 1.05rem;
}
.wm-file-chip-icon.wm-ft-pdf   { background: #e03131; }
.wm-file-chip-icon.wm-ft-doc   { background: #2f6fed; }
.wm-file-chip-icon.wm-ft-xls   { background: #2f9e44; }
.wm-file-chip-icon.wm-ft-ppt   { background: #e8590c; }
.wm-file-chip-icon.wm-ft-image { background: #ae3ec9; }
.wm-file-chip-icon.wm-ft-zip   { background: #495057; }
.wm-file-chip-icon.wm-ft-file  { background: #868e96; }
.wm-file-chip-body { min-width: 0; line-height: 1.25; }
.wm-file-chip-name { font-size: .8125rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-file-chip-meta { font-size: .7rem; color: var(--bs-secondary-color); }

/* ---------------------------------------------------------------------------
 * Chat: Zwei-Spalten-Layout (Konversationen/Kontakte links, Thread rechts),
 * nach Vuexy "app-chat"-Vorbild. Feste Höhe unter der Topbar, jede Spalte
 * scrollt für sich.
 * ------------------------------------------------------------------------- */
.wm-chat-shell {
  display: flex; height: calc(100vh - 12.5rem); min-height: 28rem;
  border: 1px solid var(--bs-border-color); border-radius: .5rem; overflow: hidden;
  background: var(--bs-body-bg);
}
.wm-chat-sidebar {
  width: 300px; flex-shrink: 0; display: flex; flex-direction: column;
  border-right: 1px solid var(--bs-border-color); background: var(--bs-body-bg);
}
/* Feste Höhe, identisch mit .wm-chat-header - sonst landen die beiden
   border-bottom-Linien (Suchfeld links / Kopfzeile rechts) auf
   unterschiedlicher Höhe und brechen sichtbar an der Spalten-Trennlinie. */
.wm-chat-sidebar-search {
  height: 68px; box-sizing: border-box; display: flex; align-items: center;
  padding: 0 .9rem; border-bottom: 1px solid var(--bs-border-color); flex-shrink: 0;
}
.wm-chat-sidebar-search > div { width: 100%; }
.wm-chat-sidebar-scroll { flex: 1 1 auto; overflow-y: auto; padding: .5rem; }
.wm-chat-sidebar-label {
  font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  color: var(--bs-secondary-color); padding: .75rem .6rem .35rem;
}
.wm-chat-list-item {
  display: flex; align-items: center; gap: .65rem; width: 100%; padding: .55rem .6rem;
  border: 0; border-radius: .45rem; background: transparent; text-align: left; color: inherit;
  cursor: pointer; text-decoration: none;
}
.wm-chat-list-item:hover { background: var(--bs-secondary-bg); color: inherit; }
.wm-chat-list-item.active { background: var(--wm-green); color: var(--wm-navy); }
.wm-chat-list-item.active .wm-chat-list-sub { color: var(--wm-navy); opacity: .7; }
.wm-chat-list-avatar { position: relative; flex-shrink: 0; }
.wm-chat-list-body { min-width: 0; flex: 1 1 auto; }
.wm-chat-list-top { display: flex; justify-content: space-between; align-items: baseline; gap: .4rem; }
.wm-chat-list-name { font-size: .8125rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-chat-list-time { font-size: .68rem; color: var(--bs-secondary-color); flex-shrink: 0; }
.wm-chat-list-sub { font-size: .75rem; color: var(--bs-secondary-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-chat-status-dot { font-size: .45rem; vertical-align: middle; margin-right: .3rem; }

.wm-chat-main { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
.wm-chat-header {
  height: 68px; box-sizing: border-box;
  display: flex; align-items: center; gap: .65rem; padding: 0 1.1rem;
  border-bottom: 1px solid var(--bs-border-color); flex-shrink: 0;
  overflow: hidden;
}
.wm-chat-header .fw-medium { line-height: 1.25; }
.wm-chat-header small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-chat-thread { flex: 1 1 auto; overflow-y: auto; padding: 1.25rem; display: flex; flex-direction: column; gap: 1.1rem; }
.wm-chat-empty { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--bs-secondary-color); gap: .5rem; }

.wm-chat-row { display: flex; gap: .6rem; max-width: 70%; }
.wm-chat-row.wm-out { align-self: flex-end; }
.wm-chat-bubble-wrap { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.wm-chat-row.wm-out .wm-chat-bubble-wrap { align-items: flex-end; }
.wm-chat-bubble {
  padding: .55rem .85rem; border-radius: .6rem; background: var(--bs-secondary-bg);
  font-size: .85rem; line-height: 1.45; word-break: break-word;
}
.wm-chat-row.wm-out .wm-chat-bubble { background: var(--wm-green); color: var(--wm-navy); border-top-right-radius: .15rem; }
.wm-chat-row:not(.wm-out) .wm-chat-bubble { border-top-left-radius: .15rem; }
/* Automatisch erkannte Links (chat_format_body()) erben sonst die globale
   Linkfarbe (Markengrün) - auf der eigenen (grünen) Bubble macht das den
   Link-Text quasi unsichtbar (grün auf grün). Immer unterstrichen +
   bubble-spezifische, lesbare Farbe statt der globalen Linkfarbe. */
.wm-chat-bubble a { text-decoration: underline; word-break: break-all; }
.wm-chat-row.wm-out .wm-chat-bubble a { color: var(--wm-navy); }
.wm-chat-row:not(.wm-out) .wm-chat-bubble a { color: var(--wm-green-700, #5d6f0c); }
.wm-chat-meta { font-size: .68rem; color: var(--bs-secondary-color); display: inline-flex; align-items: center; gap: .2rem; }
/* Lese-Status, ganz schlank: 1 Haken = gesendet, 2 graue Haken = zugestellt,
   2 farbige Haken = gelesen (WhatsApp-artig). */
.wm-chat-tick { font-size: .8rem; line-height: 1; }
.wm-chat-tick-sent, .wm-chat-tick-delivered { color: var(--bs-secondary-color); }
.wm-chat-tick-read { color: var(--wm-green-700, #5d6f0c); }

.wm-chat-composer { border-top: 1px solid var(--bs-border-color); padding: .75rem 1rem; flex-shrink: 0; }
.wm-chat-composer-attachments { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .5rem; }
.wm-chat-composer-attachments:empty { display: none; }

/* Upload-Fortschritt (nur sichtbar während ein Anhang tatsächlich hochlädt -
   siehe wmChatUploadProgress im Script, per XHR upload.onprogress befüllt). */
.wm-chat-upload-progress { display: flex; align-items: center; gap: .6rem; margin-bottom: .5rem; }
.wm-chat-upload-progress-track { flex: 1 1 auto; height: 6px; border-radius: 3px; background: var(--bs-secondary-bg); overflow: hidden; }
.wm-chat-upload-progress-bar { height: 100%; width: 0; background: var(--wm-green); transition: width .1s linear; }
.wm-chat-upload-progress-label { font-size: .72rem; color: var(--bs-secondary-color); white-space: nowrap; }

/* "Notiz an mich" - Selbst-Chat, angepinnt oberhalb der Chat-Liste, eigenes
   Icon statt Initialen-Avatar damit er sich klar von echten Kontakten abhebt. */
.wm-chat-self-avatar {
  width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--wm-green); color: var(--wm-navy); font-size: 1rem;
}
.wm-chat-list-item.wm-chat-self-item { margin-bottom: .35rem; }

/* Datei-Chip + "Link teilen"-Button nebeneinander (Chat-spezifisch - der
   Chip selbst bleibt der geteilte .wm-file-chip-Baustein für Ticket/Kunde). */
.wm-chat-file-row { display: inline-flex; align-items: center; flex-wrap: nowrap; gap: .3rem; max-width: 100%; }
.wm-chat-file-row .wm-file-chip { flex: 1 1 auto; min-width: 0; }
.wm-chat-share-btn { flex-shrink: 0; }
/* PDF-Chip ist ein <button> (öffnet die Lightbox statt eines Links) -
   Button-Defaults zurücksetzen, damit er wie der <a>-Chip aussieht. */
button.wm-file-chip {
  font: inherit; text-align: left; cursor: pointer;
}

/* Bild-Anhänge: echtes Thumbnail statt Datei-Chip ("direkt ne Vorschau"),
   Klick öffnet die Lightbox (siehe openLightbox() im Script). */
.wm-chat-image-thumb {
  display: block; padding: 0; border: 1px solid var(--bs-border-color); border-radius: .5rem;
  overflow: hidden; background: var(--bs-body-bg); cursor: pointer; line-height: 0;
  max-width: 220px; transition: border-color .12s ease;
}
.wm-chat-image-thumb:hover { border-color: var(--wm-green); }
.wm-chat-image-thumb img { display: block; width: 100%; max-height: 220px; object-fit: cover; }

/* Profilbilder klickbar vergrößern (Lightbox) - nur wenn ein echtes Foto
   hinterlegt ist (siehe $avatarUrl in message.php), Initialen-Avatare
   bleiben unklickbar (nichts zum Vergrößern). */
.wm-chat-avatar-btn { padding: 0; border: 0; background: none; line-height: 0; cursor: pointer; }

/* Platzhalter für gelöschte Nachrichten ("für alle löschen"). */
.wm-chat-bubble-deleted {
  background: transparent !important; border: 1px dashed var(--bs-border-color);
  color: var(--bs-secondary-color); font-style: italic; font-size: .8125rem;
}
.wm-chat-delete-btn { opacity: .55; margin-right: .15rem; }
.wm-chat-delete-btn:hover { opacity: 1; }

/* "Zurück"-Link nur im schmalen Layout sichtbar (siehe Media Query unten) -
   dort steuert der Server (wm-chat-has-active, je nachdem ob $active gesetzt
   ist) welche der beiden Spalten sichtbar ist, keine JS-Klasse nötig. */
.wm-chat-back { display: none; }

/* Eigener Breakpoint (nicht Bootstraps 768px/md): 280px Sidebar + darunter
   bleibt für den Thread zu wenig Platz - relevant sowohl für echte schmale
   Viewports als auch fürs eingebettete Chat-Panel (siehe .wm-chat-panel). */
@media (max-width: 649.98px) {
  .wm-chat-shell { height: calc(100vh - 10rem); }
  .wm-chat-shell.wm-chat-has-active .wm-chat-sidebar { display: none; }
  .wm-chat-shell:not(.wm-chat-has-active) .wm-chat-main { display: none; }
  .wm-chat-row { max-width: 88%; }
  .wm-chat-back { display: inline-flex; }
}

/* ---------------------------------------------------------------------------
 * Kalender: Sidebar (Termin anlegen, Mini-Monatsansicht, Kalender-Filter)
 * links + große FullCalendar-Ansicht rechts, nach Vuexy "app-calendar"-
 * Vorbild. Nutzt dieselbe Shell-Mechanik wie der Chat (eigene Klassen, da
 * FullCalendar viel eigenes CSS mitbringt, das nicht mit .wm-chat-* kollidieren soll).
 * ------------------------------------------------------------------------- */
.wm-cal-shell {
  display: flex; align-items: flex-start; gap: 1.25rem;
}
.wm-cal-sidebar {
  width: 280px; flex-shrink: 0;
  background: var(--bs-body-bg); border: 1px solid var(--bs-border-color); border-radius: .5rem;
  padding: 1rem;
}
.wm-cal-main {
  flex: 1 1 auto; min-width: 0;
  background: var(--bs-body-bg); border: 1px solid var(--bs-border-color); border-radius: .5rem;
  overflow: hidden;
}
.wm-cal-main-toolbar { display: flex; justify-content: flex-end; padding: .85rem 1rem; border-bottom: 1px solid var(--bs-border-color); }
.wm-cal-main-body { padding: 1rem; }

.wm-cal-mini { border: 1px solid var(--bs-border-color); border-radius: .4rem; padding: .35rem; }
.wm-cal-mini .fc { font-size: .72rem; }
.wm-cal-mini .fc .fc-toolbar { margin-bottom: .35rem; padding: 0 .15rem; }
.wm-cal-mini .fc .fc-toolbar-title { font-size: .8125rem; font-weight: 600; }
.wm-cal-mini .fc .fc-button { padding: .1rem .35rem; font-size: .7rem; }
.wm-cal-mini .fc .fc-daygrid-day-number { padding: .15rem .3rem; }
.wm-cal-mini .fc .fc-daygrid-day-frame { min-height: 0; }
.wm-cal-mini .fc-daygrid-day.wm-cal-mini-selected .fc-daygrid-day-frame {
  background: var(--wm-green); border-radius: .3rem;
}
.wm-cal-mini .fc-daygrid-day.wm-cal-mini-selected .fc-daygrid-day-number { color: var(--wm-navy); font-weight: 700; }

@media (max-width: 991.98px) {
  .wm-cal-shell { flex-direction: column; }
  .wm-cal-sidebar { width: 100%; }
}

/* Haupt-Kalender: FullCalendars dichte Werkseinstellung (dünne Rahmen, eckige
   Buttons, winzige Event-Balken) wirkt neben dem Rest der App unsauber -
   an die Vuexy-"app-calendar"-Optik angleichen (abgerundete Event-Pills,
   ruhigere Spaltenköpfe, dezente Heute-Markierung). */
.wm-cal-main .fc-toolbar-title { font-size: 1.1rem; font-weight: 600; }
.wm-cal-main .fc-button {
  background: var(--bs-body-bg); border-color: var(--bs-border-color); color: var(--bs-body-color);
  box-shadow: none !important; text-transform: capitalize; border-radius: .4rem; padding: .4rem .9rem;
}
.wm-cal-main .fc-button:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); }
.wm-cal-main .fc-button-primary:not(:disabled).fc-button-active {
  background: var(--wm-green); border-color: var(--wm-green); color: var(--wm-navy);
}
.wm-cal-main .fc-prev-button, .wm-cal-main .fc-next-button { background: transparent; border-color: var(--bs-border-color); }
.wm-cal-main .fc-col-header-cell {
  padding: .65rem 0; background: var(--bs-tertiary-bg); font-weight: 600;
  text-transform: uppercase; font-size: .72rem; letter-spacing: .03em;
}
.wm-cal-main .fc-daygrid-day-number { padding: .5rem; font-weight: 500; }
.wm-cal-main .fc-day-today { background: var(--bs-primary-bg-subtle, rgba(194, 209, 7, .12)) !important; }
.wm-cal-main .fc-daygrid-day-number:is(.fc-day-today *) { font-weight: 700; }
.wm-cal-main .fc-event { border: none; border-radius: .35rem; padding: .05rem .5rem; font-size: .8125rem; font-weight: 500; }
.wm-cal-main .fc-daygrid-event { margin-top: 2px; }
.wm-cal-main .fc-list-day-cushion { background: var(--bs-tertiary-bg); }
.wm-cal-main .fc-list-event:hover td { background: var(--bs-tertiary-bg); }
.wm-cal-main .fc-list-event-dot { border-width: 5px; }

/* ---------------------------------------------------------------------------
 * Schwebender Chat-Button (FAB) + Lightbox-Panel — der Chat sitzt bewusst
 * NICHT in der Hauptnavigation, sondern ist von jeder Seite aus über diese
 * Sprechblase unten rechts erreichbar (Messenger-/Intercom-Muster). Das
 * Panel embedded /chat per <iframe> (layouts/embed) statt eigenem Markup,
 * damit Chat-Liste/Thread/Anhänge nicht doppelt gepflegt werden müssen.
 * ------------------------------------------------------------------------- */
.wm-chat-fab {
  /* bottom etwas großzügiger als üblich - sonst überlappt der Button die
     Versionsnummer im Footer ("vX.Y.Z"), die ganz unten rechts sitzt. */
  /* --wm-bottom-bar setzt eine feste Aktionsleiste am unteren Rand (z. B. der
     Angebots-Editor) auf ihre eigene Hoehe - ohne das verschwindet der Knopf
     hinter der Leiste, sobald sie auf schmalen Ansichten umbricht. */
  position: fixed; right: 1.5rem; bottom: calc(4.5rem + var(--wm-bottom-bar, 0px)); z-index: 1050;
  width: 56px; height: 56px; padding: 0; border-radius: 50%; border: 0;
  background: var(--wm-green); color: var(--wm-navy);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 .5rem 1.25rem rgba(6, 18, 35, .25);
  transition: transform .15s ease;
  line-height: 1;
}
.wm-chat-fab:hover { transform: scale(1.06); }
.wm-chat-fab .icon-base {
  display: block; font-size: 1.5rem; line-height: 1;
  width: 1em; height: 1em;
}
.wm-chat-fab-badge {
  position: absolute; top: -2px; right: -2px; min-width: 20px; height: 20px; padding: 0 5px;
  border-radius: 10px; background: var(--bs-danger, #dc3545); color: #fff;
  font-size: .7rem; font-weight: 700; line-height: 20px; text-align: center;
  box-shadow: 0 0 0 2px var(--bs-body-bg);
}

.wm-chat-panel {
  position: fixed; right: 1.5rem; bottom: 8.5rem; z-index: 1050;
  /* Breit genug für Sidebar (280px) + Thread nebeneinander, sonst kippt die
     eingebettete Chat-Seite unnötig in ihre eigene <768px-Mobile-Ansicht. */
  width: 760px; height: 640px; max-width: calc(100vw - 3rem); max-height: calc(100vh - 7.5rem);
  background: var(--bs-body-bg); border-radius: .75rem; overflow: hidden;
  box-shadow: 0 1rem 3rem rgba(6, 18, 35, .3);
  display: flex; flex-direction: column;
  opacity: 0; transform: translateY(12px) scale(.98); pointer-events: none;
  transition: opacity .15s ease, transform .15s ease;
}
.wm-chat-panel.show { opacity: 1; transform: none; pointer-events: auto; }
.wm-chat-panel-header {
  flex-shrink: 0; display: flex; align-items: center; justify-content: space-between;
  padding: .6rem .75rem .6rem 1rem; background: var(--wm-navy); color: #fff;
}
.wm-chat-panel-header .badge { font-size: .65rem; }
.wm-chat-panel-header .btn-close { filter: invert(1) grayscale(1) brightness(2); }
.wm-chat-panel iframe { flex: 1 1 auto; width: 100%; border: 0; }

@media (max-width: 575.98px) {
  .wm-chat-panel {
    right: .5rem; left: .5rem; bottom: .5rem; top: .5rem;
    width: auto; height: auto; max-height: none;
  }
  .wm-chat-fab { right: 1rem; bottom: 1.75rem; }
}

/* ---------------------------------------------------------------------------
 * Dashboard-Kachel 1: Bild-Slider (Bootstrap-Carousel) aus den Titelbildern
 * der letzten Schwarzes-Brett-Beiträge - echte, bereits vorhandene Inhalte
 * statt Platzhalter-Bildern ohne Datenquelle.
 * ------------------------------------------------------------------------- */
.wm-dash-slider { min-height: 220px; }
.wm-dash-slider-slide {
  min-height: 220px; height: 100%;
  background-size: cover; background-position: center; background-color: var(--bs-secondary-bg);
  position: relative;
}
.wm-dash-slider-slide::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(6, 18, 35, .78) 0%, rgba(6, 18, 35, .12) 55%, transparent 80%);
}
.wm-dash-slider-caption { position: absolute; left: 0; right: 0; bottom: 0; padding: .85rem 1rem; z-index: 1; }
/* Punkte an den oberen Rand statt bottom:0 (Bootstrap-Default), damit sie
   nicht mit der Caption am unteren Rand kollidieren. */
.wm-dash-slider .carousel-indicators { top: .65rem; bottom: auto; margin: 0; }
.wm-dash-slider .carousel-indicators [data-bs-target] { background-color: var(--wm-green); width: 7px; height: 7px; border-radius: 50%; }

/* ---------------------------------------------------------------------------
 * Schwarzes Brett: angepinnte Beiträge wie an einer echten Pinnwand -
 * dezente Drehung + ein Reißnagel, der oben mittig durch die Karte sticht
 * (Nutzeranfrage "nachtschicht": "muss einfach schöner die angepinnten,
 * vllt auch so anpinnen wie auf einer Pinnwand mit nem Pin").
 * ------------------------------------------------------------------------- */
.wm-board-pinned { transform: rotate(-1.1deg); transition: transform .15s ease; }
.wm-board-pinned:nth-child(even) { transform: rotate(1.1deg); }
.wm-board-pinned:hover { transform: rotate(0deg) scale(1.01); }
.wm-board-pin-nail {
  position: absolute; top: -14px; left: 50%; transform: translateX(-50%);
  width: 30px; height: 30px; border-radius: 50%; z-index: 3;
  background: radial-gradient(circle at 32% 28%, #ff8a80 0%, var(--bs-danger, #dc3545) 55%, #8f1d1d 100%);
  box-shadow: 0 3px 6px rgba(6, 18, 35, .35), inset 0 -2px 3px rgba(0, 0, 0, .25);
  display: flex; align-items: center; justify-content: center; color: #fff;
}
.wm-board-pin-nail .icon-base { font-size: .8rem; }
@media (prefers-reduced-motion: reduce) {
  .wm-board-pinned, .wm-board-pinned:hover { transform: none; transition: none; }
}

/* ---------------------------------------------------------------------------
 * Angebots-Editor: Abschnitts-Überschrift liest sich wie eine echte
 * Überschrift statt wie ein kleines Formularfeld. Der Positionstitel ist das
 * auffällige, "schlaue" Feld jeder Position - größer als die übrigen kleinen
 * Formularfelder und zugleich ein durchsuchbares Katalog-Autocomplete: beim
 * Tippen erscheinen passende Katalog-Positionen, eine Auswahl füllt Code,
 * Preis, Einheit, Zusatz, Fließtext und Bullets dieser Zeile automatisch
 * (siehe offer-wizard.js, initPositionAutocomplete). Ohne Treffer bleibt der
 * eingegebene Text einfach als frei formulierter Titel stehen.
 * ------------------------------------------------------------------------- */
/* Kostenabschnitt als deutlich abgesetzte Karte. Vorher war der Abschnitt zwar
   schon eine .card, saß aber randlos auf der weißen Dokumentkarte - man sah
   nicht, wo ein Abschnitt anfängt und der nächste aufhört. */
.wm-section-card { border: 1px solid var(--bs-border-color); border-radius: .6rem; overflow: hidden; }
.wm-section-card > .card-header {
  background: var(--bs-secondary-bg, rgba(6, 18, 35, .04));
  border-bottom: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--wm-green);
}
.wm-section-card > .card-header { padding: .6rem .85rem; }
/* Luft zwischen getoenter Kopfzeile und der ersten Position - ohne sie klebt
   die erste Zeile (oder der "Position"-Knopf eines leeren Abschnitts) direkt
   unter der Kante.

   Die zweite Regel ist noetig, weil Vuexy fuer die erste Karte nach einer
   Kopfzeile ausdruecklich padding-block-start: 0 setzt
   (.card .card-header + .card-body:first-of-type in core.css). Gleiche
   Spezifitaet, aber brand.css laedt spaeter - deshalb gewinnt sie ohne
   !important. Dieselbe logische Eigenschaft verwenden, sonst greift der
   Ueberschreibung nicht. */
.wm-section-body { padding: 1rem .85rem 1rem; }
.card .card-header + .card-body.wm-section-body { padding-block-start: 1rem; }
.wm-section-body .wm-pos-row:last-of-type { margin-bottom: 1rem !important; }
/* Griff zum Verschieben eines Abschnitts. */
.wm-sec-grip {
  border: 0; background: transparent; padding: .25rem; line-height: 0;
  color: var(--bs-secondary-color); cursor: grab; border-radius: .35rem;
}
.wm-sec-grip:hover { color: var(--bs-body-color); background: rgba(6, 18, 35, .06); }
.wm-sec-grip:active { cursor: grabbing; }
.wm-sec-grip:focus-visible { outline: 2px solid var(--wm-green); outline-offset: 1px; }
.wm-section-card.is-dragging { opacity: .45; }
/* Zeigt beim Ziehen, wo der Abschnitt landet. */
.wm-section-card.is-drop-target { outline: 2px dashed var(--wm-green); outline-offset: 3px; }
/* Zwischensumme im Kopf des Abschnitts - dieselbe Zahl, die im PDF am Fuß der
   Karte steht. Sie ist ein Lesehilfe im Editor; verbindlich rechnet der
   Server (App\Modules\Offer\Domain\OfferTotals). */
.wm-sec-sub { font-size: .8125rem; color: var(--bs-secondary-color); white-space: nowrap; margin-left: auto; }
.wm-sec-sub strong { color: var(--bs-body-color); }

/* Feste Aktionsleiste am unteren Rand: Speichern bleibt erreichbar, egal wie
   weit man im Dokument nach unten gescrollt ist. */
/* Ohne JavaScript bleibt die Leiste einfach am Ende des Formulars stehen -
   erreichbar, nur nicht mitlaufend. Erst offer-wizard.js heftet sie an den
   unteren Rand und misst dafuer die Breite der Inhaltsspalte.

   Bewusst nicht position: sticky: der Vuexy-Rahmen setzt overflow-y: auto auf
   den body, damit gilt der body als Scroll-Container - er scrollt aber nie
   selbst, gescrollt wird das Dokument. Sticky bleibt dadurch wirkungslos. */
.wm-offer-bar {
  margin-top: 1.5rem;
  background: var(--bs-body-bg); border-top: 1px solid var(--bs-border-color);
}
.wm-offer-bar.is-fixed {
  position: fixed; bottom: 0; z-index: 1020; margin-top: 0;
  /* Oben abgerundet und mit weichem Schatten: darunter laeuft der Inhalt
     durch, und eine harte Kante quer ueber die Seite sieht nach Fehler aus. */
  border: 1px solid var(--bs-border-color); border-bottom: 0;
  border-radius: .6rem .6rem 0 0;
  box-shadow: 0 -10px 24px rgba(6, 18, 35, .10);
}
.wm-offer-bar-inner {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  /* Der waagerechte Abstand kommt zur Laufzeit aus der Inhaltsspalte, damit
     Text und Knoepfe auf einer Linie mit den Karten darueber stehen. */
  padding: .85rem 0;
}
.wm-offer-bar-meta { margin-right: auto; display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; min-width: 0; }
.wm-offer-bar-meta > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 575.98px) {
  /* Auf dem Handy zaehlen die Knoepfe, nicht die Zahlen. */
  .wm-offer-bar-meta { width: 100%; font-size: .8125rem; }
  .wm-offer-bar-inner { padding: .6rem 0; gap: .375rem; }
}

.wm-offer-preview-body { height: min(78vh, 900px); }
.wm-offer-preview-body iframe { width: 100%; height: 100%; border: 0; }
.wm-offer-preview-wait {
  display: flex; align-items: center; justify-content: center;
  height: 100%; color: var(--bs-secondary-color);
}

.wm-sec-title {
  border: none; border-bottom: 2px solid transparent; border-radius: 0;
  background: transparent; box-shadow: none !important;
  font-size: 1.15rem; font-weight: 700; padding: .25rem .1rem; min-width: 0; flex: 1 1 auto;
}
.wm-sec-title::placeholder { font-weight: 400; font-size: 1rem; }
.wm-sec-title:focus { border-bottom-color: var(--wm-green); background: transparent; }

.wm-p-title-wrap { position: relative; }
.wm-p-title-icon {
  position: absolute; left: .8rem; top: 50%; transform: translateY(-50%);
  color: var(--bs-secondary-color); pointer-events: none; line-height: 0;
}
.wm-p-title {
  width: 100%; padding: .55rem .75rem .55rem 2.4rem; font-size: 1.05rem; font-weight: 600;
  border: 1.5px solid var(--bs-border-color); border-radius: .5rem; background: var(--bs-body-bg);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.wm-p-title::placeholder { font-weight: 400; color: var(--bs-secondary-color); }
.wm-p-title:focus {
  outline: none; border-color: var(--wm-green);
  box-shadow: 0 0 0 .2rem rgba(194, 209, 7, .25);
}
.wm-cat-dropdown {
  display: none; position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 1050;
  max-height: 320px; overflow-y: auto; background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color); border-radius: .5rem;
  box-shadow: 0 10px 28px rgba(6, 18, 35, .18); padding: .35rem 0;
}
.wm-cat-dropdown.show { display: block; }
.wm-cat-group-label {
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--bs-secondary-color); padding: .5rem .9rem .2rem;
}
.wm-cat-item { padding: .4rem .9rem; cursor: pointer; font-size: .85rem; display: flex; justify-content: space-between; gap: .75rem; }
.wm-cat-item span.wm-cat-item-price { color: var(--bs-secondary-color); white-space: nowrap; font-variant-numeric: tabular-nums; }
.wm-cat-item:hover, .wm-cat-item.active { background: var(--bs-tertiary-bg); }
.wm-cat-item mark { background: rgba(194, 209, 7, .45); color: inherit; padding: 0; border-radius: 2px; }
.wm-cat-empty { padding: .6rem .9rem; font-size: .8rem; color: var(--bs-secondary-color); }

/* ---------------------------------------------------------------------------
 * (i)-Feature-Hinweise: dezentes Info-Icon neben neuen Funktionen, öffnet
 * per Fokus/Klick ein Popover mit Erklärung (siehe helpers.php feature_hint()
 * und Einstellungen -> Hinweise zum Deaktivieren einzelner Hinweise).
 * ------------------------------------------------------------------------- */
.wm-hint {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.1rem; height: 1.1rem; border-radius: 50%; cursor: pointer;
  color: var(--bs-secondary-color); vertical-align: middle; margin-left: .35rem;
  transition: color .15s ease, background-color .15s ease;
}
.wm-hint:hover, .wm-hint:focus { color: var(--bs-body-bg); background: var(--wm-green); outline: none; }
.wm-hint .icon-base { font-size: .85rem; }

/* ---------------------------------------------------------------------------
 * Kacheln/Cards-Auffrischung (Nutzeranfrage: "Pimpe bitte unsere Kacheln und
 * Cards an allen Stellen im System" nach Vorbild der Vuexy-Statistik-Karten,
 * https://demos.pixinvent.com/.../cards-statistics.html). Reine visuelle
 * Politur - Layout/Markup der bestehenden Cards bleibt unangetastet, wirkt
 * dadurch automatisch überall, wo `.card` genutzt wird.
 * ------------------------------------------------------------------------- */
.card {
  border: 1px solid var(--bs-border-color);
  border-radius: .75rem;
  box-shadow: 0 2px 6px rgba(6, 18, 35, .04), 0 8px 24px rgba(6, 18, 35, .04);
  transition: box-shadow .18s ease, transform .18s ease;
}
.card:hover { box-shadow: 0 4px 10px rgba(6, 18, 35, .06), 0 14px 32px rgba(6, 18, 35, .07); }
@media (prefers-reduced-motion: reduce) { .card { transition: none; } }
.card-header { border-bottom-color: var(--bs-border-color); }

/* ---------------------------------------------------------------------------
 * Runder Fortschritts-Indikator ("78%"-Kachel-Stil), siehe
 * app/Core/Views/partials/radial-gauge.php für die Verwendung.
 * ------------------------------------------------------------------------- */
.wm-radial-gauge { position: relative; display: inline-block; }
.wm-radial-gauge svg { transform: rotate(-90deg); }
.wm-radial-gauge-track { stroke: var(--bs-border-color); }
.wm-radial-gauge-value-arc { stroke: currentColor; stroke-linecap: round; transition: stroke-dashoffset .5s ease; }
.wm-radial-gauge-center {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; line-height: 1.15;
}
.wm-radial-gauge-number { font-size: 1.35rem; font-weight: 600; color: var(--bs-heading-color); font-variant-numeric: tabular-nums; }
.wm-radial-gauge-caption { font-size: .7rem; color: var(--bs-secondary-color); margin-top: .1rem; }

/* ---------------------------------------------------------------------------
 * Mobile-Politur (Nutzeranfrage: "geh bitte alle Tabellen durch, die sind
 * auf dem Handy so gut wie alle nicht responsive, auch die Buttons und Menüs
 * passen nicht"). Root Cause geprüft: Tabellen selbst sind über
 * .table-responsive bereits sauber intern scrollbar - das eigentliche
 * Problem sind Toolbar-/Filterzeilen (Monatsfilter + Von/Bis + Button etc.),
 * die zwar .d-flex + .gap-* nutzen, aber kein .flex-wrap haben und dadurch
 * die GANZE Seite in die Breite drücken (bestätigt am Beispiel /billing:
 * eine "d-flex align-items-end gap-2"-Zeile ohne flex-wrap zwang den
 * gesamten Viewport von 375px auf 557px auf).
 * ------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  /* Generelles Sicherheitsnetz: jede nicht explizit auf eine Zeile
     festgelegte Flex-Zeile darf umbrechen statt die Seite zu sprengen. Für
     Zeilen, die ohnehin auf eine Zeile passen, ändert das nichts sichtbar -
     flex-wrap greift nur, wenn der Platz tatsächlich nicht reicht. */
  .d-flex:not(.flex-nowrap):not(.flex-column) { flex-wrap: wrap; }
}
/* Zweites Sicherheitsnetz, unabhängig vom obigen: selbst falls irgendwo eine
   Zeile trotzdem zu breit wird, darf sie nie die ganze Seite seitwärts
   scrollbar machen - nur echte Scroll-Container (.table-responsive etc.)
   sollen intern scrollen. */
html, body { overflow-x: hidden; max-width: 100%; }

/* Stoppuhr in der Topbar: auf dem Handy nicht wichtig genug für den knappen
   Platz (Nutzeranfrage: "einfach die Stopuhr rausnehmen die ist aufm Handy
   nicht so wichtig") - Zeit lässt sich dort weiterhin ganz normal über
   Zeiterfassung -> Zeit erfassen eintragen. Ab lg (Tablet quer/Desktop)
   bleibt sie wie gewohnt sichtbar. */
@media (max-width: 991.98px) {
  #layout-navbar .navbar-nav-right > .navbar-nav:first-child { display: none !important; }
}

/* Mindestabstand zwischen Topbar und Seiteninhalt auf Mobile - die Topbar ist
   dort "detached" mit wenig eigenem Rand, Inhalte (z. B. Dashboard-Begrüßung)
   starteten dadurch fast ohne Luft direkt unter den Icons. core.css' eigene
   .container-p-y-Regel (Vuexy-Utility) setzt padding-block-start ebenfalls
   !important, über :not([class^=pt-], [class*=" pt-"]) - browserabhängig als
   Spezifität 0-2-0 (Maximum der :not()-Argumente) ODER 0-3-0 (Summe der
   :not()-Argumente) ausgewertet, je nach :not()-Spezifitäts-Auslegung der
   Browser-Engine. .container-p-y hier zweimal wiederholt hebt die eigene
   Spezifität sicher darüber, unabhängig von der Auslegung. */
@media (max-width: 767.98px) {
  .content-wrapper .container-xxl.container-p-y.container-p-y {
    padding-block-start: 40px !important;
    padding-top: 40px !important;
  }
}

/* Auftragsübersicht in der Angebots-Seitenleiste: enge Zeilen, Zahlen bündig.
   Bootstraps table-sm ist für eine 3-Spalten-Sidebar immer noch zu luftig. */
.wm-offer-sum > tbody > tr > td,
.wm-offer-sum > tr > td { padding: 0.15rem 0; vertical-align: top; line-height: 1.3; }
.wm-offer-sum { font-variant-numeric: tabular-nums; }
/* Betragsspalte nie umbrechen: "2.775,00" und "€" in getrennten Zeilen sind
   in einer Zahlenkolonne kaum lesbar. */
.wm-offer-sum td:last-child { white-space: nowrap; }

/* Gliederung der Auftragsübersicht: ohne Linien lief in der schmalen Spalte
   das Brutto des einen Blocks ins Netto des nächsten. Die Endzahl jeder Gruppe
   trägt deshalb eine Oberlinie und mehr Gewicht. */
.wm-sum-cap {
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  border-bottom: 1px solid var(--bs-border-color);
  padding-bottom: 0.25rem;
  margin: 1rem 0 0.5rem;
}
.wm-sum-cap .text-none { text-transform: none; letter-spacing: 0; }
.wm-sum-block + .wm-sum-block { margin-top: 0.625rem; }
.wm-sum-unit {
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--bs-secondary-color);
}
.wm-offer-sum tr.wm-sum-total > td {
  border-top: 1px solid var(--bs-border-color);
  padding-top: 0.3rem;
  font-weight: 600;
}
.wm-offer-sum tr.wm-sum-rows-sep > td { border-top: 1px dashed var(--bs-border-color); }
.wm-sum-rows > tbody > tr + tr > td,
.wm-sum-rows > tr + tr > td {
  border-top: 1px dashed var(--bs-border-color);
  padding-top: 0.35rem;
}
.wm-offer-sum tr.wm-sum-sec > td {
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  border-top: 1px solid var(--bs-border-color);
  padding-top: 0.5rem;
  padding-bottom: 0.15rem;
}
.wm-offer-sum tr.wm-sum-sec:first-child > td { border-top: 0; padding-top: 0.15rem; }

/* Scrollbares Modal, das auch im Theme funktioniert.

   Bootstraps .modal-dialog-scrollable verlaesst sich darauf, dass der
   .modal-body im Flex-Container schrumpft. Im Vuexy-Theme tut er das nicht
   (gemessen: .modal-content 712 px hoch mit overflow:hidden, .modal-body
   bleibt 1050 px) - die Liste war damit unten schlicht abgeschnitten. Eine
   feste Obergrenze am Body loest es unabhaengig von der Flex-Kette. */
.wm-modal-scroll .modal-body {
  max-height: calc(100vh - 15rem);
  overflow-y: auto;
}
