/* Project overrides layered on top of Vuexy's compiled CSS.
   Loaded after core.css/demo.css so single-class rules win by source order. */

/* The visual header is TWO stacked bands: the top .layout-navbar (logo/user)
   and the .menu-horizontal nav bar below it. By default the navbar uses
   --bs-body-bg (the page colour) while the menu uses --bs-paper-bg (a surface),
   so they read as two disconnected strips. Give the navbar the same surface as
   the menu so they form one continuous header (adapts to light/dark). */
/* Single source of truth for the (enlarged) navbar height. Vuexy assumes
   3.5rem everywhere; we grow it but must re-feed the new height into the two
   places that hard-code 3.5rem — otherwise the fixed horizontal menu stays
   pinned under the navbar and the navbar overlaps it (top padding looks
   shorter than the bottom). */
:root {
  --cashflow-navbar-h: 4.5rem;
}

.layout-navbar {
  background-color: var(--bs-paper-bg);
  /* Vuexy pins a fixed block-size (3.5rem); pin ours to the variable instead
     so the height is deterministic and the menu offset below can match it.
     align-items-center (in the markup) keeps the content vertically centred. */
  block-size: var(--cashflow-navbar-h);
  min-block-size: var(--cashflow-navbar-h);
}

/* The horizontal nav bar is position:fixed at top:3.5rem (Vuexy's navbar
   height). Push it down to sit flush under the taller navbar, and shift the
   content below it by the same delta. Mirrors core.css's @media (min-width:1200px)
   block so it wins by specificity + source order. */
@media (min-width: 1200px) {
  .layout-menu-fixed:not(.layout-navbar-hidden) .layout-horizontal .layout-page .menu-horizontal {
    inset-block-start: var(--cashflow-navbar-h);
  }
  .layout-menu-fixed:not(.layout-navbar-hidden) .layout-horizontal .layout-page .menu-horizontal + [class*="container-"] {
    /* Vuexy's 5.3rem = 3.5rem navbar + 1.8rem menu band. Keep the +1.8rem. */
    padding-block-start: calc(var(--cashflow-navbar-h) + 1.8rem) !important;
  }
}

/* Modal close (×): keep it INSIDE the modal, top-right. Vuexy's default floats
   the button outside the corner with a shadow (transform: translate(23px,-25px)),
   but disables that for scrollable modals — leaving our modals inconsistent.
   Apply the scrollable treatment to every modal so all × buttons match: still a
   paper-bg rounded button (via .modal .modal-header .btn-close position), just
   not floated/shadowed. Equal specificity to Vuexy's rule → wins by source order. */
.modal .btn-close,
.modal .btn-close:hover,
.modal .btn-close:focus,
.modal .btn-close:active {
  box-shadow: none;
  transform: translate(0, 0);
}

/* Tenant event-status icons (navbar switcher). The active-event icon pulses to
   read as "live"; expired events get a muted brown archive tone. */
.tenant-status-active {
  display: inline-block;
  animation: cashflow-pulse 1.6s ease-in-out infinite;
}
.tenant-status-expired {
  color: #8d6e63 !important; /* material brown 400 — "archived" */
}
@keyframes cashflow-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.82); }
}
@media (prefers-reduced-motion: reduce) {
  .tenant-status-active { animation: none; }
}

/* Tagify styling comes from the Vuexy bundle (vendor/libs/tagify/tagify.css),
   which is already theme-integrated — no project overrides needed. */

/* Card DataTables (assets/js/card-datatable.js): the page-length control is
   dropped and the search box is relocated into the .card-header. Its layout
   (margin reset + input width) is applied inline by that script — inline styles
   beat the DataTables vendor CSS without a specificity war, so no rule here. */

/* PROJECT PATTERN — busy buttons.
   Every htmx action button shows a spinner while its request is in flight, with
   ZERO markup: htmx adds `.htmx-request` to the requesting element (the button
   itself for hx-get/hx-post on a button, or the <form> for a submit button), so
   this is fully automatic. The button's own leading icon is swapped for the
   spinner and the button is click-locked for the duration. Do NOT hand-roll
   per-button spinners — just let a button trigger an htmx request. */
button.htmx-request,
form.htmx-request button[type="submit"] {
  pointer-events: none; /* block double-submits while in flight */
}
button.htmx-request > i,
form.htmx-request button[type="submit"] > i {
  display: none; /* hide the static icon; the spinner takes its place */
}
button.htmx-request::before,
form.htmx-request button[type="submit"]::before {
  content: "";
  display: inline-block;
  width: 1em;
  height: 1em;
  margin-right: 0.5em;
  vertical-align: -0.125em;
  border: 0.15em solid currentColor;   /* matches the button's text colour */
  border-right-color: transparent;
  border-radius: 50%;
  animation: app-btn-spin 0.6s linear infinite;
}
/* Icon-only buttons (e.g. table actions) have no text after the spinner. */
.btn-icon.htmx-request::before {
  margin-right: 0;
}

/* Vuexy forces `.table .btn-icon` to the neutral table text colour, so a tonal
   `btn-label-*` icon button inside a table shows a coloured background tint but a
   grey icon (looks off). Let the icon use its own button colour instead, so the
   tonal action buttons read as properly coloured in BOTH themes (the value is a
   theme-aware variable). */
