/* static/css/styles.css
   Minimal: only what is NOT (yet) Tailwind in templates
*/

/* Ersatz für Tailwind-Arbitrary-Values wie -top-[11px]/overflow-y-visible,
   die NICHT in der vorkompilierten app.css landen, solange niemand nach
   einer Template-Änderung erneut `tailwindcss` gebaut hat (Tailwind
   generiert nur Klassen, die es beim Build im Content-Scan findet - kein
   Live-JIT hier). Plain CSS wirkt sofort, ohne Build-Schritt. */
.pp-ftop {
  top: -11px;
}

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

/* Bündelt die 7 Utility-Klassen des wiederkehrenden Floating-Label-Musters
   ("absolute pp-ftop left-2 text-xs font-medium text-gray-500 bg-white
   px-1"), das bislang 56x wortwörtlich in Templates/JS dupliziert war -
   siehe Code-Review. Werte über die von app.css (Tailwind) auf :root
   gesetzten Custom Properties bezogen, damit sie mit den echten
   Utility-Klassen synchron bleiben, nicht hartkodiert. Plain CSS wie
   .pp-ftop oben, kein Tailwind-Rebuild nötig. */
.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) sollen als eigene Grid-Zellen im Eltern-Grid
   erscheinen, aber trotzdem gemeinsam per einer einzigen ID/Klasse
   ein-/ausblendbar bleiben (siehe #offerPriceContainer in
   booking_form.html + form.js::checkBookingVisibility()). Tailwinds
   "contents"-Utility ist dafür noch nicht im vorkompilierten app.css
   (kein Live-Build hier, siehe .pp-ftop oben), daher Plain CSS.
   Die zweite Regel erzwingt "display:none" auch dann, wenn zusätzlich
   .hidden gesetzt wird - ohne sie würde die Lade-Reihenfolge der
   Stylesheets (app.css vor styles.css) das Verstecken bei gleicher
   Selektor-Spezifität sonst überschreiben können. */
.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;
}

/* ===== PP Quill: Toolbar overlay ü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; /* JS toggled */
  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;
}

/* optionaler Pfeil */
[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 GRID
   schmal:  cal -> form -> list
   breit:   cal | list  (oben), form darunter
   ========================================================= */
.pp-owner-grid {
  display: grid;
  gap: calc(var(--spacing) * 4);
  align-items: start;

  /* 2 Spalten wenn Container breit genug, sonst 1 */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));

  /* SCHMAL: Kalender, dann Form, dann Liste */
  grid-template-areas:
    "cal"
    "form"
    "list";
}

@container (min-width: 580px) {
  .pp-owner-grid {
    grid-template-areas:
      "cal list"
      "form form";
  }
}

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

/* =========================================================
   PROPERTIES PANELS (CQ statt Tailwind lg/md 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%;
  }
}

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

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

/* Helper: 3 Spalten innerhalb eines Panels – IMMER 3-spaltig (auch schmal) */
.pp-props-grid-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* =========================================================
   FORM GRID (Reihenfolge NUR auf schmal fixen)
   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 müssen in booking_form.html gesetzt sein */
.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; }
}

/* Currency input helper */
.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;
}

/* Hochkant-Label für Quill-Editor-Felder: sitzt INNERHALB der Box, oben
   links (statt außerhalb auf der Rahmenlinie - dort war es unsichtbar,
   vermutlich durch den overflow-x-auto-Formular-Wrapper abgeschnitten).
   Die Quill-Werkzeugleiste ist kein fester Balken, sondern ein Popup, das
   erst beim Markieren von Text ÜBER der Box erscheint
   ([data-pp-quill="1"] .pp-quill-toolbar, bottom:100%, z-index:9999) -
   kollidiert damit nicht mit einem oben-links sitzenden Label.
   pointer-events:none, damit es keine Klicks/Cursor-Platzierung ganz 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;
}

/* ===== Guest calendar responsive (analog zum Owner: auto-fit, kein CQ-Breakpoint) ===== */

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

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

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

