/* Eigene Styles der App: alles, was nicht aus den Tailwind-Klassen der
   Templates kommt. Plain CSS wirkt ohne Build-Schritt; app.css (Tailwind)
   enthält nur Klassen, die beim letzten Build im Content-Scan standen. */

/* Ersatz für Tailwind-Arbitrary-Values wie -top-[11px]/overflow-y-visible,
   die in der vorkompilierten app.css nur nach einem neuen Build stünden. */
.pp-ftop {
  top: -11px;
}

/* .mb-0 steht nicht im vorkompilierten app.css - gebraucht in
   booking_form.html, um den space-y-4-Abstand (:where()-Selektor,
   Spezifität 0) für genau einen Übergang zu überschreiben (Ausweis-ID ->
   Gäste/Kinder/Kleinkinder-Zeile, siehe dortiger Kommentar). */
.mb-0 {
  margin-bottom: 0;
}

/* Bündelt das wiederkehrende Muster des schwebenden Labels ("absolute
   pp-ftop left-2 text-xs font-medium text-gray-500 bg-white px-1"). Die
   Werte kommen aus den Custom Properties, die app.css (Tailwind) auf :root
   setzt, damit sie mit den Utility-Klassen übereinstimmen. */
.pp-floating-label {
  position: absolute;
  top: -11px;
  left: calc(var(--spacing) * 2);
  font-size: var(--text-xs);
  line-height: var(--text-xs--line-height, calc(1 / 0.75));
  font-weight: var(--font-weight-medium);
  color: var(--color-gray-500);
  background-color: var(--color-white);
  padding-inline: calc(var(--spacing) * 1);
}

.pp-oy-visible {
  overflow-y: visible;
}

/* Unsichtbarer Wrapper für Grid-Layouts: die zwei Felder darin (z.B.
   Anzahlung + Sonderpreis) erscheinen als eigene Zellen im Eltern-Grid,
   lassen sich aber gemeinsam über eine ID/Klasse ein- und ausblenden (siehe
   #offerPriceContainer in booking_form.html + form.js::checkBookingVisibility()).
   Die zweite Regel erzwingt "display:none" auch zusammen mit .hidden -
   sonst könnte die Lade-Reihenfolge (app.css vor styles.css) das Verstecken
   bei gleicher Spezifität überschreiben. */
.pp-display-contents {
  display: contents;
}

.pp-display-contents.hidden {
  display: none;
}

/* Container Query Basis (entscheidet nach Box-Breite, nicht Viewport) */
.pp-cq {
  container-type: inline-size;
}

/* --- Quill: Werkzeugleiste als Popup über dem Editor --- */
[data-pp-quill="1"] {
  position: relative;
}

[data-pp-quill="1"] .pp-quill-toolbar {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 100%;
  margin-bottom: 8px;

  width: max-content;
  max-width: min(92vw, 520px);
  z-index: 9999;

  display: none; /* schaltet JS */
  padding: 6px 8px;

  background: #fff;
  border: 1px solid rgba(0,0,0,0.08);
  border-radius: 10px;
  box-shadow: 0 10px 25px rgba(0,0,0,0.12);
}

[data-pp-quill="1"] .pp-quill-toolbar.ql-toolbar {
  margin: 0 !important;
}

[data-pp-quill="1"] .pp-quill-editor {
  padding-top: 0 !important;
  min-height: 140px;
  /* Die Eingabefläche füllt den ganzen Rahmen - sonst ist sie nur so hoch
     wie der Text, und ein Klick darunter ginge ins Leere. */
  display: flex;
  flex-direction: column;
  cursor: text;
}

[data-pp-quill="1"] .pp-quill-editor .ql-editor {
  flex: 1 1 auto;
  height: auto;
}

/* Pfeil unter der Werkzeugleiste */
[data-pp-quill="1"] .pp-quill-toolbar::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 10px;
  height: 10px;
  background: #fff;
  border-right: 1px solid rgba(0,0,0,0.08);
  border-bottom: 1px solid rgba(0,0,0,0.08);
  transform: translateX(-50%) rotate(45deg);
}

/* --- Owner-Raster ---
   schmal:  cal -> pick -> form   (Liste weg, Auswahlfeld statt ihrer)
   breit:   cal | list  (oben), form darunter

   Die Schwelle 660px kommt aus dem, was die beiden Spalten wirklich
   brauchen, und gilt überall gleich - gemessen wird der Kasten, nicht das
   Gerät:

     Kalender   7 Tagesboxen + 6 Zwischenräume; unter rund 45px Boxbreite
                stehen die Bänder mit den Namen nur noch als Streifen da
                -> 320px
     Liste      70 + 70 + 30px feste Spalten, macht 170px; damit vom Namen
                mehr bleibt als drei Buchstaben, braucht die erste Spalte
                rund 150px -> 320px
     Zwischenraum                                                  -> 16px */
.pp-owner-grid {
  display: grid;
  gap: calc(var(--spacing) * 4);
  align-items: start;

  /* Eine Spalte; die zweite kommt erst mit der Regel unten dazu. Kein
     repeat(auto-fit, ...): das öffnete schon vor der Schwelle zwei Spalten,
     während die Bereichsnamen hier noch eine einzige beschreiben. */
  grid-template-columns: 1fr;

  /* Schmal: Kalender, dann die Buchungsauswahl, dann die Form. Die Auswahl
     sitzt zwischen beiden, weil sie zu beidem gehört: was im Kalender
     angetippt wurde, steht danach hier, und was hier gewählt wird, füllt
     die Preisbox direkt darunter. */
  grid-template-areas:
    "cal"
    "pick"
    "form"
    "list";
}

@container (min-width: 660px) {
  .pp-owner-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-areas:
      "cal list"
      "form form";
  }
}

/* Areas */
.pp-area-calendar { grid-area: cal; min-width: 280px; }
.pp-area-picker   { grid-area: pick; min-width: 0; }
.pp-area-form     { grid-area: form; }
.pp-area-list     { grid-area: list; min-width: 0; } /* nötig für Tabellen/Overflow */

/* --- Buchungsauswahl statt Buchungsliste ---
   Die Tabelle ist vier Spalten breit, trägt zweizeilige Zellen und einen
   20px-Löschknopf je Zeile. Wo sie nicht neben den Kalender passt, steht das
   Auswahlfeld aus partials/booking_picker.html an ihrer Stelle - unabhängig
   vom Gerät: ein schmales Fenster am Schreibtisch hat dasselbe Platzproblem
   wie ein Telefon.

   Die Bedingung ist dieselbe 660px wie beim Raster darüber: genau dort
   bekommt die Liste ihre eigene Spalte neben dem Kalender. Darunter ist sie
   ausgeblendet - nur sie, nicht ihre Zeilen: die bleiben im Dokument, weil
   Auswahlfeld und Kalender ihren Klick auslösen (siehe booking_picker.html). */
.pp-area-list {
  display: none;
}

.pp-booking-picker {
  display: flex;
  align-items: center;
  gap: 8px;
}

.pp-booking-picker-feld {
  flex: 1 1 auto;
  min-width: 0;
}

/* Die beiden Knöpfe sind quadratisch und so hoch wie das Feld daneben -
   statt der 24px hohen Leiste über die ganze Breite, die .icon-btn sonst
   ergibt.

   Zwei Klassen im Selektor: .icon-btn trägt width:100% und steht in dieser
   Datei weiter unten, und bei gleicher Spezifität gewänne die spätere Regel. */
.pp-booking-picker .pp-booking-picker-btn {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  padding: 0;
  font-size: 16px;
}

.pp-booking-picker select {
  min-height: 38px;
}

/* 16px, sonst zoomt iOS Safari beim Antippen hinein und kommt nicht wieder
   heraus - und 44px Kantenlänge nach Apple HIG, wo getippt statt geklickt
   wird. */
@media (pointer: coarse) {
  .pp-booking-picker select {
    min-height: 44px;
    font-size: 16px;
  }

  .pp-booking-picker .pp-booking-picker-btn {
    width: 44px;
    height: 44px;
  }
}

@container (min-width: 660px) {
  .pp-area-list {
    display: block;
  }

  .pp-area-picker {
    display: none;
  }
}

/* --- Objekt-Bereiche (Container Query statt Tailwind-Breakpoints) ---
   schmal:  details -> seasons
   breit:   details | seasons */
.pp-props-panels {
  display: grid;
  gap: 0;
  align-items: start;

  grid-template-columns: 1fr;
}

@container (min-width: 580px) {
  .pp-props-panels {
    grid-template-columns: 50% 50%;
  }
}

/* Verhindert Overflow/Scrollbar durch Inhalte in Grid-Items */
.pp-props-panel {
  min-width: 0;
}

/* 2 Spalten innerhalb eines Bereichs - immer, auch schmal */
.pp-props-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(var(--spacing) * 6);
}

/* 3 Spalten innerhalb eines Bereichs - immer, auch schmal */
.pp-props-grid-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* --- Formular-Raster (Reihenfolge nur schmal festlegen) ---
   schmal:  price -> meta -> left -> right
   breit:   price|meta (oben), left|right (unten) */
.booking-form {
  container-type: inline-size;
}

.pp-booking-form-grid {
  display: grid;
  gap: calc(var(--spacing) * 2);

  /* Schmal: gewünschte Reihenfolge */
  grid-template-columns: 1fr;
  grid-template-areas:
    "price"
    "meta"
    "left"
    "right";
}

.pp-form-left,
.pp-form-right {
  min-width: 0;
  align-self: start;
}

/* Diese Wrapper-Klassen stehen in booking_form.html */
.pp-form-price { grid-area: price; }
.pp-form-meta  { grid-area: meta; }
.pp-form-left  { grid-area: left; }
.pp-form-right { grid-area: right; }

@container (min-width: 580px) {
  .pp-booking-form-grid {
    grid-template-columns: minmax(280px, 1fr) minmax(280px, 1fr);
    grid-template-areas:
      "price meta"
      "left  right";
    align-items: start;
  }

  .pp-form-price { grid-column: 1; grid-row: 1; }
  .pp-form-meta  { grid-column: 2; grid-row: 1; }
  .pp-form-left  { grid-column: 1; grid-row: 2; align-self: start; }
  .pp-form-right { grid-column: 2; grid-row: 2; align-self: start; }
}

/* Eingabefeld mit Währungszeichen */
.pp-currency-field {
  position: relative;
}

.pp-currency-symbol {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-gray-500);
  font-size: 14px;
  pointer-events: none;
}

/* Beschriftung für Quill-Felder: sitzt in der Box oben links, nicht auf der
   Rahmenlinie, wo der Formular-Wrapper (overflow-x-auto) sie abschneiden
   würde. Die Quill-Werkzeugleiste ist ein Popup über der Box
   ([data-pp-quill="1"] .pp-quill-toolbar, bottom:100%, z-index:9999) und
   kollidiert nicht damit. pointer-events:none, damit es keine Klicks oben
   links im Editor blockiert. */
.pp-vlabel {
  position: absolute;
  top: 6px;
  left: -7px;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  background: #fff;
  padding: 2px 3px;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-gray-500);
  white-space: nowrap;
  z-index: 20;
  pointer-events: none;
}

/* --- Gast-Kalender (wie beim Owner: auto-fit, kein CQ-Breakpoint) --- */

.pp-guest-grid {
  display: grid;
  gap: calc(var(--spacing) * 2);
  align-items: stretch;

  /* 2 Spalten nur, wenn wirklich Platz da ist, sonst 1 */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* Verhindert Overflow/Scrollbar durch Inhalte */
.pp-guest-left,
.pp-guest-right {
  min-width: 0;
  height: 100%;
}

/* Übersicht immer in voller Breite (unter beiden Spalten) */
.pp-guest-overview {
  grid-column: 1 / -1;
}

/* --- Kalender-Dekoration --- */

.booking-checkIn-div,
.booking-checkOut-div {
  position: absolute;
  width: 50%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
}

.booking-checkIn-div {
  right: 0;
  bottom: 0;
}

.booking-checkOut-div {
  left: 0;
  top: 0;
}

/* Tooltip (das Markup nutzt opacity-0 + transition über Tailwind).

   Die Preis-Aufschlüsselung (Saisonpreise, Rabatte, Gebühren - Inhalt aus
   price.js::updatePrice()) öffnet zweigleisig: per Hover auf Zeigergeräten,
   per Tippen über die Klasse .pp-price-open (siehe
   price.js::initPriceInfoToggle() und den Button in booking_price_box.html) -
   auf Touch-Geräten gibt es kein Hover. */
@media (hover: hover) and (pointer: fine) {
  #priceBox:hover #priceTooltip {
    opacity: 1;
  }
}

#priceBox.pp-price-open #priceTooltip {
  opacity: 1;
  pointer-events: auto;
}

/* Das Markup begrenzt auf max-w-[320px]; auf schmalen Viewports liefe der
   mittig ausgerichtete Tooltip damit seitlich aus dem Bild. */
#priceTooltip {
  max-width: min(320px, calc(100vw - 2rem));
}

/* Tipp-Hinweis für die Aufschlüsselung - nur dort sichtbar, wo es kein
   Hover gibt.

   Der Marker sitzt mittig im leeren pb-4-Streifen am unteren Rand der Box;
   rechts unten verdeckte er #priceTotal. Das eigentliche Tipp-Ziel ist die
   ganze Preisbox (siehe initPriceInfoToggle() in price.js), der Button
   bleibt als fokussierbares Bedienelement für Tastatur und Screenreader. */
.pp-price-info-toggle {
  display: none;
}

@media (hover: none), (pointer: coarse) {
  #priceBox {
    cursor: pointer;
  }

  .pp-price-info-toggle {
    position: absolute;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 18px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-gray-400);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
  }

  #priceBox.pp-price-open .pp-price-info-toggle {
    color: var(--color-header-button-500);
  }
}

.booking-date-editable {
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .booking-date-editable:hover {
    text-decoration: underline;
    color: #16a34a;
  }
}
.booking-date-active {
  cursor: pointer;
  color: #16a34a;
  font-weight: 600;
}

/* --- Tabellen (Objekte, Saisons, iCal, Buchungsliste) --- */

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

th,
td {
  border: 1px solid var(--color-gray-300);
  padding: 2px 4px;
}

th {
  background: var(--color-gray-200);
  text-align: left;
}

@media (hover: hover) and (pointer: fine) {
  table:not(.booking-table) tbody tr:hover {
    background: var(--color-gray-50);
  }
}

/* Symbole in Tabellen */
.delete-btn,
.calendar-btn,
.edit-btn,
.sync-btn,
.reorder-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 16px;
}

@media (hover: hover) and (pointer: fine) {
  .delete-btn:hover {
    color: var(--color-red-500);
  }
}

@media (hover: hover) and (pointer: fine) {
  .calendar-btn:hover {
    color: var(--color-blue-600);
  }
}

.sync-btn.syncing {
  display: inline-block;
  animation: pp-spin 0.8s linear infinite;
  cursor: default;
}

@keyframes pp-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(-360deg); }
}

/* Aktionsspalten (nur Symbole) */
.prop-actions-col {
  width: 36px;
  text-align: center;
}

/* Zusammengefasste Aktionsspalte: Abgleich, Kalender und Bearbeiten nebeneinander */
.prop-actions-col.prop-actions-merged {
  width: 64px;
  text-align: center;
  padding-left: 0;
  padding-right: 0;
}

.prop-actions-merged-inner {
  display: flex;
  gap: 4px;
  justify-content: center;
  align-items: center;
}

.prop-reorder-inner {
  display: flex;
  gap: 4px;
  justify-content: center;
  align-items: center;
  margin-top: 2px;
}

.prop-reorder-inner .reorder-btn {
  font-size: 12px;
}

/* --- iCal-Tabelle: die URL nimmt den Rest, die anderen Spalten bleiben sichtbar --- */

#icalTable {
  width: 100%;
  max-width: 100%;
  table-layout: fixed; /* nötig */
}

/* URL-Spalte: der Rest */
#icalTable col.ical-col-url { width: auto; }

/* Übrige: feste/Mindestbreiten (nicht 1px, sonst verschwinden sie) */
#icalTable col.ical-col-active { width: 40px; }  /* "Aktiv" + Button */
#icalTable col.ical-col-status { width: 70px; }  /* "ok:2" */
#icalTable col.ical-col-sync   { width: 100px; } /* "07.09.26-22:27" */
#icalTable col.ical-col-action { width: 24px; }  /* 🗑️ */

/* Nur die Kopfzellen tragen diese Klassen (sie stehen im Template); die
   Datenzeilen baut prop_ical_list.js::renderRow() ohne Klassen - die
   Spaltenbreiten kommen von den <col> oben und table-layout:fixed. */

/* Außer der URL: kein Umbruch */
#icalTable th.ical-col-active,
#icalTable th.ical-col-status,
#icalTable th.ical-col-sync,
#icalTable th.ical-col-action {
  white-space: nowrap;
}

/* URL darf schrumpfen */
#icalTable th.ical-col-url {
  min-width: 0;
  overflow: hidden;
}

/* --- FAQ: Frage und Antwort nebeneinander, schmal untereinander ---
   Zwei Quill-Editoren nebeneinander brauchen Platz; mit der halben Breite
   eines Telefons ist Schreiben darin nicht zumutbar - deshalb stapeln sie
   sich unterhalb der Schwelle.

   Mobile-first wie beim Kontakt-Präferenz-Block weiter unten: der
   gestapelte Zustand ist der Normalfall, das Nebeneinander schaltet die
   @container-Regel zu, wenn der Platz da ist. @container statt @media, weil
   die Breite dieses Kastens zählt, nicht die des Fensters - der Bereich
   sitzt in .pp-cq (properties.html). */

.qa-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 4px 8px;
  margin-bottom: 12px;
}

