/*
KAI-CMS - system.css

Basis-/Struktur-Regeln, die unabhaengig vom jeweiligen Projekt-Design funktionieren muessen:
- .lightbox_*: Struktur/Mechanik der Bild-Lightbox aus js/lightbox.js (Overlay-Positionierung,
  Sichtbarkeits-Umschaltung ueber .is_open, Bedienelemente). Visuelle Gestaltung
  (Abdunkelung, Typografie der Bildunterschrift, Look der drei Bedienknoepfe) liegt wie bei
  den anderen Bloecken hier in css/styles.css.
- .company-data[data-field="oeffnungszeiten"]: sorgt dafuer, dass mehrzeilige Oeffnungszeiten
  (aus company-data.json) unabhaengig vom umgebenden Container Zeilenumbrueche behalten.
- .popup_*: Struktur/Mechanik des Website-Popups aus js/popup.js (Overlay-Positionierung/
  -Layout, Groessenbegrenzung des Dialogs, Sichtbarkeits-Umschaltung ueber .is_open, wie
  .lightbox_*). Visuelle Gestaltung (Hintergrundfarben, Ausrichtung/Abstaende, Typografie)
  liegt wie bei den anderen Bloecken hier in css/styles.css - das sind die vorgesehenen
  Customization-Hooks fuer individuelles Kunden-Design pro Kunde.
- nav.mainnav .mainnav-*: Struktur/Mechanik der aus menu.json generierten Navigation
  (js/menu.js) - Dropdown-Positionierung/Sichtbarkeit fuer Untermenues, Anker-Scrolling.
  Visuelle Gestaltung (Farben, Abstaende) liegt wie beim uebrigen nav.mainnav in
  css/styles.css.
- .calendar-*: Struktur/Mechanik des Termin-Widgets aus js/calendar.js (Listen-/Monats-
  Umschaltung, Grid-Layout, Sichtbarkeit der Tagesdetails ueber .is_selected/.has_events/
  .is_blackout, Sperrzeit-Band .calendar-blackout). Visuelle Gestaltung (Farben, Abstaende,
  Pills, Look des Sperrzeit-Bands und der gesperrten Rasterzellen) liegt wie bei den anderen
  Bloecken hier in css/styles.css.
- .cookieconsent_*: Struktur/Mechanik des Cookie-Consent-Banners aus js/cookieconsent.js
  (fixe Leiste am unteren Bildschirmrand, Sichtbarkeits-Umschaltung ueber .is_open, wie
  .popup_* bzw. .lightbox_*) sowie der Platzhalter-Mechanik fuer consent-pflichtige
  Einbettungen (z.B. Google Maps, siehe Hilfe-Sektion im CMS). Akzeptieren-Button-Farbe
  liegt wie bei .btn-link/anderen Akzent-Farben in css/styles.css.
- .contact-form-message: AUSNAHME vom Grundsatz dieser Datei - hier liegt nicht nur Mechanik,
  sondern eine schlichte GRUNDGESTALTUNG (Flaeche, Rahmen, Innenabstand, Symbol) der
  Rueckmeldung nach dem Absenden des Kontaktformulars. Warum es dazu keine Alternative gibt:
  Die Rueckmeldung soll auf allen Installationen gleich aussehen, ohne sie viermal in
  Kundendateien nachzubauen - und eine eigene Engine-CSS-Datei waere kein Weg dorthin, denn
  sie muesste in JEDER Kundenseite verlinkt werden, und Seiten liefert das Update nicht aus
  (tools/update-installation.sh syncht cms/ und eine feste Liste, niemals die Seiten). Diese
  Datei ist die einzige Engine-CSS-Datei, die jede oeffentliche Seite ohnehin einbindet.
  Damit die Gestaltung trotzdem NIE eine Kundenregel ueberstimmt, steht sie vollstaendig in
  :where() (Spezifitaet 0-0-0): jede Regel in css/styles.css gewinnt, auch eine mit nur einer
  Klasse und unabhaengig von der Ladereihenfolge. Kennt ein Browser :where() nicht (aelter als
  Chrome 88 / Safari 14 / Firefox 78), faellt die Gestaltung weg und die Installation steht da
  wie vorher. Bewusst OHNE font-size: die Starter-/Kunden-CSS setzen sie bereits, die
  Engine-Angabe wuerde dort ohnehin verlieren und nur Verwirrung stiften.
- :where(img.kai_edit_image, .kai_edit_text img): GRUNDREGEL fuer Bilder im Kundencontent
  (2.26.0). Bilder, die der Editor einsetzt, tragen ihre Pixelmasse als Attribute
  (<img width="1200" height="500" class="kai_edit_image">) und standen damit so breit da, wie
  sie sind - auf einem Handy also ueber den Rand hinaus, mitsamt seitlichem Schieben der
  ganzen Seite. Gemessen am lokalen Stand: bei 360 px Breite 64 px Ueberlauf, sechs von sieben
  Bildern ueber dem Rand. Begrenzt waren bisher nur einzelne Zusammenhaenge in der Kunden-CSS
  (.hero img, .card img, .gallery_item img); ein Bild im Fliesstext fiel durch jedes Raster.
  Warum die Regel hierher gehoert und nicht in die Kunden-CSS: Der Editor erzeugt dieses
  Markup, also gehoert die Grundlage dazu - sonst ist jede Installation ein eingesetztes Bild
  davon entfernt, seitlich zu schieben, und der Fehler faellt erst beim Kunden auf. height:auto
  ist Teil der Regel, weil ein begrenztes Bild mit stehengebliebenem height-Attribut sonst
  verzerrt; geprueft wurde vorher, dass es auf keiner Installation ein Bild mit height OHNE
  width gibt (dort wuerde height:auto vergroessern). In :where(), also Spezifitaet 0-0-0: Wo
  eine Installation schon eine eigene Regel hat (netzdienst .nd-item .kai_edit_text img,
  rasiti img{max-width:100%}), gewinnt die - unabhaengig von der Ladereihenfolge.
- .kai_icon .cms_icon: Groesse des Icon-Picker-Markers im Kundencontent (siehe
  openIconPicker()/resolveIconMarkersInEditor() in cms.js, resolveIconMarkers() in
  iconhelper.php). Gehoert bewusst hierher, nicht in cms.css: cms.css ist die Backend-
  Stylesheet und wird auf einer echten Kundenseite (index.html etc.) gar nicht eingebunden
  (nur cms/css/system.css + css/styles.css, siehe deren <head>) - .cms_icon's Backend-Regel
  (width/height:19px, cms.css) griffe dort also gar nicht, ein frisch eingefuegtes Icon
  bliebe unskaliert (SVG-Default-Groesse, deutlich zu gross). 1em statt einer festen
  Pixelzahl, damit das Icon zur jeweils umgebenden Schriftgroesse passt (Ueberschrift,
  Fliesstext, ...) statt auf jeder Kundenseite gleich gross zu wirken unabhaengig vom
  Kontext.

Wird VOR css/styles.css eingebunden (siehe <head> der Seiten sowie content_css in
cms/settings.php), damit projektspezifisches Design bei Bedarf ueberschreiben kann.
*/