/* Overview-Block immer volle Breite (unter beiden Spalten) */
.pp-guest-overview {
  grid-column: 1 / -1;
}

/* =========================================================
   Calendar decorations
   ========================================================= */

.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 (Markup nutzt opacity-0 + transition via Tailwind).

   Die Preis-Aufschlüsselung (Saisonpreise, Rabatte, Gebühren - Inhalt kommt
   aus price.js::updatePrice()) hing ausschließlich an dieser :hover-Regel.
   Auf Touch-Geräten gibt es kein Hover, also war der Inhalt dort überhaupt
   nicht erreichbar - für Gäste am Telefon genau die Information, an der die
   Buchungsentscheidung hängt. Deshalb jetzt zweigleisig: Hover nur noch für
   Zeigergeräte, dazu eine per Tap gesetzte Klasse (.pp-price-open, siehe
   price.js::initPriceInfoToggle() und den Button in booking_price_box.html). */
@media (hover: hover) and (pointer: fine) {
  #priceBox:hover #priceTooltip {
    opacity: 1;
  }
}

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

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

/* Tap-Affordanz für die Aufschlüsselung - nur dort sichtbar, wo es kein
   Hover gibt, damit das Desktop-Layout unverändert bleibt.

   Der Marker sitzt mittig im ohnehin leeren pb-4-Streifen am unteren Rand
   der Box. Rechts unten wäre er über #priceTotal gelandet, und eine auf
   44px vergrößerte Schaltfläche hätte den Preisbetrag verdeckt - das
   eigentliche Tap-Ziel ist deshalb die gesamte Preisbox (siehe
   initPriceInfoToggle() in price.js), der Button bleibt daneben als
   fokussierbares Bedienelement für Tastatur und Screenreader bestehen. */
.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;
}

/* =========================================================
   Tables (properties, seasons, iCal, booking list)
   ========================================================= */

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);
  }
}

/* Icons in tables */
.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); }
}

/* Action columns (icon-only) */
.prop-actions-col {
  width: 36px;
  text-align: center;
}

/* Merged action column: sync + calendar + edit icons side by side */
.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 table: URL nimmt Rest, andere bleiben sichtbar
   ========================================================= */

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

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

/* Rest: feste/min Breiten (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() und setzt dort bewusst
   keine Klassen - die Spaltenbreiten kommen ohnehin von den <col> oben,
   und table-layout:fixed macht den Rest. Hier standen frueher zusaetzlich
   td-Selektoren; die haben nie gegriffen und sind entfernt. */

/* Non-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 (Q&A): Frage und Antwort nebeneinander, schmal untereinander
   =========================================================
   Zwei Quill-Editoren nebeneinander brauchen Platz. Bleibt jedem nur die
   halbe Breite eines Handys, ist Schreiben darin nicht mehr zumutbar -
   deshalb stapeln sie sich unterhalb der Schwelle.

   Mobile-first wie beim Kontakt-Praeferenz-Block weiter unten: der
   gestapelte Zustand ist der Normalfall, das Nebeneinander schaltet die
   @container-Regel erst zu, wenn der Platz da ist.

   @container statt @media, weil die Breite dieses Kastens zaehlt, 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. Sitzt bewusst INNERHALB des Rahmens,
   abgesetzt wie die Legende eines Feldes - aussen daneben haette sie sich
   nicht eindeutig einem der beiden Felder zuordnen lassen.
   Quill haengt Werkzeugleiste und Editor an den Kasten an (appendChild),
   loescht ihn nicht - das erzeugte ::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 rechtsbuendig: der Knopf gehoert zur ganzen Zeile,
   nicht zu einem der beiden Felder, und steht darum darueber. */
.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; }
}