.table .btn-icon {
  color: var(--bs-btn-color);
}
@keyframes app-btn-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Vuexy zeroes a card-body's top padding when it directly follows a card-header
   (`.card .card-header + .card-body { padding-block-start: 0 }`, core.css). Our
   settings cards use a real .card-body under a border-bottom header, so the first
   line sat flush on the divider. Same-specificity rule, loaded after core.css,
   restores the symmetric top padding. */
.settings-card .card-header + .card-body {
  padding-block-start: var(--bs-card-spacer-y);
}

/* Das Download-Menue einer Tabelle ist ein reiner Symbolknopf. DataTables
   haengt einem Sammelknopf selbst `dropdown-toggle` an, und Bootstraps Pfeil
   daraus ragt bei `btn-icon` (kein rechter Innenabstand) aus dem Knopf heraus.
   Der Name steht im title, der Pfeil trägt hier nichts bei. */
.card-header .dt-buttons .btn-icon.dropdown-toggle::after {
  display: none;
}

/* Das aufgeklappte Download-Menue rechtsbuendig unter seinen Knopf. DataTables
   schreibt ihm ein inline `left` und rechnet dabei gegen den naechsten
   positionierten Vorfahren — das ist die .card, nicht der Knopf, also hing das
   Menue ueber den Kartenrand hinaus. Die Knopfgruppe wird darum selbst zum
   Bezugspunkt; das inline `left` braucht !important. */
.card-header .dt-buttons .btn-group {
  position: relative;
}
.card-header .dt-buttons .dt-button-collection {
  left: auto !important;
  right: 0;
}

/* --------------------------------------------------------------------------
   Cockpit — die Warnung vor einer Abholung.

   Ab 30 Minuten vorher bekommt die Karte einen roten Rand, der blinkt.
   Gesetzt wird die Klasse in assets/js/cockpit.js, nicht auf dem Server: die
   Grenze verschiebt sich jede Sekunde.

   `prefers-reduced-motion` schaltet das Blinken ab und laesst den roten Rand
   stehen — die Auskunft bleibt, die Bewegung geht. Wer eine Bewegungsempfind-
   lichkeit hat, soll nicht zwischen Gesundheit und Dienstplan waehlen muessen.

   Breite UND Stil muessen mit: die Karte traegt ihre Kontur sonst ueber den
   Schatten, nicht ueber einen Rand — ein blosses `border-color` faerbte dann
   eine Linie, die es gar nicht gibt.
   -------------------------------------------------------------------------- */
.cockpit-warnt {
  border: 2px solid var(--bs-danger) !important;
  box-shadow: 0 0 0 0.15rem rgba(var(--bs-danger-rgb), 0.25);
  animation: cockpit-blinken 1.1s ease-in-out infinite;
}

@keyframes cockpit-blinken {
  0%, 100% { border-color: var(--bs-danger); box-shadow: 0 0 0 0.15rem rgba(var(--bs-danger-rgb), 0.30); }
  50%      { border-color: rgba(var(--bs-danger-rgb), 0.30); box-shadow: 0 0 0 0.15rem rgba(var(--bs-danger-rgb), 0); }
}

@media (prefers-reduced-motion: reduce) {
  .cockpit-warnt { animation: none; }
}

/* Zurueckgenommen — die Gegenrichtung zur Warnung: gruener Rahmen, aber ohne
   Schatten und ohne Blinken. Erledigtes fordert nichts, es bestaetigt nur.
   Wie bei .cockpit-warnt muessen Breite und Stil mit: die Karte traegt ihre
   Kontur sonst ueber den Schatten, und eine blosse Farbe faerbte eine Linie,
   die es nicht gibt. */
.cockpit-fertig {
  border: 2px solid var(--bs-success) !important;
}

/* --------------------------------------------------------------------------
   Cockpit — Kartendichte.

   Die Vuexy-Karte ist fuer eine Seite mit einer oder zwei Karten gemacht; das
   Cockpit hat dreissig nebeneinander. Mit dem Standardpolster (1.5rem) passen
   drei Karten in den Schirm, mit diesem sechs — und um genau das geht es hier:
   die Lage auf einen Blick, nicht scrollen.

   Nur im Cockpit, ueber `.cockpit-card` — die uebrigen Seiten behalten das
   ruhige Vuexy-Mass.
   -------------------------------------------------------------------------- */
.cockpit-card > .card-body {
  padding: 0.875rem 1rem;
}

/* Die Meta-Zeile unter dem Titel: klein, leise, umbruchfaehig.

   **Ohne Trennzeichen.** Ein `·` als `::before` am Folgeelement wandert beim
   Umbruch mit und steht dann am ZEILENANFANG — genau das sah im Cockpit
   schlecht aus. Ein `::after` haette denselben Fehler am Zeilenende. Ein
   ausreichender Spaltenabstand trennt genauso gut und kann nicht verrutschen;
   die Segmente unterscheiden sich ohnehin durch Symbol und Farbe. */
.cockpit-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.1rem 0.9rem;
  font-size: 0.8125rem;
  line-height: 1.45;
}

/* Erledigtes tritt zurueck — es steht noch da, aber es draengt nicht mehr. */
.cockpit-card.is-erledigt { opacity: 0.72; }
.cockpit-card.is-erledigt:hover { opacity: 1; }