/* LIGHTBOX (Struktur/Mechanik fuer js/lightbox.js - Optik in css/styles.css, siehe dort
fuer die Liste der vorgesehenen Customization-Hooks) */

.lightbox_overlay {
	position: fixed; inset: 0;
	display: flex; align-items: center; justify-content: center;
	z-index: 1000; padding: 40px;
	/* display bleibt immer "flex" - Ein-/Ausblenden laeuft rein ueber opacity/visibility,
	damit beides (anders als bei display:none/flex) animierbar ist. visibility wird beim
	Oeffnen sofort auf "visible" umgeschaltet, beim Schliessen aber erst NACH der
	Opacity-Transition (transition-delay = gleiche Dauer wie die Opacity-Transition) - so
	bleibt das Overlay waehrend des Fade-outs klickbar/sichtbar und verschwindet danach
	komplett aus der Interaktions-/Tab-Reihenfolge, ganz ohne JS-Timer. */
	opacity: 0; visibility: hidden; pointer-events: none;
	transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.lightbox_overlay.is_open {
	opacity: 1; visibility: visible; pointer-events: auto;
	transition: opacity 0.25s ease, visibility 0s linear 0s;
}
.lightbox_figure {
	/* text-align zentriert die Bildunterschrift unter dem Grossbild. Bleibt hier, obwohl es
	Typografie ist: das Bild wird von der Mechanik zentriert, eine linksbuendige Unterschrift
	darunter saehe nach Fehler aus, nicht nach Gestaltung. Ueberschreibbar bleibt es
	trotzdem - styles.css wird nach dieser Datei geladen. */
	margin: 0; max-width: 90vw; max-height: 90vh; text-align: center;
	/* Spalte statt Standard-Blockfluss, seit unter der Bildunterschrift noch die Feldliste
	stehen kann (renderFields() in js/lightbox.js): zusammen mit "min-height: 0" am Bild
	gibt das Grossbild Platz ab, wenn Unterschrift und Felder sonst unter den unteren
	Bildschirmrand rutschen wuerden. Ohne das schneidet die max-height der figure die
	letzten Zeilen ab - mit Feldern war genau das der Fall. */
	display: flex;
	flex-direction: column;
	align-items: center;
	/* Leichtes Scale-up beim Oeffnen (0.92 -> 1), laeuft parallel zur Opacity-Transition
	des Overlays - zusammen ergibt das ein sanftes "Einwachsen" statt hartem Erscheinen. */
	transform: scale(0.92);
	transition: transform 0.25s ease;
}
.lightbox_overlay.is_open .lightbox_figure { transform: scale(1); }
.lightbox_img {
	max-width: 90vw; max-height: 80vh; object-fit: contain; display: block;
	/* Siehe .lightbox_figure: erlaubt dem Bild, unter seine Inhaltsgroesse zu schrumpfen,
	damit die Textzeilen darunter im Bild bleiben. */
	min-height: 0;
	/* Kurzes Abblenden beim Bildwechsel (siehe lightbox.js: Klasse wird vor dem
	src-Tausch gesetzt, danach wieder entfernt) - kein voller Fade auf 0, damit der
	Wechsel zuegig/knackig bleibt statt zu "blinken".

	Diese beiden Regeln bleiben als GANZES hier, obwohl der Wert 0.15 fuer sich genommen eine
	gestalterische Entscheidung waere: die Dauer 0.15s ist die CSS-Haelfte einer Mechanik, deren
	andere Haelfte im Skript steht (SWITCH_FADE_MS = 150 in lightbox.js, siehe den Kommentar
	dort - "must match the CSS transition on .lightbox_img"). Faende sich die Transition in
	styles.css und eine Installation haette sie nicht, wartete das Skript weiterhin 150 ms, das
	Bild stuende so lange hart bei 15 % Deckkraft und spraenge dann um. */
	opacity: 1;
	transition: opacity 0.15s ease;
}
.lightbox_img.lightbox_img_switching { opacity: 0.15; }
.lightbox_close, .lightbox_prev, .lightbox_next {
	position: fixed;
	/* Reset gegen die Browser-Defaults des <button> - die eigentliche Optik (Hintergrund,
	Textfarbe, Schriftgroesse des Symbols, Eckenradius, Hover-Zustand) liegt komplett in
	styles.css, wie bei .calendar-filter/.calendar-view-button weiter unten. Groesse und
	Position dagegen bleiben hier: die Knoepfe haben keinen Innenabstand, die 44 bzw. 52 px
	SIND die Trefferflaeche.

	"background: none" gehoert mit in den Reset, obwohl die Farbe selbst Optik ist: ohne die
	Zeile faellt eine Installation, deren styles.css den Optik-Block noch nicht hat, nicht auf
	"keinen Hintergrund" zurueck, sondern auf den grauen Standardknopf des Browsers - drei
	graue Kaesten ueber dem Bild, die aussehen, als waeren sie so gemeint. Ein Reset, der die
	Defaults nur halb neutralisiert, verschiebt den Fehler bloss; sichtbar nichts ist besser
	als sichtbar etwas Falsches. Die Kundenregel gewinnt trotzdem: gleiche Spezifitaet,
	styles.css wird nach dieser Datei geladen. */
	background: none;
	border: none; cursor: pointer; line-height: 1;
}
/* Feldliste unter der Bildunterschrift (renderFields() in js/lightbox.js). Nur zwei Regeln
gehoeren hierher, alles Weitere - Farben, Schriftgroessen, Abstaende, Spaltenaufteilung -
liegt in css/styles.css:
- :empty aus dem Fluss nehmen. Der Container steht immer im Overlay, auch bei einem Bild
  ganz ohne Felder; ohne diese Regel bliebe dort ein leerer Block mit Aussenabstand stehen.
- text-align: left, weil .lightbox_figure zentriert. Gleiche Begruendung wie bei der
  Bildunterschrift: zentrierte Label/Wert-Paare untereinander saehen nach Fehler aus, nicht
  nach Gestaltung. Ueberschreibbar bleibt es, styles.css wird spaeter geladen. */
.lightbox_fields:empty { display: none; }
.lightbox_fields { text-align: left; }
.lightbox_close { top: 20px; right: 24px; width: 44px; height: 44px; }
.lightbox_prev, .lightbox_next { top: 50%; transform: translateY(-50%); width: 52px; height: 52px; }
.lightbox_prev { left: 20px; }
.lightbox_next { right: 20px; }

/* COMPANY DATA (Struktur, unabhaengig vom umgebenden Container) */

.company-data[data-field="oeffnungszeiten"] {
	white-space: pre-line;
}

/* POPUP (Struktur/Mechanik fuer js/popup.js - Optik in css/styles.css, siehe dort fuer die
Liste der vorgesehenen Customization-Hooks) */

.popup_overlay {
	position: fixed; inset: 0;
	display: flex; align-items: center; justify-content: center;
	z-index: 1000; padding: 40px;
	/* Analog zu .lightbox_overlay (siehe dort fuer Details): display bleibt immer "flex",
	Ein-/Ausblenden laeuft nur ueber opacity/visibility (animierbar, anders als display:none/
	flex). transition-delay auf visibility haelt das Overlay waehrend des Fade-outs sichtbar/
	klickbar und schaltet es erst danach komplett aus der Interaktion, ganz ohne JS-Timer. */
	opacity: 0; visibility: hidden; pointer-events: none;
	transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.popup_overlay.is_open {
	opacity: 1; visibility: visible; pointer-events: auto;
	transition: opacity 0.25s ease, visibility 0s linear 0s;
}
.popup_dialog {
	position: relative;
	max-width: 560px; width: 100%;
	max-height: 90vh; overflow-y: auto;
	box-sizing: border-box;
	/* Leichtes Scale-up beim Oeffnen (0.92 -> 1), analog zu .lightbox_figure - laeuft
	parallel zur Opacity-Transition des Overlays. */
	transform: scale(0.92);
	transition: transform 0.25s ease;
}
.popup_overlay.is_open .popup_dialog { transform: scale(1); }
.popup_close {
	position: absolute; top: 10px; right: 14px;
	border: none; cursor: pointer;
	font-size: 28px; line-height: 1;
}
.popup_image { display: block; max-width: 100%; height: auto; }

/* COOKIE CONSENT (Struktur/Mechanik fuer js/cookieconsent.js) */

.cookieconsent_banner {
	position: fixed; left: 0; right: 0; bottom: 0;
	z-index: 1000;
	background: #fff;
	border-top: 1px solid #eee;
	box-shadow: 0 -4px 20px rgba(0,0,0,0.08);
	/* Analog zu .popup_overlay/.lightbox_overlay (siehe dort fuer Details): display bleibt
	immer vorhanden, Ein-/Ausblenden laeuft nur ueber opacity/visibility/transform
	(animierbar). Zusaetzlich ein leichtes Herein-/Heraus-Gleiten von unten (transform),
	passend zur Position am unteren Bildschirmrand - bei Popup/Lightbox (mittig) war
	stattdessen ein Scale-Effekt passender. */
	opacity: 0; visibility: hidden; pointer-events: none;
	transform: translateY(12px);
	transition: opacity 0.25s ease, transform 0.25s ease, visibility 0s linear 0.25s;
}
.cookieconsent_banner.is_open {
	opacity: 1; visibility: visible; pointer-events: auto;
	transform: translateY(0);
	transition: opacity 0.25s ease, transform 0.25s ease, visibility 0s linear 0s;
}
.cookieconsent_banner_inner {
	max-width: 1100px; margin: 0 auto;
	padding: 18px 24px;
	display: flex; flex-wrap: wrap; align-items: center; gap: 16px 24px;
}
.cookieconsent_text { flex: 1 1 380px; font-size: 14px; line-height: 1.5; color: #333; }
.cookieconsent_text p { margin: 0 0 8px; }
.cookieconsent_text p:last-child { margin-bottom: 0; }
.cookieconsent_actions { flex: 0 0 auto; display: flex; gap: 10px; }
.cookieconsent_accept, .cookieconsent_reject {
	font-family: inherit; font-size: 14px;
	padding: 10px 20px;
	border-radius: 6px;
	cursor: pointer;
	white-space: nowrap;
}
.cookieconsent_accept {
	border: none;
	background: #333; color: #fff;
}
.cookieconsent_reject {
	border: 1px solid #ccc;
	background: transparent; color: #555;
}

/* OEFFNUNGSZEITEN, LANGFORM (2.30.0)

Die Langform eines Zeitplans kommt als Markup aus dem Motor, nicht als Text mit <br />:
je Zeile eine .kai_hours_row mit .kai_hours_days und .kai_hours_time darin.

Hier steht NUR, dass jede Zeile eine eigene Zeile ist - alles andere ist Gestaltung und
gehoert dem Kunden. In :where() gewickelt (Spezifitaet 0-0-0), damit jede Regel in seiner
styles.css gewinnt, ohne !important. Play Bar setzt damit den Tag fett in eine eigene Zeile
und die Zeit darunter:

  .kai_hours_days { display: block; font-weight: bold; }
  .kai_hours_time { display: block; margin-bottom: 1em; }

Im Motor steht davon nichts - eine Vorgabe waere hier eine Behauptung ueber ein Layout,
das wir nicht kennen. */
:where(.kai_hours_row) { display: block; }

/* EXTERNE INHALTE ERST NACH KLICK (.kai_embed, 2.28.0)

Loest .cookieconsent_embed ab, das bis 2.27.0 an dieser Stelle stand. Der Unterschied ist
nicht die Optik, sondern die Zustaendigkeit: der alte Baustein haengte am Cookie-Banner und
loeste beim Klick die GLOBALE Zustimmung aus. .kai_embed funktioniert mit und ohne Banner,
und der Klick gilt genau fuer diese eine Einbettung - nicht fuer alle weiteren.

Gesperrt wird ueber das Attribut (data-kai-src / data-kai-href), nicht ueber die Klasse:
ein iframe mit gewoehnlichem src laedt sofort, egal wie seine Huelle heisst. Der
Konsistenz-Check meldet diesen Fall. Syntax siehe Hilfe-Sektion im CMS.

Struktur steht hier, Optik ist in :where() gewickelt (Spezifitaet 0-0-0) - jede Regel in der
styles.css des Kunden gewinnt dagegen, ohne !important. */
.kai_embed { position: relative; }
.kai_embed iframe { display: block; width: 100%; border: 0; }
.kai_embed_placeholder {
	position: absolute; inset: 0;
	display: flex; flex-direction: column; align-items: center; justify-content: center;
	gap: 12px;
	text-align: center; padding: 20px; box-sizing: border-box;
}
:where(.kai_embed_placeholder) { background: #f0f0ee; }
.kai_embed.is_loaded .kai_embed_placeholder { display: none; }
:where(.kai_embed_button) {
	font-family: inherit; font-size: 14px;
	padding: 10px 20px;
	border: none; border-radius: 6px;
	background: #333; color: #fff;
	cursor: pointer;
}

/* MAINNAV (Struktur/Mechanik fuer js/menu.js) */

/* Sanftes Scrollen zu Section-Ankern (#id) - deckt sowohl Klicks auf einen
Section-Menuepunkt auf der bereits offenen Zielseite ab (nativer Anker-Sprung des
Browsers wird dadurch automatisch sanft) als auch, in Kombination mit dem
scrollIntoView-Aufruf in menu.js, den Seitenwechsel-Fall. */
html { scroll-behavior: smooth; }

nav.mainnav ul.mainnav-list {
	list-style: none;
	margin: 0;
	padding: 0;
}
nav.mainnav li.mainnav-item {
	position: relative;
}
/* Dropdown ist standardmaessig unsichtbar UND aus dem Layout-Fluss (display:none) - anders
als beim Lightbox-/Popup-Overlay reicht hier reines display, da kein Ein-/Ausblenden
animiert werden muss (ein Untermenue klappt bewusst hart auf/zu, nicht sanft). */
nav.mainnav ul.mainnav-dropdown {
	list-style: none;
	margin: 0;
	padding: 0;
	position: absolute;
	top: 100%;
	left: 0;
	min-width: 180px;
	display: none;
	z-index: 100;
}
/* Aufklappen per Hover (Desktop-Komfort) ODER per .is_open-Klasse (von menu.js beim Klick
gesetzt - noetig fuer Touch-Geraete, wo :hover nicht zuverlaessig feuert). Beide Wege
fuehren zum selben sichtbaren Zustand. */
nav.mainnav li.mainnav-item.has-dropdown:hover > ul.mainnav-dropdown,
nav.mainnav li.mainnav-item.has-dropdown.is_open > ul.mainnav-dropdown {
	display: block;
}
nav.mainnav ul.mainnav-dropdown li {
	list-style: none;
}

/* Beruehrungsziel des Untermenue-Knopfs (2.25.0). PageSpeed (netzdienst.ch, mobil) bemaengelte
ihn als zu klein und zu nah am Link. Mindestgroesse und Abstand gehoeren deshalb in die Engine
statt in jede Kunden-CSS; Farbe, Rahmen und Symbol bleiben wie bisher Sache der Installation.
css/styles.css laedt nach dieser Datei - eine Kundenregel mit gleicher Spezifitaet gewinnt
also weiterhin. */
nav.mainnav .mainnav-toggle {
	min-width: 24px;
	min-height: 24px;
	margin-left: 4px;
}

/* Burger-Umschalter (nur auf schmalen Viewports, siehe Media Query unten) - Basis-Reset
hier, visuelle Gestaltung (Balken-Farbe/-Abstand) liegt wie beim uebrigen mainnav in
css/styles.css. */
.mainnav-burger {
	display: none;
	background: transparent;
	border: none;
	cursor: pointer;
	padding: 0;
}

/* Open state: the three bars turn into a cross. Keyed on aria-expanded, which menu.js already
sets on every path that opens or closes the panel (burger click, link click, outside click,
Escape), so no class and no JS are involved. Without JS there is no burger at all - the menu
snapshot list stays visible as before.
The offset moves the outer bars onto the middle one: (button height - bar height) / 2. The
default fits the starter geometry in css/styles.css (26x20 px button, 2 px bars); an
installation with other bars sets --mainnav-burger-cross-offset in its css/styles.css.
Deliberately plain: no overshoot, no staggered delays - every installation gets this unasked.
An installation that wants its own animation overrides these selectors in css/styles.css;
equal specificity is enough, because styles.css loads after system.css.
The closed state is not touched, so an installation without a transition sees no change until
the menu opens. */
.mainnav-burger {
	--mainnav-burger-cross-offset: 9px;
}
.mainnav-burger[aria-expanded="true"] span:nth-child(1) {
	transform: translateY(var(--mainnav-burger-cross-offset)) rotate(45deg);
}
.mainnav-burger[aria-expanded="true"] span:nth-child(2) {
	opacity: 0;
}
.mainnav-burger[aria-expanded="true"] span:nth-child(3) {
	transform: translateY(calc(-1 * var(--mainnav-burger-cross-offset))) rotate(-45deg);
}
/* Movement only for visitors who have not asked for reduced motion; with reduced motion the
cross appears at once. */
@media (prefers-reduced-motion: no-preference) {
	.mainnav-burger span {
		transition: transform 0.25s ease, opacity 0.25s ease;
	}
}

/* Bis 720px, OHNE Bedingung an Skripte: Die Navigation steht sichtbar in der Seite, samt
Untermenues. Das ist der Zustand, den ein Besucher ohne JavaScript sieht - und seit 2.25.0
auch der Zustand des ERSTEN Bildes, weil die Navigation in dieser Form gebacken wird
(renderMenuNavHtml() in cms/php/menuhelper.php). Ausgeklappt statt Burger, aber vollstaendig
bedienbar. */
@media (max-width: 720px) {
	nav.mainnav {
		position: relative;
	}
	/* Untermenues stapeln sich eingerueckt innerhalb der senkrechten Liste, statt wie auf dem
	Desktop schwebend danebenzuklappen. display:block hebt hier die Standardregel oben auf -
	ohne Skript gibt es keinen Knopf, der sie aufklappen koennte. */
	nav.mainnav ul.mainnav-dropdown {
		position: static;
		display: block;
	}
}

/* Bis 720px UND mit laufenden Skripten: erst hier wird aus der Liste das Burger-Panel.
Die Bedingung (scripting: enabled) ist der Kern der Aenderung von 2.25.0 - vorher wurde die
Liste immer eingeklappt, auch ohne JavaScript, weshalb der Schnappschuss bewusst klassenlos
war und beim Eingreifen von menu.js sichtbar umsprang (gemessen: CLS rund 0,39 auf
netzdienst.ch).

ACHTUNG fuer spaeter: (scripting) kennen nicht alle Browser. Wo die Eigenschaft unbekannt
ist, gilt die Bedingung als NICHT erfuellt - die Liste bleibt dann ausgeklappt stehen, auch
wenn Skripte laufen. Das ist bedienbar und ohne Sprung, sieht aber anders aus als der Burger.
Das ist kein Fehler, sondern der bewusst gewaehlte sichere Ausgang. */
@media (max-width: 720px) and (scripting: enabled) {
	/* display:flex statt nur "block" - die drei Balken-Spans werden per
	flex-direction:column gestapelt (siehe .mainnav-burger span in css/styles.css), das
	muss also schon hier beim Sichtbarmachen mitkommen. */
	.mainnav-burger {
		display: flex;
	}
	/* .mainnav-list dient auf schmalen Viewports gleichzeitig als aufklappbares
	Burger-Panel - standardmaessig unsichtbar, erst .is_open (vom Burger-Button in
	menu.js gesetzt) blendet sie ein. Auf breiten Viewports (ausserhalb dieser Media
	Query) bleibt sie immer sichtbar, die Klasse wird dort schlicht ignoriert. */
	nav.mainnav ul.mainnav-list {
		display: none;
		position: absolute;
		top: 100%;
		right: 0;
		z-index: 200;
		flex-direction: column;
	}
	nav.mainnav ul.mainnav-list.is_open {
		display: flex;
	}
	/* Mit Skript uebernimmt wieder der Knopf: Untermenue zu, bis es aufgeklappt wird. */
	nav.mainnav ul.mainnav-dropdown {
		display: none;
	}
	nav.mainnav li.mainnav-item.has-dropdown.is_open > ul.mainnav-dropdown {
		display: block;
	}
}

/* BACK-TO-TOP (structure, mechanics and default look for the button js/menu.js creates) */

/* Unlike the other blocks here, this one also carries a neutral default look: menu.js creates
the element at runtime, so every existing installation gets it with an engine update - without
a look of its own it would show a raw browser button. The look is adjusted per installation by
overriding the custom properties below in css/styles.css (same selector .totop_button is
enough, styles.css loads later); display:none there switches the button off.
Hidden via visibility, not only opacity: a hidden button must not be reachable with Tab.
The fade uses the same visibility-delay pattern as .lightbox_overlay/.popup_overlay. */
.totop_button {
	--totop-bg: rgba(34, 34, 34, 0.85);
	--totop-bg-hover: #222;
	--totop-color: #fff;
	--totop-size: 44px;
	--totop-radius: 6px;
	--totop-offset: 20px;
	position: fixed;
	right: calc(var(--totop-offset) + env(safe-area-inset-right, 0px));
	bottom: calc(var(--totop-offset) + env(safe-area-inset-bottom, 0px));
	z-index: 900; /* below the cookie banner and the overlays (1000) */
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--totop-size);
	height: var(--totop-size);
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: var(--totop-radius);
	background: var(--totop-bg);
	color: var(--totop-color);
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
	cursor: pointer;
	opacity: 0;
	visibility: hidden;
}
.totop_button.is_visible {
	opacity: 1;
	visibility: visible;
}
.totop_button:hover,
.totop_button:focus-visible {
	background: var(--totop-bg-hover);
}
.totop_button:focus-visible {
	outline: 2px solid var(--totop-bg-hover);
	outline-offset: 2px;
}
.totop_button svg {
	display: block;
	width: 50%;
	height: 50%;
}
/* Fade only for visitors who have not asked for reduced motion; otherwise the button simply
appears and disappears. */
@media (prefers-reduced-motion: no-preference) {
	.totop_button {
		transition: opacity 0.25s ease, visibility 0s linear 0.25s, background-color 0.15s ease;
	}
	.totop_button.is_visible {
		transition: opacity 0.25s ease, visibility 0s linear 0s, background-color 0.15s ease;
	}
}
@media print {
	.totop_button {
		display: none;
	}
}

/* PREISLISTE: DRUCKKNOPF UND FUSSTEXT (2.31.0)

Nur das Noetigste, und alles mit :where() - Spezifitaet 0, der Kunde gestaltet.
Der Knopf entsteht erst durch pricelist.js; ohne JavaScript gibt es ihn nicht, was
richtig ist - er koennte dann nichts tun.

Gedruckt wird NICHT diese Seite, sondern ein eigenes Dokument (siehe pricelist.js).
Deshalb steht hier kein @media print fuer die Liste: es wuerde nie greifen. */

:where(.pricelist-print-button) {
	display: block;
	margin-left: auto;
	cursor: pointer;
}

:where(.pricelist-footnote) {
	margin-top: 1em;
}

/* TERMINE/KALENDER (Struktur/Mechanik fuer js/calendar.js) */

.calendar-toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
}

.calendar-view-toggle {
	display: flex;
	flex: 0 0 auto;
}

.calendar-filters {
	display: flex;
	flex-wrap: wrap;
	flex: 1 1 auto;
}

.calendar-filter, .calendar-view-button {
	cursor: pointer;
	background: none;
	/* Reset gegen Browser-Default-Buttonrahmen/-Innenabstand - die eigentliche Optik
	(Rahmenfarbe, Radius, aktiver Zustand) liegt komplett in styles.css. */
	border: 0;
	font: inherit;
}

.calendar-month-group {
	width: 100%;
}

.calendar-list-item {
	display: flex;
	width: 100%;
	box-sizing: border-box;
}

.calendar-list-date {
	display: flex;
	flex-direction: column;
	flex: 0 0 auto;
}

.calendar-list-body {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-width: 0; /* erlaubt text-overflow/Umbruch in Titel/Beschreibung innerhalb des flex-Items */
}

/* Sperrzeit-Band (Liste und Tagesbereich): absichtlich KEIN flex wie .calendar-list-item.
Die Teile (Datumsspanne, Trenner, Titel) stehen im normalen Textfluss, damit die Leerzeichen
um den Trenner erhalten bleiben (in einem flex-Container fielen sie an den Raendern der
Kind-Elemente weg). Ohne Optik-Regeln in css/styles.css steht so eine lesbare Zeile
"6. Juli bis 16. August - Sommerferien" da, kein halbfertiger Kasten. */
.calendar-blackout {
	width: 100%;
	box-sizing: border-box;
}

.calendar-grid-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
}

.calendar-grid-nav {
	cursor: pointer;
	background: none;
	border: 0;
	font: inherit;
}

.calendar-grid-weekdays, .calendar-grid-days {
	display: grid;
	grid-template-columns: repeat(7, 1fr);
}

.calendar-grid-day {
	cursor: default;
	background: none;
	border: 0;
	font: inherit;
	display: flex;
	flex-direction: column;
	align-items: center;
	width: 100%;
	box-sizing: border-box;
}

.calendar-grid-day.has_events {
	cursor: pointer;
}

/* Tag innerhalb einer Sperrzeit: klickbar auch ohne Termine (der Tagesbereich zeigt dann die
Sperrzeit). Hier bewusst NUR die Mechanik - die Markierung selbst (Hintergrund, Schraffur)
liegt in css/styles.css. Fehlt sie dort, sieht die Zelle aus wie jede andere und zeigt beim
Klick trotzdem das Richtige, statt halb gestylt zu wirken. */
.calendar-grid-day.is_blackout {
	cursor: pointer;
}

.calendar-grid-day.is_empty {
	cursor: default;
	visibility: hidden;
}

.calendar-grid-dots {
	display: flex;
}

.calendar-grid-dot {
	border-radius: 50%;
	display: inline-block;
}

@media (max-width: 720px) {
	.calendar-toolbar {
		flex-direction: column;
		align-items: flex-start;
	}
}

/* CONTENT ITEMS / NEWS / AKTUELLES (Struktur/Mechanik fuer js/content-items.js) */

.content-items-widget {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.content-items-card {
	display: flex;
	flex-direction: column;
	text-decoration: none;
}

.content-items-card-image {
	width: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
}

.content-items-card-body {
	display: flex;
	flex-direction: column;
}

/* PREISLISTE (Struktur/Mechanik fuer js/pricelist.js - Optik in css/styles.css, siehe dort
fuer die Liste der vorgesehenen Customization-Hooks) */

.pricelist-toc {
	list-style: none;
	margin: 0;
	padding: 0;
}

.pricelist-table {
	width: 100%;
	border-collapse: collapse;
}

/* KONTAKTFORMULAR (Struktur/Mechanik fuer js/contactform.js - Optik in css/styles.css,
siehe dort fuer die Liste der vorgesehenen Customization-Hooks) */

/* Honeypot-Feld fuer den Spam-Schutz - MUSS ausserhalb des sichtbaren Bereichs bleiben
(nicht display:none/visibility:hidden, das erkennen manche Bots und lassen das Feld dann
ebenfalls leer) - echte Struktur/Sicherheitsmechanik, deshalb hier statt in styles.css,
nicht als Customization-Hook gedacht. */
.contact-form-honeypot {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* KONTAKTFORMULAR-RUECKMELDUNG (Grundgestaltung, siehe die Ausnahme-Begruendung am Kopf
dieser Datei). Alles in :where(), damit jede Kundenregel gewinnt. */

:where(.contact-form-message) {
	display: flex;
	align-items: flex-start;
	gap: 0.6em;
	margin-top: 16px;
	padding: 12px 14px;
	/* Etwas Luft, wenn contactform.js nach dem Absenden hierher scrollt - sonst steht die
	Meldung bündig am Fensterrand. */
	scroll-margin-block: 24px;
	border: 1px solid currentColor;
	border-radius: 4px;
	/* currentColor fuer Rahmen und Symbol: setzt die Installation eine eigene Farbe fuer
	.success/.error, uebernehmen beide sie automatisch mit. */
	background: #f6f6f4;
	color: #444;
}

/* Symbol per mask-image statt als Bild: so traegt es die Textfarbe (currentColor) und damit
auch eine Farbe, die die Installation selbst gesetzt hat. Ohne mask-Unterstuetzung bleibt die
Flaeche leer - der Text steht trotzdem. */
:where(.contact-form-message)::before {
	content: '';
	flex: 0 0 auto;
	width: 1.25em;
	height: 1.25em;
	background-color: currentColor;
	-webkit-mask: center / contain no-repeat;
	mask: center / contain no-repeat;
}

:where(.contact-form-message.success) {
	background: #f1f7f4;
	color: #2f5d50;
}

:where(.contact-form-message.success)::before {
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

:where(.contact-form-message.error) {
	background: #fdf2f1;
	color: #c0392b;
}

:where(.contact-form-message.error)::before {
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3Cpath d='M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3Cpath d='M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z'/%3E%3C/svg%3E");
}

/* Das Meldungselement steht seit 2.21.0 schon beim Laden im Formular, leer, als aria-live-
Bereich (siehe getMessageElement() in js/contactform.js) - leer darf es nichts anzeigen. Muss
NACH der display:flex-Regel oben stehen: in :where() haben beide Spezifitaet 0, es entscheidet
allein die Reihenfolge. */
:where(.contact-form-message:empty) {
	display: none;
}

/* Waehrend des Versands (weder .success noch .error): kein Symbol, nur der Hinweis. */
:where(.contact-form-message:not(.success):not(.error))::before {
	display: none;
}

/* Formular nach erfolgreichem Versand ausblenden - FERTIG, aber NICHT eingeschaltet: es
greift erst, wenn eine Installation ihrem Formular zusaetzlich die Klasse kai-hide-on-sent
gibt (eine Zeile im Markup der Seite). Ohne sie aendert sich an bestehenden Seiten nichts;
automatisch auszublenden wuerde das Verhalten aller Seiten ungefragt aendern. Die Meldung
selbst bleibt stehen - sie ist direktes Kind des Formulars (siehe getMessageElement()).

BEWUSST NICHT in :where(), anders als die Grundgestaltung darueber: Das hier ist ein
Schalter, den die Installation selbst setzt, kein Vorschlag - er muss gewinnen. Mit
Spezifitaet 0 verloere er gegen jede Kundenregel, die ueberhaupt ein display setzt (in der
Starter-styles.css z.B. ".contact-form-field { display: flex }", 0-1-0), und die Felder
blieben trotz Klasse stehen. So kommt die Regel auf 0-4-0 (drei Klassen plus die Klasse im
:not()) und schlaegt solche Regeln, ohne !important. */
.contact-form.kai-hide-on-sent.is-sent > :not(.contact-form-message) {
	display: none;
}

/* ICON-PICKER (Struktur fuer den Kundencontent-Marker, siehe die Erklaerung am Kopf dieser
Datei) */

.kai_icon .cms_icon {
	width: 1em;
	height: 1em;
	vertical-align: -0.125em;
}

/* BILDER IM KUNDENCONTENT (Grundregel, siehe die Erklaerung am Kopf dieser Datei) */

:where(img.kai_edit_image, .kai_edit_text img) {
	max-width: 100%;
	height: auto;
}