/* Die Spaltenkoepfe stehen einmalig ueber 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 ueber der Loeschen-Spalte, sonst sitzen die beiden Koepfe um
   die Knopfbreite verschoben ueber 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 (Web-Seite): Handlung / Einstellungen / Ergebnis
   =========================================================
   Drei durch feine Linien getrennte Baender. 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 erst 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;
}

/* Feine Linie, deutlich zurueckhaltender als der <hr> der Abschnittstrenner
   weiter oben - sie gliedert innerhalb eines Kastens, trennt nicht Kaesten. */
.pp-astro-rule {
  border: 0;
  border-top: 1px solid var(--color-gray-200, #e5e7eb);
  margin: 12px 0;
}

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

/* Knappe Beschriftung. "Sprachen" steht ueber 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);
}

/* Bewusst OHNE @container/@media: hier gibt es keinen Umschaltpunkt, sondern
   nur Umbrechen nach Platz. Zwei Ebenen:

     Zeile 1  Sprachen - immer fuer sich, die Chips brechen intern um
     Zeile 2  Vorlage und Status nebeneinander, solange sie zusammen passen;
              sonst rutscht der Status darunter

   Der Umbruch entsteht allein aus den Flex-Basen, nicht aus einer Abfrage. */

/* 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;
}

/* Natuerliche Breite, schrumpft nicht: "Vorlage:" und das Auswahlfeld sollen
   lesbar bleiben, notfalls rutscht lieber der Status weg. */
.pp-astro-field {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
}

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

/* margin-left:auto haelt den Status am rechten Rand, solange er neben der
   Vorlage steht. Reicht der Platz nicht, bricht er darunter um - dort schiebt
   ihn dasselbe margin ebenfalls nach rechts, sodass er in beiden Faellen an
   derselben Kante sitzt. */
.pp-astro-status {
  flex: 0 1 auto;
  margin-left: auto;
  font-size: 0.875rem;
  color: var(--color-gray-600, #4b5563);
  text-align: right;
}

/* =========================================================
   Übersetzungs-Korrekturen: Zeile anzeigen, im Overlay bearbeiten
   =========================================================
   Auf Touch-Geräten rendert 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 Koerper sind ZWEI Tabellen
   =========================================================
   Der Kopf steht fest, der Koerper scrollt - dafuer sind es zwei getrennte
   <table>-Elemente. Ohne feste Spaltenbreiten richtet jede ihre Spalten nach
   ihrem eigenen Inhalt aus, und die Ueberschriften stehen nicht ueber ihren
   Werten. table-layout:fixed plus gleiche Breiten bringt sie zur Deckung. */
.pp-cat-table {
  table-layout: fixed;
  width: 100%;
}

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

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

/* Der Koerper bekommt eine Bildlaufleiste, der Kopf nicht - dadurch waere die
   nutzbare Breite unterschiedlich und die letzte Ueberschrift saesse um deren
   Breite versetzt. scrollbar-gutter reserviert den Platz in BEIDEN, auch wo
   gar nicht gescrollt wird. Kein JS noetig; wo der Browser es nicht kennt,
   bleibt hoechstens der bisherige kleine Versatz. */
.pp-cat-head,
.pp-cat-body {
  scrollbar-gutter: stable;
}

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

/* =========================================================
   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);
}

/* Ohne diese Zeile bliebe das Overlay dauerhaft sichtbar: 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: 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;
}

/* 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 fuer 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 vergroesserten 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, unabhaengig davon, welche Zellen
     ausgeblendet sind. Auf Touch ist die Aufteilung eine andere:

       Spalte 1-3  von der Anzeigezelle zusammengefasst (colspan 3)
       Spalte 4    Aktiv          <- war fuer "Letzter Sync" auf 100px ausgelegt
       Spalte 5    Aktion

     Deshalb werden hier die Breiten umgehaengt, 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 haelt sie dagegen in der Zelle und kuerzt 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 */
  }
}

/* Rueckmeldung nach dem Kopieren - kurz und deutlich, statt nur eines
   Farbwechsels von 400ms, den man auf dem Telefon leicht uebersieht. */