.qa-cell {
  flex: 0 0 100%;
  min-width: 0;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 4px;
  background: var(--color-white, #fff);
}

/* Beschriftung im gestapelten Zustand, innerhalb des Rahmens wie die
   Legende eines Feldes - so gehört sie eindeutig zu einem der beiden
   Felder. Quill hängt Werkzeugleiste und Editor an den Kasten an
   (appendChild), löscht ihn nicht - das ::before bleibt also stehen. */
.qa-cell::before {
  content: attr(data-label);
  display: block;
  padding: 3px 6px;
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
  border-radius: 3px 3px 0 0;
  background: var(--color-gray-50, #f9fafb);
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--color-gray-500, #6b7280);
}

/* Gestapelt zuerst und rechtsbündig: der Knopf gehört zur ganzen Zeile,
   nicht zu einem der beiden Felder, und steht darum darüber. */
.qa-del {
  order: -1;
  margin-left: auto;
  flex: 0 0 auto;
  padding: 4px 8px;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 4px;
  background: transparent;
  color: var(--color-gray-500, #6b7280);
  font-size: 12px;
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .qa-del:hover {
    color: var(--color-red-700, #b91c1c);
    border-color: var(--color-red-700, #b91c1c);
  }
}

@media (pointer: coarse) {
  .qa-del { min-width: 44px; min-height: 44px; }
}

/* Auf Touch trägt die Zeile nur den Anzeigeblock und den Papierkorb;
   die beiden Editor-Kästen legt prop_qa_list.js dort gar nicht erst an,
   getippt wird im Overlay (app_shared/row_editor.js). */
.qa-row-touch {
  flex-wrap: nowrap;
  align-items: center;
}

.qa-row-touch .qa-del {
  order: 0;
  margin-left: 6px;
  margin-top: 0;
  flex: 0 0 auto;
}

/* Auf einem breiten Touch-Gerät (Tablet) schaltete die Container-Abfrage
   weiter unten sonst die Spaltenköpfe ein - über Zeilen ohne Spalten. */
@media (pointer: coarse) {
  .qa-head {
    display: none;
  }
}

/* Die Spaltenköpfe stehen einmalig über den Spalten - das ergibt nur
   nebeneinander Sinn. Gestapelt beschriftet ::before jedes Feld selbst. */
.qa-head { display: none; }

.qa-head-col {
  flex: 1;
  min-width: 0;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-gray-500, #6b7280);
}

/* Platzhalter über der Löschen-Spalte, sonst sitzen die beiden Köpfe um
   die Knopfbreite verschoben über ihren Feldern. */
.qa-head-gap { flex: 0 0 42px; }

@container (min-width: 580px) {
  .qa-head {
    display: flex;
    gap: 8px;
    margin-bottom: 6px;
  }

  .qa-row { flex-wrap: nowrap; }

  .qa-cell { flex: 1 1 0; }
  .qa-cell::before { display: none; }

  .qa-del {
    order: 0;
    margin-left: 0;
    margin-top: 4px;
  }
}

/* --- Export-Bereich (Website): Handlung / Einstellungen / Ergebnis ---
   Drei durch feine Linien getrennte Bänder. Oben mittig die eine
   Haupthandlung, darunter ihre Einstellungen, darunter das Ergebnis.

   Mobile-first wie im FAQ-Block: die Einstellungen stehen von Haus aus
   untereinander, das Nebeneinander schaltet @container zu, wenn der Kasten
   breit genug ist. */

.pp-astro-action,
.pp-astro-files {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
}

/* Abo-Zustand in der Fußzeile, neben der Version. Eigener Punkt davor statt
   eines Trennzeichens im Text: der Text steht in neun Sprachen in den
   Sprachdateien, das Trennzeichen gehört nicht hinein. */
.pp-abo::before {
  content: " · ";
}

/* Der Satz unter dem inaktiven Export-Knopf. Ein title allein reicht nicht:
   auf dem Telefon gibt es keinen Mauszeiger, der ihn hervorholen könnte. */
.pp-astro-hinweis {
  margin: 8px 0 0;
  text-align: center;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--color-gray-500, #6b7280);
}

.pp-astro-opts {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 12px;
}

/* Knappe Beschriftung. "Sprachen" steht über seinen Feldern, "Vorlage:"
   daneben - deshalb hier nur die Schrift, die Anordnung machen die
   Eltern-Container. */
.pp-astro-label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-gray-500, #6b7280);
}

/* Drei Ebenen im Einstellungsband:

     Zeile 1  Sprachen - immer für sich, die Chips brechen intern um
     Zeile 2  die drei Auswahlfelder (.pp-astro-felder)
     Zeile 3  der Status - er steht neben den Feldern, solange beide zusammen
              passen, sonst darunter

   Der Umbruch zwischen diesen Ebenen entsteht allein aus den Flex-Basen. Wie
   die drei Auswahlfelder sich innen ordnen, entscheidet dagegen @container
   weiter unten. */

/* Basis 100% = immer eine eigene Zeile. Die Chips darin brechen selbst um. */
.pp-astro-langs {
  flex: 0 0 100%;
  min-width: 0;
}

.pp-astro-langs .pp-astro-label {
  margin-bottom: 4px;
}

/* Vorlage, Schrift, Farbe. Drei Stufen, nach Platz - mobile-first wie im
   FAQ-Block, die breiteren Zustände schaltet @container zu:

     schmal   Beschriftung über dem Feld, jedes Paar in einer eigenen Zeile
     mittel   Beschriftung über dem Feld, die drei Paare nebeneinander
     breit    Beschriftung neben dem Feld, die drei Paare nebeneinander

   Die Reihenfolge ist Absicht: die Beschriftung über ihr Feld zu legen
   kostet nur Höhe und hält die drei Felder nebeneinander - gestapelt wird
   erst, wenn auch das nicht mehr reicht. Auf der mittleren Stufe teilen sich
   die drei Felder den Platz zu gleichen Teilen (flex-basis 0); der halbe
   Zustand, in dem zwei Felder oben und eines darunter steht, entsteht so gar
   nicht erst.

   Die Schwellen messen den Kasten, nicht das Fenster - der Bereich sitzt in
   .pp-cq (properties.html), dessen Inhaltsbreite bei max-w-[800px] und
   Polsterung nie über 736px kommt. Davon gehen innen noch die 16px des
   grauen Feldes auf jeder Seite ab. 460px trägt die drei Felder ohne
   Beschriftung nebeneinander, 600px mit - gerechnet mit den längsten
   Beschriftungen aller neun Sprachen (griechisch "Γραμματοσειρά:"). */
.pp-astro-felder {
  flex: 1 1 100%;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 12px;
}

.pp-astro-field {
  flex: 1 1 100%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
}

/* Gibt es keine Schriften, nimmt prop_astro_export.js das Feld mit hidden aus
   dem Band. Das display:flex oben blendete es sonst wieder ein - das
   UA-Blatt verliert gegen jede Autoren-Regel. */
.pp-astro-field[hidden] {
  display: none;
}

.pp-astro-field .pp-astro-label {
  white-space: nowrap;
}

/* Unter seiner Beschriftung füllt das Auswahlfeld die Zeile: in natürlicher
   Breite sähe es wie ein Rest aus, und die größere Trefferfläche ist auf
   dem Telefon der Sinn der Übung. */
.pp-astro-field select {
  width: 100%;
}

@container (min-width: 460px) {
  .pp-astro-field {
    flex: 1 1 0;
  }
}

@container (min-width: 600px) {
  /* Hier brauchen die Felder nur ihre natürliche Breite, und der Status
     passt daneben. */
  .pp-astro-felder {
    flex: 1 1 auto;
  }

  .pp-astro-field {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    gap: 6px;
  }

  .pp-astro-field select {
    width: auto;
  }
}

/* iOS Safari zoomt beim Antippen in jedes Auswahlfeld unter 16px hinein - und
   kommt nicht wieder heraus. Dieselbe Regel wie bei den Gast-Feldern weiter
   unten; hier zählt sie doppelt, weil der Export-Knopf darüber nach dem
   Zoom aus dem Bild läuft. */
@media (pointer: coarse) {
  .pp-astro-field select {
    font-size: 16px;
  }
}

/* Der Status steht immer in einer eigenen Zeile unter den Optionen,
   linksbündig und um ein Fünftel der Breite eingerückt. Innen ein Raster:
   Spalte 1 das Symbol, Spalte 2 der Satz ("Status: fertig") und darunter
   der laufende Schritt - beide beginnen so an derselben Kante, egal wie
   breit das Symbol gerade ist. */
.pp-astro-status {
  flex: 0 0 100%;
  box-sizing: border-box;
  padding-left: 20%;
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr);
  column-gap: 8px;
  align-items: center;
  font-size: 0.875rem;
  color: var(--color-gray-600, #4b5563);
  text-align: left;
}

.pp-astro-status-icon {
  display: inline-block;
  width: 14px;
  height: 14px;
}

.pp-astro-schritt {
  grid-column: 2;
}

/* --- Übersetzungs-Korrekturen: Zeile anzeigen, im Overlay bearbeiten ---
   Auf Touch-Geräten zeigt prop_translate_overrides.js statt der drei
   Eingabefelder nur Text - iOS Safari zoomt sonst beim Fokussieren in jedes
   Feld unter 16px hinein und kommt nicht wieder heraus. Bearbeitet wird im
   Overlay darunter, mit Feldern in voller Größe. */

.trov-row-touch .trov-cell-edit {
  cursor: pointer;
}

/* Der Kopf muss demselben Aufbau folgen wie die Zeilen darunter - sonst
   stehen drei Spaltenüberschriften über einer einzigen Zelle. */
.trov-head-touch {
  display: none;
}

.trov-head-line2 {
  margin-top: 2px;
  font-weight: 400;
}

@media (pointer: coarse) {
  .trov-head-wide  { display: none; }
  .trov-head-touch { display: table-row; }
}

.trov-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.trov-src-text {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.trov-lang-badge {
  flex: 0 0 auto;
  padding: 0 6px;
  border-radius: 9999px;
  background: var(--color-gray-100, #f3f4f6);
  font-size: 0.7rem;
  color: var(--color-gray-500, #6b7280);
}

.trov-tgt-text {
  margin-top: 2px;
  color: var(--color-gray-600, #4b5563);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Raumbezeichnungen: Kopf und Körper sind zwei Tabellen ---
   Der Kopf steht fest, der Körper scrollt - dafür sind es zwei getrennte
   <table>-Elemente. table-layout:fixed plus gleiche Spaltenbreiten bringt
   die Überschriften über ihre Werte. */
.pp-cat-table {
  table-layout: fixed;
  width: 100%;
}

.pp-cat-table th:first-child,
.pp-cat-table td:first-child {
  width: auto;
}

/* Breit genug für das Auswahlfeld mit der Anzahl. */
.pp-cat-table th:last-child,
.pp-cat-table td:last-child {
  width: 88px;
}

/* Der Körper hat eine Bildlaufleiste, der Kopf nicht - die nutzbare Breite
   wäre damit unterschiedlich und die letzte Überschrift versetzt.
   scrollbar-gutter reserviert den Platz in beiden, auch wo gar nicht
   gescrollt wird. */
.pp-cat-head,
.pp-cat-body {
  scrollbar-gutter: stable;
}

.pp-cat-head {
  overflow-y: auto;
}

/* Die Liste ist 10,5 Zeilen hoch. Die halbe Zeile am unteren Rand ist der
   Hinweis, dass es weitergeht - eine glatt abschließende Liste sieht
   vollständig aus, auch wenn sie es nicht ist.

   Die Zeilenhöhe steht als eigener Wert da, weil sie sich aus dem
   Auswahlfeld ergibt (text-xs + py-1 im Feld, py-1 in der Zelle) und im
   Browser mit 32px gemessen wurde. Ändert sich eines davon, gehört hier
   der neue Wert hin - deploy/checks/raumliste_check.py misst nach. */
.pp-cat-body {
  --pp-cat-zeile: 32px;
  max-height: calc(var(--pp-cat-zeile) * 10.5);
  overflow-y: auto;
}

/* --- Sperrmodus im Owner-Kalender (bookings_page/sperrmodus.js) ---
   Zwei Karteireiter unter dem Kalender schalten um, was ein Klick im Raster
   bedeutet: Zeitraum wählen oder Tag sperren. Weil man das dem Raster nicht
   ansieht, färbt sich im Sperrmodus der ganze Rahmen ein.

   Die Farbe steht als Variable am Rahmen, damit das aktive Reiterblatt
   dieselbe annehmen kann: es hängt dann sichtbar am Kalenderblatt, und der
   inaktive Reiter liegt dahinter. */

/* Der Rahmen färbt nichts - er hält nur die Farbe bereit. Gefärbt wird das
   Blatt darin, und das aktive Reiterblatt nimmt dieselbe Farbe an. */
.pp-kalender-rahmen {
  --pp-rahmen-bg: #eaf3f3;  /* zartes Petrol: der gewöhnliche Buchungskalender */
  margin-bottom: 1rem;
}

/* Gelb statt Grau: Grau ist im Kalender schon die Hinterlegung belegter
   Innentage (BUSY_BG in calendar.js) und würde sich damit beißen. */
.pp-kalender-rahmen--sperren {
  --pp-rahmen-bg: #fefce8;
}

/* Nur wo es Reiter gibt (Owner-Kalender), trägt das Blatt eine Farbe; im
   Gast- und Embed-Kalender bleibt es weiß. */
.pp-kalender-rahmen--reiter .pp-kalender-blatt {
  background: var(--pp-rahmen-bg);
  padding: 8px 8px 6px;
  /* Unten eckig: dort hängen die Reiter an, ohne Zwischenraum. */
  border-radius: 8px 8px 0 0;
  transition: background-color 0.15s ease;
}

/* Kein Abstand nach oben: die Reiter sollen am Blatt hängen, nicht darunter
   schweben. */
.pp-sperrmodus {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0;
}

.pp-reiter {
  display: flex;
  gap: 4px;
  padding-left: 8px;
}

/* Kein oberer Rand und nur unten gerundet: das Blatt hängt unter dem
   Kalender, wie der Reiter an einer Karteikarte. */
.pp-reiter-blatt {
  font-size: 0.8125rem;
  line-height: 1;
  padding: 7px 12px;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-top: none;
  border-radius: 0 0 6px 6px;
  background: var(--color-gray-100, #f3f4f6);
  color: var(--color-gray-600, #4b5563);
  cursor: pointer;
}

.pp-reiter-blatt[aria-selected="true"] {
  background: var(--pp-rahmen-bg, #ffffff);
  color: var(--color-gray-900, #111827);
  font-weight: 600;
}

/* --- Karteireiter im Overlay (app_shared/reiter.js) ---
   Dieselben Blätter, andersherum angeschlagen: am Kalender hängen sie unter
   ihrer Karte, in einem Dialog sitzen sie oben auf ihr - deshalb hier der
   obere Rand statt des unteren und die Rundung nach oben.

   Ein Dialog mit Kontaktdaten und Mail-Historie untereinander würde länger
   als ein Telefon hoch ist, und ein zentriertes position:fixed-Overlay lässt
   sich nicht wegscrollen. Zwei Blätter halbieren die Höhe.

   Das -1px zieht das gewählte Blatt über die Trennlinie, damit es mit
   seiner Tafel verbunden aussieht. */
.pp-tafel-reiter {
  display: flex;
  gap: 4px;
  padding: 0 4px;
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
}

.pp-tafel-reiter .pp-reiter-blatt {
  border-top: 1px solid var(--color-gray-300, #d1d5db);
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  margin-bottom: -1px;
  padding: 4px 10px;
}

/* Etwas mehr Kante zum Tippen, aber nicht die vollen 44pt der Apple HIG:
   die Blätter sitzen oben im Dialog, wo kein anderes Ziel danebenliegt,
   das man verfehlen könnte - und jeder Millimeter hier fehlt dem Inhalt. */
@media (pointer: coarse) {
  .pp-tafel-reiter .pp-reiter-blatt {
    min-height: 32px;
    padding-left: 14px;
    padding-right: 14px;
  }
}

/* [hidden] trägt im UA-Blatt nur display:none - jede Autoren-Regel mit
   einer Klasse schlägt das. Hier steht sie deshalb ausdrücklich. */
.pp-tafel[hidden] {
  display: none;
}

/* --- Dialog der externen Buchung (showExtBookingModal) ---
   Drei Teile: Kopf mit Titel, Zeitraum und Reitern, darunter die Tafel, die
   scrollt, und eine Fußzeile, die stehen bleibt - Speichern und Abbrechen
   gelten für beide Blätter und bleiben immer erreichbar.

   Die Höhe ist begrenzt, weil ein zentriertes position:fixed-Overlay nicht
   mitscrollt. dvh statt vh, weil die Adressleiste mobiler Browser vh nicht
   mitwandern lässt. */
.pp-ext-dialog {
  display: flex;
  flex-direction: column;
  width: 90%;
  min-width: 300px;
  max-width: 640px;
  max-height: 90vh;
  max-height: 90dvh;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18);
}

.pp-ext-kopf {
  padding: 1rem 1.25rem 0;
}

/* Plattform, Zeitraum und das Kreuz in einer Zeile. Der Zeitraum steht auf
   flex:1 und schiebt das Kreuz an den Rand; beide Texte kürzen zur Not mit
   Auslassungspunkten, statt die Zeile zu sprengen. */
.pp-ext-kopfzeile {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.pp-ext-titel {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  font-size: 0.875rem;
  color: #1a1a1a;
}

.pp-ext-zeitraum {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.8rem;
  color: var(--color-gray-500, #6b7280);
}

/* Der Storno-Vermerk im Kopf des Dialogs: in der Fehlerfarbe, aber ohne
   Kasten - er meldet keinen Fehler, er erklärt einen Zustand. */
.pp-ext-storno {
  margin-bottom: 0.5rem;
  font-size: 0.75rem;
  color: var(--color-red-600, #dc2626);
}

.pp-ext-storno[hidden] {
  display: none;
}

/* Das Übernahme-Angebot: ein Streifen über den Feldern, in derselben
   Aufmerksamkeitsfarbe wie der Hinweis auf der Objektseite. */
.pp-ext-adopt {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
  padding: 0.375rem 0.5rem;
  border: 1px solid var(--color-amber-300, #fcd34d);
  border-radius: 6px;
  background: var(--color-amber-50, #fffbeb);
  font-size: 0.75rem;
  color: var(--color-amber-800, #92400e);
}

.pp-ext-adopt[hidden] {
  display: none;
}

#pp-ext-modal-adopt-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pp-ext-schliessen {
  flex: 0 0 auto;
  padding: 0 4px;
  border: none;
  background: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--color-gray-500, #6b7280);
  cursor: pointer;
}

/* min-height:0, sonst gibt ein Flex-Kind seine Inhaltshöhe nicht her und
   scrollt nie - es wäre wieder der ganze Dialog, der zu hoch ist. */
.pp-ext-koerper {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 1.25rem;
}

/* Trägt die drei Knöpfe - und ist der Bezugspunkt für die Meldung, die
   darüber schwebt. */
.pp-ext-fuss {
  position: relative;
  padding: 0.75rem 1.25rem 1rem;
  border-top: 1px solid var(--color-gray-100, #f3f4f6);
}

/* Die Meldung schwebt über der Fußzeile und nimmt keinen Platz ein - im
   Fluss würde die Zeile beim Melden höher, und die Knöpfe rückten unter dem
   Finger weg.

   Dasselbe Aussehen wie .pp-kopiert weiter unten, aber nicht dessen
   position:fixed: der Dialog liegt auf z-index 1000, ein Label an <body>
   mit z-index 70 läge dahinter. An der Fußzeile aufgehängt braucht es weder
   Koordinaten noch eine Stapelordnung.

   Leer bleibt sie durchsichtig statt display:none, damit das Auf- und
   Abblenden laufen kann; pointer-events:none hält sie aus dem Weg, solange
   sie unsichtbar über der Tafel liegt. */
.pp-ext-meldung {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  max-width: calc(100% - 2rem);
  margin-bottom: 0.25rem;
  padding: 3px 8px;
  border-radius: 6px;
  background: var(--color-gray-800, #1f2937);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  color: #fff;
  font-size: 0.75rem;
  line-height: 1.3;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease-out;
}

.pp-ext-meldung:not(:empty) {
  opacity: 1;
}

/* Misserfolg in der Fehlerfarbe - dort steht auch ein längerer Satz, und
   der soll nicht wie eine Bestätigung aussehen (wie .pp-kopiert--fehler). */
.pp-ext-meldung--fehler {
  background: var(--color-red-600, #dc2626);
}

@media (prefers-reduced-motion: reduce) {
  .pp-ext-meldung {
    transition: none;
  }
}

/* nowrap ist hier Absicht: die drei bleiben in einer Zeile, auch auf dem
   schmalsten Telefon. Was dort nicht mehr passt, ist die Beschriftung des
   ersten - nicht seine Zeile. */
.pp-ext-knoepfe {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 0.5rem;
}

.pp-ext-knopf {
  flex: 0 1 auto;
  min-width: 0;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 6px;
  background: #fff;
  font-size: 0.875rem;
  color: #374151;
  /* nowrap hält die Beschriftung einzeilig, ellipsis fängt den Fall ab,
     dass eine Sprache selbst in der Kurzfassung zu breit baut - lieber ein
     gekürztes Wort als eine Zeile, die aus dem Dialog läuft. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

/* Die Anfrage geht nach draußen, Abbrechen und Speichern schließen den
   Dialog - das auto hält die beiden Gruppen auseinander. */
.pp-ext-knopf--anfrage {
  margin-right: auto;
}

.pp-ext-knopf--speichern {
  border: none;
  background: #009FA6;
  color: #fff;
  font-weight: 600;
}

/* Lang- und Kurzfassung der Anfrage, umgeschaltet nach Viewport-Breite: der
   Dialog nimmt unterhalb von 480px ohnehin die ganze Breite.

   420px: darunter liegt jedes verbreitete Telefon außer den größten
   (iPhone Pro Max 430, Pixel XL 412 - die tragen die Langfassung noch). */
.pp-ext-kurz {
  display: none;
}

@media (max-width: 420px) {
  .pp-ext-lang {
    display: none;
  }

  .pp-ext-kurz {
    display: inline;
  }

  .pp-ext-knoepfe {
    gap: 0.375rem;
  }

  .pp-ext-knopf {
    padding: 0.25rem 0.375rem;
  }
}

/* Die schmalsten Geräte (320px) mit den längsten Sprachen - eine Stufe
   kleiner, damit "Bewertung", "Abbrechen" und "Speichern" ungekürzt
   nebeneinander stehen. */
@media (max-width: 360px) {
  .pp-ext-knopf {
    font-size: 0.8125rem;
  }
}

/* Auf dem Telefon zählt jeder Rand: schmalere Polsterung und die vollen
   90dvh, damit die Tafel selbst möglichst viel davon bekommt. */
@media (max-width: 480px) {
  .pp-ext-dialog {
    width: 100%;
    margin: 0 8px;
  }

  .pp-ext-kopf {
    padding: 1rem 1rem 0;
  }

  .pp-ext-koerper {
    padding: 0 1rem;
  }

  .pp-ext-fuss {
    padding: 0.75rem 1rem;
  }

}

/* --- Quill: Abstände und weiche Umbrüche ---
   Im Editor soll ungefähr zu sehen sein, wie der Text später steht - nicht
   genau. Die Werte hängen nicht an denen der exportierten Seite
   (astro/builder_templates, partials/css.part); beides lässt sich
   unabhängig ändern.

   Der Unterschied, um den es geht:

     Enter        neuer Absatz  -> line-height + margin, die größere Lücke
     Shift+Enter  <br> im Absatz -> nur line-height, die enge Zeile

   Dazu markiert app_shared/pp_quill.js jeden weichen Umbruch mit einem
   kleinen Zeichen, in einer eigenen Ebene über dem Text. */

/* Zwei Klassen im Selektor: quill.snow.css wird nach styles.css geladen
   (base.html) und setzt .ql-editor p { margin: 0 } - bei gleichem Gewicht
   gewänne das spätere Blatt. */
.ql-container .ql-editor p {
  line-height: 1.2;
  margin: 0 0 0.5em;
}

.pp-umbrueche {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  /* Über dem Text, aber unter der Werkzeugleiste. */
  z-index: 1;
}

.pp-umbruch {
  position: absolute;
  color: var(--color-gray-400, #9ca3af);
  font-size: 0.75em;
  line-height: 1;
  user-select: none;
}

/* Zeichenzähler unter einem Textfeld (Owner-Einstellungen: der
   Vorschautext der Startseite). Die Warnung ist eine Farbe, keine Sperre -
   länger ist erlaubt, Google schneidet dann nur ab. */
.pp-zaehler-zeile {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 4px;
}

.pp-zaehler {
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--color-gray-500, #6b7280);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pp-zaehler--warnung {
  color: var(--color-red-600, #dc2626);
  font-weight: 600;
}

/* --- Nachfrage über dem Sichern-Knopf (booking_shared/form.js) ---
   Der Owner darf den Mindestaufenthalt unterschreiten - gefragt wird beim
   Sichern, bei neuen wie bei geänderten Buchungen. Fest positioniert und
   über dem Knopf, damit kein overflow den Kasten abschneidet und die Frage
   dort steht, wo eben geklickt wurde. */

.pp-nachfrage-deckel {
  position: fixed;
  inset: 0;
  z-index: 79;
  background: transparent;
}

.pp-nachfrage {
  position: fixed;
  z-index: 80;
  max-width: min(22rem, calc(100vw - 16px));
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--color-gray-300, #d1d5db);
  background: #fff;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}

.pp-nachfrage-text {
  margin: 0 0 10px;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--color-gray-700, #374151);
}

.pp-nachfrage-knoepfe {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* Der Abbruch ist der ruhige von beiden - er soll sich nicht anbieten wie
   eine Bestätigung. */
.pp-btn.pp-btn--still {
  background: var(--color-gray-200, #e5e7eb);
  color: var(--color-gray-700, #374151);
}

/* Und der Gegenpol: was etwas wegwirft, sieht danach aus. Gedeckt, nicht
   knallrot - der Übersetzungs-Cache ist ein Cache, sein Verlust kostet eine
   Übersetzung und keine Daten. Kein Grund für einen Alarmknopf, aber auch
   keiner, ihn wie "Speichern" aussehen zu lassen. */
.pp-btn.pp-btn--gefahr {
  background: var(--color-red-50, #fef2f2);
  color: var(--color-red-600, #dc2626);
  border: 1px solid var(--color-red-300, #fca5a5);
}

.pp-btn.pp-btn--gefahr:hover:not(:disabled) {
  background: var(--color-red-100, #fee2e2);
}

/* Das Kreuz an einem einzelnen Eintrag der Cache-Liste. Es sitzt in einer
   dichten Liste und soll sie nicht zerreißen: sichtbar erst, wenn die Maus
   auf dem Eintrag steht - auf Tastatur und Touch dagegen immer, sonst gäbe
   es dort keinen Weg dorthin. */
.pp-tcache-eintrag .pp-tcache-weg {
  opacity: 0;
  transition: opacity 0.12s;
}

.pp-tcache-eintrag:hover .pp-tcache-weg,
.pp-tcache-eintrag .pp-tcache-weg:focus-visible {
  opacity: 1;
}

@media (hover: none) {
  .pp-tcache-eintrag .pp-tcache-weg { opacity: 1; }
}

/* --- Zeilen-Editor (app_shared/row_editor.js) ---
   Gemeinsames Overlay für alle dichten Tabellen-Editoren auf Touch-Geräten. */

.pp-roweditor {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
}

/* Tailwinds .hidden steht in @layer utilities, diese Datei ist ungelayert -
   und ungelayertes CSS schlägt jede Layer-Regel, unabhängig von der
   Spezifität. Das display oben würde .hidden also aushebeln. Gleiches
   Muster wie bei .pp-display-contents.hidden weiter oben. */
.pp-roweditor.hidden {
  display: none;
}

.pp-roweditor-box {
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto;
  padding: 16px;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
  border-radius: 12px 12px 0 0;
  background: var(--color-white, #fff);
}

.pp-roweditor-title {
  margin-bottom: 12px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-gray-800, #1f2937);
}

.pp-roweditor-label {
  display: block;
  margin-bottom: 2px;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-gray-500, #6b7280);
}

/* 16px ist hier keine Geschmacksfrage: darunter zoomt iOS beim Fokussieren. */
.pp-roweditor-field {
  width: 100%;
  margin-bottom: 12px;
  padding: 8px 10px;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 6px;
  background: var(--color-white, #fff);
  font-size: 16px;
  line-height: 1.4;
}

/* Formatierter Text im Overlay (Typ "richtext"). Quill hängt seine
   Werkzeugleiste und den Editor in diesen Kasten; der Rahmen gehört
   deshalb hierher und nicht an ein Eingabefeld, das es nicht gibt.

   Die Höhe ist begrenzt und scrollt innen: eine lange Antwort würde den
   Kasten sonst über den Schirm hinaus wachsen lassen, und die beiden
   Knöpfe darunter wären nicht mehr zu erreichen. 16px auch hier - Quill
   erbt die Schriftgröße, und darunter zoomt iOS beim Antippen hinein. */
.pp-roweditor-rich {
  margin-bottom: 12px;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 6px;
  background: var(--color-white, #fff);
  font-size: 16px;
}

/* 16px muss am .ql-container und am .ql-editor stehen, nicht am Kasten
   darum: quill.snow.css (CDN, base.html) setzt .ql-container auf 13px, und
   das schlägt eine geerbte Größe. Unter 16px zoomt iOS Safari beim Antippen
   in den Editor hinein und kommt nicht wieder heraus; der Editor ist ein
   contenteditable und zählt dafür wie ein Eingabefeld.

   Zwei Klassen im Selektor, damit es die Regel aus dem Quill-Blatt sicher
   schlägt, unabhängig von der Ladereihenfolge der beiden Dateien. */
.pp-roweditor-rich .ql-container,
.pp-roweditor-rich .ql-editor {
  font-size: 16px;
  line-height: 1.45;
}

.pp-roweditor-rich .ql-editor {
  min-height: 6rem;
  max-height: 40vh;
  max-height: 40dvh;
  overflow-y: auto;
}

/* Ankreuzfeld: nicht auf volle Breite ziehen wie die Textfelder. */
.pp-roweditor-check {
  width: 20px;
  height: 20px;
  margin-bottom: 12px;
}

.pp-roweditor-field[readonly] {
  background: var(--color-gray-50, #f9fafb);
  color: var(--color-gray-500, #6b7280);
}

.pp-roweditor-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

@media (min-width: 640px) {
  .pp-roweditor { align-items: center; }
  .pp-roweditor-box { border-radius: 12px; }
}

/* Export-URL des iCal-Bereichs. Kein Umschaltpunkt, sondern Umbrechen nach
   Platz: die Beschriftung steht immer für sich, URL und Knopf teilen sich
   eine Zeile, solange die URL ihre Mindestbreite bekommt - sonst rutscht der
   Knopf darunter. Gilt am Rechner im schmalen Fenster genauso wie auf Touch. */
.pp-ical-export-label {
  flex: 0 0 100%;
}

.pp-ical-export-url {
  flex: 1 1 240px;
  min-width: 0;
}

.pp-ical-export-btn {
  flex: 0 0 auto;
  margin-left: auto;
}

/* Das Feld ist zwar readonly, aber fokussierbar - und damit zoomt iOS Safari
   beim Antippen hinein und kommt nicht wieder heraus. Danach liegt auch jedes
   Overlay im vergrößerten Viewport und ist nicht mehr ganz zu sehen.
   16px verhindert den Zoom an der Wurzel. */
@media (pointer: coarse) {
  #ical-export-url {
    font-size: 16px;
  }

  /* Die <col>-Breiten gelten spaltenweise, unabhängig davon, welche Zellen
     ausgeblendet sind. Auf Touch ist die Aufteilung eine andere:

       Spalte 1-3  von der Anzeigezelle zusammengefasst (colspan 3)
       Spalte 4    Aktiv
       Spalte 5    Aktion

     Deshalb werden hier die Breiten umgehängt, statt sie freizugeben:
     table-layout bleibt fixed. Mit "auto" bestimmt eine lange, nicht
     umbrechbare iCal-URL die Tabellenbreite und schiebt alles nach rechts aus
     dem Kasten heraus - fixed hält sie dagegen in der Zelle und kürzt sie
     mit Auslassungspunkten. */
  #icalTable col.ical-col-active,
  #icalTable col.ical-col-status {
    width: 0;              /* gehen in der Anzeigezelle auf */
  }

  #icalTable col.ical-col-sync {
    width: 40px;           /* hier sitzt auf Touch die Aktiv-Spalte */
  }
}

/* Kurzes Label über einem Element (app_shared/kurzmeldung.js): das
   "Kopiert!" der iCal-Liste, die Meldungen des Sperrmodus und der
   Owner-Verwaltung. position: fixed, weil die Ziele teils in seitlich
   scrollenden Tabellen stehen; die Koordinaten setzt das Skript. */
.pp-kopiert {
  position: fixed;
  z-index: 70;
  padding: 3px 8px;
  border-radius: 6px;
  background: var(--color-gray-800, #1f2937);
  color: #fff;
  font-size: 0.75rem;
  line-height: 1.3;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  animation: pp-kopiert-ein 1.6s ease-out forwards;
}

/* Misserfolg in der Fehlerfarbe - dort steht auch ein längerer Satz, und
   der soll nicht wie eine Bestätigung aussehen. */
.pp-kopiert--fehler {
  background: var(--color-red-600, #dc2626);
}

@keyframes pp-kopiert-ein {
  0%        { opacity: 0; transform: translateY(4px); }
  12%, 75%  { opacity: 1; transform: none; }
  100%      { opacity: 0; transform: translateY(-2px); }
}

/* Wer Bewegung abbestellt hat, bekommt das Label ohne Ein- und Ausblenden -
   es verschwindet trotzdem, das Skript räumt es weg. */
@media (prefers-reduced-motion: reduce) {
  .pp-kopiert { animation: none; }
}

.pp-copied-flash {
  animation: pp-copied 1.2s ease-out;
}

@keyframes pp-copied {
  0%, 60% { background-color: var(--color-green-100, #d1fae5); }
  100%    { background-color: transparent; }
}

/* --- Tabellenzeilen: bearbeiten am Rechner, ansehen auf Touch ---
   Die Zellen mit den Eingabefeldern bleiben im DOM - collectData() und das
   Dirty-Tracking der Listen lesen daraus. Auf Touch werden sie nur
   ausgeblendet und durch eine Anzeigezelle ersetzt; das Overlay schreibt in
   dieselben Felder zurück. */

.pp-row-view {
  display: none;
}

@media (pointer: coarse) {
  .pp-row-view {
    display: table-cell;
    cursor: pointer;
  }

  .pp-row-edit {
    display: none;
  }

  /* Auf Touch sichert jede Zeile beim Schließen ihres Overlays für sich,
     und das Löschen ruft die API direkt - ein globaler Speichern-Knopf
     hätte nichts zu tun. */
  #season-save-btn,
  #rebate-save-btn,
  #fee-save-btn,
  #trov-save-btn,
  #ical-save-btn {
    display: none;
  }
}

/* Die Block-Variante für Listen, die keine Tabelle sind (FAQ). Sichtbar
   ist sie nur auf Touch - am Zeigergerät wird in der Liste selbst getippt. */
.pp-row-view-block {
  display: none;
}

@media (pointer: coarse) {
  .pp-row-view-block {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    cursor: pointer;
  }
}

.pp-row-view-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.pp-row-view-main {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pp-row-view-side {
  flex: 0 0 auto;
  color: var(--color-gray-500, #6b7280);
}

/* Im Tabellenkopf tragen die Bausteine dieselben Klassen, damit Kopf und
   Zeile bündig stehen - dort aber ohne die Hervorhebung der Datenzeile. */
thead .pp-row-view-main {
  font-weight: inherit;
}

/* .season-table th zentriert - für die zweizeilige Touch-Überschrift ist
   das falsch: die Bezeichnungen gehören nach links, nur die Nebenangabe
   (Status / % / Betrag) steht rechts, und die schiebt der Flex-Umbruch
   ohnehin dorthin.

   Zwei Typen + eine Klasse (0,1,2), damit die Regel .season-table th (0,1,1)
   sicher geschlagen wird; .season-table th steht weiter unten in dieser
   Datei. */
thead th.pp-row-view {
  text-align: left;
}

.pp-row-view-sub {
  margin-top: 2px;
  color: var(--color-gray-600, #4b5563);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Vorschau der Vorlage im Export-Bereich ---
   Klappt auf, zugeklappt bei jedem Aufruf. Anmutung und Mechanik wie die
   Übersetzungs-Korrekturen weiter unten auf derselben Seite: Linie,
   Beschriftung, Knopf mit Dreieck. Das Auf- und Zuklappen macht AppToggle
   (static/js/app_shared/toggle.js) mit remember:false.

   Der Rahmen darin hat erst beim Aufklappen eine Breite, deshalb misst
   prop_astro_export.js auf das pp:toggle-Ereignis hin nach. */

/* Trägt die Lupe. Sie sitzt über dem Rahmen, nicht darin: im Rahmen läuft
   die Musterseite, und die gehört der Vorlage. */
.pp-astro-preview-huelle {
  position: relative;
}

.pp-astro-lupe-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-gray-200, #e5e7eb);
  border-radius: 6px;
  /* Nicht durchsichtig: darunter liegt die Musterseite in wechselnden
     Farbwelten, auf einer davon wäre das Symbol sonst nicht zu sehen. */
  background: rgba(255, 255, 255, 0.92);
  color: var(--color-gray-600, #4b5563);
  cursor: pointer;
  padding: 0;
}

.pp-astro-lupe-btn:hover {
  background: #fff;
  color: var(--color-gray-900, #111827);
}

.pp-astro-lupe-btn svg {
  width: 18px;
  height: 18px;
  fill: currentColor;
}

/* Die große Fassung: mittig auf der Seite, so groß wie der Schirm hergibt.
   margin:auto zentriert ein modales <dialog> - der Rücken kommt vom
   ::backdrop. */
.pp-astro-lupe {
  /* Nicht breiter als nötig, auch auf einem breiten Schirm: die Vorschau
     rendert in fester Breite (PP_VORSCHAU_BREITE = 1100px), alles darüber
     wäre leere Fläche neben der Seite.

     1100 Vorschau + 2x16 Polsterung + 16 für die senkrechte Bildlaufleiste.
     Ist das Fenster schmaler, greift 96vw und die Vorschau wird verkleinert. */
  width: min(96vw, 1148px);
  height: 94vh;
  max-width: none;
  max-height: none;
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: 10px;
  overflow: hidden;
}

/* display nur im offenen Zustand setzen. Die Vorgabe des Browsers ist
   "dialog:not([open]) { display: none }" - ein unbedingtes display:flex hier
   überschriebe sie, und der Dialog stünde immer offen auf der Seite. */
.pp-astro-lupe[open] {
  display: flex;
  flex-direction: column;
}

.pp-astro-lupe::backdrop {
  background: rgba(17, 24, 39, 0.55);
}

.pp-astro-lupe-kopf {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
  font-weight: 600;
}

/* Den ganzen Rest der Höhe, statt eines festen Maßes: so nutzt die Vorschau
   das Fenster aus, und der Kopf darüber bleibt sichtbar. */
.pp-astro-preview--gross {
  flex: 1 1 auto;
  height: auto;
  min-height: 0;
  border: 0;
  border-radius: 0;
  padding: 16px;
}

/* Mittig, wenn Platz übrig ist. Bleibt die Bühne schmaler als ihr Kasten -
   etwa weil die Vorschau nicht weiter vergrößert wird -, stünde sie sonst
   links und rechts daneben wäre Luft. */
.pp-astro-preview-buehne {
  margin-left: auto;
  margin-right: auto;
}

/* Die Export-Vorschau.

   Der Rahmen läuft in echter Bildschirmbreite (PP_VORSCHAU_BREITE in
   prop_astro_export.js) und wird als Ganzes verkleinert. zoom oder transform
   im Dokument änderten die Breite nicht, die Medienabfragen messen - die
   Vorlage zeigte dann ihre Handy-Anordnung.

   Höhe und Verkleinerung setzt das Skript; hier steht nur, was fest ist. */
.pp-astro-preview {
  border: 1px solid var(--color-gray-200, #e5e7eb);
  border-radius: 8px;
  background: var(--color-white, #fff);
  height: 320px;
  /* Senkrecht blättern, waagerecht nie: die Bühne ist genau so breit wie
     der Kasten. */
  overflow-y: auto;
  overflow-x: hidden;
}

/* Trägt die verkleinerte Größe. Der Rahmen selbst behält seine echten
   Maße - transform ändert die Darstellung, nicht das Layout; ohne diese
   Bühne bliebe darunter ein leerer Bereich in Originalhöhe. */
.pp-astro-preview-buehne {
  position: relative;
  overflow: hidden;
}

.pp-astro-preview iframe {
  display: block;
  border: 0;
  transform-origin: 0 0;
}

@media (pointer: coarse) {
  .pp-astro-preview { height: 220px; }
}

/* --- Objekttabelle: festes Layout, Auslassungspunkte, Spaltenbreiten --- */

.props-table {
  table-layout: fixed;
  max-width: 100%;
}

.props-table th,
.props-table td {
  vertical-align: top;
}

.prop-cell-main {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.prop-cell-sub {
  font-size: 12px;
  color: var(--color-gray-500);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.props-table th.props-th-center {
  text-align: center;
  padding-left: 0;
  padding-right: 0;
}

.props-table td.props-td-left {
  text-align: left;
  overflow: hidden; /* nötig, damit die Auslassungspunkte darin greifen */
}

.props-table td.props-td-center {
  text-align: center;
  white-space: nowrap;
  padding-left: 0;
  padding-right: 0;
}

/* Breiten: Objekttabelle (6 Spalten) */
.props-table th:nth-child(1),
.props-table td:nth-child(1) {
  width: 55%;
}

.props-table th:nth-child(2),
.props-table td:nth-child(2) {
  width: 25%;
}

.props-table th:nth-child(3),
.props-table td:nth-child(3),
.props-table th:nth-child(4),
.props-table td:nth-child(4) {
  width: 56px;
  white-space: nowrap;
}

.props-table th:nth-child(6),
.props-table td:nth-child(6) {
  width: 30px;
  text-align: center;
}

/* Dezente Hervorhebung beim Überfahren (nicht in der Buchungstabelle) */
@media (hover: hover) and (pointer: fine) {
  .props-table:not(.booking-table) tbody tr:hover td {
    background-color: #f7faff;
  }
}

/* Einzug der Kind-Objekte (Spalte 1+2) */
.props-table .prop-row-child td:nth-child(-n + 2) :is(.prop-cell-main, .prop-cell-sub) {
  padding-left: 20px;
}

/* --- Belegungsübersicht (Objektübersicht): endloses Belegungsband --- */

.occ-timeline {
  display: flex;
  align-items: flex-start;
  border: 1px solid var(--color-gray-300);
  border-radius: 6px;
  background: #fff;
  overflow: hidden;
}

.occ-names {
  flex: 0 0 160px;
  width: 160px;
  border-right: 1px solid var(--color-gray-300);
}

.occ-names-spacer {
  border-bottom: 1px solid var(--color-gray-300);
  background: var(--color-gray-200);
}

.occ-name-row {
  height: 22px;
  display: flex;
  align-items: center;
  padding: 0 8px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-bottom: 1px solid var(--color-gray-100);
  box-sizing: border-box;
}

.occ-name-row-child {
  padding-left: 20px;
  font-weight: 400;
}

.occ-scroll {
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
}

.occ-header {
  position: relative;
}

/* Die Monatszeile ist eine Reihe von Blöcken - je Monat einer, so breit wie
   der Monat Tage hat. So hat jede Beschriftung einen umschließenden Block,
   an dessen Rand sie per position:sticky hängen bleibt: sie klebt am linken
   Rand des Sichtfensters, bis der Block nach links herausläuft und sie
   mitnimmt - genau dann, wenn der nächste Monat nachrückt. */
.occ-header-months {
  display: flex;
  height: 18px;
  background: var(--color-gray-200);
  border-bottom: 1px solid var(--color-gray-300);
}

.occ-month {
  flex: 0 0 auto;
  /* Kein overflow:hidden - das machte den Block zum Scroll-Container der
     Beschriftung und schaltete das Kleben aus. Begrenzt wird die
     Beschriftung ohnehin durch ihren Block. */
  box-sizing: border-box;
  border-right: 1px solid var(--color-gray-300);
}

.occ-month:last-child {
  border-right: 0;
}

.occ-month-label {
  position: sticky;
  left: 0;
  display: inline-block;
  max-width: 100%;
  line-height: 18px;
  font-size: 11px;
  font-weight: 600;
  color: var(--color-gray-700, #374151);
  padding-left: 4px;
  padding-right: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.occ-header-days {
  display: flex;
  height: 22px;
  border-bottom: 1px solid var(--color-gray-300);
}

.occ-day-cell {
  flex: 0 0 auto;
  height: 22px;
  line-height: 22px;
  text-align: center;
  font-size: 10px;
  color: var(--color-gray-500);
  border-right: 1px solid var(--color-gray-100);
  box-sizing: border-box;
}

.occ-day-weekend {
  background: var(--color-gray-50);
}

.occ-day-today {
  background: #dbeafe;
  font-weight: 700;
  color: var(--color-blue-600);
}

.occ-rows {
  position: relative;
}

.occ-row {
  position: absolute;
  left: 0;
  width: 100%;
  border-bottom: 1px solid var(--color-gray-100);
  box-sizing: border-box;
}

.occ-row-alt {
  background: var(--color-gray-50);
}

.occ-band {
  position: absolute;
  top: 3px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: pointer;
  box-sizing: border-box;
  padding: 0 4px;
}

@media (hover: hover) and (pointer: fine) {
  .occ-band:hover {
    filter: brightness(1.08);
  }
}

.occ-band-implied {
  cursor: default;
}

/* Sperrtage als Verlängerung des Bandes: gestreift in der Farbe des Bandes.
   Die Streifen kommen als background-image aus dem Skript (dort steht die
   Farbe), hier nur, was für alle gilt. Nicht anklickbar - es ist kein
   eigener Vorgang, sondern der Rand einer Buchung. */
.occ-band-sperr {
  cursor: default;
  padding: 0;
}

@media (hover: hover) and (pointer: fine) {
  .occ-band-sperr:hover {
    filter: none;
  }
}

@media (hover: hover) and (pointer: fine) {
  .occ-band-implied:hover {
    filter: none;
  }
}

.occ-band-label {
  min-width: 0;
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}

.occ-today-marker {
  position: absolute;
  top: 0;
  width: 2px;
  background: var(--color-red-500, #ef4444);
  pointer-events: none;
  z-index: 1;
}

.occ-empty {
  padding: 1rem;
  font-size: 13px;
  color: var(--color-gray-500);
}

/* --- Saison-Tabelle (auf den allgemeinen Tabellen-Styles aufbauend) --- */

.season-table th,
.season-table td {
  padding: 4px 10px;
  font-size: 12px;
}

.season-row-empty td {
  background: var(--color-white);
}

@media (hover: hover) and (pointer: fine) {
  .season-row-empty:hover td {
    background: var(--color-white) !important;
    cursor: default;
  }
}

/* --- Saison- und Rabatt-Tabelle: kompakt, Breite zuerst ---
   Das JS setzt dafür die Zellklassen price-cell, nights-cell, status-cell
   und action-cell. */

.season-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

/* Spaltenbreiten */
.season-table col.season-col-range-a {
  width: 27%;
}

.season-table col.season-col-range-b {
  width: 27%;
}

.season-table col.season-col-price {
  width: 26%;
}

.season-table col.season-col-nights {
  width: 50px;
}

.season-table col.season-col-status {
  width: 50px;
}

.season-table col.season-col-action {
  width: 20px;
}

.season-table th,
.season-table td {
  padding: 0;
  font-size: 12px;
  vertical-align: middle;
}

.season-table th {
  text-align: center;
  white-space: nowrap;
}

.season-table td > input,
.season-table td > select,
.season-table td > button {
  width: 100%;
  height: 100%;
  margin: 0;
  box-sizing: border-box;
}

.season-table td > input {
  border: 0;
  border-radius: 0;
  padding: 0 2px;
  min-height: 24px;
  background: #fff;
  font-size: 12px;
  line-height: 1.2;
}

.season-table td > input:focus {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--color-gray-400);
}

.season-table td > input[type="number"] {
  text-align: right;
}

/* Zeitraum */
.season-table td[colspan="2"] input {
  text-align: left;
}

/* Preis */
.season-table .price-cell {
  position: relative;
}

.season-table .price-cell::before {
  content: "€";
  position: absolute;
  left: 3px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--color-gray-500);
  pointer-events: none;
}

.season-table .price-cell input {
  padding-left: 14px;
  padding-right: 2px;
  text-align: left;
}

.date-hint-note {
  display: block;
  font-size: 10px;
  font-weight: normal;
  color: #9ca3af;
}


/* Saison/Rabatt-Raster - Container Query statt Media Query */
.season-rebate-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
@container (min-width: 640px) {
  .season-rebate-grid {
    grid-template-columns: 5fr 4fr;
  }
}

/* Zeitraum-Eingabe mit Löschknopf */
.range-input-wrap {
  display: flex;
  align-items: center;
  gap: 2px;
}
.range-date-input {
  flex: 1;
  min-width: 0;
}
.range-clear-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: #d1d5db;
  font-size: 13px;
  padding: 0 2px;
  line-height: 1;
}
@media (hover: hover) and (pointer: fine) {
  .range-clear-btn:hover {
    color: #6b7280;
  }
}

/* Nächte + Status */
.season-table .nights-cell,
.season-table .status-cell {
  white-space: nowrap;
  text-align: center;
}

.season-table .nights-cell input {
  text-align: center;
}

/* Aktion */
.season-table .action-cell,
.season-table th:last-child,
.season-table td:last-child {
  width: 20px;
  min-width: 20px;
  max-width: 20px;
  text-align: center;
  padding: 0;
}

.season-table .season-delete-btn,
.season-table .icon-btn {
  width: 20px;
  min-width: 20px;
  max-width: 20px;
  height: 20px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .season-table .season-delete-btn:hover {
    color: var(--color-red-500);
  }
}

.season-row-empty td {
  background: var(--color-white);
}

@media (hover: hover) and (pointer: fine) {
  .season-row-empty:hover td {
    background: var(--color-white) !important;
    cursor: default;
  }
}

/* --- Ausgewählte Zeile --- */

.props-table tr.prop-row-selected td,
.props-table tr.selected td,
.season-table tr.selected td {
  background-color: #e6f0ff !important;
}

/* --- Spaltenbreiten der Zusatzkosten-Tabelle --- */

.season-table col.fee-col-label  { width: 28%; }
.season-table col.fee-col-type   { width: 24%; }
.season-table col.fee-col-amount { width: 60px; }

/* Detailzeile der Zusatzkosten */
.fee-row-detail > td {
  background: #f3f4f6;
  padding: 3px 8px !important;
  border-top: 0;
}

@media (hover: hover) and (pointer: fine) {
  .fee-row-detail:hover > td {
    background: #f3f4f6 !important;
    cursor: default;
  }
}

.fee-detail-inner {
  display: flex;
  gap: 16px;
  align-items: center;
  font-size: 11px;
  color: #374151;
}

.fee-detail-item {
  display: flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

.fee-detail-item input[type="number"] {
  width: 52px;
  border: 1px solid #d1d5db;
  border-radius: 3px;
  padding: 1px 3px;
  font-size: 11px;
  background: white;
  text-align: right;
}

.fee-detail-item input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}

/* --- Registrierungsseite: die zwei Seiten derselben Sache ---
   Gast und Vermieter benutzen dasselbe Ergebnis von zwei Seiten aus;
   abgesetzt liest man die beiden Sätze als das Paar, das sie sind. */

.pp-zwei-seiten {
  display: grid;
  gap: 8px;
  max-width: 600px;
  margin: 0 auto 18px;
}

@media (min-width: 640px) {
  .pp-zwei-seiten { grid-template-columns: 1fr 1fr; gap: 12px; }
}

.pp-zwei-seiten p {
  margin: 0;
  padding: 10px 12px;
  border-left: 3px solid var(--color-header-button-500);
  background: var(--color-gray-50, #f9fafb);
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--color-gray-700, #374151);
}

/* --- Anmeldeseite: Kasten mit den Funktionen (login.html) ---
   Eigene Klassen statt Tailwind-Utilities: app.css ist vorkompiliert, eine
   neue Kombination wäre dort nicht enthalten. */

.pp-login-titel {
  margin: 0 0 10px;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-gray-800);
}

.pp-login-liste {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 7px;
}

.pp-login-liste li {
  display: grid;
  /* Der Gedankenstrich sitzt in einer eigenen Spalte: bei mehrzeiligen
     Punkten bleibt der Text dadurch ausgerichtet und rutscht nicht unter
     den Strich. */
  grid-template-columns: 14px 1fr;
  gap: 8px;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--color-gray-700, #374151);
}

.pp-login-liste li::before {
  content: "–";
  color: var(--color-header-button-500);
}

/* Der Fuß trägt die Trennlinie, nicht die beiden Absätze darin: bei zwei
   Blöcken nebeneinander stünden sonst zwei kurze Linien statt einer. */
.pp-login-fuss {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--color-gray-200, #e5e7eb);
  display: grid;
  gap: 14px;
}

@media (min-width: 640px) {
  .pp-login-fuss {
    grid-template-columns: 1fr 1fr;
    gap: 24px;
  }

  /* Nebeneinander braucht es eine Kante zwischen den beiden - untereinander
     nicht, dort trennt der Abstand. */
  .pp-login-fuss > :nth-child(2) {
    padding-left: 24px;
    border-left: 1px solid var(--color-gray-200, #e5e7eb);
  }
}

.pp-login-cta {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--color-gray-700, #374151);
}

/* Link und Knopf sehen gleich aus: beide führen zum selben Angebot, nur
   einmal auf eine andere Seite und einmal in das Overlay. */
.pp-login-cta a,
.pp-login-link {
  color: var(--color-header-button-500);
  font-weight: 600;
  white-space: nowrap;
}

/* Ein <button> bringt eigene Schrift, Polsterung und Rahmen mit - alles weg,
   damit er im Fließtext sitzt wie der Link daneben. */
.pp-login-link {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .pp-login-cta a:hover,
  .pp-login-link:hover { text-decoration: underline; }
}

/* --- Overlay mit den Schritten (login.html) --- */

.pp-schritte {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  background: rgba(0, 0, 0, 0.5);
}

/* Gleiches Muster wie bei .pp-roweditor.hidden weiter unten: Tailwinds
   .hidden steht in @layer utilities, diese Datei ist ungelayert - und
   ungelayertes CSS schlägt jede Layer-Regel. Ohne diese Zeile stünde das
   Overlay dauerhaft über der Anmeldeseite. */
.pp-schritte.hidden {
  display: none;
}

.pp-schritte-box {
  width: 100%;
  max-width: 560px;
  /* Lange Listen scrollen im Kasten, nicht auf der Seite dahinter. */
  max-height: calc(100vh - 24px);
  max-height: calc(100dvh - 24px);
  overflow-y: auto;
  padding: 18px 20px;
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}

.pp-schritte-kopf {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.pp-schritte-titel {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--color-gray-800);
}

.pp-schritte-zu {
  flex: 0 0 auto;
  margin: -4px -6px 0 0;
  padding: 0 6px;
  border: 0;
  background: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-gray-500, #6b7280);
  cursor: pointer;
}

.pp-schritte-unterzeile {
  margin: 4px 0 0;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--color-gray-500, #6b7280);
}

.pp-schritte-liste {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: schritt;
  display: grid;
  gap: 2px;
}

/* Die Nummern kommen aus einem Zähler, nicht aus der Zahl im Text: so
   stimmen sie auch mit einem Schritt mehr oder weniger - und die
   Zwischenbemerkung wird nicht mitgezählt, weil sie kein .pp-schritt ist. */
.pp-schritt {
  counter-increment: schritt;
  display: grid;
  grid-template-columns: 24px 1fr;
  column-gap: 10px;
  align-items: start;
  border-top: 1px solid var(--color-gray-100, #f3f4f6);
}

.pp-schritte-liste > .pp-schritt:first-child {
  border-top: 0;
}

.pp-schritt::before {
  content: counter(schritt);
  width: 24px;
  height: 24px;
  /* Auf der Höhe der ersten Textzeile des Titels, nicht der ganzen Zeile:
     ein zweizeiliger Titel würde die Zahl sonst mittig danebenstellen. */
  margin-top: 9px;
  border-radius: 999px;
  background: var(--color-header-button-500);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Der Titel ist ein Knopf: er klappt die Erklärung auf. Ein <button> bringt
   eigene Schrift, Polsterung und Rahmen mit - alles weg. */
.pp-schritt-kopf {
  grid-column: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  margin: 0;
  padding: 8px 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.875rem;
  line-height: 1.45;
  text-align: left;
  color: var(--color-gray-700, #374151);
  cursor: pointer;
}

/* Mehrere Absätze: der Abstand kommt aus dem Zwischenraum, nicht aus
   Rändern - sonst schöbe der letzte Absatz die Polsterung auf. */
.pp-schritt-text {
  grid-column: 2;
  display: grid;
  gap: 8px;
  padding: 0 24px 10px 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--color-gray-500, #6b7280);
}

/* Nötig, weil display hier gesetzt wird: Tailwinds .hidden steht in
   @layer utilities und verlöre sonst gegen diese ungelayerte Regel - die
   Erklärungen stünden dann alle offen. Siehe .pp-schritte.hidden. */
.pp-schritt-text.hidden {
  display: none;
}

.pp-schritt-text p {
  margin: 0;
}

/* Die Zwischenbemerkung ist kein Schritt: keine Nummer, volle Breite. */
.pp-schritt-pause {
  margin: 10px 0;
  padding: 8px 12px;
  border-left: 3px solid var(--color-header-button-500);
  background: var(--color-gray-50, #f9fafb);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--color-gray-700, #374151);
}

.pp-schritte-fuss {
  margin: 18px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--color-gray-200, #e5e7eb);
  text-align: center;
}

.pp-schritte-fuss a {
  color: var(--color-header-button-500);
  font-weight: 600;
}

@media (hover: hover) and (pointer: fine) {
  .pp-schritte-fuss a:hover { text-decoration: underline; }
}

/* --- Bildvorschau im Gäste-Kalender (#imagesModal) ---
   Der Bildbereich gibt die Höhe vor, und zwar im Hochformat - nicht die
   weiße Box dahinter, die im schmalen Layout Kalender, Preisbox und
   Formular untereinander trägt. Ein Querformat wird eingepasst; die Höhe
   bleibt dieselbe, also bleiben auch der Schließen-Knopf oben, die Pfeile in
   der Mitte und die Fußzeile unten an ihrem Platz, wenn das Bild wechselt. */

/* Außenpolsterung der Bildbox: 6px, ab 640px 8px. Hier statt als
   Tailwind-Klasse: das vorkompilierte app.css kennt weder p-[6px] noch
   sm:p-2. */
.pp-vorschau-rahmen {
  padding: 6px;
}

@media (min-width: 640px) {
  .pp-vorschau-rahmen {
    padding: 8px;
  }
}

.pp-vorschau-panel {
  width: min(100%, 520px);
  /* Oben ausgerichtet, nicht mittig - siehe Kommentar im Template. */
  align-self: flex-start;
  /* --pp-band setzt das Skript auf die Höhe des sichtbaren Bandes. Passt die
     Vorschau nicht hinein, wird sie kleiner statt aus dem Bild zu ragen. */
  max-height: min(100%, var(--pp-band, 100%));
}

.pp-vorschau-bild {
  /* Die Höhe kommt aus dem Verhältnis, nicht aus der Seite dahinter: die
     flex-basis ist auto, und mit aspect-ratio ist das die Hochformat-Höhe.
     Schrumpfen darf der Bereich trotzdem - aber nur, wenn das Panel am Band
     gedeckelt wird (siehe --pp-band oben), nie wegen des Bildes selbst. */
  flex: 1 1 auto;
  /* Hochformat gibt die Höhe vor: die Vorschau soll so hoch sein wie ein
     Hochformat-Bild, auch wenn gerade ein Querformat darin steht. Bei 520px
     Breite sind das 693px; die Panelbreite ist gedeckelt, damit die Höhe
     daraus nicht davonläuft, und --pp-band deckelt sie zusätzlich auf das,
     was gerade zu sehen ist. */
  aspect-ratio: 3 / 4;
  min-height: 0;
}

.pp-vorschau-bild img {
  max-width: 100%;
  max-height: 100%;
}

/* --- Eingebettet im iFrame (.pp-embedded) ---
   Der Buchungskalender der Detailseite läuft in einem iFrame, das schon in
   der Karte der einbettenden Seite sitzt. Jeder Rand hier ist ein Rand im
   Rand - und er kostet Breite, die dem Kalender fehlt.

   Die Außenabstände des Inhaltsrahmens aus base.html (my-3/mb-6) fallen
   weg: scrollHeight kennt keine Margins, und die einbettende Seite setzte
   das iFrame (scrolling="no") sonst zu niedrig. */

.pp-embedded .pp-page {
  padding-left: 0;
  padding-right: 0;
  margin: 0;
}

/* Innenabstand der weißen Box: 8px, ab 640px 10px. Die Box behält
   Hintergrund, Rundung und Schatten - der Inhalt soll ihre Kante nicht
   berühren. */
.pp-embedded .pp-cq {
  padding: 8px;
}

@media (min-width: 640px) {
  .pp-embedded .pp-cq {
    padding: 10px;
  }
}

/* --- Flash-Meldungen (partials/flashes.html) --- */

.pp-flashes {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
}

.pp-flash {
  border: 1px solid;
  border-radius: 6px;
  padding: 8px 12px;
  font-size: 0.8125rem;
  line-height: 1.35;
}

.pp-flash--error {
  color: #b91c1c;
  background: #fef2f2;
  border-color: #fecaca;
}

.pp-flash--success {
  color: #15803d;
  background: #f0fdf4;
  border-color: #bbf7d0;
}

.pp-flash--info {
  color: #1d4ed8;
  background: #eff6ff;
  border-color: #bfdbfe;
}

/* --- Gemeinsame kleine Hilfe: Plus-Knöpfe --- */

.icon-btn {
  /* Auch als <a> im Einsatz (das ➕ der Objektliste führt auf die
     Anlegen-Seite) - deshalb ohne Unterstreichung und mit eigener Farbe. */
  text-decoration: none;
  background: var(--color-white);
  color: var(--color-gray-800);
  border: 1px solid var(--color-gray-300);

  border-radius: 6px;
  width: 100%;
  height: 24px;

  font-size: 14px;
  line-height: 1;

  display: inline-flex;
  align-items: center;
  justify-content: center;

  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .icon-btn:hover:not(:disabled) {
    background: var(--color-gray-100);
  }
}

.icon-btn:disabled,
/* Das ➕ ist ein <a> und kennt kein disabled. Ohne Abo steht dort ein <span>
   mit dieser Klasse - gleiches Aussehen, nur ohne Ziel. */
.icon-btn.is-disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* --- Buchungsformular: Kontakt-Pills --- */

/* Container Query statt Viewport Media Query (wie .pp-cq weiter oben) -
   misst die Breite dieses Elements selbst, egal ob im zweispaltigen
   Gast-Layout, im eingebetteten iframe oder im Owner-Backend. Schmal:
   Pills-Zeile oben, Freitext darunter, Trenner ausgeblendet; reicht der
   Platz (@container unten), alles in einer Zeile mit sichtbarem Trenner. */
.pp-contact-pref-cq {
  container-type: inline-size;
}

.pp-contact-pref-wrap {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pp-contact-pref-pills {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}

.pp-contact-pref-divider {
  display: none;
  width: 1px;
  align-self: stretch;
  background: var(--color-gray-300);
  margin: 0 4px;
  flex-shrink: 0;
}

.pp-contact-pref-other {
  width: 100%;
  min-width: 0;
  background: transparent;
}

/* Schwelle: 5 Pills + Trenner + ein nutzbares Eingabefeld. */
@container (min-width: 420px) {
  .pp-contact-pref-wrap {
    flex-direction: row;
    align-items: center;
  }

  .pp-contact-pref-divider {
    display: block;
  }

  .pp-contact-pref-other {
    flex: 1;
    width: auto;
  }
}

.contact-pref-pill {
  padding: 2px 8px;
  border-radius: 4px;
  border: 1px solid var(--color-gray-300);
  background: var(--color-white);
  color: var(--color-gray-700);
  font-size: 0.75rem;
  line-height: 1.4;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: background-color 0.1s, color 0.1s, border-color 0.1s;
}

/* Hover nur für Zeigergeräte: auf Touch bleibt der Zustand nach dem Antippen
   sonst hängen und sieht aus wie eine Auswahl, die keine ist. */
@media (hover: hover) and (pointer: fine) {
  .contact-pref-pill:hover:not(.active) {
    background: var(--color-gray-100);
  }
}

.contact-pref-pill.active {
  background: var(--color-header-button-500);
  color: #fff;
  border-color: var(--color-header-button-500);
}

/* --- Zusatzkosten-Tabelle: Bearbeiten- und Löschen-Knöpfe --- */

.fee-td-action {
  display: flex;
  gap: 4px;
  align-items: center;
  white-space: nowrap;
}

.fee-td-action .fee-action-btn {
  width: 28px;
  flex-shrink: 0;
}

.fee-action-btn:active:not(:disabled) {
  box-shadow: 0 0 0 3px rgba(0, 159, 166, 0.45);
  outline: none;
}

.pp-resize-handle {
  height: 12px;
  cursor: ns-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-user-select: none;
}
.pp-resize-handle::before {
  content: '';
  width: 40px;
  height: 4px;
  border-radius: 2px;
  background: #e5e7eb;
}
@media (hover: hover) and (pointer: fine) {
  .pp-resize-handle:hover::before {
    background: #9ca3af;
  }
}

.prop-row-empty td,
.prop-row-action td,
.season-row-empty td,
.season-row-action td,
.rebate-row-empty td,
.rebate-row-action td,
.ical-row-empty td,
.ical-row-action td {
  background: var(--color-white);
}

@media (hover: hover) and (pointer: fine) {
  .prop-row-empty:hover td,
  .prop-row-action:hover td,
  .season-row-empty:hover td,
  .season-row-action:hover td,
  .rebate-row-empty:hover td,
  .rebate-row-action:hover td,
  .ical-row-empty:hover td,
  .ical-row-action:hover td {
    background: var(--color-white) !important;
    cursor: default;
  }
}

  /* Overlay-Text besser lesbar */
  .overlay-footer {
    background: rgba(0,0,0,0.30);
    color: #fff;
  }

  .overlay-footer #imgLabelLeft,
  .overlay-footer #imgCounterRight {
    font-weight: 500;
    text-shadow: 0 1px 3px rgba(0,0,0,0.9);
  }

  .overlay-footer #imgCounterRight {
    font-weight: 400;
  }

.astro-raw-pre {
  overflow-x: hidden;       /* keine horizontale Scrollbar */
  overflow-y: auto;         /* vertikal scrollen erlaubt */
  white-space: pre-wrap;    /* Zeilen umbrechen */
  word-break: break-word;   /* lange Tokens umbrechen */
  overflow-wrap: anywhere;  /* aggressive Umbrüche bei JSON/URLs */
}

/* --- Header-Buttons ---
   Eine Klasse statt einer Reihe von Tailwind-Utilities; Plain CSS, weil
   app.css vorkompiliert ist (siehe .pp-ftop oben).

   Zwei Ausprägungen, weil im Header zwei verschiedene Dinge nebeneinander
   stehen:
     .pp-btn            Navigation - führt woandershin (Logout, Meine Objekte,
                        Einstellungen, Bewertungen)
     .pp-btn--action    Aktion - macht etwas auf dieser Seite, meist ein Modal
                        (Belegungslisten, Mail-Historie)

   Ein Element je Button (kein <button> in einem <a>), damit "deaktiviert"
   über natives disabled bzw. .is-disabled geht. */
/* Höhe: 2px + 2px Padding + 18px Zeilenhöhe + 2x1px Rahmen = 24px. */
.pp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2px 8px;
  border: 1px solid var(--color-header-button-500);
  border-radius: 6px;
  background: var(--color-header-button-500);
  color: #fff;
  font-family: inherit;
  font-size: 0.875rem;
  line-height: 1.125rem;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.pp-btn--action {
  background: transparent;
  color: var(--color-header-button-500);
}

/* Kleine Variante für dichte Kontexte (z.B. "Zeile hinzufügen" in den
   Ausstattungslisten). Höhe: 1+1+16+2 = 20px. */
.pp-btn--sm {
  padding: 1px 6px;
  font-size: 0.75rem;
  line-height: 1rem;
}

/* Zwei Wege zum selben Zustand: <button disabled> nativ, <a> über die Klasse
   (Links kennen kein disabled). aria-disabled setzt das Markup zusätzlich. */
.pp-btn:disabled,
.pp-btn.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .pp-btn:not(:disabled):not(.is-disabled):hover {
    filter: brightness(1.08);
  }
}

/* Der Touch-Layer weiter unten greift nur innerhalb von .pp-guest. Der Header
   erscheint aber auf jeder Seite, auch im Owner-Backend - deshalb hier
   unabhängig davon. */
@media (pointer: coarse) {
  .pp-btn {
    min-height: 44px;
    padding-inline: 12px;
  }
}

/* Badge (z.B. Anzahl offener Bewertungen). Eigener Wrapper, damit das
   absolut positionierte Badge nicht am Flex-Layout der Button-Zeile hängt. */
.pp-btn-wrap {
  position: relative;
  display: inline-flex;
}

.pp-btn-badge {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  background: var(--color-red-600, #dc2626);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
  pointer-events: none;
}

/* Trenner zwischen Navigation und Aktionen. Unter 480px entfällt er, dort
   bricht die Zeile ohnehin um und ein senkrechter Strich mitten im Umbruch
   sieht nach Fehler aus. */
.pp-header-sep {
  width: 1px;
  align-self: stretch;
  margin-inline: 4px;
  background: var(--color-gray-300);
}

/* --- Header auf schmalen Viewports --- */

/* Die Button-Zeile in partials/header.html kann bis zu vier Buttons tragen
   (properties.html setzt alle vier: Logout / Einstellungen / Belegungslisten
   / Bewertungen). Ohne Umbruch passt das mit deutschen Labels unter ~430px
   nicht mehr nebeneinander - der Umbruch selbst sitzt als flex-wrap im
   Markup, hier nur was sich nicht als Utility ausdrücken lässt. */
@media (max-width: 480px) {
  /* Der Trenner zwischen Navigation und Aktionen entfällt hier: in der
     schiebbaren Button-Zeile (siehe .pp-header-actions im Kompakt-Block
     weiter unten) ist ein senkrechter Strich nur Rauschen. Logo,
     Überschriftgröße und Abstände regelt der Kompakt-Block, weil sie auch
     bei geringer Höhe (Querformat) gebraucht werden. */
  .pp-header-sep {
    display: none;
  }
}

/* --- Touch-Ebene (Gast-Seiten) ---
   Greift nur auf Geräten ohne präzisen Zeiger; die Desktop-Dichte bleibt
   unangetastet. Plain CSS statt Tailwind-Utilities, weil app.css
   vorkompiliert ist (siehe .pp-ftop ganz oben).

   Gilt für .pp-guest (booking_guest_calendar.html, booking_request.html,
   embed_calendar.html) - die Seiten, die Gäste praktisch nur vom Telefon aus
   sehen. Im Owner-Backend würde eine pauschale Mindesthöhe die dichten
   Tabellen-Editoren (20px-Aktionsspalten, table-layout:fixed) sprengen. */
@media (pointer: coarse) {

  /* iOS Safari zoomt beim Fokussieren in jedes Feld mit weniger als 16px
     Schriftgröße hinein - und danach nicht wieder heraus. Betrifft hier vor
     allem die text-sm-Felder (14px) im Chat und in der Kontakt-Präferenz. */
  .pp-guest input,
  .pp-guest select,
  .pp-guest textarea {
    font-size: 16px;
  }

  /* Apple HIG: 44pt, Material: 48dp. Die Felder tragen im Markup px-2 py-1
     und landen damit bei rund 30px. */
  .pp-guest input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  .pp-guest select,
  .pp-guest button,
  .pp-guest a.pp-tap-target {
    min-height: 44px;
  }

  .pp-guest textarea {
    min-height: 88px;
  }

  /* Pills sind sekundäre Auswahl, keine Primäraktion - 40px reicht, sonst
     wird die Kontakt-Präferenz-Zeile unverhältnismäßig hoch. */
  .pp-guest .contact-pref-pill {
    min-height: 40px;
    padding: 6px 12px;
    font-size: 0.875rem;
  }

  /* Ausnahmen: Elemente, die absichtlich klein sind und deren Größe das
     Layout trägt (Pfeile im Kalender, Icon-Overlay im Bildmodal, das
     unsichtbare Anker-Input des Datepickers). */
  .pp-guest .pp-price-info-toggle,
  .pp-guest #bookingDatePickerAnchor,
  .pp-guest .range-clear-btn {
    min-height: 0;
  }
}



/* Als Link gestalteter Button (z.B. "Dev-Vorschau anlegen" in der
   Owner-Verwaltung). Plain CSS, weil bg-transparent/border-0/p-0 im
   vorkompilierten app.css nicht enthalten sind. */
.pp-linkbtn {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-gray-500);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .pp-linkbtn:hover {
    color: var(--color-gray-700, #374151);
  }
}

/* --- Owner-Verwaltung: eigene Spaltenbreiten ---
   Die Tabelle nutzt .props-table für Rahmen, Hover und Auslassungspunkte,
   aber nicht deren Spaltenbreiten (die gelten für die sechsspaltige
   Objekttabelle). ID-Selektor, weil die geerbten Regeln über
   .props-table th:nth-child() kommen und von einer reinen Klassenregel nicht
   geschlagen würden.

   Die vier Haken (Entw., Sup., Abo, Gesp.) sind so breit wie ihre
   Überschrift - und die ist je Sprache verschieden lang. Deshalb rechnet
   diese Tabelle automatisch: width 1% plus nowrap lässt die Hakenspalten auf
   ihren Inhalt schrumpfen. max-width 0 an den drei Textspalten ist der
   Kniff, der sie trotzdem mit "..." kürzen lässt, statt die Tabelle mit
   einer langen Adresse zu sprengen - sie teilen sich den Rest nach ihren
   Prozenten. */
#admin-owners-table {
  table-layout: auto;
  width: 100%;
}

#admin-owners-table th:nth-child(1),
#admin-owners-table td:nth-child(1) { width: 34%; max-width: 0; }

#admin-owners-table th:nth-child(2),
#admin-owners-table td:nth-child(2) { width: 32%; max-width: 0; }

#admin-owners-table th:nth-child(3),
#admin-owners-table td:nth-child(3) { width: 26%; max-width: 0; white-space: normal; }

#admin-owners-table th:nth-child(4),
#admin-owners-table td:nth-child(4),
#admin-owners-table th:nth-child(5),
#admin-owners-table td:nth-child(5),
#admin-owners-table th:nth-child(6),
#admin-owners-table td:nth-child(6),
#admin-owners-table th:nth-child(7),
#admin-owners-table td:nth-child(7) {
  width: 1%;
  white-space: nowrap;
  padding-left: 6px;
  padding-right: 6px;
  text-align: center;
}

/* Nur der Name wird gekürzt, der Pfeil dahinter bleibt immer sichtbar -
   sonst verschwände er bei einem langen Namen hinter dem "...". */
.pp-owner-name-zeile {
  display: flex;
  align-items: baseline;
  min-width: 0;
}

.pp-owner-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pp-owner-name-zeile .pp-support-pfeil {
  flex: 0 0 auto;
}

/* Der Pfeil hinter dem Namen: wechselt in das Konto (Support). Knapp wie ein
   Verweis, nicht wie ein Knopf - er steht in jeder Zeile. */
.pp-support-pfeil {
  margin-left: 4px;
  padding: 0 2px;
  border: 0;
  background: none;
  color: var(--color-header-button-500, #2563eb);
  font-size: 0.95em;
  line-height: 1;
  cursor: pointer;
}

.pp-support-pfeil:hover,
.pp-support-pfeil:focus-visible {
  text-decoration: underline;
}

.pp-support-pfeil:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Bild im Vollbild-Overlay (property_edit.html). Plain CSS statt
   max-h-[90vh]: dvh lässt sich als Tailwind-Utility nicht ausdrücken, und vh
   ist auf Mobilbrowsern die große Viewport-Höhe - die Unterkante des Bildes
   läge hinter der ein- und ausfahrenden Adressleiste. vh bleibt als
   Rückfall für Browser ohne dvh. */
.pp-overlay-img,
.pp-overlay-video {
  max-height: 90vh;
  max-height: 90dvh;
}
.pp-overlay-video {
  display: block;
  max-width: 90vw;
  border-radius: 0.5rem;
  background: #000;
}
.pp-overlay-img[hidden],
.pp-overlay-video[hidden] {
  display: none;
}

/* --- Touch-Ziele: Symbol-Knöpfe im Owner-Backend ---
   .pp-btn deckt diese nicht ab - es sind eigene Klassen ohne Rahmen und
   Hintergrund (Emoji-Symbole in den Aktionsspalten der Tabellen), mit
   16-20px auf dem Telefon kaum zu treffen.

   Angehoben wird vor allem die Höhe. Die Aktionsspalten liegen in Tabellen
   mit table-layout:fixed und festen Breiten (.prop-actions-col 36px,
   zusammengefasst 64px) - deshalb min-width nur bis 32px. */
@media (pointer: coarse) {
  .delete-btn,
  .calendar-btn,
  .edit-btn,
  .sync-btn,
  .icon-btn {
    min-height: 44px;
    min-width: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Sortier-Pfeile stehen zu zweit übereinander in derselben Zelle - mit je
     44px wäre die Zeile 88px hoch. 32px ist hier der vertretbare Kompromiss. */
  .reorder-btn {
    min-height: 32px;
    min-width: 32px;
  }

  /* Die zusammengefasste Aktionsspalte wächst mit: sie enthält drei Knöpfe
     nebeneinander (Abgleich, Kalender, Bearbeiten) und darunter zwei
     Sortierpfeile. Mit 32px Mindestbreite braucht die breitere Reihe
     3 x 32px + 2 x 4px Abstand = 104px - auch auf Tablets, wo
     (pointer: coarse) greift, die Container-Query aber noch das
     Rechner-Layout liefert. */
  .props-table .prop-actions-col.prop-actions-merged {
    width: 108px;
  }

  /* Ausgenommen: die Saison-/Rabatt-/Zusatzkosten-Tabellen. Deren
     Aktionsspalte ist fest auf 20px begrenzt (.season-table .action-cell);
     auf Touch bearbeitet man dort ohnehin im Overlay
     (app_shared/row_editor.js). */
  .season-table .season-delete-btn,
  .season-table .icon-btn {
    min-height: 0;
    min-width: 0;
  }
}

/* --- Flache Viewports: zurück ins einspaltige Layout ---
   Die tragenden Layouts der App entscheiden nach Breite - die meisten bei
   @container (min-width: 580px), das Owner-Raster bei 660px. Keine dieser
   Bedingungen fragt nach der Höhe.

   Ein Telefon im Querformat (z.B. 844x390) ist breit genug für die
   Zweispaltigkeit, die knappe Ressource ist aber die Höhe: 390px, davon
   Kopfzeile und eingeblendete Tastatur.

   Container Queries kennen die Viewport-Höhe nicht, deshalb eine Media
   Query, die die Zweispaltigkeit zurücknimmt. 560px deckt alle Telefone im
   Querformat ab (das größte liegt bei 430px) und lässt Tablets im
   Querformat (768px+) unberührt.

   grid-template-columns muss mitgesetzt werden: die Breitenregeln schalten
   sonst weiter auf zwei Spalten, und die Bereichsnamen allein ließen die
   zweite nur leer stehen. */
@media (max-height: 560px) {
  .pp-owner-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "cal"
      "pick"
      "form"
      "list";
  }

  /* Hier steht die Liste nicht neben dem Kalender, sondern unter der Form -
     damit gilt für sie dasselbe wie bei einem schmalen Kasten: an ihrer
     Stelle das Auswahlfeld. Ein Telefon im Querformat ist breit genug für
     die 660px der Breitenregel. */
  .pp-area-list {
    display: none;
  }

  .pp-area-picker {
    display: block;
  }

  .pp-props-panels {
    grid-template-columns: 1fr;
  }

  .pp-booking-form-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "price"
      "meta"
      "left"
      "right";
  }

  /* Die 580er-Regel setzt für diese vier feste Zeilen/Spalten - die müssen
     mit zurückgenommen werden, sonst landen sie im einspaltigen Raster an
     Positionen, die es nicht mehr gibt. */
  .pp-form-price,
  .pp-form-meta,
  .pp-form-left,
  .pp-form-right {
    grid-column: auto;
    grid-row: auto;
  }

  .pp-guest-grid {
    grid-template-columns: 1fr;
  }

  .season-rebate-grid {
    grid-template-columns: 1fr;
  }
}

/* --- Kompakte Kopfzeile auf schmalen oder flachen Viewports ---
   Der Header ist zweizeilig, mit text-2xl-Überschrift, zwei Logos zu je
   100px, Sprachwahl und bis zu fünf umbrechenden Buttons - zusammen grob
   100-140px. Auf einem Telefon im Querformat (390px hoch) wäre das über ein
   Viertel des Bildschirms, dauerhaft, auf jeder Seite.

   Die Höhen-Bedingung ist hier genauso wichtig wie die Breite: quer ist der
   Viewport breit, aber flach - eine reine max-width-Regel griffe dort nicht. */
@media (max-width: 640px), (max-height: 560px) {

  /* Außenabstand und Polster deutlich zurück: allein das spart rund 30px. */
  .pp-header {
    margin-bottom: 10px;
    padding-top: 6px;
    padding-bottom: 6px;
  }

  /* Beide Marken raus. Auf dem Telefon weiß der Nutzer, in welcher App er
     ist; der Platz ist dort mehr wert als das Logo. */
  .pp-header-logo,
  .pp-header-logo-secondary {
    display: none;
  }

  .pp-header-title {
    font-size: 1.125rem;
    line-height: 1.5rem;
  }

  /* Der Versatz hängt am zweizeiligen Aufbau mit Logos - ohne die sitzt das
     Select sonst schief über der Überschrift. */
  .pp-header-lang {
    top: 0;
  }

  .pp-header-row2 {
    margin-top: 4px;
  }

  /* Buttons in einer seitlich schiebbaren Zeile statt umbrechend.
     Mit 44px Touch-Höhe würden fünf umbrechende Buttons zwei bis drei
     Zeilen belegen - also 90 bis 140px allein für Navigation. Eine
     schiebbare Zeile kostet 44px und versteckt nichts hinter einem Menü;
     der angeschnittene letzte Button zeigt, dass es weitergeht. */
  .pp-header-actions {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    /* Damit der letzte Button nicht am Rand klebt. */
    padding-right: 4px;
  }

  .pp-header-actions::-webkit-scrollbar {
    display: none;
  }

  .pp-header-actions > * {
    flex: 0 0 auto;
  }
}

/* --- Objekttabelle als Karten (schmale Container) ---
   Zwei Spalten: links die Infos als Fließtext, rechts die Knöpfe.
   Zwei Spalten: links die Infos als Fließtext, rechts die Knöpfe.

     Name, Preis                        [Papierkorb]
     Gäste: 4, Nächte: 2              [hoch][runter]
     E-Mail                   [Sync][Kalender][Bearbeiten]

   Flex, nicht Grid: ein Grid erzwingt Spaltenausrichtung ("Nächte" landete
   zwangsläufig unter "Preis"). Als Flex-Zeile mit Umbruch stehen die
   Wertepaare einfach hintereinander, durch Komma getrennt.
   genau der Spalteneindruck, der hier weg soll. Als Flex-Zeile mit Umbruch
   stehen die Wertepaare einfach hintereinander, durch Komma getrennt.

   Die Zeilenumbrüche kommen aus tr::before und tr::after: Pseudoelemente
   eines Flex-Containers sind selbst Flex-Elemente, und mit flex-basis:100%
   bei Höhe 0 brechen sie die Zeile um, ohne Platz zu kosten. Zwei davon
   ergeben die drei Zeilen - ohne ein einziges zusätzliches Element im
   Markup.

   margin-left:auto schiebt die jeweilige Knopfgruppe ans rechte Ende ihrer
   Zeile; dadurch entsteht die zweite Spalte, ohne eine zu definieren.

   Die Reihenfolge im Markup (Name, Beschreibung, Preis, E-Mail, Gäste,
   Nächte, Aktionen, Löschen) wird über order umsortiert.

   Rein über CSS aus demselben Markup: prop_table.js erzeugt keine Zeilen,
   es arbeitet nur mit ihnen.

   Container Query statt Media Query: maßgeblich ist die Breite der weißen
   Box, nicht die des Fensters - im Querformat ist der Container ~800px breit
   und die Tabelle dort völlig in Ordnung. */
@container (max-width: 560px) {

  .props-table,
  .props-table tbody,
  .props-table tr,
  .props-table td {
    display: block;
  }

  .props-table thead {
    display: none;
  }

  .props-table tr {
    margin-bottom: 6px;
    padding: 4px 8px;
    border: 1px solid var(--color-gray-300);
    border-radius: 8px;
    background: #fff;
  }

  .props-table td {
    border: 0;
    padding: 0;
    text-align: left;
  }

  /* Prozent- und Pixelbreiten der Tabellenansicht aufheben. Gleiche
     Spezifität wie die Ursprungsregeln, gewinnt über die Reihenfolge. */
  .props-table th:nth-child(1), .props-table td:nth-child(1),
  .props-table th:nth-child(2), .props-table td:nth-child(2),
  .props-table th:nth-child(3), .props-table td:nth-child(3),
  .props-table th:nth-child(4), .props-table td:nth-child(4),
  .props-table th:nth-child(6), .props-table td:nth-child(6) {
    width: auto;
  }

  /* --- Kartenaufbau --- */
  .props-table tr.prop-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 4px;
    row-gap: 0;
  }

  /* Die Zellen lösen sich auf, damit ihre Kinder einzeln in die Flex-Zeile
     einsortiert werden können. */
  .props-table tr.prop-row > td:nth-child(1),
  .props-table tr.prop-row > td:nth-child(2),
  .props-table tr.prop-row > td.prop-actions-merged {
    display: contents;
  }

  /* Zeilenumbrüche ohne eigene Höhe. */
  .props-table tr.prop-row::before,
  .props-table tr.prop-row::after {
    content: "";
    flex: 0 0 100%;
    height: 0;
    margin: 0;
  }

  /* --- Reihenfolge: Zeile 1 --- */
  .props-table tr.prop-row > td:nth-child(1) > .prop-cell-main { order: 1; }
  .props-table tr.prop-row > td:nth-child(2) > .prop-cell-main { order: 2; }
  .props-table tr.prop-row > td:nth-child(6)                   { order: 3; }

  .props-table tr.prop-row::before { order: 4; }

  /* --- Zeile 2 --- */
  .props-table tr.prop-row > td:nth-child(3)          { order: 5; }
  .props-table tr.prop-row > td:nth-child(4)          { order: 6; }
  .props-table tr.prop-row .prop-actions-merged-inner { order: 7; }

  .props-table tr.prop-row::after { order: 8; }

  /* --- Zeile 3 --- */
  .props-table tr.prop-row > td:nth-child(2) > .prop-cell-sub { order: 9; }
  .props-table tr.prop-row .prop-reorder-inner                { order: 10; }

  /* Jede Knopfgruppe ans rechte Ende ihrer Zeile - das ergibt die zweite
     Spalte, ohne dass eine definiert wäre. */
  .props-table tr.prop-row > td:nth-child(6),
  .props-table tr.prop-row .prop-reorder-inner,
  .props-table tr.prop-row .prop-actions-merged-inner {
    margin-left: auto;
    margin-top: 0;
    display: flex;
    flex-direction: row;
    gap: 2px;
  }

  /* Komma zwischen den Wertepaaren - direkt am Wert. */
  .props-table tr.prop-row > td:nth-child(1) > .prop-cell-main::after,
  .props-table tr.prop-row > td:nth-child(3)::after {
    content: ",";
  }

  /* Die Beschreibung entfällt in der Karte - ohnehin auf 50 Zeichen gekürzt
     und hier nur Höhe. Auf der Detailseite steht sie vollständig. */
  .props-table tr.prop-row > td:nth-child(1) > .prop-cell-sub {
    display: none;
  }

  /* --- Typografie --- */
  .props-table .prop-cell-main,
  .props-table .prop-cell-sub {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.25;
  }

  .props-table tr.prop-row > td:nth-child(1) > .prop-cell-main {
    font-size: 14px;
  }

  .props-table tr.prop-row > td:nth-child(2) > .prop-cell-main {
    font-size: 14px;
    font-weight: 400;
    white-space: nowrap;
  }

  .props-table tr.prop-row > td:nth-child(2) > .prop-cell-sub {
    font-size: 12px;
  }

  .props-table td.props-td-center {
    text-align: left;
    white-space: nowrap;
  }

  /* Beschriftung aus data-label - "4" allein sagt nichts. Name, Preis und
     E-Mail erklären sich selbst und bleiben ohne. */
  .props-table td[data-label] {
    display: flex;
    align-items: baseline;
    gap: 3px;
    font-size: 12px;
  }

  .props-table td[data-label]::before {
    content: attr(data-label) ":";
    flex: 0 0 auto;
    color: var(--color-gray-500);
  }

  /* --- Knöpfe: größer, aber ohne Polster drumherum --- */
  .props-table tr.prop-row .prop-actions-merged-inner > button,
  .props-table tr.prop-row .prop-reorder-inner > button,
  .props-table tr.prop-row > td:nth-child(6) > button {
    min-height: 26px;
    min-width: 30px;
    padding: 0;
    font-size: 17px;
    line-height: 1;
  }

  /* --- Sonderzeilen --- */
  .props-table .prop-row-child {
    margin-left: 14px;
  }

  .props-table .prop-row-child td:nth-child(-n + 2) :is(.prop-cell-main, .prop-cell-sub) {
    padding-left: 0;
  }

  .props-table tr.prop-row-selected,
  .props-table tr.selected {
    background-color: #e6f0ff;
  }

  /* Füllzeilen halten in der Tabelle eine Mindesthöhe - als leere Karten
     wären sie nur Kästen ohne Inhalt. */
  .props-table tr.prop-row-empty {
    display: none;
  }

  /* Die Aktionszeile besteht aus fünf leeren Zellen plus dem Plus-Knopf. */
  .props-table tr.prop-row-action {
    border: 0;
    padding: 0;
    background: none;
  }

  .props-table tr.prop-row-action td:not(.prop-actions-col) {
    display: none;
  }

  .props-table tr.prop-row-action .icon-btn {
    width: auto;
    min-width: 44px;
    padding-inline: 12px;
  }
}

/* --- Owner-Verwaltung als Karte ---
   Der Block darüber macht jede .props-table zu Karten - sein Feinaufbau
   (order, tr::before/::after) gilt aber nur für tr.prop-row, also für die
   Objekttabelle. Die Owner-Zeilen bekommen hier ihren eigenen: drei Zeilen,
   jede mit einem Wert links und einem rechts:

     Name (Sie)                      domain.de
     mail@example.com             Status: aktiv
     [x] Entwickler [x] Gesperrt [x] Abo

   Flex wie bei der Objekttabelle, nicht Grid - ein Raster richtete Spalten
   aneinander aus. Als Flex-Zeile sitzt rechts, was ein margin-left:auto
   bekommt - sonst nichts.

   Die Zeilenumbrüche kommen aus tr::before und tr::after: Pseudoelemente
   eines Flex-Containers sind selbst Flex-Elemente und brechen mit
   flex-basis:100% bei Höhe 0 die Zeile um, ohne Platz zu kosten. Die erste
   Zelle löst sich per display:contents auf, damit Name und E-Mail - die
   zusammen in ihr stehen - einzeln in verschiedene Zeilen einsortiert
   werden können.

   Die Überschriften stehen einmal über der Tabelle und fehlen der Karte;
   ::before trägt sie aus dem data-label im Markup nach - am Status als
   "Status:" davor, an den Haken hinter dem Kästchen (wie bei .qa-cell im
   FAQ-Block).

   Dieselbe Schwelle wie der Block darüber, und danach: was dort steht -
   display:block, thead weg, Rahmen und Radius der Karte - gilt weiter, hier
   stehen nur die Abweichungen. Die ID-Selektoren schlagen die Klassenregeln
   von dort unabhängig von der Reihenfolge; die Nachbarschaft ist für den
   Leser, nicht für die Kaskade. */
@container (max-width: 560px) {

  #admin-owners-table tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 10px;
    row-gap: 0;
  }

  /* Prozentbreiten der Tabellenansicht aufheben - mit gleicher Spezifität
     wie die Ursprungsregeln (ID + nth-child); ein
     "#admin-owners-table td { width: auto }" wäre um eine Klasse schwächer. */
  #admin-owners-table td:nth-child(1),
  #admin-owners-table td:nth-child(2),
  #admin-owners-table td:nth-child(3),
  #admin-owners-table td:nth-child(4),
  #admin-owners-table td:nth-child(5),
  #admin-owners-table td:nth-child(6),
  #admin-owners-table td:nth-child(7) {
    width: auto;
    min-width: 0;
  }

  /* Löst sich auf: Name und E-Mail stehen darin und gehören in zwei
     verschiedene Zeilen der Karte. */
  #admin-owners-table td:nth-child(1) {
    display: contents;
  }

  #admin-owners-table tr::before,
  #admin-owners-table tr::after {
    content: "";
    flex: 0 0 100%;
    height: 0;
    margin: 0;
  }

  /* --- Zeile 1: Name ... Domain --- */
  #admin-owners-table td:nth-child(1) > .prop-cell-main { order: 1; min-width: 0; }
  #admin-owners-table td:nth-child(2)                   { order: 2; }
  #admin-owners-table tr::before                        { order: 3; }

  /* --- Zeile 2: E-Mail ... Status --- */
  #admin-owners-table td:nth-child(1) > .prop-cell-sub  { order: 4; min-width: 0; }
  #admin-owners-table td:nth-child(3)                   { order: 5; }
  #admin-owners-table tr::after                         { order: 6; }

  /* --- Zeile 3: die vier Haken --- */
  #admin-owners-table td:nth-child(4) { order: 7; }
  #admin-owners-table td:nth-child(5) { order: 8; }
  #admin-owners-table td:nth-child(6) { order: 9; }
  #admin-owners-table td:nth-child(7) { order: 10; }

  /* Was rechts steht, steht dort wegen dieses auto - nicht wegen einer
     Spalte. Der Status bringt sein "Status:" schon mit: das data-label-
     Muster des Blocks darüber setzt es als ::before davor. */
  #admin-owners-table td:nth-child(2),
  #admin-owners-table td:nth-child(3) {
    flex: 0 1 auto;
    margin-left: auto;
    overflow: hidden;
  }

  /* In der Karte gilt die Kürz-Regel der Tabelle nicht: dort hat jede
     Zelle ihre natürliche Breite. */
  #admin-owners-table td:nth-child(1),
  #admin-owners-table td:nth-child(2),
  #admin-owners-table td:nth-child(3) {
    max-width: none;
  }

  /* Status: der Zustand, darunter der letzte HTML-Export - nicht daneben. */
  #admin-owners-table td:nth-child(3) {
    flex-wrap: wrap;
    justify-content: flex-end;
  }

  #admin-owners-table td:nth-child(3) > .prop-cell-sub {
    flex: 0 0 100%;
    text-align: right;
  }

  /* Die Domain-Zelle ist ein Block (Domain, darunter ggf. der
     Anlegen-Knopf) - sie braucht die Ausrichtung selbst. */
  #admin-owners-table td:nth-child(2) {
    text-align: right;
  }

  /* Bei den Haken steht die Beschriftung hinter dem Kästchen und ohne
     Doppelpunkt - so liest man ein Kontrollkästchen. Alles andere (flex,
     Abstand, Schriftgröße, Farbe) kommt vom data-label-Muster oben.
     flex:0 0 auto statt Quetschen: passen die drei nicht nebeneinander,
     rutscht die letzte in eine eigene Zeile. */
  #admin-owners-table td:nth-child(4),
  #admin-owners-table td:nth-child(5),
  #admin-owners-table td:nth-child(6),
  #admin-owners-table td:nth-child(7) {
    align-items: center;
    flex: 0 0 auto;
    margin-top: 4px;
    white-space: nowrap;
  }

  #admin-owners-table td:nth-child(4)::before,
  #admin-owners-table td:nth-child(5)::before,
  #admin-owners-table td:nth-child(6)::before,
  #admin-owners-table td:nth-child(7)::before {
    content: attr(data-label);
    order: 2;
  }

  /* Das Kästchen darf nicht mitschrumpfen, wenn die Beschriftung lang ist. */
  #admin-owners-table td[data-label] input[type="checkbox"] {
    flex: 0 0 auto;
  }
}

/* --- Gästeliste im Belegungs-Overlay ---
   Ein Raster, keine Flex-Zeilen: Nachname, Vorname, Sprache, E-Mail und
   Telefon stehen untereinander. Das Raster liegt auf der ganzen Liste, nicht
   auf der einzelnen Zeile: alle fünf Felder eines Gastes sind Zellen
   desselben Rasters, und die Spaltenbreite ergibt sich aus dem breitesten
   Eintrag - über alle Gäste hinweg.

   Wird es zu eng, rutschen E-Mail und Telefon eine Zeile tiefer, bei allen
   Gästen gleichzeitig: das Raster hat dann drei Spalten statt fünf, und die
   beiden Kontaktfelder bekommen ihre Plätze ausdrücklich zugewiesen (1/3
   und 3) - so füllt jeder Gast genau zwei Rasterzeilen, und der nächste
   beginnt wieder links.

   Die Schwelle misst den Kasten, nicht das Fenster: der Reiter sitzt in
   einem Overlay, dessen Breite mit dem Fenster nur lose zusammenhängt. */
#gaeste-text {
  container-type: inline-size;
}

.pp-gaeste {
  display: grid;
  grid-template-columns: auto auto auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0 10px;
}

.pp-gaeste > span {
  min-width: 0;
  cursor: pointer;
  padding: 2px 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border-bottom: 1px solid var(--color-gray-100, #f3f4f6);
}

.pp-gast-nn {
  font-weight: 500;
}

.pp-gast-em,
.pp-gast-tel {
  color: var(--color-gray-500, #6b7280);
}

@container (max-width: 32rem) {
  .pp-gaeste {
    grid-template-columns: auto auto minmax(0, 1fr);
  }

  /* Zeile 2 des Gastes: die E-Mail über die ersten beiden Spalten, das
     Telefon in die dritte. Zusammen füllen sie die Zeile - deshalb fängt
     der nächste Gast wieder in Spalte 1 an. */
  .pp-gast-em {
    grid-column: 1 / 3;
  }

  .pp-gast-tel {
    grid-column: 3;
  }

  /* Die Trennlinie gehört unter den ganzen Gast, also unter seine zweite
     Zeile - nicht mitten hinein. */
  .pp-gast-nn,
  .pp-gast-vn,
  .pp-gast-fl {
    border-bottom: none;
    padding-bottom: 0;
  }

  .pp-gast-em,
  .pp-gast-tel {
    padding-top: 0;
  }
}

/* --- Sterne der Gast-Bewertung (booking_guest_calendar.html) ---
   Zwei Zustände: die abgegebene Bewertung als reine Anzeige, und die Wahl
   im Formular.

   Die Wahl steht im Markup rückwärts (5 bis 1): CSS kann mit dem
   Geschwister-Selektor (~) nur nach vorn zeigen, und so lassen sich die
   Sterne links des gewählten mitfärben. row-reverse dreht die Anzeige
   zurück - ohne JavaScript. */
.pp-sterne {
  font-size: 1.25rem;
  line-height: 1;
  color: var(--color-gray-300, #d1d5db);
}

.pp-sterne .ist-voll {
  color: #f59e0b;
}

.pp-sterne-wahl {
  display: flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
  gap: 2px;
}

.pp-sterne-wahl input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.pp-sterne-wahl label {
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-gray-300, #d1d5db);
  cursor: pointer;
}

/* Der gewählte Stern und alle nach ihm im Markup (= links von ihm auf dem
   Schirm, wegen row-reverse). */
.pp-sterne-wahl input:checked ~ label,
.pp-sterne-wahl label:hover,
.pp-sterne-wahl label:hover ~ label {
  color: #f59e0b;
}

/* Tastaturbedienung: ohne sichtbaren Rahmen wäre nicht zu erkennen, wo man
   gerade steht - die Kästchen selbst sind ausgeblendet. */
.pp-sterne-wahl input:focus-visible + label {
  outline: 2px solid var(--color-gray-400, #9ca3af);
  outline-offset: 2px;
}

/* --- Header-Menü ---
   Breit: Auslöser versteckt, die Button-Zeile steht offen im Fluss.
   Schmal/flach: die Zeile klappt unter dem Header auf.

   Dasselbe Markup für beides. Der Auslöser sitzt in Zeile 1 neben der
   Sprachwahl, das Panel bleibt in Zeile 2 - so ändert sich die
   Desktop-Anordnung nicht. Positioniert wird gegen .pp-header (trägt
   position:relative), nicht gegen den Auslöser; die beiden sind keine
   Geschwister. */
.pp-header-menu-btn {
  display: none;
}

@media (max-width: 640px), (max-height: 560px) {
  .pp-header-menu-btn {
    display: inline-flex;
    min-width: 34px;
    padding-inline: 8px;
    font-size: 15px;
    line-height: 1;
  }

  /* Als Klappmenü statt als schiebbare Zeile: so sieht man, welche
     Einträge es gibt. */
  .pp-header-actions {
    position: absolute;
    top: calc(100% + 4px);
    right: 8px;
    z-index: 60;

    display: none;
    flex-direction: column;
    align-items: stretch;
    min-width: 190px;
    max-width: 80vw;
    padding: 6px;

    background: #fff;
    border: 1px solid var(--color-gray-300);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
  }

  .pp-header.is-open .pp-header-actions {
    display: flex;
  }

  /* Im Menü sind die Einträge Zeilen, keine Chips: linksbündig, volle
     Breite, ohne eigenen Rahmen - der Kasten ist der Rahmen. */
  .pp-header-actions .pp-btn {
    justify-content: flex-start;
    width: 100%;
    border-color: transparent;
    border-radius: 6px;
  }

  .pp-header-actions .pp-btn--action {
    background: transparent;
  }

  .pp-header-actions .pp-header-sep {
    display: block;
    width: auto;
    height: 1px;
    align-self: stretch;
    margin: 4px 2px;
  }

  /* In einer vollbreiten Zeile gehört das Badge ans Ende des Eintrags. */
  .pp-header-actions .pp-btn-wrap {
    width: 100%;
  }

  .pp-header-actions .pp-btn-badge {
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
  }
}

/* --- Belegungs-Timeline: kompakte Darstellung ---
   Breit: feste Namensspalte links (160px), daneben der scrollbare Kalender.

   Kompakt (die Klasse setzt prop_occupancy_timeline.js, sobald der
   Container unter 560px liegt): die Namensspalte entfällt, jede
   Beschriftung steht über ihrem Band und klebt beim Scrollen per
   position:sticky am linken Rand - Bänder und Kalenderkopf laufen darunter
   durch. Mit der auf 20px verringerten Tagesbreite passen auf ein Telefon
   rund 16 statt 6 Tage. */
.occ-timeline.occ-compact .occ-names {
  display: none;
}

/* Die Zeilen fließen normal statt absolut gestapelt - das Raster aus "alle
   22px eine Zeile" trägt nicht, sobald zwischen den Bändern Beschriftungen
   stehen. */
.occ-timeline.occ-compact .occ-row {
  position: relative;
  top: auto;
}

/* Beschriftung und Band gehören zusammen - der Hintergrund liegt deshalb am
   ganzen Block, nicht nur an der Bandzeile, und wechselt von Einheit zu
   Einheit. */
.occ-timeline.occ-compact .occ-unit {
  background: var(--color-white);
  border-radius: 4px;
  padding: 1px 0;
}

.occ-timeline.occ-compact .occ-unit-alt {
  background: var(--color-gray-100);
}

/* Die Bandzeile bringt ihre eigene Wechselfarbe mit (occ-row-alt) - kompakt
   übernimmt das der Block, sonst läge Farbe auf Farbe. Der Trennstrich
   entfällt ebenfalls, die Blöcke grenzen sich über die Farbe ab. */
.occ-timeline.occ-compact .occ-row,
.occ-timeline.occ-compact .occ-row-alt {
  background: transparent;
  border-bottom: 0;
}

.occ-timeline.occ-compact .occ-unit + .occ-unit {
  margin-top: 2px;
}

/* width:max-content ist entscheidend: ohne das wäre die Beschriftung so
   breit wie der gesamte Scrollbereich (mehrere tausend Pixel) und sticky
   hätte nichts, woran es kleben könnte. */
.occ-timeline.occ-compact .occ-unit-label {
  position: sticky;
  left: 0;
  z-index: 2;

  display: flex;
  align-items: center;
  width: max-content;
  max-width: 100%;
  padding: 0 4px;

  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--color-gray-700, #374151);
}

.occ-timeline.occ-compact .occ-unit-label-child {
  padding-left: 14px;
  font-weight: 400;
}

/* Der Marker spannt über den ganzen Inhalt - dessen Höhe steht hier nicht
   fest, weil die Zeilen fließen. */
.occ-timeline.occ-compact .occ-today-marker {
  top: 0;
  bottom: 0;
  height: auto;
}

/* --- Farbwelt der einbettenden Seite im iFrame --- */

/* Schaltflächen und die Kopfzeile im eingebetteten Kalender übernehmen die
   Farbe der Seite, in der das iFrame steckt: die Monatspfeile, "Bilder
   ansehen", die Pfeile der Bildvorschau und der Buchungs-Button. Gemeldet
   wird sie per postMessage (sendeFarben() im js.part der Vorlage,
   applyParentTheme() in booking_shared/calendar.js).

   Zwei Absicherungen, damit das nichts anderes trifft:

     * --pp-embed-btn setzt ausschließlich applyParentTheme(), also nur im
       eingebetteten Fall. Fehlt die Variable, bleibt der Rückfall - das
       Petrol dieser App.
     * Eingegrenzt auf .pp-guest. Das Owner-Backend benutzt .pp-btn an vielen
       Stellen und darf davon nie berührt werden.

   .pp-btn--action ist ausgenommen: die Variante ist bewusst durchsichtig mit
   farbiger Schrift, ein Hintergrund würde sie zerstören. */
.pp-guest .pp-btn:not(.pp-btn--action) {
  background: var(--pp-embed-btn, var(--color-header-button-500));
  border-color: var(--pp-embed-btn, var(--color-header-button-500));
  color: #fff;
}

.pp-guest .pp-btn:not(.pp-btn--action):hover {
  background: var(--pp-embed-btn-hover, var(--color-header-button-600, var(--color-header-button-500)));
  border-color: var(--pp-embed-btn-hover, var(--color-header-button-600, var(--color-header-button-500)));
}

/* Die Kopfzeile im iFrame trägt den Namen des Objekts. Sie soll in derselben
   Farbe stehen wie die Schaltflächen darunter, statt im Petrol der App.
   Die Regel schlägt die Tailwind-Utility am Element (text-[color:...]):
   .pp-embed-header h1 ist spezifischer, und diese Datei liegt außerhalb von
   @layer utilities. */
.pp-guest .pp-embed-header h1 {
  color: var(--pp-embed-btn, var(--color-header-h1-500));
}

/* Zustände des Buchungs-Buttons.

   updateSubmitButton() in app_shared/tooltip.js setzt nur die Klasse; die
   Farbe kommt von hier.

   Die Selektoren hängen nur am Zustand, nicht zusätzlich an .pp-btn: der
   Buchungs-Button trägt diese Klasse nicht - er ist im Markup mit
   Tailwind-Utilities aufgebaut (w-full bg-gray-300 text-gray-700 ...).
   Gegen diese Utilities kommen die Regeln an, weil diese Datei außerhalb von
   @layer utilities liegt - ungelayert gewinnt unabhängig von der
   Spezifität.

   Die Reihenfolge ist wichtig: .pp-guest .pp-btn:not(.pp-btn--action)
   weiter oben trifft die übrigen Schaltflächen im iFrame; bei Gleichstand
   entscheidet die Quellreihenfolge, deshalb stehen die Zustände danach. */

/* Bereit zum Absenden. Im Owner-Backend das gewohnte Grün als Signal. */
.pp-submit--ready {
  background: #77A146;
  border-color: #77A146;
  color: #fff;
}

/* Im eingebetteten Kalender stattdessen die Farbe der einbettenden Seite.
   Ohne Meldung bleibt es beim Grün. */
.pp-guest .pp-submit--ready {
  background: var(--pp-embed-btn, #77A146);
  border-color: var(--pp-embed-btn, #77A146);
  color: #fff;
}

.pp-guest .pp-submit--ready:hover {
  background: var(--pp-embed-btn-hover, #6a9040);
  border-color: var(--pp-embed-btn-hover, #6a9040);
}

/* Noch nicht vollständig - ausgegraut, in beiden Ansichten gleich. */
.pp-submit--incomplete {
  background: #e5e7eb;
  border-color: #e5e7eb;
  color: #6b7280;
}

/* Neutraler Zustand ("Neue Buchungsanfrage beginnen"). */
.pp-submit--neutral {
  background: #f3f4f6;
  border-color: #d1d5db;
  color: #374151;
}

/* --- Stammgast-Rabatt im Buchungsformular (guest_page/guest_discount.js) ---
   Erscheint nur, wenn der Gast einen Rabatt hat - über den Link aus der
   Mail oder seine erkannte E-Mail. Ein kurzer Auftritt (.pp-gd-in): kommt
   leicht federnd herein, ein Glanz streicht einmal darüber, und der neue
   Preis in der Preisbox leuchtet kurz auf (.pp-gd-flash). Danach steht er
   ruhig da - er soll auffallen, nicht zappeln.

   Farben fest statt aus dem Theme des Owners: Grün heißt hier "gespart",
   egal wie die Seite sonst aussieht. */
.pp-gd-banner {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid #86efac;
  border-radius: 10px;
  background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
  color: #14532d;
  font-size: 0.875rem;
  line-height: 1.3;
}

.pp-gd-icon {
  font-size: 1.4rem;
  line-height: 1;
}

.pp-gd-title {
  display: block;
  font-weight: 700;
}

.pp-gd-sub {
  display: block;
  margin-top: 2px;
  font-size: 0.75rem;
  color: #166534;
}

.pp-gd-banner.pp-gd-in {
  animation: pp-gd-pop 520ms cubic-bezier(0.2, 0.9, 0.3, 1.25) both;
}

.pp-gd-banner.pp-gd-in .pp-gd-icon {
  animation: pp-gd-wackel 700ms ease-out 250ms both;
}

.pp-gd-banner.pp-gd-in::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg, transparent 30%, rgba(255, 255, 255, 0.75) 50%, transparent 70%);
  transform: translateX(-100%);
  animation: pp-gd-glanz 1100ms ease-out 350ms both;
  pointer-events: none;
}

.pp-gd-price.pp-gd-flash {
  border-radius: 4px;
  animation: pp-gd-aufleuchten 1400ms ease-out both;
}

@keyframes pp-gd-pop {
  0%   { opacity: 0; transform: translateY(-6px) scale(0.94); }
  100% { opacity: 1; transform: none; }
}

@keyframes pp-gd-wackel {
  0%   { transform: rotate(0) scale(1); }
  30%  { transform: rotate(-14deg) scale(1.25); }
  60%  { transform: rotate(10deg) scale(1.1); }
  100% { transform: rotate(0) scale(1); }
}

@keyframes pp-gd-glanz {
  to { transform: translateX(100%); }
}

@keyframes pp-gd-aufleuchten {
  0%   { background: #bbf7d0; box-shadow: 0 0 0 4px #bbf7d0; color: #14532d; }
  100% { background: transparent; box-shadow: 0 0 0 4px transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .pp-gd-banner.pp-gd-in,
  .pp-gd-banner.pp-gd-in .pp-gd-icon,
  .pp-gd-banner.pp-gd-in::after,
  .pp-gd-price.pp-gd-flash {
    animation: none;
  }
  .pp-gd-banner.pp-gd-in::after {
    display: none;
  }
}

/* --- Support-Modus: ein Entwickler arbeitet im Konto eines Owners ---
   (services/support.py). Das Band steht oben auf jeder Seite und soll
   auffallen - wer es übersieht, hält fremde Daten für die eigenen. */
.pp-support-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 12px;
  padding: 8px 12px;
  border: 1px solid #f59e0b;
  border-radius: 6px;
  background: #fef3c7;
  color: #92400e;
  font-size: 0.875rem;
}

.pp-support-text {
  font-weight: 600;
}

.pp-support-sprache a {
  color: inherit;
  text-decoration: none;
}

.pp-support-sprache a.is-active {
  font-weight: 600;
  text-decoration: underline;
}

.pp-support-zurueck {
  margin: 0;
}

/* --- Sortieren per Ziehen (js/app_shared/sortier_raster.js) --- */

/* Langes Drücken hebt ein Bild an: dabei weder Text markieren noch das
   iOS-Menü "Bild sichern" öffnen. */
[data-sort-id] {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  cursor: grab;
}
[data-sort-id] img {
  -webkit-user-drag: none;
  pointer-events: none;
}
[data-sort-id]:focus-visible {
  outline: 2px solid var(--color-header-button-500, #3b82f6);
  outline-offset: 2px;
}

/* Der Platz, an dem das Bild landen würde - die Nachbarn rücken dafür auseinander. */
.pp-sort-platzhalter {
  opacity: 0.35;
  outline: 2px dashed var(--color-gray-400, #9ca3af);
  outline-offset: -2px;
  border-radius: 0.5rem;
}
.pp-sort-platzhalter > * {
  visibility: hidden;
}

/* Die Kopie unter dem Finger. */
.pp-sort-geist {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 1000;
  margin: 0;
  pointer-events: none;
  cursor: grabbing;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25);
  border-radius: 0.5rem;
  opacity: 0.95;
  will-change: transform;
}

/* Während des Ziehens: alle Gruppen treten zurück, die unter dem Zeiger
   leuchtet auf, die Herkunft bleibt leicht umrandet. */
.pp-sort-aktiv [data-sort-gruppe] {
  transition: background-color 120ms, box-shadow 120ms, opacity 120ms;
  opacity: 0.6;
}
.pp-sort-aktiv [data-sort-gruppe].pp-sort-herkunft {
  box-shadow: 0 0 0 1px var(--color-gray-300, #d1d5db);
}
.pp-sort-aktiv [data-sort-gruppe].pp-sort-ziel {
  opacity: 1;
  background-color: rgba(251, 191, 36, 0.12);
  box-shadow: 0 0 0 2px rgb(251, 191, 36);
}

/* Tastatur: angehobenes Bild. */
.pp-sort-gehoben {
  transform: scale(1.04);
  box-shadow: 0 0 0 3px rgb(251, 191, 36);
  border-radius: 0.5rem;
}

/* Bildgruppen eines Objekts (js/property_edit_page/propedit_images.js):
   gerahmt, der Raumname sitzt fett auf dem oberen Rahmen. */
.pp-bildgruppe {
  position: relative;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 0.5rem;
  padding: 1rem 0.75rem 0.75rem;
  margin: 1.25rem 0 0;
}
.pp-bildgruppe-kopf {
  position: absolute;
  top: -0.7rem;
  left: 0.6rem;
  margin: 0;
  padding-inline: 0.35rem;
  background-color: var(--color-white, #fff);
  font-size: var(--text-sm, 0.875rem);
  line-height: 1.25rem;
  font-weight: var(--font-weight-bold, 700);
  color: var(--color-gray-800, #1f2937);
  white-space: nowrap;
}
.pp-bildgruppe-kopf small {
  margin-left: 0.35rem;
  font-weight: normal;
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-500, #6b7280);
}
.pp-bildgruppe-liste {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
  min-height: 5rem;
}
@media (min-width: 640px)  { .pp-bildgruppe-liste { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 768px)  { .pp-bildgruppe-liste { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .pp-bildgruppe-liste { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

/* Leiste unten im Bild: Dateiname und Papierkorb. */
.pp-bild-leiste {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 1.1rem 0.4rem 0.3rem;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0));
  color: #fff;
  font-size: var(--text-xs, 0.75rem);
  pointer-events: none;
}
.pp-bild-leiste-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.pp-bild-leiste-knopf {
  pointer-events: auto;
  flex: none;
  line-height: 1;
  padding: 0.3rem;
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.85);
}
.pp-bild-leiste-knopf:hover {
  background: #fff;
}
/* Das rote Kreuz in der Leiste: klickbar, obwohl die Leiste Zeiger durchlässt. */
.pp-bild-leiste .pp-btn {
  pointer-events: auto;
  flex: none;
}

/* Videos im Raster: Abspielzeichen mittig, oben links der Zustand, solange
   das Video umgerechnet wird oder es nicht geklappt hat. */
.pp-bild-video {
  position: absolute;
  left: 50%;
  top: 40%;
  width: 2.5rem;
  height: 2.5rem;
  margin: -1.25rem 0 0 -1.25rem;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M9 6.5v11l9-5.5z'/%3E%3C/svg%3E") center / 1.25rem no-repeat;
  pointer-events: none;
}
.pp-bild-zustand {
  position: absolute;
  top: 0.35rem;
  left: 0.35rem;
  padding: 0.1rem 0.4rem;
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.9);
  color: var(--color-gray-700, #374151);
  font-size: var(--text-xs, 0.75rem);
  pointer-events: none;
}
.pp-bild-zustand--fehler {
  background: #fee2e2;
  color: #b91c1c;
}

/* "Nicht zugeordnet": immer da, leer als Ablagefläche. */
.pp-bildgruppe--frei {
  border-style: dashed;
}
.pp-bildgruppe-leer {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 5rem;
  border: 2px dashed var(--color-gray-300, #d1d5db);
  border-radius: 0.5rem;
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-500, #6b7280);
  text-align: center;
  padding: 0.5rem;
}
/* Sobald ein Bild in der leeren Ablage liegt, weicht der Hinweis. */
.pp-bildgruppe-liste:has([data-sort-id]) .pp-bildgruppe-leer {
  display: none;
}

/* Nur für Screenreader: die Ansage beim Sortieren per Tastatur. */
.pp-sort-ansage {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* --- Ablagefeld für Bild-Uploads (js/app_shared/ablage.js) --- */

.pp-ablage {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.75rem;
  padding: 1rem;
  min-height: 90px;
  border: 1px dashed var(--color-gray-300, #d1d5db);
  border-radius: 0.5rem;
  background: var(--color-white, #fff);
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-gray-700, #374151);
  cursor: pointer;
  user-select: none;
  transition: box-shadow 120ms, background-color 120ms;
}
.pp-ablage:focus-visible {
  outline: 2px solid var(--color-header-button-500, #3b82f6);
  outline-offset: 2px;
}
.pp-ablage--aktiv {
  background: var(--color-gray-50, #f9fafb);
  box-shadow: 0 0 0 2px var(--color-gray-300, #d1d5db);
}
.pp-ablage[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}
.pp-ablage[aria-disabled="true"] label {
  pointer-events: none;
}
/* Neben den Fotos (Über uns): schmal, Knopf unter dem Text. */
.pp-ablage--seite {
  margin-top: 0;
  flex: 0 1 15rem;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}

/* --- Rückfälle für ältere Browser --- */

/* Tailwind v4 verschiebt über die Eigenschaft translate (Chrome 104,
   Safari 14.1). Davor bleibt nur transform - gebraucht im Buchungsformular
   (Hinweisblasen, Knopf im Telefonfeld). */
@supports not (translate: 0) {
  .-translate-x-1\/2 { transform: translateX(-50%); }
  .-translate-y-1\/2 { transform: translateY(-50%); }
  .-translate-x-1 { transform: translateX(-0.25rem); }
  .-translate-y-1 { transform: translateY(-0.25rem); }
}

/* --- Besucherstatistik (js/app_shared/statistik_anzeige.js) --- */

.pp-stat {
  container-type: inline-size;
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-gray-700, #374151);
}
.pp-stat-auswahl {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-bottom: 0.75rem;
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-500, #6b7280);
}
.pp-stat-auswahl select {
  margin-left: 0.25rem;
  padding: 0.15rem 0.4rem;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 0.375rem;
  background: #fff;
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-800, #1f2937);
  max-width: 14rem;
}
.pp-stat-kacheln {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5rem;
  margin-bottom: 1rem;
}
@container (max-width: 480px) {
  .pp-stat-kacheln { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.pp-stat-kachel {
  border: 1px solid var(--color-gray-200, #e5e7eb);
  border-radius: 0.5rem;
  padding: 0.5rem;
  text-align: center;
  background: var(--color-gray-50, #f9fafb);
}
.pp-stat-kachel-zahl {
  font-size: 1.35rem;
  font-weight: var(--font-weight-semibold, 600);
  color: var(--color-gray-800, #1f2937);
  line-height: 1.2;
}
.pp-stat-kachel-text {
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-500, #6b7280);
}
.pp-stat-abschnitt {
  margin-bottom: 1rem;
}
.pp-stat-abschnitt h4 {
  margin: 0 0 0.35rem;
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--color-gray-800, #1f2937);
}
.pp-stat-tabelle {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-xs, 0.75rem);
}
.pp-stat-tabelle th {
  text-align: left;
  font-weight: var(--font-weight-medium, 500);
  color: var(--color-gray-500, #6b7280);
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
  padding: 0.2rem 0.4rem 0.2rem 0;
}
.pp-stat-tabelle td {
  border-bottom: 1px solid var(--color-gray-100, #f3f4f6);
  padding: 0.25rem 0.4rem 0.25rem 0;
  overflow-wrap: break-word;
}
.pp-stat-tabelle .pp-stat-rechts,
.pp-stat-rechts {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.pp-stat-klein {
  color: var(--color-gray-500, #6b7280);
}
.pp-stat-warn {
  color: var(--color-amber-600, #d97706);
}
.pp-stat-balken {
  display: grid;
  gap: 0.25rem;
  font-size: var(--text-xs, 0.75rem);
}
.pp-stat-balken-zeile {
  display: grid;
  grid-template-columns: 5.5rem 1fr 3rem;
  align-items: center;
  gap: 0.5rem;
}
.pp-stat-balken-spur {
  height: 0.5rem;
  border-radius: 999px;
  background: var(--color-gray-100, #f3f4f6);
  overflow: hidden;
}
.pp-stat-balken-spur > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--color-header-button-500, #0d9488);
}
.pp-stat-hinweis {
  margin: 0.35rem 0 0;
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-500, #6b7280);
}
.pp-stat-leer {
  margin: 0.5rem 0;
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-gray-500, #6b7280);
}
.pp-stat-fehler {
  color: var(--color-red-600, #dc2626);
}

/* Owner-Verwaltung: in der Zeile "Objekte: 4 - Aufrufe: 321" sitzt rechts
   ein Dreieck, das darunter die Statistik des Owners aufklappt - zeigt nach
   links, aufgeklappt nach unten. */
/* .prop-cell-sub ist einzeilig mit Abschneiden - bei rechtsbündiger Zelle
   (Karte auf dem Telefon) fiele der Überstand links heraus. Hier lieber
   umbrechen. */
.prop-cell-sub.pp-stat-zeilenkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  white-space: normal;
  overflow: visible;
}
.pp-stat-zeilenkopf > span {
  min-width: 0;
  overflow-wrap: anywhere;
}
.pp-stat-dreieck {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  /* Nicht über die Zelle hinaus: sie schneidet ab (overflow: hidden), und
     beim Fokus scrollte der Browser sie sonst seitwärts. */
  margin: -0.25rem 0;
  border: 0;
  border-radius: 0.25rem;
  background: none;
  color: var(--color-header-button-500, #0d9488);
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
  transition: transform 150ms ease;
}
.pp-stat-dreieck:hover {
  background: var(--color-gray-100, #f3f4f6);
}
.pp-stat-dreieck[aria-expanded="true"] {
  transform: rotate(-90deg);
}
/* Eigene Regeln mit höherer Spezifität als die der Owner-Spalten
   (#admin-owners-table td:nth-child(1): max-width 0, als Karte
   display: contents). */
#admin-owners-table tr.pp-stat-zeile > td.pp-stat-zelle {
  display: table-cell;
  width: auto;
  max-width: none;
  white-space: normal;
  padding: 0 0 0.75rem;
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
}
.pp-stat-box {
  margin: 0.25rem 0.5rem 0;
  padding: 0.75rem;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 0.5rem;
  background: var(--color-gray-50, #f9fafb);
  text-align: left;
}
.pp-stat-box .pp-stat-kachel {
  background: #fff;
}
@container (max-width: 560px) {
  #admin-owners-table tr.pp-stat-zeile {
    display: block;
    padding: 0;
    border: 0;
    background: none;
  }
  #admin-owners-table tr.pp-stat-zeile > td.pp-stat-zelle {
    display: block;
    border: 0;
    padding: 0 0 6px;
  }
  .pp-stat-box {
    margin: -2px 0 0;
  }
}

/* In der Owner-Verwaltung liegen die Tabellen der Statistik in der
   Owner-Tabelle - deren Spaltenbreiten, Kopfzeilen und Rahmen gelten dort
   nicht (#admin-owners-table th:nth-child(...), .props-table th/td). */
#admin-owners-table .pp-stat-tabelle th,
#admin-owners-table .pp-stat-tabelle td {
  width: auto;
  max-width: none;
  min-width: 0;
  height: auto;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
  padding: 0.25rem 0.5rem 0.25rem 0;
  font-size: var(--text-xs, 0.75rem);
  font-weight: normal;
  text-align: left;
  vertical-align: top;
  white-space: normal;
  color: var(--color-gray-700, #374151);
}
#admin-owners-table .pp-stat-tabelle th {
  font-weight: var(--font-weight-medium, 500);
  color: var(--color-gray-500, #6b7280);
}
#admin-owners-table .pp-stat-tabelle th:first-child,
#admin-owners-table .pp-stat-tabelle td:first-child {
  width: 100%;
}
#admin-owners-table .pp-stat-tabelle .pp-stat-rechts {
  text-align: right;
  white-space: nowrap;
  padding-left: 0.6rem;
}
@container (max-width: 400px) {
  #admin-owners-table .pp-stat-tabelle th,
  #admin-owners-table .pp-stat-tabelle td {
    padding-right: 0.2rem;
  }
  #admin-owners-table .pp-stat-tabelle .pp-stat-rechts {
    padding-left: 0.3rem;
  }
}
#admin-owners-table .pp-stat-tabelle .pp-stat-klein {
  white-space: nowrap;
  color: var(--color-gray-500, #6b7280);
}
@container (max-width: 560px) {
  #admin-owners-table .pp-stat-tabelle,
  #admin-owners-table .pp-stat-tabelle tbody {
    display: table-row-group;
  }
  #admin-owners-table .pp-stat-tabelle { display: table; }
  #admin-owners-table .pp-stat-tabelle thead { display: table-header-group; }
  #admin-owners-table .pp-stat-tabelle tr {
    display: table-row;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
  }
  #admin-owners-table .pp-stat-tabelle th,
  #admin-owners-table .pp-stat-tabelle td {
    display: table-cell;
  }
}

/* Reiterleiste der Listen (properties.html): vier Reiter passen auf dem
   Telefon nicht nebeneinander. Enger, und was dann noch fehlt, lässt sich
   in der Leiste selbst schieben - nicht im ganzen Dialog. */
.pp-reiterleiste {
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.pp-reiterleiste::-webkit-scrollbar {
  display: none;
}
.pp-reiterleiste > button {
  flex: none;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .pp-reiterleiste > button {
    padding-left: 0.55rem;
    padding-right: 0.55rem;
  }
}

/* Tabellen der Statistik: werden sie zu breit, schieben sie sich für sich. */
.pp-stat-rollen {
  overflow-x: auto;
}

/* --- Abo-Seite (templates/abo.html) --- */

.pp-abo-stand {
  font-size: 1.05rem;
  font-weight: 600;
}
.pp-abo-stand[data-zustand="expired"],
.pp-abo-stand[data-zustand="trial_expired"] {
  color: var(--color-red-600, #dc2626);
}

.pp-abo-titel {
  font-weight: 600;
}

.pp-abo-meldung {
  margin-top: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  font-size: 0.875rem;
  background: var(--color-gray-100, #f3f4f6);
}
.pp-abo-meldung[data-art="gut"] {
  background: var(--color-green-50, #f0fdf4);
  color: var(--color-green-700, #15803d);
}
.pp-abo-meldung[data-art="schlecht"] {
  background: var(--color-red-50, #fef2f2);
  color: var(--color-red-600, #dc2626);
}

/* Ein Tarif zur Wahl: die ganze Karte ist das Label des Radioknopfs. */
.pp-abo-tarif {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.6rem;
  row-gap: 0.1rem;
  align-items: center;
  padding: 0.75rem;
  border: 1px solid var(--color-gray-300, #d1d5db);
  border-radius: 0.5rem;
  cursor: pointer;
}
.pp-abo-tarif:has(input:checked) {
  border-color: var(--color-header-button-500, #009fa6);
  box-shadow: 0 0 0 1px var(--color-header-button-500, #009fa6);
}
.pp-abo-tarif > span {
  grid-column: 2;
}
.pp-abo-tarif-name {
  font-weight: 600;
}
.pp-abo-tarif-preis {
  font-size: 0.95rem;
}

/* Zahlungen: Spalten so breit wie ihr Inhalt, Beträge rechtsbündig. */
.pp-abo-rollen {
  overflow-x: auto;
  margin-top: 0.75rem;
}
.pp-abo-tabelle {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
.pp-abo-tabelle th,
.pp-abo-tabelle td {
  padding: 0.25rem 0.5rem;
  text-align: left;
  vertical-align: top;
  white-space: nowrap;
  border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
}
.pp-abo-tabelle th {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-gray-500, #6b7280);
}
.pp-abo-tabelle th:first-child,
.pp-abo-tabelle td:first-child {
  padding-left: 0;
}
.pp-abo-tabelle tbody tr:last-child td {
  border-bottom: 0;
}
.pp-abo-tabelle .pp-abo-zahl {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* IBAN, Karte oder PayPal-Adresse unter der Zahlart. */
.pp-abo-angabe {
  display: block;
  font-size: 0.75rem;
  color: var(--color-gray-500, #6b7280);
}
.pp-abo-tabelle td[data-status="paid"] {
  color: var(--color-green-700, #15803d);
}
.pp-abo-tabelle td[data-status="failed"],
.pp-abo-tabelle td[data-status="chargeback"] {
  color: var(--color-red-600, #dc2626);
}

/* Schmal: jede Zahlung eine Karte, die Spaltenüberschrift steht vor dem Wert. */
@media (max-width: 560px) {
  .pp-abo-tabelle thead {
    display: none;
  }
  .pp-abo-tabelle tr {
    display: block;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
  }
  .pp-abo-tabelle tbody tr:last-child {
    border-bottom: 0;
  }
  .pp-abo-tabelle td,
  .pp-abo-tabelle .pp-abo-zahl {
    display: grid;
    grid-template-columns: 6.5rem 1fr;
    padding: 0.1rem 0;
    border: 0;
    text-align: left;
    white-space: normal;
  }
  .pp-abo-tabelle td::before {
    content: attr(data-label);
    grid-row: span 2;
    font-size: 0.75rem;
    color: var(--color-gray-500, #6b7280);
  }
  .pp-abo-angabe {
    grid-column: 2;
  }
}

/* Kündigungsfenster: was nach dem Ende des Abos gilt. Was bleibt, steht
   zuerst und ruhig; was wegfällt, trägt ein Kreuz. */
.pp-abo-folgen {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}
.pp-abo-folgen li {
  position: relative;
  padding: 0.2rem 0 0.2rem 1.5rem;
}
.pp-abo-folgen li::before {
  content: "\2715";
  position: absolute;
  left: 0.2rem;
  color: var(--color-red-600, #dc2626);
}
.pp-abo-folgen li[data-art="bleibt"]::before {
  content: "\2713";
  color: var(--color-green-700, #15803d);
}
.pp-abo-fenster-knoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

/* --- Abo-Verwaltung für Entwickler (partials/abo_verwaltung_modal.html) --- */

/* Kopf einer Owner-Zeile: Dreieck, wer, Abo-Stand, Zahlungsperiode. Feste
   Breiten rechts, damit die Spalten über alle Zeilen fluchten. */
.pp-aboverw-kopf {
  display: grid;
  grid-template-columns: 1.1rem minmax(0, 1fr) 9rem 12rem;
  gap: 0.5rem;
  align-items: baseline;
}
.pp-aboverw-werte {
  display: grid;
  gap: 0.15rem;
}
.pp-aboverw-paar {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 0.5rem;
  overflow-wrap: anywhere;
}
.pp-aboverw-name {
  opacity: 0.7;
}
/* Aufgeklappte Zeile: links die Einzelheiten, rechts daneben
   "Abo zurücksetzen". */
.pp-aboverw-oben {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}
.pp-aboverw-oben > .pp-aboverw-werte {
  flex: 1 1 auto;
  min-width: 0;
}
.pp-aboverw-oben > .pp-btn {
  flex: none;
}
.pp-aboverw-klapp-inhalt {
  margin-top: 0.35rem;
}
/* Stand bei Mollie und Zahlungen: je unter einer Linie, zum Aufklappen. */
.pp-aboverw-klapp {
  padding-top: 0.5rem;
  border-top: 1px solid currentColor;
  border-top-color: color-mix(in srgb, currentColor 25%, transparent);
  overflow-wrap: anywhere;
}
/* Das kräftige Dreieck gehört der Abo-Zeile, das leise dem Unterpunkt
   darin: so liest sich, was wozu gehört. Deshalb hier kein Standardzeichen
   des Browsers, sondern ein eigenes. */
.pp-aboverw-dreieck {
  font-size: 0.7em;
}
.pp-aboverw-klapp > summary {
  cursor: pointer;
  list-style: none;
}
.pp-aboverw-klapp > summary::-webkit-details-marker {
  display: none;
}
.pp-aboverw-klapp > summary::before {
  content: "▸";
  display: inline-block;
  width: 1.1rem;
  opacity: 0.6;
}
.pp-aboverw-klapp[open] > summary::before {
  content: "▾";
}
.pp-aboverw-titel {
  margin-top: 0.4rem;
  font-weight: 600;
}
@media (max-width: 560px) {
  .pp-aboverw-kopf {
    grid-template-columns: 1.1rem minmax(0, 1fr);
  }
  .pp-aboverw-kopf > span:nth-child(n+3) {
    grid-column: 2;
  }
  .pp-aboverw-paar {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

/* Trennt im Entwicklerkonto das Preisformular vom Abo-Stand darüber. */
.pp-abo-trenner {
  margin: 1rem 0;
  border: 0;
  border-top: 1px solid var(--color-gray-200, #e5e7eb);
}

/* Papierkorb am Zeilenende. Steht in jeder Zeile; wo nichts zu löschen ist,
   blass und ohne Zeiger - so fluchtet die Spalte, und man sieht, dass es
   das Löschen gibt. */
.pp-abo-tabelle .pp-abo-korb-spalte {
  width: 1.75rem;
  padding-left: 0;
  padding-right: 0;
  text-align: center;
}
.pp-abo-korb {
  padding: 0;
  border: 0;
  background: none;
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
}
.pp-abo-korb:disabled {
  opacity: 0.2;
  filter: grayscale(1);
  cursor: default;
}

/* Preis-Historie im Entwicklerkonto: zugeklappt unter dem Preisformular. */
.pp-abo-historie {
  margin-top: 0.75rem;
  font-size: 0.875rem;
}
.pp-abo-historie > summary {
  cursor: pointer;
  color: var(--color-gray-600, #4b5563);
}

/* Kopfzeile der ersten Box: links der Abo-Stand, rechts der Testmodus. */
.pp-abo-kopfzeile {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.25rem 1rem;
}
/* Leiser Zusatz hinter dem Abo-Stand ("kein Abo nötig"). */
.pp-abo-leise {
  margin-left: 0.5rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--color-gray-500, #6b7280);
}
.pp-abo-testmodus {
  font-size: 0.75rem;
  color: var(--color-red-600, #dc2626);
}

/* Preise: beide Felder und der Knopf in einer Zeile; umgebrochen wird erst,
   wenn der Platz nicht reicht. */
.pp-abo-preiszeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}
.pp-abo-preiszeile > .relative {
  flex: 1 1 9rem;
}
.pp-abo-preiszeile > .pp-btn {
  flex: none;
}

/* Rechnungsadresse: zwei Spalten, schmal untereinander. */
.pp-abo-adresse {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
.pp-abo-spalte {
  display: grid;
  gap: 1rem;
  align-content: start;
}
.pp-abo-plzort {
  display: grid;
  grid-template-columns: minmax(6rem, 1fr) 2fr;
  gap: 1rem;
}
@media (max-width: 640px) {
  .pp-abo-adresse {
    grid-template-columns: 1fr;
  }
}

/* --- Spinner im Knopf, solange er arbeitet (z.B. "HTML-ZIP wird erzeugt") --- */
.pp-btn-spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-left: 0.5em;
  vertical-align: -0.12em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: pp-spin 0.7s linear infinite;
}

/* Sortierbare Spaltenköpfe (Owner-Verwaltung): sehen aus wie die Überschrift */
.pp-sort-kopf {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.pp-sort-kopf:hover,
.pp-sort-kopf.is-aktiv {
  color: var(--color-gray-800, #1f2937);
}

/* Unter einem Haken der Owner-Verwaltung, solange gespeichert wird */
.pp-haken-spinner {
  display: flex;
  justify-content: center;
  margin-top: 0.25rem;
  color: var(--color-gray-400, #9ca3af);
}
.pp-haken-spinner .pp-btn-spinner {
  margin-left: 0;
}

/* --- Export-Übersicht: Spinner an dem, was gerade arbeitet (export_status.js) --- */
.pp-exstat-spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-left: 0.35em;
  vertical-align: -0.1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  opacity: 0.6;
  animation: pp-spin 0.7s linear infinite;
}
#exstat-loading .pp-exstat-spinner {
  width: 1.6rem;
  height: 1.6rem;
  color: var(--color-gray-400, #9ca3af);
}

/* --- Export-Übersicht: Lage der Warteschlange (export_status_modal.html) --- */
.pp-exstat-lage {
  display: grid;
  grid-template-columns: max-content 1fr 1fr;
  column-gap: 1rem;
  row-gap: 0.15rem;
}

/* Abmelden in Zeile 1 links neben der Sprachwahl. In der Kompaktansicht
   (einzeilig, mit Menü - dieselbe Bedingung wie .pp-header-menu-btn) steht
   er stattdessen als letzter Eintrag im Menü. */
.pp-header-logout {
  flex: 0 0 auto;
}
.pp-header-logout-menu {
  display: none;
}
@media (max-width: 640px), (max-height: 560px) {
  .pp-header-logout {
    display: none;
  }
  .pp-header-actions .pp-btn.pp-header-logout-menu {
    display: inline-flex;
  }
  .pp-header-actions .pp-header-sep.pp-header-logout-menu {
    display: block;
  }
}


/* --- Rückfrage beim Verlassen mit ungesicherten Änderungen
   (app_shared/ungesichert.js) --- */
.pp-ungesichert {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10000;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.45);
}
.pp-ungesichert-kasten {
  width: 100%;
  max-width: 440px;
  padding: 1.25rem 1.5rem;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18);
}
.pp-ungesichert-titel {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
  font-weight: 600;
}
.pp-ungesichert-text {
  margin: 0 0 1rem;
  font-size: 0.9rem;
  line-height: 1.5;
}
.pp-ungesichert-fehler {
  margin: 0 0 1rem;
  font-size: 0.85rem;
  color: var(--color-red-700, #b91c1c);
}
.pp-ungesichert-knoepfe {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
}