.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 Schliessen ihres Overlays fuer sich,
     und das Loeschen ruft die API ohnehin direkt. Ein globaler
     Speichern-Knopf haette damit nichts mehr zu tun. */
  #season-save-btn,
  #rebate-save-btn,
  #fee-save-btn,
  #trov-save-btn,
  #ical-save-btn {
    display: none;
  }
}

.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 buendig stehen - dort aber ohne die Hervorhebung der Datenzeile. */
thead .pp-row-view-main {
  font-weight: inherit;
}

/* .season-table th zentriert - fuer die zweizeilige Touch-Ueberschrift ist
   das falsch: die Bezeichnungen gehoeren 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. Mit "thead .pp-row-view" waeren beide gleich stark
   gewesen - dann entscheidet die Reihenfolge, und .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;
}

/* Von unten hereingeschoben statt mittig: auf dem Telefon ist der untere
   Bildschirmrand die einzige Stelle, die der Daumen sicher erreicht - und die
   eingeblendete Tastatur schiebt einen mittigen Kasten sonst aus dem Bild.
   dvh statt vh, weil vh die ein- und ausfahrende Adressleiste ignoriert. */
/* Vorschau der Vorlage im Export-Bereich.

   Feste Hoehe mit eigenem Bildlauf: die Musterseite ist laenger als der
   Ausschnitt, soll die Export-Box aber nicht aufblaehen. Auf Touch etwas
   flacher, dort ist der Bildschirm knapper. */
.pp-astro-preview {
  border: 1px solid var(--color-gray-200, #e5e7eb);
  border-radius: 8px;
  overflow: hidden;
  background: var(--color-white, #fff);
}

.pp-astro-preview iframe {
  display: block;
  width: 100%;
  height: 320px;
  border: 0;
}

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

/* =========================================================
   Properties table: fixed layout + ellipsis + column widths
   ========================================================= */

.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; /* needed so inner ellipsis can work */
}

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

/* widths: properties table (6 cols) */
.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;
}

/* subtle hover highlight for props-table (except booking-table) */
@media (hover: hover) and (pointer: fine) {
  .props-table:not(.booking-table) tbody tr:hover td {
    background-color: #f7faff;
  }
}

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

/* =========================================================
   Occupancy timeline (properties overview): 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 Bloecken - je Monat einer, so breit wie
   der Monat Tage hat. Frueher lagen die Beschriftungen absolut positioniert
   uebereinander in einem gemeinsamen Kasten; das war mit position:sticky
   unvereinbar (absolut positionierte Elemente kleben nicht). Erst durch die
   eigenen Bloecke bekommt jede Beschriftung einen umschliessenden Block, an
   dessen Rand sie haengen bleibt: sie klebt am linken Rand des Sichtfensters,
   bis der Block nach links herauslaeuft und sie mitnimmt - genau in dem
   Moment, in dem der naechste Monat nachrueckt. */
.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 haette das Kleben wieder ausgeschaltet. Begrenzt wird
     die Beschriftung ohnehin durch ihren Block: sticky laesst sie nie
     darueber hinaus wandern. */
  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;
}

@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);
}

/* =========================================================
   Season table tweaks (base table styles apply)
   ========================================================= */

.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;
  }
}

/* =========================================================
   Season + Rebate tables: compact width-first layout
   Voraussetzung im JS:
   - Preis-TD:   class="price-cell"
   - Nächte-TD:  class="nights-cell"
   - Status-TD:  class="status-cell"
   - Action-TD:  class="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;
}

/* Range */
.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 Grid — 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;
  }
}

/* Datumsbereich-Eingabe mit Clear-Button */
.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;
}

/* Action */
.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;
  }
}

/* =========================================================
   Selected state
   ========================================================= */

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

/* =========================================================
   Fee table column widths
   ========================================================= */

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

/* Fee detail sub-row */
.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;
}

/* =========================================================
   Shared small helper: round plus icon buttons
   ========================================================= */

.icon-btn {
  background: var(--color-white);
  color: var(--color-gray-800);
  border: 1px solid var(--color-gray-300);

  border-radius: 6px;        /* statt rund */
  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 {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

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

/* =========================================================
   Buchungsformular: Kontakt-Präferenz Pills
   ========================================================= */

/* Container Query statt Viewport Media Query (siehe .pp-cq weiter oben für
   dasselbe Muster an anderer Stelle) - misst die tatsächliche Breite dieses
   Elements selbst, egal ob im Zwei-Spalten-Gast-Layout, im eingebetteten
   iframe oder im Owner-Dashboard. Standard (schmal): Pills-Zeile oben,
   Freitext-Feld 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;
}

/* Schwellwert grob geschätzt (5 Pills + Trenner + nutzbares Eingabefeld) -
   ggf. gegen echte Breiten nachkalibrieren. */
@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);
}

/* =========================================================
   Fee-Tabelle: Aktions-Buttons (Edit + Delete)
   ========================================================= */

.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 der sieben Tailwind-Utilities, die vorher fünfmal
   wortwörtlich im Header-Partial standen. Plain CSS, weil app.css
   vorkompiliert ist und hier kein Build-Schritt läuft (siehe .pp-ftop oben).

   Zwei Ausprägungen, weil im Header zwei verschiedene Dinge nebeneinander
   standen und identisch aussahen:
     .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)

   Bewusst EIN Element pro Button: vorher steckte an drei Stellen ein <button>
   in einem <a>, was das HTML-Content-Model verbietet (interaktiv in
   interaktiv) - und genau deshalb musste "deaktiviert" dort über
   pointer-events am <a> gebastelt werden statt über natives disabled. */
/* Höhe: 2px + 2px Padding + 18px Zeilenhöhe + 2x1px Rahmen = 24px.
   Vorher 30px (4/10er-Padding, 20px Zeilenhöhe) und damit klobiger als die
   alten Tailwind-Buttons, die ohne Rahmen auf 28px kamen. Horizontal nur
   leicht zurückgenommen (10px -> 8px), damit die Labels nicht am Rand kleben. */
.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, in denen vorher text-xs stand
   (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 entfaellt hier: in der
     schiebbaren Button-Zeile (siehe .pp-header-actions im Kompakt-Block
     weiter unten) ist ein senkrechter Strich nur Rauschen.

     Logo, Ueberschriftgroesse und der -mt-3-Ausgleich standen frueher
     ebenfalls hier. Sie sind in den Kompakt-Block gewandert, weil sie nicht
     nur bei geringer BREITE gebraucht werden, sondern genauso bei geringer
     HOEHE - also im Querformat, wo eine max-width-Regel gar nicht greift. */
  .pp-header-sep {
    display: none;
  }
}

/* =========================================================
   TOUCH-LAYER (Gast-Seiten)
   =========================================================
   Greift nur auf Geräten ohne präzisen Zeiger; die Desktop-Dichte bleibt
   unangetastet. Bewusst als Plain CSS und nicht über Tailwind-Utilities:
   app.css ist vorkompiliert und es läuft hier kein Build-Schritt, neue
   Utility-Klassen in Templates landen also nicht in app.css (siehe .pp-ftop
   ganz oben).

   Scope ist vorerst .pp-guest (booking_guest_calendar.html,
   booking_request.html, embed_calendar.html) - die Seiten, die Gäste
   praktisch nur vom Telefon aus sehen. Das Owner-Backend bekommt den Layer
   erst, wenn seine dichten Tabellen-Editoren (Saison-/Rabatt-/Fee-/iCal-
   Tabellen mit 20px-Aktionsspalten und table-layout:fixed) ein eigenes
   Mobile-Layout haben - dort würde eine pauschale Mindesthöhe die
   Spaltenbreiten 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 statt der Tailwind-Kombination
   bg-transparent/border-0/p-0 - die drei Utilities sind im vorkompilierten
   app.css gar nicht enthalten und haetten ohne laufenden Tailwind-Watch
   (ppcss) nicht gewirkt. */
.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 Ellipsis - erbt damit
   aber auch deren Spaltenbreiten, und die sind für die SECHS-spaltige
   Objekttabelle gemacht (55% + 25% + 2x56px + 30px). Auf diese fünf Spalten
   angewandt summierten sie sich über 100%: daher die waagerechte Scrollbar
   und die viel zu breite erste Spalte.

   ID-Selektor, weil die geerbten Regeln über .props-table th:nth-child()
   kommen und von einer reinen Klassenregel nicht geschlagen würden. */
#admin-owners-table th:nth-child(1),
#admin-owners-table td:nth-child(1) { width: 32%; }

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

#admin-owners-table th:nth-child(3),
#admin-owners-table td:nth-child(3) { width: 20%; 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) { width: 9%; text-align: center; }

/* Bild im Vollbild-Overlay (property_edit.html). Plain CSS statt
   max-h-[90vh]: dvh laesst sich als Tailwind-Utility nicht ausdruecken, und
   vh ist auf Mobilbrowsern die GROSSE Viewport-Hoehe - die Unterkante des
   Bildes lag damit hinter der ein- und ausfahrenden Adressleiste.
   vh bleibt als Rueckfallebene fuer Browser ohne dvh stehen. */
.pp-overlay-img {
  max-height: 90vh;
  max-height: 90dvh;
}

/* =========================================================
   TOUCH-ZIELE: Icon-Buttons im Owner-Backend
   =========================================================
   .pp-btn deckt diese nicht ab - das sind eigene Klassen ohne Rahmen und
   Hintergrund (Emoji-Icons in den Aktionsspalten der Tabellen). Mit 16-20px
   sind sie auf dem Telefon kaum zu treffen.

   Angehoben wird vor allem die HOEHE. Die Aktionsspalten liegen in Tabellen
   mit table-layout:fixed und festen Breiten (.prop-actions-col 36px, gemergt
   64px) - eine Verbreiterung darueber hinaus wuerde das Spaltenraster
   sprengen, 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 uebereinander in derselben Zelle - mit je
     44px waere die Zeile 88px hoch. 32px ist hier der vertretbare Kompromiss. */
  .reorder-btn {
    min-height: 32px;
    min-width: 32px;
  }

  /* Die gemergte Aktionsspalte muss mitwachsen.
     Sie ist am Rechner 64px breit, enthaelt aber DREI Knoepfe nebeneinander
     (Sync, Kalender, Bearbeiten) und darunter zwei Sortierpfeile. Mit den
     32px Mindestbreite von oben braucht die breitere der beiden Reihen
     3 x 32px + 2 x 4px Abstand = 104px. Ohne diese Regel quollen die Knoepfe
     auf Tablets aus der Spalte heraus - dort greift (pointer: coarse), waehrend
     die Container-Query wegen der Breite noch das Rechner-Layout liefert.
     Der urspruengliche Deckel von 32px war nur fuer die EINZELNE 36px-Spalte
     gerechnet, nicht fuer diese. */
  .props-table .prop-actions-col.prop-actions-merged {
    width: 108px;
  }

  /* Ausgenommen: die Saison-/Rabatt-/Fee-Tabellen. Deren Aktionsspalte ist
     hart auf 20px begrenzt (.season-table .action-cell), eine groessere
     Schaltflaeche passt dort schlicht nicht hinein. Diese Editoren brauchen
     auf dem Telefon ohnehin ein eigenes Layout statt groesserer Knoepfe -
     bis dahin bleiben sie unveraendert, statt die Zeilen auf 44px
     aufzublaehen, ohne dass es bedienbarer wuerde. */
  .season-table .season-delete-btn,
  .season-table .icon-btn {
    min-height: 0;
    min-width: 0;
  }
}

/* =========================================================
   FLACHE VIEWPORTS: zurueck ins einspaltige Layout
   =========================================================
   Alle tragenden Layouts der App entscheiden an EINER Schwelle:
   @container (min-width: 580px). Die fragt ausschliesslich nach Breite.

   Auf einem Telefon im Querformat (z.B. 844x390) ist diese Bedingung erfuellt
   - die App haelt sich fuer einen Desktop und schaltet zweispaltig - waehrend
   die knappe Ressource die HOEHE ist: 390px, davon Kopfzeile und eingeblendete
   Tastatur. Ergebnis: sieht ordentlich aus, laesst sich nicht bedienen.

   Container Queries kennen die Viewport-Hoehe nicht, deshalb hier eine Media
   Query, die die Zweispaltigkeit zuruecknimmt. 560px deckt alle Telefone im
   Querformat ab (das groesste liegt bei 430px) und laesst Tablets im
   Querformat (768px+) unberuehrt.

   grid-template-columns muss mitgesetzt werden: die Basisregeln arbeiten mit
   repeat(auto-fit, minmax(280px, 1fr)) und erzeugen bei 800px Containerbreite
   von sich aus zwei Spalten - die Bereichsnamen allein wuerden die zweite
   Spalte nur leer stehen lassen. */
@media (max-height: 560px) {
  .pp-owner-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "cal"
      "form"
      "list";
  }

  .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 fuer diese vier feste Zeilen/Spalten - die muessen
     mit zurueckgenommen 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-Ueberschrift, ZWEI Logos zu je
   100px, Sprachwahl und bis zu fuenf umbrechenden Buttons - zusammen grob
   100-140px. Auf einem Telefon im Querformat (390px hoch) ist das ueber ein
   Viertel des Bildschirms, dauerhaft, auf jeder Seite.

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

  /* Aussenabstand und Polster deutlich zurueck: allein das spart rund 30px. */
  .pp-header {
    margin-bottom: 10px;
    padding-top: 6px;
    padding-bottom: 6px;
  }

  /* Beide Marken raus. Auf dem Telefon weiss 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 haengt am zweizeiligen Aufbau mit Logos - ohne die sitzt das
     Select sonst schief ueber der Ueberschrift. */
  .pp-header-lang {
    top: 0;
  }

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

  /* Buttons in EINER seitlich schiebbaren Zeile statt umbrechend.
     Mit 44px Touch-Hoehe wuerden fuenf umbrechende Buttons zwei bis drei
     Zeilen belegen - also 90 bis 140px allein fuer Navigation. Eine
     schiebbare Zeile kostet 44px und versteckt nichts hinter einem Menue;
     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 Fliesstext, rechts die Knoepfe.

     Name, Preis                        [Papierkorb]
     Gaeste: 4, Naechte: 2              [hoch][runter]
     eMail                    [Sync][Kalender][Bearbeiten]

   FLEX, nicht Grid. Ein Grid erzwingt Spaltenausrichtung: "Naechte" landet
   zwangslaeufig unter "Preis", weil beide in derselben Rasterspalte liegen -
   genau der Spalteneindruck, der hier weg soll. Als Flex-Zeile mit Umbruch
   stehen die Wertepaare einfach hintereinander, durch Komma getrennt.

   Die Zeilenumbrueche kommen aus tr::before und tr::after: Pseudoelemente
   eines Flex-Containers sind selbst Flex-Elemente, und mit flex-basis:100%
   bei Hoehe 0 brechen sie die Zeile um, ohne Platz zu kosten. Zwei davon
   ergeben die drei Zeilen - ohne ein einziges zusaetzliches 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, eMail, Gaeste,
   Naechte, Aktionen, Loeschen) wird ueber order umsortiert.

   Rein ueber CSS aus DEMSELBEN Markup: prop_table.js erzeugt keine Zeilen,
   es arbeitet nur mit ihnen.

   Container Query statt Media Query: massgeblich ist die Breite der weissen
   Box, nicht die des Fensters - im Querformat ist der Container ~800px breit
   und die Tabelle dort voellig 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
     Spezifitaet wie die Ursprungsregeln, gewinnt ueber 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 loesen sich auf, damit ihre Kinder einzeln in die Flex-Zeile
     einsortiert werden koennen. */
  .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;
  }

  /* Zeilenumbrueche ohne eigene Hoehe. */
  .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 waere. */
  .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 entfaellt in der Karte - ohnehin auf 50 Zeichen gekuerzt
     und hier nur Hoehe. Auf der Detailseite steht sie vollstaendig. */
  .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
     eMail erklaeren 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);
  }

  /* ---- Knoepfe: groesser, 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;
  }

  /* Fuellzeilen halten in der Tabelle eine Mindesthoehe - als leere Karten
     waeren sie nur Kaesten ohne Inhalt. */
  .props-table tr.prop-row-empty {
    display: none;
  }

  /* Die Aktionszeile besteht aus fuenf 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;
  }
}

/* =========================================================
   HEADER-MENUE
   =========================================================
   Breit: Ausloeser versteckt, Button-Zeile steht offen im Fluss - unveraendert
   zu vorher. Schmal/flach: die Zeile klappt unter dem Header auf.

   Dasselbe Markup fuer beides. Der Ausloeser sitzt in Zeile 1 neben der
   Sprachwahl, das Panel bleibt in Zeile 2 - dadurch aendert sich die
   Desktop-Anordnung nicht. Positioniert wird gegen .pp-header (traegt bereits
   position:relative), nicht gegen den Ausloeser; 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 Klappmenue statt als schiebbare Zeile: bei fuenf Eintraegen war das
     Schieben zwar platzsparend, aber man sah nie, was es ueberhaupt 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 Menue sind die Eintraege Zeilen, keine Chips: linksbuendig, 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;
  }

  /* Badge sass am Chip oben rechts - in einer vollbreiten Zeile gehoert es
     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 bleibt alles wie bisher: feste Namensspalte links (160px), daneben
   der scrollbare Kalender.

   Auf einem 390px-Telefon blieben davon nach Layout- und Boxpolster 334px,
   minus 160px Namensspalte = 173px fuer den Kalender - bei 30px Tagesbreite
   also knapp sechs Tage. Fast die Haelfte der Breite ging an Beschriftungen.

   Kompakt (Klasse wird von prop_occupancy_timeline.js gesetzt, sobald der
   Container unter 560px liegt): die Namensspalte entfaellt, jede Beschriftung
   steht ueber ihrem Band und klebt beim Scrollen per position:sticky am
   linken Rand - Baender und Kalenderkopf laufen darunter durch. Zusammen mit
   der auf 20px verringerten Tagesbreite sind das rund 16 statt 6 Tage. */
.occ-timeline.occ-compact .occ-names {
  display: none;
}

/* Die Zeilen fliessen jetzt normal statt absolut gestapelt zu sein - das
   Raster aus "alle 22px eine Zeile" traegt nicht mehr, sobald zwischen den
   Baendern Beschriftungen stehen. */
.occ-timeline.occ-compact .occ-row {
  position: relative;
  top: auto;
}

/* Beschriftung und Band gehoeren zusammen - der Hintergrund liegt deshalb am
   ganzen Block, nicht mehr nur an der Bandzeile, und wechselt von Einheit zu
   Einheit. Ohne das stehen die beiden optisch beziehungslos untereinander. */
.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
   uebernimmt das der Block, sonst laege Farbe auf Farbe und der Wechsel
   erschiene doppelt versetzt. Der Trennstrich entfaellt ebenfalls, die
   Bloecke grenzen sich jetzt ueber 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 waere die Beschriftung so
   breit wie der gesamte Scrollbereich (mehrere tausend Pixel) und sticky
   haette nichts, woran es kleben koennte. */
.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 ueber den gesamten Inhalt - dessen Hoehe steht hier
   nicht mehr fest, weil die Zeilen fliessen. */
.occ-timeline.occ-compact .occ-today-marker {
  top: 0;
  bottom: 0;
  height: auto;
}
