/* ==========================================================================
   Shared design tokens + site chrome (navigation, language switcher).
   Loaded on every front-end view. Tokens are self-contained so the plugin
   components look right under any theme; values mirror the theme's theme.json.
   ========================================================================== */

/* ----- The page may not be pushed sideways --------------------------------
   Reported from Brave on an iPhone and confirmed by screenshot: every page
   could be dragged to the right, revealing an empty strip about a quarter of
   the screen wide, with the header band and the bottom bar painted across it.
   The document was genuinely wider than the viewport.

   It does not reproduce in emulation - fourteen pages at 320, 360, 375, 390
   and 430px, portrait and landscape, in a browser and in standalone mode, all
   report scrollWidth === clientWidth. So this is a WebKit-only widening, and
   this rule is a GUARD, not a diagnosis: it makes the symptom impossible
   while the cause is still open.

   `clip` rather than `hidden`: hidden turns the root into a scroll container,
   which silently breaks `position: sticky` for everything inside it. The
   fallback matters for iOS below 16, where clip is not supported - there the
   root is not sticky-positioning anything today, so hidden is safe.

   And text-size-adjust, because WebKit inflates text on its own judgement of
   a "narrow" column, which widens content that fits everywhere else. */
html {
	overflow-x: clip;
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
}

@supports not (overflow-x: clip) {
	html {
		overflow-x: hidden;
	}
}

body {
	overflow-x: clip;
}

@supports not (overflow-x: clip) {
	body {
		overflow-x: hidden;
	}
}


:root {
	/* Die Rahmenflaeche des Desktops (Vorschaugrafik): Kopfkarte und Hero
	   schweben mit demselben Abstand zum Fensterrand und enden buendig. Ein
	   Paar Tokens, damit die beiden Flaechen nicht auseinanderdriften. */
	--vb-frame-gap: clamp( 0.75rem, 1.9vw, 1.75rem );
	--vb-frame-max: 1440px;
	/* The palette lives in the theme's theme.json; these tokens read it, with
	   the literal as fallback. Both files carried the same 16 colours with
	   nothing keeping them in step - two truths, one of which would drift.
	   The fallback is not decoration: under a foreign theme the presets do not
	   exist, and the plugin has to keep its own looks. Dark stays literal
	   below, because theme.json carries no dark variant. */
	/* Fresh-green / kinetic palette (replaces the teal+amber scheme). The
	   dominant brand colour is a vivid emerald; lime is a sparing youthful
	   highlight. The three legacy coral tokens are gone: nothing read them,
	   and a token named --vb-coral that resolves to #14b870 green is a trap
	   for whoever reaches for it next. The one preset still in use was
	   renamed to say what it is
	   family so nothing orange leaks through components that still use them. */
	--vb-primary: var(--wp--preset--color--primary, #b66a80);
	--vb-primary-dark: var(--wp--preset--color--primary-dark, #68192e);
	--vb-primary-darker: var(--wp--preset--color--primary-darker, #300b17);
	--vb-primary-soft: var(--wp--preset--color--primary-soft, #f3e2e7);
	--vb-lime: var(--wp--preset--color--lime, #c99b84);
	--vb-lime-ink: #300b17;
	--vb-accent: var(--wp--preset--color--accent, #e0a82e); /* ratings / stars (gold, distinct from the old orange) */
	--vb-gold: var(--wp--preset--color--accent, #e0a82e);
	--vb-ink: var(--wp--preset--color--ink, #30272b);
	--vb-muted: var(--wp--preset--color--muted, #6b5b60);
	/* WCAG 2.5.8 (AA): 24 CSS px in each direction for anything a finger
	   hits. --vb-tap-primary is for a page's main action, where 24px is
	   legal but mean. Measured before these existed: 105 controls under the
	   minimum, 49 of them the letter jump-nav of an unstyled A-Z page. */
	--vb-tap-min: 24px;
	--vb-tap-primary: 44px;
	/* What the fixed app bar covers, so the page can reserve exactly that
	   much and no focused field lands underneath it. site-theme.js
	   overwrites this with the bar's MEASURED height; the value here is only
	   what applies before that runs.
	   Why measured rather than typed: the bar is 77px live while the old
	   literals reserved 72px and 64px. Broken down - 1px border, 12px of the
	   bar's own padding, and an item that is 64px because min-height: 52px
	   is a CONTENT height (this project has no border-box reset) with 12px
	   of padding on top of it. A sum spread over three files and kept by
	   hand is exactly how a reservation goes stale.
	   Since Aufgabe #222 the bar is 64px with its line (1 + 7 + 48 + 8,
	   border-box) and the home indicator's inset REPLACES its 8px of bottom
	   padding instead of being added to it - the same sum here. */
	--vb-appnav-h: calc( 56px + max( 8px, env( safe-area-inset-bottom, 0px ) ) );
	/* The sticky header's height, measured by site-theme.js so the burger
	   sheet knows where the header ends; 63px is what applies before the
	   script runs, and the header is taller as soon as a label wraps. */
	--vb-header-h: 63px;
	/* The profile's floating action bar, when it is up; profile-stickybar.js
	   writes its measured height here and 0 when it is down. The two
	   floating elements below add it, so they stand on top of the bar
	   instead of covering it for the twelve seconds the report offer is
	   on screen. */
	--vb-stickybar-h: 0px;
	/* What the on-screen keyboard covers from below while it is up;
	   sticky-stack.js writes it (Aufgabe #222). */
	--vb-keyboard-h: 0px;
	/* What the fixed bars cover (Aufgabe #211): measured by sticky-stack.js
	   in whole pixels; 0 is what applies before it runs, and without it.
	   --vb-sticky-top deliberately has no default here: the paw trail
	   (#212) takes it as soon as it is set, and a 0 default placed its band
	   at the top of the window until the measurement came (+0.0044 CLS on
	   the search page). Nothing in the CSS reads it. */
	--vb-sticky-admin: 0px;
	--vb-sticky-bars: 0px;
	--vb-sticky-reserve: 0px;
	--vb-sticky-bottom: 0px;
	/* Where the profile bar's rows start: the left edge of the profile's
	   columns, measured by profile-bar.js. */
	--vb-pbar-inset: 16px;
	--vb-base: var(--wp--preset--color--base, #fbf7f2);
	--vb-surface: var(--wp--preset--color--surface, #f2e7df);
	--vb-cream: var(--wp--preset--color--cream, #f2e7df);
	/* Verlaufs-Anfang für Held, Assistent und Anzeigenband. Eigenes Token,
	   weil an genau diesen drei Stellen ein LITERAL stand: der Held rechnete
	   im Dunkelmodus Titeltext auf 1,02:1 herunter, weil die Schrift dem
	   Schema folgte und der Verlauf nicht. */
	--vb-tint: #f3e2e7;
	--vb-tint-warm: #fbf7f2;
	/* Der Kopf der Startseite nach der Vorschaugrafik (Inhaber 24.09.2026):
	   marineblaue Schlagzeile, schiefergrauer Vorspann, ein kuehler heller
	   Nebel ueber dem Foto statt der Mintflaeche - gemessen an der Grafik
	   #0f1c45, #213955 und #dae9f3. */
	--vb-hero-ink: #30272b;
	--vb-hero-lead: #6b5b60;
	--vb-hero-fog: #f2e7df;
	--vb-line: var(--wp--preset--color--line, #d9c7c1);
	/* Aliases kept for components that grew their own names; defined here so
	   nothing falls back to a hardcoded pre-rebrand colour. */
	--vb-border: var(--vb-line, #d9c7c1);
	--vb-line-strong: #c7b1aa;
	/* Form-control boundary. --vb-line is 1.28:1 on white, which is fine for a
	   decorative hairline and not fine for the edge of a control: WCAG 1.4.11
	   wants 3:1 for anything that tells you where a field begins. Measured
	   3.76:1 on white, 3.41:1 on --vb-surface. */
	--vb-field-line: #8c716e;
	--vb-text: var(--wp--preset--color--ink, #30272b);
	--vb-warning-soft: #fff0d8;
	--vb-success: var(--wp--preset--color--success, #176849);
	--vb-success-soft: #e6f3ea;
	/* The text that goes ON --vb-success-soft. It existed only as a literal
	   inside profile.css, which meant the "open now" badge kept its light
	   green under [data-theme="dark"] - a lit pill on a dark page. */
	--vb-success-ink: #176849;
	--vb-display: "AniLevia Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	--vb-body: "AniLevia Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	/* The word mark and the hero's one display line only (#408). */
	--vb-serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
	--vb-radius: 16px;
	--vb-radius-lg: 22px;
	/* The small end of the type scale. Measured 7.9. over seven live pages:
	   eighteen different sizes at or below 17px, from 11.5px to 17px, most of
	   them within half a pixel of a neighbour. Five steps now, and the fluid
	   headline clamps (hero, page title, card title, app bar) stay outside -
	   each of those was set by its own measurement. */
	--vb-text-caption: 0.78rem;
	--vb-text-small: 0.85rem;
	--vb-text-label: 0.9rem;
	--vb-text-body: 1rem;
	--vb-text-lead: 1.0625rem;
	--vb-radius-xs: 6px;
	--vb-radius-sm: 11px;
	--vb-radius-pill: 999px;
	--vb-shadow: 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40);
	--vb-shadow-hover: 0 22px 48px -22px rgba(104, 25, 46, 0.32), 0 4px 12px -6px rgba(48, 39, 43, 0.20);
	--vb-glow: 0 18px 44px -18px rgba(104, 25, 46, 0.30);
	--vb-focus: 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c);
	/* Role tokens (dark-mode groundwork): fills keep white text in BOTH
	   schemes (primary-dark / danger fills barely move), so only grounds
	   and inks rotate. Accent TEXT gets its own token - primary-dark reads
	   4.9:1 on white but 2.1:1 on the dark ground, and one token cannot
	   serve both a fill and a text colour. */
	--vb-accent-text: #68192e;
	--vb-danger-text: #b52216;
	/* Measured, not chosen: #cf222e on the 12% red tint the emergency pill
	   sets itself came out at 4.44:1, just under AA for body text. The red
	   is two shades darker so the pill passes, which only ever raises
	   contrast everywhere else the token is used. */
	--vb-danger-text-strong: #b52216;
	--vb-danger-soft: #ffece6;
	--vb-danger-ink: #b52216;
	--vb-warn-text: #86520a;
	--vb-warn-soft: #fff0d8;
	--vb-warn-ink: #86520a;
	--vb-header-glass: #fbf7f2;
	/* Opaque (Aufgabe #222, graphics lead B2): at 0.96 with a blur the
	   lines scrolling underneath stayed readable through the app bar and
	   the action bar standing on it. */
	--vb-appnav-bg: #fffcf8;
	--vb-neutral-soft: #efe9e6;
	--vb-neutral-ink: #5a5348;
	--vb-zebra: rgba(48, 39, 43, 0.035);
	color-scheme: light;
	/* AniLevia (#408): roles the green palette did not have. Fills carry
	   white text; the *-text and *-ink roles are for text only. */
	--vb-raised: #fffcf8;
	--vb-rose: #c99b84;
	--vb-rose-ink: #80533f;
	--vb-focus-color: #205f9c;
	--vb-info-text: #205f8b;
	--vb-info-soft: #e8f2fb;
	--vb-success-fill: #176849;
	--vb-warn-fill: #86520a;
	--vb-danger-fill: #b52216;
	--vb-info-fill: #205f8b;
	--vb-tariff-free-ink: #5e5762;
	--vb-tariff-free-soft: #ede9ef;
	--vb-tariff-bronze-ink: #79503b;
	--vb-tariff-bronze-soft: #f5e6da;
	--vb-tariff-silver-ink: #4e5a66;
	--vb-tariff-silver-soft: #e7edf2;
	--vb-tariff-gold-ink: #715417;
	--vb-tariff-gold-soft: #f8efcd;
}

/* ===== Dark scheme (F): tokens only, never component CSS ==================
   Light is the default, full stop. The dark palette applies only when a
   visitor asks for it with the switch (data-theme="dark") - never because
   their operating system happens to be dark. This is a directory people
   land on from a search result; it should look the same to everyone until
   they choose otherwise.

   Every value below is a deliberate decision: a deep desaturated
   green-black ground developed from --vb-primary-darker, cards one step
   lighter, inks and soft grounds re-mixed for that ground - never a naive
   inversion. Fills (primary-dark, danger, amber) stay put: their white
   text keeps its contrast either way. Print is paper, so the block is
   screen-only. */
@media screen {
	:root[data-theme="dark"] {
		color-scheme: dark;
		/* Neutrales Anthrazit statt Gruenstich, wie beide Dunkel-Grafiken es
		   zeigen (Nachpruefung P20): Grund rgb(14,18,21), Flaechen rgb(22,27,31).
		   Das Gruen bleibt Akzent, nicht Untergrund. */
		--vb-primary: #d9a9b7;
		--vb-primary-soft: #432530;
		--vb-accent: #e8b445;
		--vb-gold: #e8b445;
		--vb-ink: #f8f0ea;
		--vb-text: #f8f0ea;
		--vb-muted: #c9b7be;
		--vb-base: #191418;
		--vb-surface: #261e23;
		--vb-cream: #221a1f;
		--vb-tint: #221a1f;
		--vb-tint-warm: #2a2018;
		--vb-hero-ink: #f8f0ea;
		--vb-hero-lead: #c9b7be;
		--vb-hero-fog: #221a1f;
		--vb-line: #56434d;
		--vb-border: #56434d;
		--vb-line-strong: #6a5560;
		--vb-field-line: #a68a96;
		--vb-success-soft: #18362a;
		--vb-success-ink: #82d4a7;
		--vb-warning-soft: #3b2d15;
		--vb-accent-text: #f2b3c3;
		--vb-danger-text: #ffaf8e;
		--vb-danger-text-strong: #ffaf8e;
		--vb-danger-soft: #48231c;
		--vb-danger-ink: #ffaf8e;
		--vb-warn-text: #f3cb7a;
		--vb-warn-soft: #3b2d15;
		--vb-warn-ink: #f3cb7a;
		--vb-neutral-soft: #2e262b;
		--vb-neutral-ink: #cfc3c8;
		--vb-zebra: rgba(255, 255, 255, 0.045);
		--vb-header-glass: #191418;
		--vb-appnav-bg: #30262c;
		--vb-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.55), 0 18px 40px -26px rgba(0, 0, 0, 0.85);
		--vb-shadow-hover: 0 22px 48px -22px rgba(217, 169, 183, 0.22), 0 4px 12px -6px rgba(0, 0, 0, 0.6);
		--vb-glow: 0 18px 44px -18px rgba(217, 169, 183, 0.28);
		--vb-focus: 0 0 0 2px var(--vb-raised, #30262c), 0 0 0 5px var(--vb-focus-color, #80c4ff);
		/* The theme's preset tokens feed body and block colours - they turn
		   with us or the page ground stays white under dark cards. */
		--wp--preset--color--base: #191418;
		--wp--preset--color--surface: #261e23;
		--wp--preset--color--cream: #221a1f;
		--wp--preset--color--ink: #f8f0ea;
		--wp--preset--color--muted: #c9b7be;
		--wp--preset--color--line: #56434d;
		/* AniLevia (#408): roles the green palette did not have. */
		--vb-raised: #30262c;
		--vb-rose: #ddb79f;
		--vb-rose-ink: #ddb79f;
		--vb-focus-color: #80c4ff;
		--vb-info-text: #9bcdf2;
		--vb-info-soft: #1b3043;
		--vb-primary-dark: #a8405c;
		--vb-primary-darker: #160d12;
		--vb-success: #82d4a7;
		--vb-tariff-free-ink: #cbbfd3;
		--vb-tariff-free-soft: #312937;
		--vb-tariff-bronze-ink: #e9bca0;
		--vb-tariff-bronze-soft: #3d2d25;
		--vb-tariff-silver-ink: #c3d3e2;
		--vb-tariff-silver-soft: #29333e;
		--vb-tariff-gold-ink: #ead18e;
		--vb-tariff-gold-soft: #3b321d;
	}
}


/* Visible keyboard focus for all plugin interactive elements. */
.vb-nav a:focus-visible,
.vb-langdrop__toggle:focus-visible,
.vb-langdrop__item:focus-visible,
.vb-search-form :focus-visible,
.vb-practice-card a:focus-visible,
.vb-callback :focus-visible,
.vb-claim :focus-visible,
.vb-dashboard :focus-visible,
.vb-account :focus-visible,
.vb-booking :focus-visible,
.vb-appnav__item:focus-visible,
.vb-search-map__load:focus-visible {
	outline: 2px solid transparent;
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
	border-radius: var(--vb-radius-sm, 11px);
}

/* ----- Readable measure for prose/forms -----------------------------------
   The page content now spans a roomy app width (theme.json contentSize); cap
   genuinely text- or form-heavy components to a comfortable measure so line
   length and field width stay sane. Left-aligned (no auto-centering) so they
   line up with the left edge of the page-head title above them. */
/* .vb-faq left this list on 25.09.2026: the help centre has its own layout
   now (tiles, a reading column of 42rem, a column beside it), and a cap of
   820px here squeezed it into the old single column. */
.vb-onboarding {
	max-width: 780px;
}

.vb-booking-confirm {
	max-width: 620px;
}

.vb-callback,
.vb-claim {
	max-width: 540px;
}

/* The customer account is a personal page (appointment cards + small forms),
   not a wide data app — keep it to a comfortable width. */
.vb-account {
	max-width: 940px;
}

/* Single-line inputs/selects should not stretch across the roomy content
   width; textareas and the tab strip stay full width. */
.vb-account input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.vb-account select,
.vb-dashboard input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="time"]),
.vb-dashboard select {
	max-width: 32.5rem;
}

/* Defensive, not corrective: measuring at 320px showed the bottom bar merely
   MATCHED the document width the header had already forced (hiding the bar
   left scrollWidth unchanged at 358), so it was never the offender - see the
   header rule above for the one that was. These two lines keep it from
   becoming one if a label ever grows. */
.vb-appnav__item {
	min-width: 0;
	overflow-wrap: anywhere;
}

/* ----- Primary navigation -------------------------------------------------- */

/* At 320px - what a 1280px window reflows to at 400% zoom, so the low-vision
   case rather than a rare phone - the header ran the document 38px wide and
   every page scrolled sideways. The nav is a nowrap flex row, and its call to
   action carries white-space: nowrap, so in Luxembourgish "Déierendokter
   fannen" measured 182px and simply pushed: brand 98 + CTA 182 + burger 46
   plus gaps does not fit in 320. German is shorter, which is why only one
   locale showed it.

   Letting the label wrap costs 20px of header height and loses nothing.
   Truncating it would have fit too, and would have shortened the primary
   action on the page to "Déierendokt…". */
/* The same overflow as at 26.5rem, one breakpoint up: between the burger
   taking over and a screen wide enough for the whole menu, the header is
   still a nowrap row. In Luxembourgish "Déierendokter fannen" is 146px and
   brand 67 + menu 562 + gaps + CTA came to 863 in an 834px tablet, so every
   lb page scrolled sideways. Letting the row shrink and the label wrap costs
   a line of header height and nothing else. */
@media ( max-width: 60rem ) {
	.vb-nav,
	.vb-nav__menu {
		min-width: 0;
		flex-wrap: wrap;
	}

	.vb-nav__cta {
		min-width: 0;
		white-space: normal;
		text-align: center;
		line-height: 1.2;
	}
}

@media ( max-width: 26.5rem ) {
	.vb-nav {
		min-width: 0;
	}

	.vb-nav__cta {
		min-width: 0;
		white-space: normal;
		text-align: center;
		line-height: 1.2;
	}
}
.vb-nav {
	display: flex;
	align-items: center;
	gap: 1.4rem;
	font-size: var(--vb-text-body, 1rem);
}

.vb-nav__burger {
	display: none;
	padding: 0;
	background: none;
	width: 44px;
	height: 44px;
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
	position: relative;
	/* --vb-field-line, not --vb-line: the audit measures a BUTTON's border as
	   the boundary of a control (1.4.11, 3:1), and --vb-line is #e3eae8 -
	   1.28:1 on white. It went unmeasured while this was a <label>, which
	   the harness does not count as a control; turning it into a real button
	   made an old value visible rather than creating a new one. */
	border: 1px solid var(--vb-field-line, #8c716e);
}

.vb-nav__burger span,
.vb-nav__burger span::before,
.vb-nav__burger span::after {
	content: "";
	position: absolute;
	left: 50%;
	width: 20px;
	height: 2px;
	background: var(--vb-ink);
	transform: translateX(-50%);
	transition: transform 0.2s ease, opacity 0.2s ease;
}

.vb-nav__burger span {
	top: 50%;
	margin-top: -1px;
}

.vb-nav__burger:focus-visible {
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

.vb-nav__burger span::before { top: -6px; }
.vb-nav__burger span::after { top: 6px; }

.vb-nav__menu {
	display: flex;
	align-items: center;
	gap: 1.4rem;
}

.vb-nav__link {
	position: relative;
	/* 26px of clickable height in a header whose other controls are 40 and
	   52. These are navigation items, not links inside a sentence, so the
	   inline-link exemption does not cover them - and the height costs
	   nothing: the row is already as tall as the language switcher, so
	   growing the links changes no layout at all. */
	display: inline-flex;
	align-items: center;
	min-height: 40px;
	color: var(--vb-ink);
	text-decoration: none;
	font-weight: 600;
}

.vb-nav__link:hover {
	color: var(--vb-accent-text, #68192e);
}

/* Active page indicator: teal label + underline grown from the link. */
.vb-nav__link.is-active:not(.vb-nav__cta) {
	color: var(--vb-accent-text, #68192e);
}

.vb-nav__link.is-active:not(.vb-nav__cta)::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: -0.45rem;
	height: 2px;
	border-radius: 2px;
	background: var(--vb-primary);
}

/* Paint and size come from .vb-btn--sm (M-109); only the ring below is its own. */

/* Active CTA: solid ring so the current page reads even on the button. */
.vb-nav__cta.is-active {
	box-shadow: 0 0 0 2px var(--vb-base), 0 0 0 4px var(--vb-primary);
}

/* Die zurueckgehaltene Pille der Startseite (header-cta.js) behaelt ihre Box,
   solange sie wartet - unsichtbar und nicht erreichbar, aber an ihrem Platz,
   damit ihr Erscheinen die Zeile nicht neu verteilt (Nachpruefung KH8). */
.vb-nav__cta[data-vb-waiting] {
	visibility: hidden;
	opacity: 0;
	pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
	.vb-nav__cta--deferred {
		transition: opacity var(--vb-motion-hover) var(--vb-ease), visibility 0s;
	}

	.vb-nav__cta--deferred[data-vb-waiting] {
		transition: opacity var(--vb-motion-hover) var(--vb-ease), visibility 0s var(--vb-motion-hover);
	}
}

/* ----- Language switcher (dropdown) ---------------------------------------- */
.vb-flag {
	display: block;
	width: 1.3rem;
	height: auto;
	border-radius: 2px;
	box-shadow: 0 0 0 1px rgba(48, 39, 43, 0.12);
	flex: none;
}

.vb-langdrop {
	position: relative;
	display: inline-block;
}

/* Toggle: current flag + name + caret, styled as a compact pill. */
.vb-langdrop__toggle {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	/* The same 40px the theme toggle beside it has carried since the
	   analysis asked for it. This control was left out and measured 34px
	   live - two header controls of two different sizes, one of them under
	   the size the other was raised to. */
	box-sizing: border-box;
	min-height: 40px;
	padding: 0.3rem 0.6rem;
	border: 1px solid var(--vb-line);
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-base);
	color: var(--vb-ink);
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	cursor: pointer;
	list-style: none;
	white-space: nowrap;
}

.vb-langdrop__toggle::-webkit-details-marker {
	display: none;
}

.vb-langdrop__toggle:hover {
	border-color: var(--vb-primary);
}

.vb-langdrop__caret {
	width: 0;
	height: 0;
	border-left: 4px solid transparent;
	border-right: 4px solid transparent;
	border-top: 5px solid var(--vb-muted);
	transition: transform 0.15s ease;
}

.vb-langdrop[open] .vb-langdrop__caret {
	transform: rotate(180deg);
}

.vb-langdrop__code {
	font-weight: 700;
	font-size: var(--vb-text-caption, 0.78rem);
}

/* Right-anchored, because the header switcher sits at the right edge of a
   wide screen. Both other placements are left-aligned and get corrected
   below - a 186px panel hung off a left-aligned trigger reached 56px past
   the left edge of a phone, where nothing can scroll to it. */
.vb-langdrop__menu {
	position: absolute;
	right: 0;
	left: auto;
	top: calc(100% + 6px);
	z-index: 60;
	min-width: 11rem;
	/* Last line of defence: never wider than the screen it opens on. */
	max-width: calc(100vw - 24px);
	margin: 0;
	padding: 4px;
	list-style: none;
	background: var(--vb-base);
	border: 1px solid var(--vb-line);
	border-radius: var(--vb-radius-sm, 11px);
	box-shadow: var(--vb-shadow-hover, 0 22px 48px -22px rgba(104, 25, 46, 0.32), 0 4px 12px -6px rgba(48, 39, 43, 0.20));
}

/* The footer switcher drops up, and wraps onto its own flush-left line on a
   phone - so it anchors left as well. The --up modifier used to flip only
   the vertical axis, which is how the panel ended up off-canvas. */
/* The three display preferences as one control in the header.
   Three separate switches in a row were what pushed a navigation label onto
   two lines on every profile page; this is the same three groups the account
   and the console show, behind one word. It borrows .vb-langdrop for the
   popover and its Escape/click-outside behaviour. */
.vb-nav__prefs-menu {
	min-width: 15rem;
	padding: 12px 14px;
	display: flex;
	flex-direction: column;
	gap: 12px;
	text-align: left;
}

.vb-nav__prefs-menu fieldset {
	margin: 0;
	padding: 0;
	border: 0;
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.vb-nav__prefs-menu legend {
	padding: 0;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 700;
	color: var(--vb-muted);
}

.vb-nav__prefs-menu label {
	display: flex;
	align-items: center;
	gap: 8px;
	/* A 44px row, so the choice is a target and not a word with a dot.
	   The comment always said 44 and the value was 2.25rem - 36px
	   (re-audit C16). */
	min-height: var(--vb-tap-primary, 44px);
	padding: 0 4px;
	border-radius: var(--vb-radius-xs, 6px);
	font-weight: 500;
	cursor: pointer;
}

.vb-nav__prefs-menu label:hover {
	background: var(--vb-surface);
}

.vb-nav__prefs-menu input {
	flex: 0 0 auto;
	margin: 0;
	accent-color: var(--vb-cat, var(--vb-accent-text, #68192e));
}

.vb-nav__prefs-menu .vb-prefs__options {
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.vb-nav__prefs-menu .vb-prefs__hint {
	max-inline-size: 16rem;
	padding-inline-start: 4px;
}

.vb-nav__prefs-menu .vb-prefs__switch {
	font-weight: 600;
}

.vb-langdrop--up .vb-langdrop__menu {
	top: auto;
	bottom: calc(100% + 6px);
	left: 0;
	right: auto;
}

.vb-langdrop__item {
	display: flex;
	align-items: center;
	/* Five entries in a stack, 38px each - just under the size the rest of
	   the header was raised to. box-sizing on this one rule because the
	   project has no border-box reset: without it min-height is the CONTENT
	   height and the padding lands on top, so "40" measured 54. */
	box-sizing: border-box;
	min-height: 40px;
	gap: 0.55rem;
	padding: 0.45rem 0.6rem;
	border-radius: var(--vb-radius-xs, 6px);
	text-decoration: none;
	color: var(--vb-ink);
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 500;
}

a.vb-langdrop__item:hover {
	background: var(--vb-primary-soft);
	color: var(--vb-accent-text, #68192e);
}

.vb-langdrop__item.is-current {
	background: var(--vb-primary-soft);
	color: var(--vb-accent-text, #68192e);
	font-weight: 700;
}

/* ----- Mobile nav ----------------------------------------------------------
 * The seam sits at 60rem, the same place the wrap rule above ends, and it
 * used to sit at 719px. That gap was the two-line header: below 720px the
 * burger, from 720px the full row - which does not fit until much later.
 * Measured in WebKit, one language at a time, header height at 16px steps:
 * German and Luxembourgish reach a single row at 896px, Portuguese at 928,
 * French only at 960. Below that the row wrapped and the header stood at
 * 108-116px where it is 59px everywhere else.
 *
 * One seam for every language, set by the widest of them. The cost is a
 * 64px band where a German tablet gets the burger although the row would
 * have fitted; the alternative was a doubled header in two languages, and
 * a burger is a control that works.
 *
 * The wrap itself is not the fault and is not removed: it is what keeps an
 * lb tablet from scrolling sideways. What was wrong was showing a row where
 * it does not fit. Now the two rules meet at one seam instead of leaving
 * 241px between them.
 */
@media ( max-width: 59.99rem ) {
	.vb-nav__burger {
		display: inline-flex;
	}

	/* Fixed to the viewport, not absolute under the header.

	   Measured on 7.9. at 390x844 in Chromium and WebKit: scrolled to y=2000
	   the sticky header sat at top 0 and the burger at top 10, so the button
	   was reachable - and one tap later the header was at top -2000 and the
	   menu with it, 1937px above the screen. The cause was the scroll lock:
	   `overflow: hidden` on the body takes away the scrolling ancestor that
	   `position: sticky` resolves against, so the header fell back to where
	   the document says it belongs, and the menu, anchored to it with
	   `top: 100%`, followed. A reader had to scroll back to the top to see a
	   menu they had already opened.

	   A sheet anchored to the viewport cannot be moved by any of that. It
	   starts where the header ends - measured into --vb-header-h, because the
	   header is 63px with one line and taller with two - and scrolls on its
	   own when the list is longer than the screen. */
	.vb-nav__menu {
		display: none;
		position: fixed;
		left: 0;
		right: 0;
		top: var(--vb-header-h, 63px);
		z-index: 95;
		max-height: calc( 100dvh - var(--vb-header-h, 63px) );
		overflow-y: auto;
		overscroll-behavior: contain;
		flex-direction: column;
		align-items: stretch;
		gap: 0.4rem;
		padding: 1rem 1.5rem 1.4rem;
		background: var(--vb-base);
		border-bottom: 1px solid var(--vb-line);
		box-shadow: var(--vb-shadow, 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40));
	}

	/* Inside the burger menu everything is a left-aligned column, including
	   the switcher, so its panel must open to the right of its trigger. */
	.vb-nav__menu .vb-langdrop__menu {
		left: 0;
		right: auto;
	}

	.vb-nav.is-open .vb-nav__menu {
		display: flex;
	}

	.vb-nav.is-open .vb-nav__burger span {
		background: transparent;
	}

	.vb-nav.is-open .vb-nav__burger span::before {
		transform: translateX(-50%) translateY(6px) rotate(45deg);
	}

	.vb-nav.is-open .vb-nav__burger span::after {
		transform: translateX(-50%) translateY(-6px) rotate(-45deg);
	}

	.vb-nav__link {
		padding: 0.5rem 0.7rem;
	}

	/* Mobile dropdown: a left accent + tint reads better than an underline
	   between stacked rows. */
	.vb-nav__link.is-active:not(.vb-nav__cta) {
		border-left: 3px solid var(--vb-primary);
		background: var(--vb-primary-soft);
		border-radius: 0 var(--vb-radius-sm, 11px) var(--vb-radius-sm, 11px) 0;
	}

	.vb-nav__link.is-active:not(.vb-nav__cta)::after {
		display: none;
	}

	.vb-nav {
		gap: 0.6rem;
	}

	.vb-nav__cta {
		text-align: center;
		white-space: nowrap;
	}

	.vb-nav__langs {
		margin-top: 0.4rem;
	}

	/* C1: im Menueblatt klappt die Darstellung IN der Spalte auf, statt als
	   schwebendes Feld aus einem 204px hohen Blatt zu ragen, das man eigens
	   scrollen musste - beim Oeffnen war keine der sieben Optionen zu sehen. */
	.vb-nav__menu .vb-nav__prefs .vb-langdrop__menu {
		position: static;
		inline-size: auto;
		max-inline-size: none;
		margin-block-start: 0.5rem;
		box-shadow: none;
	}

	/* KH16: die Sprache steht im Kopf, nicht im Menue. */
	.vb-nav__menu .vb-nav__langs {
		display: none;
	}

	.vb-nav .vb-nav__langs--bar {
		display: inline-flex;
		margin: 0 0 0 auto;
	}

	.vb-nav__langs--bar .vb-langdrop__menu {
		left: auto;
		right: 0;
		/* Above the open menu sheet (.vb-nav__menu, 95): both sit in the
		   header's stacking context, and at 60 the language list opened
		   behind the sheet - with the menu open, choosing a language showed
		   nothing but a sliver under the button (owner, 01.10.2026; measured
		   in WebKit and Chromium, browser and installed app alike). */
		z-index: 96;
	}
}

/* Ueber der Naht steht die Sprache in der Zeile selbst; die zweite Kopie
   bleibt dort weg. */
.vb-nav__langs--bar {
	display: none;
}

/* ----- Onboarding / "for practices" page (Task 17) ------------------------ */
.vb-onboarding__lead {
	font-size: 1.2rem;
	line-height: 1.6;
	color: var(--vb-muted, #6b5b60);
}

.vb-onboarding__steps {
	margin: 0 0 1.5rem;
	padding-left: 1.3rem;
	display: grid;
	gap: 12px;
}

.vb-onboarding__steps li {
	line-height: 1.5;
}

.vb-onboarding__benefits {
	margin: 0 0 1.5rem;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 10px;
}

.vb-onboarding__benefits li {
	padding-left: 1.8rem;
	position: relative;
}

.vb-onboarding__benefits li::before {
	content: "✓";
	position: absolute;
	left: 0;
	color: var(--vb-success, #176849);
	font-weight: 900;
}

.vb-onboarding__cta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.8rem;
	margin-top: 1.5rem;
}

/* .vb-onboarding__btn and its --ghost are .vb-btn / .vb-btn--ghost (M-109). */

/* Die Probe-Zeile ("7 Tage Gold ...") stand in profile.css, das die Seite fuer
   Betriebe nicht laedt - ungestaltet war sie die wuchtigste Zeile ueber dem
   Formular (Inhaber-Screenshot 24.09.2026). Ein ruhiger Hinweis, keine
   Schlagzeile. */
.vb-onboarding__trial {
	margin: 0 0 14px;
	padding: 12px 16px;
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-sm, 11px);
	color: var(--vb-ink, #30272b);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-onboarding__trial strong {
	font-weight: 600;
}

/* ==========================================================================
   Photo slots + branded placeholders (swappable via options / meta).
   ========================================================================== */
.vb-photo {
	display: block;
	margin: 0;
	width: 100%;
	overflow: hidden;
	border-radius: var(--vb-radius-lg, 22px);
	background: var(--vb-surface, #f2e7df);
}

.vb-photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Placeholder: soft brand wash + paw + "photo follows" tag. */
.vb-photo--ph {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	color: var(--vb-accent-text, #68192e);
	background:
		radial-gradient( 120% 120% at 0% 0%, var(--vb-primary-soft, #f3e2e7), transparent 55% ),
		radial-gradient( 120% 120% at 100% 100%, var(--vb-primary-soft, #f3e2e7), transparent 55% ),
		var(--vb-surface, #f2e7df);
}

.vb-photo--ph > .vb-ic {
	opacity: 0.45;
}

.vb-photo__tag {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	padding: 5px 12px;
	border-radius: var(--vb-radius-pill, 999px);
	/* The page's own ground, not a white literal: this pill sits on the
	   placeholder frame of every photo that is still to come, and stayed a
	   lit white rectangle on the dark scheme. */
	background: var(--vb-base, #fbf7f2);
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 600;
}

/* Homepage species photo strip */







/* Onboarding photo */
.vb-onboarding__photo {
	margin: 0 0 1.6rem;
	box-shadow: var(--vb-shadow, 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40));
}

/* ----- Installed-PWA bottom navigation ------------------------------------
   A native-app-style tab bar that only appears when the site runs as an
   installed app (standalone display mode) — in the browser it stays hidden,
   so the regular website is untouched. Safe-area insets keep it clear of the
   iOS home indicator. */
/* One navigation per screen (M-108). Wherever the tab bar below is drawn,
   the header copies of its destinations - home, help, the account door,
   the green search button - are not. Measured on a phone before this: four
   destinations offered twice, in the burger and in the bar under it. The
   two conditions are the bar's own two conditions, kept in step by test. */
/* Drei Klassen, nicht eine: bis zum 23.09.2026 stand hier die blanke
   Klasse, und .vb-nav__link--account { display: inline-flex } - gleiche
   Spezifitaet, spaeter in der Datei - gewann. "Anmelden" stand damit unter
   1280px wieder doppelt, im Kopf und in der Leiste darunter (Nachpruefung
   KH7). Die Regel existierte, sie griff nur nicht. */
@media ( max-width: 79.99rem ) {
	.vb-nav .vb-nav__link.vb-nav__link--tabbed {
		display: none;
	}
}

@media all and (display-mode: standalone) {
	.vb-nav .vb-nav__link.vb-nav__link--tabbed {
		display: none;
	}
}

.vb-appnav {
	display: none;
}

/* Phones and tablets alike, installed or not. Search and help are what a
   first-time visitor needs most, and a fixed bar is what keeps them in thumb
   reach - on a tablet held in one hand just as much as on a phone. Only the
   desktop, which has room for the full header, goes without - unless it is
   the installed app, which has no browser chrome to navigate with.
   ONE rule set for both conditions (Aufgabe #222, graphics lead B11): until
   04.10.2026 the installed app had its own copy of every declaration below,
   and the two drifted - the copy kept a typed 64px body padding with the
   inset on top, and a size changed in one stayed old in the other. */
@media ( max-width: 79.99rem ), all and (display-mode: standalone) {
	/* The bar is fixed over the page, so anything the browser scrolls to -
	   a focused field reached by Tab, a #fragment, a validation error - can
	   land underneath it and look like nothing happened. Reserving its
	   height plus the safe-area inset as scroll padding keeps the target
	   above the bar; scroll-margin does the same for elements that get
	   scrolled to directly. */
	/* One number for all three reservations below, and it is MEASURED:
	   site-theme.js writes the bar's real height into this property and
	   keeps it up to date. Live it is 77px, while these rules reserved 72px
	   and the body 64px - so a focused field could still land under it. */
	/* And whatever else stands on it - the profile's action bar: the whole
	   bottom stack, measured by sticky-stack.js (--vb-sticky-bottom, Aufgabe
	   #211). With the app bar alone a focused field could end under the
	   action bar above it (review of PR #216, round 2). */
	html {
		scroll-padding-bottom: max( var( --vb-appnav-h ), var( --vb-sticky-bottom, 0px ) );
	}

	:target,
	.vb-search-results__count,
	[id] > :focus-visible {
		scroll-margin-bottom: max( var( --vb-appnav-h ), var( --vb-sticky-bottom, 0px ) );
	}

	/* 64px with its line on a phone (Aufgabe #222, graphics lead B1/rank 1;
	   it was 83): 1 + 7 + a 48px item + 8, border-box, the same build as the
	   profile's action bar standing on it - together 128px at most, the
	   budget for the bottom of a 390x844 screen. The home indicator's inset
	   takes the place of the 8px under the items rather than coming on top
	   of them, and it is counted ONCE: the measured height already holds
	   it, nothing adds it again. min-height, not height: a larger text size
	   or a label on two lines makes the bar taller instead of cutting it.
	   Opaque, a line and no shadow or glass - nothing shows through it. */
	.vb-appnav {
		position: fixed;
		inset: auto 0 0 0;
		z-index: 1000;
		display: grid;
		grid-auto-flow: column;
		grid-auto-columns: minmax(0, 1fr);
		align-items: stretch;
		gap: 2px;
		box-sizing: border-box;
		padding: 7px 6px max( 8px, env( safe-area-inset-bottom, 0px ) );
		background: var(--vb-appnav-bg, #fffcf8);
		border-top: 1px solid var(--vb-line, #d9c7c1);
	}

	body:has( .vb-appnav ) {
		padding-bottom: var( --vb-appnav-h );
	}

	.vb-appnav .vb-appnav__item {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 2px;
		/* 48px with its padding (border-box): icon 24 + 2 + one line of
		   label, 4px above and below. Was 52px of CONTENT plus 12px of
		   padding, 64px a tab. */
		box-sizing: border-box;
		min-height: 48px;
		padding: 4px;
		border-radius: var(--vb-radius-sm, 11px);
		color: var(--vb-muted, #6b5b60);
		/* 0.68rem is 10.88px, and the measure asks for 12. Measured across
		   four languages: at 12px two French labels run wider than their
		   column on a 320px screen and at 11.5px one still does - while at
		   390px and up, 12px fits every label in every language. So the size
		   follows the width instead of picking one of those two answers:
		   10.88px where the space really is that tight, 12px from 390px on,
		   which is every phone sold this decade. */
		font-size: clamp(0.68rem, 0.36rem + 1.6vw, 0.8rem);
		font-weight: 600;
		line-height: 1.1;
		text-decoration: none;
		text-align: center;
		/* A tab label may wrap between words. It may never be cut through
		   the middle of one: measured in WebKit on a 320px phone, "Anmelden"
		   is 52px in a 52px box and came out as "Anmelde / n". A shorter
		   source string fixes today's five labels; these three declarations
		   are what keeps the next translation from doing it again. */
		overflow-wrap: normal;
		word-break: normal;
		hyphens: none;
	}

	.vb-appnav .vb-appnav__item svg {
		width: 24px;
		height: 24px;
		flex: none;
	}

	.vb-appnav .vb-appnav__item.is-active {
	/* One rule for both conditions now (above); it used to be two copies,
	   and the installed app - where the higher-specificity one won - read
	   2.23:1 on a surface no browser tab ever renders. */
		/* accent-TEXT, not primary-darker: the soft ground turns dark with
		   the scheme, and a near-black label on it reads 1.35:1. */
		color: var(--vb-accent-text, #68192e);
		background: var(--vb-primary-soft, #f3e2e7);
	}
}

@media all and (display-mode: standalone) {
	.vb-appnav .vb-appnav__item:active {
		background: var(--vb-surface, #f2e7df);
	}

	/* Reserve space so the fixed bar never covers page content/footer -
	   for a browser without :has(), the same measured number. */
	body {
		padding-bottom: var( --vb-appnav-h );
	}
}

/* The on-screen keyboard (Aufgabe #222, graphics lead B9 and rank 14).
   sticky-stack.js sets data-vb-keyboard while a text field has the focus and
   the visual viewport has lost a quarter of the window. Then the extra bars
   go - the app bar here, the profile's action bar and its fixed anchor row
   (profile.css) - so the field, its error sentence and the way to "Send"
   are not covered by them; the bars' measured heights fall to 0 with them,
   and the scroll padding at the bottom becomes what the keyboard covers.
   When the keyboard goes, the attribute goes and everything is back. No
   transition: a bar is there or not. */
:root[data-vb-keyboard] .vb-appnav,
:root[data-vb-keyboard] .vb-profile__stickybar {
	display: none;
}

:root[data-vb-keyboard] {
	scroll-padding-bottom: calc( var( --vb-keyboard-h, 0px ) + 8px );
}

/* ----- Link colour: one rule, both schemes --------------------------------
   theme.json paints links with the primary-dark PRESET, which is also the
   fill behind white button text. One token cannot be both: on the dark
   ground that green reads 2.8:1. Links take the accent-TEXT token (identical
   #0c7a4a in light, a bright green in dark); buttons keep the fill.
   :root:root outranks WP's `:root :where(a…)` without !important. */
/* Link colour in DARK only. theme.json paints links with the primary-dark
   preset, which is also the fill behind white button text - on the dark
   ground that green reads 2.8:1. In light it reads 4.9:1 on white and is
   right as it stands, so this must not touch the light scheme: a blanket
   rule also recoloured links sitting on filled green bands, and green on
   green is worse than the problem it fixed. Exclusions live in :where() so
   they cost no specificity; Leaflet's own chrome is recoloured where the
   map is (search.css, profile.css), not here. */
:root[data-theme="dark"] a:not(:where(
	.wp-element-button,
	.wp-block-button__link,
	.vb-btn,
	.vb-cta__btn,
	[class*="__cta"],
	[class*="__btn"],
	.leaflet-container a,
	/* Der Notfallweg behaelt sein Rot. Gemessen im Dunkelmodus, bevor er
	   hier stand: rgb(59,220,149) - diese Regel ist eine Klasse
	   spezifischer als .vb-emergencylink und hatte ihn uebernommen. Ein
	   Notfallhinweis in Minzgruen ist keiner, und site.css nennt ihn eine
	   Zeile weiter unten ausdruecklich "the only red link on either page". */
	.vb-emergencylink,
	/* Die Notfallzeile der Terminbox ebenso, und der Name in der
	   Profilleiste steht in Schriftfarbe wie im Kopf (Aufgabe #211). */
	.vb-booking__boxsos a,
	.vb-profilebar__name,
	/* Navigation, die im Hellen ihre eigene Farbe traegt, behaelt sie auch
	   im Dunkeln: Kopflinks, Wortmarke, Kacheln, Reiter der App-Leiste,
	   Ankerleiste, Sprachmenue und Kartentitel standen alle in Minzgruen,
	   der aktive Reiter war nur noch an einer Flaeche mit 1,42:1 zu erkennen
	   (Nachpruefung M3, P21). Ihre Farben kommen aus Tokens und drehen mit. */
	.vb-nav__link,
	.wp-block-site-title a,
	.vb-typetile,
	.vb-appnav__item,
	.vb-profile__anchors a,
	.vb-langdrop__item,
	.vb-practice-card__title a,
	.vb-discover__list a,
	/* Ein Knopf in der Gewerbefarbe, kein Textlink: Minzschrift auf der
	   Knopfflaeche las 3,04:1 (Nachpruefung Nr. 4). */
	.vb-profile__stickybar-link
)) {
	color: var(--vb-accent-text, #68192e);
}


/* ----- Touch: no auto-zoom on focus ---------------------------------------
   iOS Safari zooms the page IN when a focused field's font-size is under
   16px - and it never zooms back out. The chat composer (14.2px) did
   exactly that: tapping the reply box magnified the page, so the send
   button and the outgoing bubbles sat outside the right edge. Every field
   a finger can reach keeps at least 16px; larger sizes survive via max(). */
@media (max-width: 47.99rem), (hover: none) and (pointer: coarse) {
	/* :root:root, not a bare element selector: component rules like
	   `.vb-chat__bar textarea { font: inherit }` are one class more
	   specific and site.css is printed first, so a plain `textarea` rule
	   loses both ways. Doubling :root wins on specificity without
	   spraying !important through the sheet. */
	:root:root input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]),
	:root:root textarea,
	:root:root select {
		font-size: max(16px, 1em);
	}
}


/* ----- Theme switch (site-theme.js) ---------------------------------------- */
.vb-nav__theme {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	/* Header controls sit side by side with the language picker; 40px is
	   the size the analysis asked for and comfortably clears the 24px
	   minimum the audit measures.
	   box-sizing, because the project has no border-box reset: without it
	   min-height is the CONTENT height and the padding lands on top.
	   Measured 18.09.2026 - this one came out 54px, the language picker 52
	   and the account pill 40, three controls in one row at three heights.
	   The same note already stood on .vb-langdrop__item; it had not reached
	   the two controls beside it. */
	box-sizing: border-box;
	min-height: 40px;
	background: transparent;
	/* --vb-line wie Sprache und Konto daneben. Mit --vb-field-line war er
	   der einzige der drei mit dunklem Rand (Nachpruefung KH9); was ihn als
	   Knopf ausweist, ist das Symbol darin, nicht der Rahmen. */
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	color: var(--vb-ink, #30272b);
	font-size: var(--vb-text-body, 1rem);
	line-height: 1;
	padding: 6px 12px;
	cursor: pointer;
}


.vb-nav__theme-label {
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
}

/* Das Wort "Darstellung" bleibt sichtbar - Inhaberentscheidung 8. Am
   23.09.2026 kurz zu einem reinen Symbol gemacht, weil die Grafik runde
   Symbolknoepfe zeigt; die Entscheidung geht der Grafik vor (C7). */


.vb-nav__theme:hover {
	border-color: var(--vb-accent-text, #68192e);
}


.vb-appnav__theme {
	background: transparent;
	border: 0;
	font-size: 1.15rem;
	cursor: pointer;
}


/* ----- On-duty (garde) banner ------------------------------------------------ */
.vb-guard {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 6px 10px;
	padding: 8px 16px;
	background: #7c241a;
	color: #fff;
	font-size: var(--vb-text-label, 0.9rem);
	text-align: center;
}

.vb-guard a {
	color: #fff;
	font-weight: 600;
}

.vb-guard__call {
	padding: 2px 10px;
	border-radius: var(--vb-radius-pill, 999px);
	background: rgba(255, 255, 255, 0.18);
	text-decoration: none;
}

.vb-guard__dot {
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: #ff8b7c;
	animation: vb-guard-pulse 1.6s ease-in-out infinite;
}

@keyframes vb-guard-pulse {
	50% { opacity: 0.35; }
}

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

/* ----- "Free today" strip (home) --------------------------------------------- */
.vb-todaystrip__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: 14px;
}

.vb-todaystrip__card {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 14px 16px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
	text-decoration: none;
	color: var(--vb-ink, #30272b);
	transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.vb-todaystrip__card:hover {
	transform: translateY(-2px);
	border-color: var(--vb-primary, #b66a80);
	box-shadow: 0 14px 30px -22px rgba(48, 39, 43, 0.35);
	text-decoration: none;
}

.vb-todaystrip__meta {
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-todaystrip__slot {
	align-self: flex-start;
	margin-top: 4px;
	padding: 0.2rem 0.7rem;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-primary-soft, #f3e2e7);
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 700;
}

.vb-todaystrip__all {
	margin: 14px 0 0;
}

/* Inline-icon baseline (always loaded, so plugin markup renders standalone
   regardless of theme — audit P3). */
.vb-ic {
	display: inline-block;
	vertical-align: -0.18em;
	flex: none;
}


/*
 * Footer chrome. Lives here, not in home.css: the footer renders on every
 * page, so keeping its rules in the front-page sheet forced home.css onto
 * the whole site (found in the design review).
 */
.vb-footer-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem 3rem;
}

.vb-footer-nav__title {
	margin: 0 0 0.8rem;
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--vb-muted);
}

.vb-footer-nav__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.55rem;
}

.vb-footer-nav__list a {
	/* The footer is the one place a whole column of targets sits a few
	   pixels apart: measured 20px tall with 11px between neighbours, which
	   fails the minimum and its spacing exception together. inline-flex so
	   the box grows without stretching the column. */
	display: inline-flex;
	align-items: center;
	min-height: var(--vb-tap-min);
	color: var(--vb-ink);
	text-decoration: none;
}

.vb-footer-nav__list a:hover {
	color: var(--vb-accent-text, #68192e);
	text-decoration: underline;
}

.vb-footer-nav__soon {
	color: var(--vb-muted, #6b5b60);
	opacity: 1;
}

/*
 * iOS Safari zooms the page in when a focused control renders below 16px,
 * and never zooms back out - which is what made tapping the hero search feel
 * broken on an iPhone. Raising only the too-small ones keeps the design
 * intact. Deliberately not solved with user-scalable=no: that would take
 * pinch-zoom away from everyone (WCAG 1.4.4).
 */
@media (hover: none) and (pointer: coarse) {
	input:not([type="checkbox"]):not([type="radio"]),
	select,
	textarea {
		font-size: max(16px, 1em);
	}
}

/*
 * Any region that scrolls sideways must keep that gesture to itself. On iOS
 * a swipe that reaches the end of an inner scroller otherwise continues into
 * the page and drags the whole layout off-screen.
 */
[style*="overflow-x"],
.vb-scroll-x,
.vb-pricing__matrix-scroll,
.vb-table-scroll {
	overscroll-behavior-x: contain;
}

/* Registration folds away: most arrivals already have an account, and on a
   phone its seven fields pushed the sign-in button off the first screen. */
.vb-account__register > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	cursor: pointer;
	list-style: none;
}
.vb-account__register > summary::-webkit-details-marker {
	display: none;
}
/* The heading moved from h3 to h2 when the account page's outline was
   straightened; a selector left on h3 stops matching silently and the title
   comes back with its default margins inside the summary. Both, so the rule
   survives the next change of level too. */
.vb-account__register > summary h2,
.vb-account__register > summary h3 {
	margin: 0;
}
.vb-account__register > summary::after {
	content: "+";
	font-size: 1.5rem;
	line-height: 1;
	color: var(--vb-accent-text, #68192e);
}
.vb-account__register[open] > summary::after {
	content: "–";
}

/* ----- Touch behaviour ------------------------------------------------------
   Small things that separate a web page from an app on a phone: no grey flash
   on every tap, no 300ms wait before a button responds, and no accidental
   text selection when someone double-taps a control. */
a,
button,
summary,
label,
[role="button"],
input[type="submit"] {
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}
button,
summary,
[role="button"] {
	-webkit-user-select: none;
	user-select: none;
}

/* ----- The emergency path (M-008) ----------------------------------------
   A page somebody reads in a hurry, one-handed: the red band says what
   this is and what it is not, the entries are name, place and a number
   big enough to hit. Red is the only place on the site that uses the
   danger family for a whole band, and it is used because the page is the
   one thing on the site that must not be mistaken for anything else. */
.vb-emergency {
	display: flex;
	flex-direction: column;
	gap: 28px;
}

.vb-emergency__lead {
	padding: 18px 20px;
	border-left: 4px solid var(--vb-danger-text, #b52216);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-danger-soft, #ffece6);
	color: var(--vb-ink, #30272b);
}

.vb-emergency__title {
	margin: 0 0 8px;
	color: var(--vb-danger-text, #b52216);
}

.vb-emergency__lead p {
	margin: 0 0 8px;
	max-width: 70ch;
}

.vb-emergency__disclaimer {
	margin: 0;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
}

.vb-emergency__block h2 {
	margin: 0 0 12px;
	/* AniLevia Sans draws its capitals about 5px above the heading's box
	   (WebKit hit-tests the glyphs there), so the heading would seem to
	   stand between the red head and its own section. 4px inside keeps the
	   glyphs in the section the emergency gate measures (#408). */
	padding-block-start: 4px;
}

.vb-emergency__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.vb-emergency__item {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 12px;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
}

.vb-emergency__name {
	font-weight: 700;
	color: var(--vb-ink, #30272b);
}

.vb-emergency__where {
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-emergency__call {
	margin-left: auto;
}

.vb-emergency__nonumber {
	margin-left: auto;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
}

.vb-emergency__none {
	margin: 0;
	max-width: 70ch;
	color: var(--vb-muted, #6b5b60);
}

.vb-emergency__all,
.vb-emergency__read {
	margin: 14px 0 0;
}

/* The way in, from the home page and the search: red, and the only red
   link on either page. */
.vb-emergencylink {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-height: var(--vb-tap-min, 24px);
	color: var(--vb-danger-text, #b52216);
	font-weight: 600;
	text-decoration: none;
}

.vb-emergencylink:hover,
.vb-emergencylink:focus-visible {
	text-decoration: underline;
}

/* ----- Buttons (M-109) ---------------------------------------------------
   One scale for every button on the public site. Measured before this:
   36 button surfaces on a phone in 12 different heights, 75 on a desktop
   in 14, with radii of 8, 10, 11, 12 and 999px - eight implementations of
   the same thing. Two layers, on purpose:

   GEOMETRY sits in :where(), at specificity 0, so a component class still
   wins on any property it sets - the console's own .vb-dashboard .vb-btn
   rules (0-2-0) are untouched, and a public component keeps only what is
   layout and its typeface.

   PAINT cannot sit at 0: half of these buttons are links, and a link rule
   as plain as `a { color }` (0-0-1) outranks :where(). Measured after the
   first deploy: green text on the green pricing button, 100 contrast
   findings. So colour, fill and edge are written as `a.vb-btn,
   button.vb-btn, .vb-btn` - 0-1-1 for the two elements that matter, which
   beats `a` and `a:hover` and still loses to the console's 0-2-0.

   Bare .vb-btn is the primary; four more variants, three sizes, a pressed
   state, and a hover that exists only for a pointer that can hover, so a
   phone never shows a button stuck in its hover look. */
:where(.vb-btn) {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	box-sizing: border-box;
	min-height: var(--vb-tap-primary, 44px);
	margin: 0;
	padding: 10px 20px;
	font: inherit;
	font-weight: 600;
	line-height: 1.2;
	text-align: center;
	border-width: 1px;
	border-style: solid;
	/* Abgerundete Rechtecke, keine Pillen: alle sechs Vorschaugrafiken
	   zeichnen die Knoepfe mit einem Radius von rund einem Zehntel bis einem
	   Sechstel ihrer Hoehe (Nachpruefung P10). In einer Spalte standen
	   sonst Pille, 16px und 11px nebeneinander. */
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
	appearance: none;
	-webkit-appearance: none;
	transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

/* Three sizes. The console's --small predates this scale and keeps its
   own padding; here it only gives back the height it never asked for. */
:where(.vb-btn--sm) {
	min-height: 36px;
	padding: 6px 14px;
	font-size: var(--vb-text-label, 0.9rem);
}

:where(.vb-btn--lg) {
	min-height: 52px;
	padding: 14px 26px;
	font-size: var(--vb-text-lead, 1.0625rem);
}

:where(.vb-btn--small) {
	min-height: 0;
}

:where(.vb-btn--block) {
	width: 100%;
}

:where(.vb-btn--text) {
	padding-inline: 6px;
}

/* Paint: the primary. */
a.vb-btn,
button.vb-btn,
.vb-btn {
	/* --vb-cat is the business's own tone and exists only inside a provider
	   profile (profile.css). Everywhere else it is undefined and the fallback
	   is the brand green, byte for byte what this rule painted before. Read
	   here rather than overridden in profile.css, because profile.css is
	   enqueued BEFORE this sheet: a rule of equal specificity there loses,
	   which is how five industries shared one green button for an afternoon.
	   --vb-cat-ink comes with it, because a light tone in the dark scheme
	   cannot carry white text. */
	color: var(--vb-cat-ink, #fff);
	background: var(--vb-cat, var(--vb-primary-dark, #68192e));
	border-color: var(--vb-cat, var(--vb-accent-text, #68192e));
	text-decoration: none;
}

/* The quiet fill: soft green, accent ink. */
a.vb-btn--secondary,
button.vb-btn--secondary,
.vb-btn--secondary {
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border-color: transparent;
}

/* The outline: for the second of two offers. */
a.vb-btn--ghost,
button.vb-btn--ghost,
.vb-btn--ghost {
	color: var(--vb-accent-text, #68192e);
	background: transparent;
	border-color: var(--vb-field-line, #8c716e);
}

/* The one that removes or refuses something. */
a.vb-btn--danger,
button.vb-btn--danger,
.vb-btn--danger {
	color: var(--vb-danger-text, #b52216);
	background: var(--vb-danger-soft, #ffece6);
	border-color: var(--vb-danger-text, #b52216);
}

/* A link that happens to sit in a row of buttons. */
a.vb-btn--text,
button.vb-btn--text,
.vb-btn--text {
	color: var(--vb-accent-text, #68192e);
	background: transparent;
	border-color: transparent;
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

:where(.vb-btn):focus-visible {
	outline: 2px solid transparent;
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

/* Pressed: the button goes down, not up. */
:where(.vb-btn):active {
	transform: translateY(1px);
	box-shadow: none;
}

:where(.vb-btn)[disabled],
:where(.vb-btn)[aria-disabled="true"] {
	opacity: 0.55;
	cursor: default;
	transform: none;
	box-shadow: none;
}

@media (hover: hover) {
	:where(.vb-btn):hover {
		transform: translateY(-1px);
		box-shadow: var(--vb-shadow-hover, 0 22px 48px -22px rgba(104, 25, 46, 0.32), 0 4px 12px -6px rgba(48, 39, 43, 0.20));
	}

	a.vb-btn:not([class*="vb-btn--"]):hover,
	button.vb-btn:not([class*="vb-btn--"]):hover,
	.vb-btn:not([class*="vb-btn--"]):hover {
		/* Same substitution as the resting state: inside a provider profile
		   the hover keeps the business's hue instead of going near-black,
		   which read as a different button altogether. */
		color: var(--vb-cat-ink, #fff);
		background: var(--vb-cat-hover, var(--vb-primary-darker, #300b17));
		border-color: var(--vb-cat-hover, var(--vb-primary-darker, #300b17));
	}

	a.vb-btn--secondary:hover,
	button.vb-btn--secondary:hover,
	.vb-btn--secondary:hover,
	a.vb-btn--ghost:hover,
	button.vb-btn--ghost:hover,
	.vb-btn--ghost:hover {
		color: var(--vb-accent-text, #68192e);
		background: var(--vb-primary-soft, #f3e2e7);
		border-color: var(--vb-accent-text, #68192e);
		box-shadow: none;
	}

	a.vb-btn--danger:hover,
	button.vb-btn--danger:hover,
	.vb-btn--danger:hover {
		color: #fff;
		background: var(--vb-danger-text, #b52216);
		box-shadow: none;
	}

	a.vb-btn--text:hover,
	button.vb-btn--text:hover,
	.vb-btn--text:hover {
		transform: none;
		box-shadow: none;
		text-decoration-thickness: 2px;
	}
}

@media all and (display-mode: standalone) {
	/* Installed, so there is no browser chrome to rubber-band against: a
	   downward drag at the top must not look like a broken pull-to-refresh. */
	html,
	body {
		overscroll-behavior-y: contain;
	}

	/* Landscape on a tablet puts the notch at the side, not the bottom. */
	.vb-appnav {
		padding-left: calc( 6px + env( safe-area-inset-left ) );
		padding-right: calc( 6px + env( safe-area-inset-right ) );
	}

}

/* A phone bar stretched across a tablet looks wrong: keep it thumb-sized and
   centred, where a hand actually rests. */
@media ( min-width: 48rem ) and ( max-width: 79.99rem ) {
	.vb-appnav {
		left: 50%;
		right: auto;
		transform: translateX( -50% );
		width: min( 32rem, 100% );
		border-radius: var(--vb-radius-lg, 22px) var(--vb-radius-lg, 22px) 0 0;
		border-left: 1px solid var(--vb-line, #d9c7c1);
		border-right: 1px solid var(--vb-line, #d9c7c1);
	}
}

/* Installed on a desktop there is still no browser chrome, so the bar has to
   appear there too - centred, as on a tablet. Everything else it takes from
   the one rule set above, which holds for the installed app at any width. */
@media all and (display-mode: standalone) and ( min-width: 80rem ) {
	.vb-appnav {
		left: 50%;
		right: auto;
		transform: translateX( -50% );
		width: min( 32rem, 100% );
		border: 1px solid var(--vb-line, #d9c7c1);
		border-bottom: 0;
		border-radius: var(--vb-radius-lg, 22px) var(--vb-radius-lg, 22px) 0 0;
	}
}


.vb-navmenu--desktop-account {
	display: none;
}

/* The desktop header carries the tab destinations, installed or in a tab.
   Measuring the hidden bar publishes zero to the shared height contract. */
@media ( min-width: 62.5rem ) {
	:root {
		--vb-appnav-h: 0px;
	}

	.vb-appnav {
		display: none;
	}

	body,
	body:has( .vb-appnav ) {
		padding-bottom: 0;
	}

	.vb-nav .vb-nav__link.vb-nav__link--tabbed {
		display: inline-flex;
	}

	.vb-nav .vb-nav__link.vb-nav__link--account-door {
		display: none;
	}

	.vb-navmenu.vb-navmenu--desktop-account {
		display: block;
	}
}

/* ----- A-Z directory index ------------------------------------------------
   The page had no stylesheet at all: 210 business names as bare 17px links
   and a jump-nav of single characters 9-13px wide, 11px apart. That is a
   list of targets, not a sentence, so WCAG's inline exception does not
   apply to it - it was simply too small to hit. */
.vb-az {
	margin: 0 0 2rem;
}


.vb-az__nav {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0 0 1.5rem;
	padding: 12px;
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-az__nav a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: var(--vb-tap-min);
	min-height: var(--vb-tap-min);
	padding: 0 6px;
	border-radius: var(--vb-radius-xs, 6px);
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-field-line, #8c716e);
	color: var(--vb-accent-text, #68192e);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	text-decoration: none;
}


.vb-az__nav a:hover,
.vb-az__nav a:focus-visible {
	/* The brand's soft ground: a hover, not a success (#408). */
	background: var(--vb-primary-soft, #f3e2e7);
}


.vb-az__group {
	margin: 0 0 1.75rem;
}


.vb-az__letter {
	margin: 0 0 .5rem;
	padding-bottom: .3rem;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
	font-size: 1.15rem;
}


.vb-az__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 2px;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
}


.vb-az__list li {
	margin: 0;
}

/* Name und Haekchen in einem Kasten: der Link ist inline-flex, und hinter
   einem solchen Kasten haelt das geschuetzte Leerzeichen nicht - bei einem
   langen Namen stand das Zeichen allein in der naechsten Zeile (gemessen
   03.10.2026, 390 px). */
.vb-az__name {
	display: inline-flex;
	align-items: center;
	max-inline-size: 100%;
	vertical-align: middle;
}

.vb-az__name > a {
	min-inline-size: 0;
}

.vb-az__name > .vb-namemark {
	flex: 0 0 auto;
}


.vb-az__list a {
	display: inline-flex;
	align-items: center;
	min-height: var(--vb-tap-min);
	padding: 3px 6px;
	border-radius: var(--vb-radius-xs, 6px);
	color: var(--vb-ink, #30272b);
	text-decoration: none;
}


.vb-az__list a:hover,
.vb-az__list a:focus-visible {
	background: var(--vb-surface, #f2e7df);
	text-decoration: underline;
}


.vb-az__empty {
	color: var(--vb-muted, #6b5b60);
}


/* ----- Hero search, second version ---------------------------------------
   Visible labels, not placeholders: a placeholder disappears the moment
   somebody types, so the field they are halfway through loses its name -
   and a screen reader was the only visitor here who ever had one. */
.vb-herosearch__label {
	display: block;
	margin-bottom: 4px;
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	letter-spacing: 0.03em;
	color: var(--vb-muted, #6b5b60);
}


.vb-herosearch__control {
	display: flex;
	align-items: center;
	gap: 8px;
}


.vb-herosearch__near {
	margin: 10px 0 0;
	font-size: var(--vb-text-label, 0.9rem);
}


.vb-herosearch__near a {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-height: var(--vb-tap-min, 24px);
	color: var(--vb-accent-text, #68192e);
	font-weight: 600;
}


/* The row wrapped mid-chip on a phone, which reads as a broken row rather
   than a second line. Each field takes a full row instead.

   The seam moved from 48rem to 40rem on 9.9.2026. At 48rem it also caught
   768, 820 and 834 - every common tablet - and stacked their search into
   three tall rows, which is the phone treatment on a screen with room for
   the row. Below 40rem the row genuinely does not fit: three segments and a
   button in 640px give each about 150px. The geometry of the stacked form
   lives in home.css beside the row it replaces. */
@media ( max-width: 40rem ) {

	.vb-herosearch__field {
		width: 100%;
	}
}

/* The placeholder that stands where a profile map used to load itself.
   A control, not an overlay: the keyboard reaches it and a screen reader is
   told what it does. */
.vb-map-ask {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.35rem;
	width: 100%;
	min-height: 10rem;
	padding: 1rem;
	font: inherit;
	font-weight: 600;
	color: var(--vb-ink, #30272b);
	text-align: center;
	background: var(--vb-surface, #f2e7df);
	border: 1px dashed var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius, 16px);
	cursor: pointer;
}

.vb-map-ask:hover,
.vb-map-ask:focus-visible {
	background: var(--vb-primary-soft, #f3e2e7);
}

/* The button ships disabled and the script enables it. Without JavaScript
   there is no way to draw a map, so what stays is the sentence explaining
   why - not a control that does nothing when pressed. The cursor says so
   too, because a dashed box that looks pressable and is not is worse than
   one that does not. */
.vb-map-ask[disabled] {
	cursor: default;
	color: var(--vb-muted, #6b5b60);
	border-style: dotted;
}

.vb-map-ask[disabled]:hover {
	background: var(--vb-surface, #f2e7df);
}

.vb-map-ask__note {
	max-width: 26rem;
	font-weight: 400;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
	line-height: 1.4;
}

/* ----- Consent ------------------------------------------------------------
 * Rendered on every page and hidden; the script decides. Both cache layers
 * would otherwise hand the first visitor's decision to everybody.
 *
 * The three choices are one shape on purpose. CNPD 3.2.4 names form,
 * typeface, colour, size and contrast one by one, and the EDPB taskforce
 * report puts a refusal on any layer that carries a consent button - so the
 * accept button gets no colour the reject button does not have. */
.vb-consent {
	position: fixed;
	inset: auto 0 0 0;
	z-index: 1003;
	display: flex;
	justify-content: center;
	padding: 0 max( 0.75rem, env( safe-area-inset-left ) ) max( 0.75rem, env( safe-area-inset-bottom ) );
	pointer-events: none;
}

body.vb-consent-open::after {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 1002;
	background: rgba(48, 11, 23, 0.45 );
}

.vb-consent__sheet {
	pointer-events: auto;
	width: 100%;
	max-width: 34rem;
	max-height: min( 80vh, 40rem );
	overflow-y: auto;
	padding: 1.15rem;
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-lg, 22px);
	box-shadow: var(--vb-shadow, 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40));
}

/* No entrance animation (Aufgabe #146). The sheet used to rise 10px over
   220ms; for the one dialog every visitor meets first, a still page is the
   calmer start, and it is the same for everybody - including those whose
   calm view or animation choice the banner is about to ask for. */

/* The language row, above the question and separated from it: whoever cannot
   read the question needs this first. */
.vb-consent__langs {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	margin: 0 0 0.9rem;
	padding: 0 0 0.9rem;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
}

.vb-consent__langs-hint {
	flex: 1 0 100%;
	margin: 0 0 0.15rem;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-caption, 0.78rem);
}

.vb-consent__lang {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	padding: 0.4rem 0.62rem;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-base, #fbf7f2);
	color: inherit;
	font-size: var(--vb-text-label, 0.9rem);
	line-height: 1;
	text-decoration: none;
}

a.vb-consent__lang:hover,
a.vb-consent__lang:focus-visible {
	border-color: var(--vb-accent-text, #68192e);
}

.vb-consent__lang[aria-current="true"] {
	border-color: var(--vb-accent-text, #68192e);
	box-shadow: inset 0 0 0 1px var(--vb-accent-text, #68192e);
	font-weight: 600;
}

/* One size for every flag, everywhere it appears. The hairline is a ring and
   not a border: white flags - Luxembourg, France - otherwise dissolve into a
   white sheet, and the ring has to survive forced colours, where a background
   would not. */
.vb-flag {
	flex: 0 0 auto;
	width: 1.3em;
	height: 0.93em;
	border-radius: 2px;
	box-shadow: 0 0 0 1px rgba(48, 39, 43, 0.18 );
}

/* The language hint bar. Hidden by default and revealed by an attribute the
   head script sets before the body is parsed, so the bar is in the layout
   from the first frame instead of pushing the page down in front of the
   reader. One rule per language because CSS cannot match an attribute
   against another attribute; a sixth language needs a sixth line here, and
   a test says so. */
.vb-langhint {
	display: none;
}

:root[data-vb-hint] .vb-langhint {
	display: block;
}

.vb-langhint__row {
	display: none;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.3rem 0.7rem;
	padding: 0.4rem clamp( 1rem, 4vw, 2rem );
	background: var(--vb-surface, #f2e7df);
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
	font-size: var(--vb-text-label, 0.9rem);
}

:root[data-vb-hint="lb"] .vb-langhint__row[data-lang="lb"],
:root[data-vb-hint="de"] .vb-langhint__row[data-lang="de"],
:root[data-vb-hint="fr"] .vb-langhint__row[data-lang="fr"],
:root[data-vb-hint="en"] .vb-langhint__row[data-lang="en"],
:root[data-vb-hint="pt"] .vb-langhint__row[data-lang="pt"] {
	display: flex;
}

.vb-langhint__text {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	margin: 0;
}

.vb-langhint__go,
.vb-langhint__no {
	display: inline-flex;
	align-items: center;
	min-height: 40px;
	padding: 0.35rem 0.5rem;
	border-radius: var(--vb-radius-xs, 6px);
	font: inherit;
}

.vb-langhint__go {
	color: var(--vb-accent-text, #68192e);
	font-weight: 600;
}

.vb-langhint__no {
	margin-left: auto;
	border: 0;
	background: none;
	color: var(--vb-muted, #6b5b60);
	cursor: pointer;
}

.vb-consent__title {
	margin: 0 0 0.5rem;
	font-size: var(--vb-text-lead, 1.0625rem);
}

.vb-consent__lead {
	margin: 0 0 0.6rem;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
	line-height: 1.5;
}

.vb-consent__more {
	margin: 0 0 0.9rem;
	font-size: var(--vb-text-label, 0.9rem);
}

/* A grid and not a wrapping row: flex-wrap gave two buttons on the first
   line and a wider third under them at mid widths (pre-review of #146, M4) -
   same classes, different sizes. Grid columns are equal by construction and
   grid-auto-rows: 1fr makes every row as tall as the tallest label, so the
   three are the same size on every screen. Stacked below 30rem; from there
   side by side, where a label may take two lines and the other two grow
   with it. */
.vb-consent__choices {
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	grid-auto-rows: 1fr;
	gap: 0.5rem;
	margin-inline: -0.25rem;
	padding: 0.5rem 0.25rem;
	background: var(--vb-base, #fbf7f2);
}

@media ( min-width: 30rem ) {
	.vb-consent__choices {
		grid-template-columns: repeat( 3, minmax( 0, 1fr ) );
	}
}

/* Never below the fold of the sheet (pre-review M4). Measured at 320x568:
   language row, question and text end 405-426px into the sheet, which shows
   454px - the three buttons began under its lower edge in every language.
   Pinned to the sheet's edges they are visible from the first frame and
   stay visible when the reader scrolls down to the display options; where
   they fit they stand where they are. The ground hides what passes behind,
   and the line says something does.

   Only where the sheet is tall enough for it (review of PR #150, M1): the
   stacked block is 164px, and in a sheet under ~350px - a phone on its
   side, 300-400 % zoom - it covered a focused link, radio or checkbox
   completely (WCAG 2.4.11). So the stack is pinned from 35rem of screen
   height, the row of three (60px) from 22.5rem; below that the buttons
   stay where they are in the text and nothing is ever behind them.
   scroll-padding keeps a stop reached by Tab beside the pinned block. */
@media ( max-width: 29.99rem ) and ( min-height: 35rem ), ( min-width: 30rem ) and ( min-height: 22.5rem ) {
	.vb-consent__choices {
		position: sticky;
		top: 0;
		bottom: 0;
		z-index: 1;
		box-shadow: 0 -1px 0 var(--vb-line, #d9c7c1);
	}
}

@media ( max-width: 29.99rem ) and ( min-height: 35rem ) {
	.vb-consent__sheet {
		scroll-padding-block: 11rem;
	}
}

@media ( min-width: 30rem ) and ( min-height: 22.5rem ) {
	.vb-consent__sheet {
		scroll-padding-block: 5rem;
	}
}

/* One class for all three. A rule that gave the accepting button its own
   look would be the dark pattern the guidance names. */
.vb-consent__btn {
	min-height: var(--vb-tap-primary, 44px);
	padding: 0.35rem 0.75rem;
	font: inherit;
	font-weight: 600;
	line-height: 1.25;
	color: var(--vb-ink, #30272b);
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
}

.vb-consent__btn:hover,
.vb-consent__btn:focus-visible {
	background: var(--vb-primary-soft, #f3e2e7);
}

.vb-consent__detail {
	margin-top: 0.9rem;
	padding-top: 0.9rem;
	border-top: 1px solid var(--vb-line, #d9c7c1);
}

.vb-consent__row {
	margin: 0 0 0.8rem;
}

.vb-consent__switch {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	min-height: var(--vb-tap-min, 24px);
	cursor: pointer;
}

.vb-consent__name {
	font-weight: 600;
}

.vb-consent__text {
	display: block;
	margin-top: 0.15rem;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
	line-height: 1.45;
}

.vb-consent__btn--save {
	margin-top: 0.2rem;
}

/* Closing is a refusal and says so, so it is a labelled control and not a
   cross people press expecting "later". */
.vb-consent__shut {
	display: block;
	margin: 0.9rem 0 0;
	padding: 0.25rem 0;
	min-height: var(--vb-tap-min, 24px);
	font: inherit;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
	background: none;
	border: 0;
	text-decoration: underline;
	cursor: pointer;
}

/* A short screen gives the sheet its height rather than a fifth of it to
   the page behind: at 568px, 80vh left 454px for language, question and
   the three answers. */
@media ( max-height: 44rem ) {
	.vb-consent__sheet {
		/* 1.5rem of page around it, and the sheet's own padding and
		   border, which max-height does not include. */
		max-height: calc( 100vh - 3.8rem - 2px );
		max-height: calc( 100dvh - 3.8rem - 2px );
	}
}

@media ( min-width: 48rem ) {
	.vb-consent {
		justify-content: flex-start;
		padding-inline: 1.25rem;
	}
}

/* ----- Report a problem ----------------------------------------------------
   A dialog rather than a page: a defect is reported from the page it happened
   on, and navigating away takes the evidence with it - the measurements the
   report is for describe THIS render. */
.vb-bug {
	width: min(34rem, calc(100% - 2rem));
	padding: 0;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
	color: var(--vb-ink, #30272b);
}


.vb-bug::backdrop {
	background: rgba(48, 11, 23, 0.45);
}


.vb-bug__form {
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: 20px;
}


.vb-bug__form h2 {
	margin: 0;
	font-size: 1.15rem;
}


.vb-bug__lead {
	margin: 0;
	color: var(--vb-muted, #6b5b60);
}


.vb-bug__field {
	display: flex;
	flex-direction: column;
	gap: 6px;
}


.vb-bug__field textarea {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	font: inherit;
	color: inherit;
	background: var(--vb-base, #fbf7f2);
}


/* The payload, readable before it is sent. Small and quiet, but present:
   a page that collects quietly has to be trusted rather than read. */
.vb-bug__data {
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	padding: 8px 12px;
	font-size: var(--vb-text-small, 0.85rem);
}


.vb-bug__data summary {
	min-height: var(--vb-tap-min, 24px);
	cursor: pointer;
	color: var(--vb-muted, #6b5b60);
}


.vb-bug__rows {
	display: grid;
	gap: 4px;
	margin-top: 8px;
}


.vb-bug__row {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
}


.vb-bug__k {
	flex: 0 0 auto;
	min-width: 9rem;
	color: var(--vb-muted, #6b5b60);
}


.vb-bug__v {
	flex: 1 1 12rem;
	overflow-wrap: anywhere;
	font-variant-numeric: tabular-nums;
}


.vb-bug__actions {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
}


.vb-bug__status {
	margin: 0;
	min-height: 1.2em;
	color: var(--vb-muted, #6b5b60);
}

/* The bar the page raises itself when it measures something wrong. Above the
   app nav, out of the thumb's way, and gone after twelve seconds - a bar that
   keeps returning is one people learn to dismiss without reading. */
.vb-bug-offer {
	position: fixed;
	left: 12px;
	right: 12px;
	/* Above the app bar and above the profile's action bar - both measured,
	   neither typed. 76px was a typed guess at the app bar's height. */
	bottom: calc( var(--vb-appnav-h) + var(--vb-stickybar-h, 0px) + 12px );
	z-index: 70;
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 12px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
	box-shadow: var(--vb-shadow-hover, 0 22px 48px -22px rgba(104, 25, 46, 0.32), 0 4px 12px -6px rgba(48, 39, 43, 0.20));
	font-size: var(--vb-text-label, 0.9rem);
}


.vb-bug-offer span {
	flex: 1 1 auto;
}


.vb-bug-offer__close {
	flex: 0 0 auto;
	min-width: var(--vb-tap-min, 24px);
	min-height: var(--vb-tap-min, 24px);
	border: 0;
	background: none;
	color: var(--vb-muted, #6b5b60);
	font-size: 1.2rem;
	line-height: 1;
	cursor: pointer;
}


@media ( min-width: 80rem ) {
	.vb-bug-offer {
		left: auto;
		right: 24px;
		/* --vb-appnav-h is 0 here and the bar's real height in an installed
		   desktop app, where the tab bar comes back. */
		bottom: calc( var(--vb-appnav-h) + 24px );
		max-width: 26rem;
	}
}

/* ----- Support panel ------------------------------------------------------
   Help and "something is broken" behind one door, because they are the same
   moment: somebody who cannot do what they came for does not yet know which
   of the two they have.

   The trigger appears in exactly one place - the desktop browser, the only
   width where the fixed tab bar is absent. Everywhere else the Help tab in
   that bar opens this panel, so nothing ever floats over the navigation. */
.vb-support__open {
	position: fixed;
	right: max( 1rem, env( safe-area-inset-right ) );
	bottom: max( 1rem, env( safe-area-inset-bottom ) );
	z-index: 1001;
	display: none;
	place-items: center;
	width: 3.4rem;
	height: 3.4rem;
	font-size: 1.35rem;
	font-weight: 700;
	color: #fff;
	background: var(--vb-primary-dark, #68192e);
	border: 0;
	border-radius: var(--vb-radius-pill, 999px);
	box-shadow: 0 10px 28px -12px rgba(48, 39, 43, 0.6 );
	cursor: pointer;
}

.vb-support__open:hover,
.vb-support__open:focus-visible {
	background: var(--vb-primary-darker, #300b17);
}

/* One glyph at a time. A trigger that stays "?" while the panel it opened
   covers the corner leaves the reader nothing to aim at to close it. */
.vb-support__glyph {
	grid-area: 1 / 1;
}

.vb-support__glyph--shut,
.vb-support.is-open .vb-support__glyph--ask {
	display: none;
}

.vb-support.is-open .vb-support__glyph--shut {
	display: block;
}

@media ( min-width: 80rem ) {
	.vb-support__open {
		display: grid;
	}
}

/* Installed on a desktop the bar is back, and with it the Help tab. */
@media all and (display-mode: standalone) and ( min-width: 80rem ) {
	.vb-support__open {
		display: none;
	}
}

/* A sheet, not a floating card. Below the desktop width the tab bar is what
   opens this, so it rises from the bar: flush against it, edge to edge, its
   top corners rounded and its own bottom padding carrying the home
   indicator. It used to sit 4.75rem above the bar to clear its own open
   button - a button that only exists at the desktop width, so on a phone
   that was 76px of nothing between the sheet and the bar that opened it. */
.vb-support__panel {
	position: fixed;
	z-index: 1002;
	right: 0;
	left: 0;
	bottom: var(--vb-appnav-h);
	margin-inline: auto;
	/* 520px: wide enough for the answers, narrow enough on a tablet that the
	   sheet reads as a sheet and not as a second page. */
	max-width: 32.5rem;
	max-height: min( 70vh, 32rem );
	overflow-y: auto;
	padding: 0.9rem 1.15rem calc( 1.15rem + env( safe-area-inset-bottom ) );
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-block-end: 0;
	border-radius: var(--vb-radius-lg, 22px) var(--vb-radius-lg, 22px) 0 0;
	box-shadow: 0 -18px 48px -20px rgba(48, 39, 43, 0.55 );
}

/* The handle, drawn only where the thing is a sheet. */
.vb-support__grip {
	width: 2.5rem;
	height: 4px;
	margin: 0 auto 0.75rem;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-line, #d9c7c1);
}

/* Modal below the desktop width, so it needs something behind it. */
body.vb-support-open::before {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 1000;
	background: rgba(48, 11, 23, 0.45 );
}

@media ( prefers-reduced-motion: no-preference ) {
	.vb-support.is-open .vb-support__panel {
		animation: vb-sheet-up 0.2s var(--vb-ease, ease) both;
	}
}

:root[data-focus="reduced"] .vb-support.is-open .vb-support__panel {
	animation: none;
}

@keyframes vb-sheet-up {
	from {
		opacity: 0;
		transform: translateY(16px);
	}
}

/* At the desktop width it is a popover anchored to its own button again,
   and the button is what it has to clear. */
@media ( min-width: 80rem ) {
	.vb-support__panel {
		right: max( 0.75rem, env( safe-area-inset-right ) );
		left: auto;
		bottom: calc( var(--vb-appnav-h) + 4.75rem );
		max-width: 24rem;
		margin-inline: 0;
		padding-block: 1rem;
		border-block-end: 1px solid var(--vb-line, #d9c7c1);
		border-radius: var(--vb-radius-lg, 22px);
		box-shadow: 0 18px 48px -20px rgba(48, 39, 43, 0.55 );
	}

	.vb-support__grip {
		display: none;
	}

	body.vb-support-open::before {
		display: none;
	}
}

.vb-support__title {
	margin: 0 0 0.5rem;
	font-size: var(--vb-text-lead, 1.0625rem);
}

.vb-support__lead {
	margin: 0 0 0.35rem;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
}

.vb-support__topics,
.vb-support__actions {
	margin: 0 0 0.75rem;
	padding: 0;
	list-style: none;
}

.vb-support__topics li + li {
	margin-top: 0.35rem;
}

.vb-support__topics summary {
	min-height: var(--vb-tap-min, 24px);
	padding: 0.35rem 0;
	font-weight: 600;
	cursor: pointer;
}

.vb-support__topics p {
	margin: 0.15rem 0 0.5rem;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
}

.vb-support__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.vb-support__actions a,
.vb-support__report {
	display: inline-flex;
	align-items: center;
	min-height: var(--vb-tap-primary, 44px);
	padding: 0 0.9rem;
	font: inherit;
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
}

.vb-support__close {
	min-height: var(--vb-tap-min, 24px);
	padding: 0.25rem 0;
	font: inherit;
	color: var(--vb-muted, #6b5b60);
	background: none;
	border: 0;
	text-decoration: underline;
	cursor: pointer;
}

/* On the narrowest phones the five columns are 60px each and the padding
   takes eight of them. Two pixels of padding and a hair less type give the
   label back the width it needs to stay on one line - measured, not guessed:
   "Anmelden" is 52px at 0.68rem and 49px at 0.64rem. */
@media ( max-width: 22.5rem ) {
	.vb-appnav .vb-appnav__item {
		padding-left: 2px;
		padding-right: 2px;
		font-size: var(--vb-text-caption, 0.78rem);
	}
}

/* ----- [hidden] means hidden ---------------------------------------------
 * The UA stylesheet's `[hidden] { display: none }` lives in the user-agent
 * origin, so ANY author rule that sets `display` on the same element wins -
 * without touching the attribute and without anything looking wrong in the
 * source. booking.js creates the series options with hidden="hidden" and
 * booking.css gives .vb-booking__seriesopts `display: flex`, so they were
 * on screen before the series box was ever ticked.
 *
 * Two places in search.css already carried their own guard against exactly
 * this. One rule replaces both and covers every element the next stylesheet
 * touches. `!important` is the whole point: it is what puts the attribute
 * back above author styles, which is where it was always meant to be.
 *
 * If something genuinely needs to be laid out while carrying the attribute,
 * that is a contradiction in the markup, not a reason to weaken this.
 */
[hidden] {
	display: none !important;
}

/* ----- The sticky header, actually sticky --------------------------------
 * The theme declares `position: sticky; top: 0` on the header and it has
 * never worked. Measured: after scrolling 600px the header's top was -600.
 *
 * The reason is not the declaration and not an overflow anywhere in the
 * chain - it is that WordPress wraps a template part in a plain div, and
 * that div is exactly as tall as the header inside it (59px and 59px). A
 * sticky element sticks within its PARENT's box, so it had nowhere to go.
 *
 * `display: contents` takes the wrapper out of the box tree and leaves the
 * header in the flow of the page, which is the box it was always meant to
 * stick inside. Scoped with :has() to the wrapper that holds the header, so
 * the footer's own wrapper is untouched; a browser without :has() ignores
 * the rule and gets today's behaviour rather than a broken one.
 */
.wp-block-template-part:has( > .vb-site-header ) {
	display: contents;
}

/* And the seam under it. The entry content brings a 24px top margin, which
 * is right when a heading follows and wrong when the full-bleed hero does:
 * a glass header floating 24px above a coloured band shows the page sliding
 * through the gap. Closed only where the hero actually leads - measured, the
 * hero is the first child there and carries 80px of padding of its own, so
 * nothing moves closer to the header than it already was.
 */
.vb-main:has( > .entry-content > .vb-hero:first-child ),
.vb-main > .entry-content:has( > .vb-hero:first-child ) {
	margin-block-start: 0;
}

/* ----- The burger, opened and closed like a menu -------------------------
 * The backdrop is what makes "click anywhere to close" discoverable, and the
 * scroll lock is what stops the page moving underneath an open menu on a
 * phone. Both hang off one class, so nothing here depends on where the
 * button sits in the markup - it sits after the menu, and a sibling
 * combinator only ever looks forwards.
 */
/* No `overflow: hidden` here. It was the scroll lock that broke the menu:
   taking the scrolling ancestor away from a `position: sticky` header drops
   it back to its document position, and the menu hanging under it went 2000px
   out of sight. The sheet is fixed and carries `overscroll-behavior: contain`,
   so a scroll inside it stays inside it; the page behind may move, and that
   costs nothing. */
body.vb-nav-open {
	overscroll-behavior: contain;
}

body.vb-nav-open::before {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 90;
	background: rgba(48, 11, 23, 0.35 );
}

@media ( min-width: 60rem ) {
	/* Above the seam the row is the navigation; a stray open state must not
	   leave a backdrop over a page that has no menu to close. */
	body.vb-nav-open::before {
		display: none;
	}
}

/* ----- Forced colours -----------------------------------------------------
 * Windows High Contrast, and the "increase contrast" setting on iOS, replace
 * every colour on the page with a pair the reader chose. That is the point of
 * the mode, and most of what it does to this site is fine.
 *
 * Three things it takes away are not: box-shadow is forced to none, which is
 * where the whole focus ring lived; background-color stops telling an active
 * tab from an inactive one; and a shape drawn out of backgrounds on an empty
 * pseudo-element - the burger, a section mark - simply disappears.
 *
 * Measured across five page groups before any of this existed: 23 findings on
 * five causes, six of them leaving a control that cannot be found at all.
 * Everything merely LOOKING different was left alone; forced colours are
 * supposed to look different.
 */
@media (forced-colors: active) {

	/* The safety net. Every focus rule in this project now writes a
	   transparent outline rather than `outline: none`, which this mode
	   recolours - but a rule added tomorrow without that care lands here. */
	:focus-visible {
		outline: 3px solid Highlight;
		outline-offset: 2px;
	}

	/* What is chosen, current or pressed. Highlight and HighlightText are
	   system colours: they are what the reader picked for exactly this. */
	.vb-nav__link.is-active,
	.vb-appnav__item.is-active,
	.vb-langdrop__item.is-current,
	.vb-nav__focus[aria-pressed="true"],
	.vb-nav__motion[aria-pressed="true"] {
		background: Highlight;
		color: HighlightText;
	}

	.vb-nav__link.is-active::after {
		background: Highlight;
	}

	/* A body drawn out of fill and shadow has neither here. An outline and
	   not a border, so nothing moves: the cards keep the geometry that was
	   measured, and the radius comes along. */
	.vb-support__open,
	.vb-practice-card,
	.vb-callback__form button,
	.vb-claim__form button {
		outline: 1px solid CanvasText;
	}

	.vb-site-header {
		outline: 1px solid CanvasText;
		outline-offset: -1px;
	}

	/* Shapes drawn out of backgrounds on empty elements: the burger - the
	   only way into the navigation on a phone - and the mark beside every
	   section heading. */
	.vb-nav__burger span,
	.vb-nav__burger span::before,
	.vb-nav__burger span::after {
		background: CanvasText;
	}
}

/* ----- Motion, asked for on its own ---------------------------------------
 * The calm view stops motion too, but it also takes away decoration. Somebody
 * who only wants the page to hold still gets exactly that here, and keeps the
 * gradients, the lift on hover and the rest.
 *
 * The same declarations prefers-reduced-motion switches off, so a reader who
 * has set it in their system and one who presses this button land in the same
 * place - and neither can switch anything back on.
 */
:root[data-motion="reduced"] *,
:root[data-motion="reduced"] *::before,
:root[data-motion="reduced"] *::after {
	animation: none !important;
	transition: none !important;
	scroll-behavior: auto !important;
}

/* The switch keeps the tab-bar's shape, like the calm view's does. */
.vb-nav__motion[aria-pressed="true"] {
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-pill, 999px);
}

/* ----- Calm view ----------------------------------------------------------
 * A standing request made on the site itself, remembered, and applied before
 * the first paint by the boot script in the head.
 *
 * prefers-reduced-motion is a system setting only some people have found,
 * and it speaks only about motion. This also takes away the decoration that
 * competes for attention: the drifting blobs, the gradients behind text, the
 * lift on hover. Nothing that carries meaning is removed - a badge, a price,
 * a status stays exactly where it was.
 */
:root[data-focus="reduced"] *,
:root[data-focus="reduced"] *::before,
:root[data-focus="reduced"] *::after {
	animation: none !important;
	transition: none !important;
	scroll-behavior: auto !important;
}

:root[data-focus="reduced"] .vb-hero__visual {
	display: none !important;
}

:root[data-focus="reduced"] .vb-cta,
:root[data-focus="reduced"] .vb-hero {
	background-image: none !important;
}

:root[data-focus="reduced"] .vb-practice-card:hover,
:root[data-focus="reduced"] .vb-typetile:hover,
:root[data-focus="reduced"] .vb-guides__item:hover {
	transform: none !important;
	box-shadow: none !important;
}

/* The switch itself keeps the tab-bar's shape, so the header row does not
   change height when a second control joins it. */
.vb-nav__focus[aria-pressed="true"] {
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-pill, 999px);
}

/* The sentence under a consent box: who stores the data and where the details
   are (Legal\ConsentNotice). In site.css rather than one of the form
   stylesheets because three different forms - booking widget, stay request,
   account registration - carry the same paragraph, and it must look the same
   in all three. Muted, but not opacity: a container with reduced opacity
   takes its link's contrast down with it, which the design audit measures. */
.vb-consent__note {
	margin: 6px 0 14px;
	font-size: var(--vb-text-small, 0.85rem);
	line-height: 1.45;
	color: var(--vb-muted, #6b5b60);
}

.vb-consent__note a {
	color: inherit;
	text-underline-offset: 2px;
}

/* ----- The measure of running text ---------------------------------------
 * A line of prose is read, not scanned. At the theme's 1080px content width
 * the legal pages ran to 127 characters a line - measured, and roughly twice
 * the length at which the eye reliably finds the start of the next one.
 *
 * Only DIRECT children, which is what makes this safe: the pages built from
 * shortcodes keep their own containers and their own widths. Measured across
 * eight pages, the home page, the search and the help centre have no direct
 * p/ul/ol under .entry-content at all, while the three legal pages have ten
 * to fifteen each.
 */
.entry-content > p,
.entry-content > ul,
.entry-content > ol,
.entry-content > dl {
	max-width: 70ch;
}

/* ----- Display preferences, as a set ------------------------------------
 * Three named groups rather than three switches. A switch shows its state and
 * hides the alternatives; here the question is "what are my options", and the
 * answer belongs on the screen.
 */
.vb-prefs {
	display: flex;
	flex-direction: column;
	gap: 14px;
	padding: 16px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
}

.vb-prefs__lead,
.vb-prefs__noscript {
	margin: 0;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-small, 0.85rem);
}

.vb-prefs__group {
	margin: 0;
	padding: 0;
	border: 0;
}

.vb-prefs__group legend {
	padding: 0 0 6px;
	font-weight: 600;
}

.vb-prefs__options {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

/* The whole label is the target, not the dot beside it. */
.vb-prefs__option {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: var(--vb-tap-primary, 44px);
	padding: 0 14px 0 12px;
	/* The field line, not the hairline: the pill is the control's edge, and
	   the hairline measured 1.35:1 against the dark sheet (WCAG 1.4.11
	   asks 3:1; Aufgabe #146). */
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
}

/* Chosen is said twice: the ground changes AND the radio stays visible. A
   reader who cannot tell the two grounds apart still has the dot. */
.vb-prefs__option:has(input:checked) {
	border-color: var(--vb-field-line, #8c716e);
	background: var(--vb-primary-soft, #f3e2e7);
	font-weight: 600;
}

.vb-prefs__option:focus-within {
	outline: 2px solid transparent;
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

@media (forced-colors: active) {
	.vb-prefs__option:has(input:checked) {
		background: Highlight;
		color: HighlightText;
	}
}

/* ----- The calm view as one switch, the rest folded (Aufgabe #146) --------
 * One checkbox with its sentence under it, on every surface: account,
 * console, header menu and cookie banner. The label row is the target. */
.vb-prefs__switchrow {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.vb-prefs__switch {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	min-height: var(--vb-tap-primary, 44px);
	font-weight: 600;
	cursor: pointer;
}

.vb-prefs__switch input {
	flex: 0 0 auto;
	inline-size: 20px;
	block-size: 20px;
	margin: 0;
	accent-color: var(--vb-accent-text, #68192e);
}

.vb-prefs__switch:focus-within {
	outline: 2px solid transparent;
	border-radius: var(--vb-radius-xs, 6px);
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

.vb-prefs__hint {
	margin: 0;
	padding-inline-start: 30px;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-small, 0.85rem);
	line-height: 1.45;
}

.vb-prefs__more {
	border-top: 1px solid var(--vb-line, #d9c7c1);
	padding-top: 4px;
}

.vb-prefs__more-toggle {
	display: flex;
	align-items: center;
	gap: 8px;
	min-height: var(--vb-tap-primary, 44px);
	font-weight: 600;
	color: var(--vb-accent-text, #68192e);
	cursor: pointer;
}

/* The marker as a drawn chevron: display:flex takes the native triangle
   away, and a fold without a sign that it opens reads as a heading. */
.vb-prefs__more-toggle::-webkit-details-marker {
	display: none;
}

.vb-prefs__more-toggle {
	list-style: none;
}

.vb-prefs__more-toggle::after {
	content: "";
	flex: 0 0 auto;
	inline-size: 7px;
	block-size: 7px;
	margin-inline-start: 2px;
	border-inline-end: 2px solid currentColor;
	border-block-end: 2px solid currentColor;
	transform: translateY( -2px ) rotate( 45deg );
}

.vb-prefs__more[open] > .vb-prefs__more-toggle::after {
	transform: translateY( 2px ) rotate( -135deg );
}

.vb-prefs__more-toggle:focus-visible {
	outline: 2px solid transparent;
	border-radius: var(--vb-radius-xs, 6px);
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

.vb-prefs__more-body {
	display: flex;
	flex-direction: column;
	gap: 14px;
	padding: 6px 0 4px;
}

/* While the calm view is on, the animation choice would change nothing you
   could see - the calm view already switches every animation off. So the
   group says that instead of offering the choice. Keyed on the attribute the
   head script sets before the first paint, so the right one shows from the
   first frame and on every surface at once. The stored choice is kept and
   applies again when the calm view goes off. */
.vb-prefs__locked {
	display: none;
	margin: 0;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-small, 0.85rem);
}

:root[data-focus="reduced"] [data-vb-motion-locked] {
	display: block;
}

:root[data-focus="reduced"] [data-vb-motion-options] {
	display: none;
}

/* ----- The display area of the cookie banner -------------------------------
 * After the three buttons and the way to close, set apart by a rule and its
 * own heading: it is not part of the consent answer, and it must not look
 * like one. No box of its own - the panel's frame would be a second card in
 * a card. */
.vb-prefs--banner {
	gap: 6px;
	margin-top: 0.9rem;
	padding: 0.9rem 0 0;
	border: 0;
	border-top: 1px solid var(--vb-line, #d9c7c1);
	border-radius: 0;
	background: none;
}

.vb-consent__display-title {
	margin: 0;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 700;
	color: var(--vb-muted, #6b5b60);
}

.vb-prefs--banner .vb-prefs__options {
	gap: 6px;
}

.vb-prefs--banner .vb-prefs__option {
	padding: 0 12px 0 10px;
	font-size: var(--vb-text-label, 0.9rem);
}

/* ----- "Show more" in the calm view ---------------------------------------
 * The calm view folds supporting texts away; what is information and not
 * decoration stays one press away (docs/RUHIGE-ANSICHT.md lists every rule).
 * The button exists only in the calm view - outside it nothing is folded -
 * and it opens its own block only, by an attribute the stylesheet's calm
 * rules step back from. */
.vb-calm-more {
	display: none;
}

:root[data-focus="reduced"] .vb-calm-more {
	display: inline-flex;
	/* Its own width in a column of stretched children too. */
	align-self: flex-start;
	justify-self: start;
	inline-size: auto;
	align-items: center;
	gap: 6px;
	min-height: var(--vb-tap-primary, 44px);
	margin: 0.4rem 0 0;
	padding: 0 14px;
	font: inherit;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
}

:root[data-focus="reduced"] .vb-calm-more:hover {
	background: var(--vb-primary-soft, #f3e2e7);
}

:root[data-focus="reduced"] .vb-calm-more:focus-visible {
	outline: 2px solid transparent;
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

/* ----- Der Kontoeintrag als Bedienelement --------------------------------
   Die verbindliche Referenzgrafik zeigt die Bedienelemente rechts im Kopf als
   Gruppe umrandeter Pillen; bei uns stand der Kontoeintrag als fuenfter
   Textlink daneben, und der Inhaber hat genau das als den Unterschied
   benannt.

   Umrandet, nicht gefuellt: §93 verbietet eine Reihe von Button-CTAs im Kopf,
   §94 verlangt EINEN Kontoeintrag - die Referenz zeigt daneben noch einen
   gruenen "Jetzt registrieren"-Knopf, und §95 sagt, die Suche ist der einzige
   primaere CTA der Startseite. Der gruene bleibt also weg. */
.vb-nav__link--account {
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	min-height: 40px;
	padding-inline: 0.85rem;
	border: 1px solid var(--vb-line);
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-base);
}

.vb-nav__link--account:hover,
.vb-nav__link--account:focus-visible {
	border-color: var(--vb-accent-text, #68192e);
}

/* Aktiv heisst hier "Sie sind auf dieser Seite", nicht "gedrueckt": der
   Unterstrich der Textlinks wuerde an einer Pille unter dem Rahmen kleben. */
.vb-nav__link--account.is-active {
	border-color: var(--vb-accent-text, #68192e);
	box-shadow: inset 0 0 0 1px var(--vb-accent-text, #68192e);
}

/* ----- Der Fuss als EIN Band (Vorschaugrafik) ---------------------------
   Nachpruefung 23.09.2026: vier gestapelte Bloecke, 456px hoch, achtzehn
   Links in drei Reihen (acht auf dem Telefon), eine Marke ohne Pfote und
   unter ihr ein anderer Satz als der Slogan im Kopf. Die Grafik zeigt ein
   flaches Band: Pfote, Marke und Slogan links, die Linkreihe, die
   Merkmale. Gebaut wird es in HomePage::render_footer_band(); hier steht,
   wie es liegt.

   Auf dem Desktop zwei Zeilen: Marke | Links oben, Merkmale | Sprachen
   darunter. Die Links selbst sind zwei Reihen - die sechs der Grafik und
   eine leise Reihe mit dem, was das Recht einen Klick entfernt haben will.
   Auf dem Telefon alles untereinander. Die Breite ist die der Kopfkarte,
   damit die beiden Pfoten buendig stehen (vorher 88px versetzt). */
footer.vb-site-footer {
	padding-block: 1.75rem;
}

.vb-site-footer__band {
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	grid-template-areas:
		"brand"
		"nav"
		"traits"
		"langs";
	gap: 1.1rem 2.5rem;
	box-sizing: border-box;
	/* Dieselbe Kante wie Kopf und Inhalt. Bis zum 23.09.2026 stand hier die
	   breite Deckelung mit dem Spacing-Preset, das live WordPress' 0,67rem
	   liefert: der Fuss begann auf jeder Breite bis 1280px bei x=11, Kopf
	   und Inhalt bei 16 bzw. 24 - und auf 1440px bei 91 statt 180. Jetzt die
	   Inhaltsbreite plus der Seitenrand aus theme.json, wie jede Seite. */
	max-width: calc( var(--wp--style--global--content-size, 1080px) + 2 * var(--wp--style--root--padding-left, 1rem) );
	margin-inline: auto;
	padding-inline: var(--wp--style--root--padding-left, 1rem);
}

@media (min-width: 62.5rem) {
	.vb-site-footer__band {
		grid-template-columns: auto minmax( 0, 1fr ) auto;
		grid-template-areas:
			"brand  nav    nav"
			"traits traits langs";
		align-items: center;
	}
}

.vb-site-footer__brand {
	grid-area: brand;
	display: flex;
	flex-direction: column;
	gap: 2px;
	max-width: none;
}

.vb-site-footer__home {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	color: var(--vb-ink, #30272b);
	font-family: var(--vb-display);
	font-size: 1.3rem;
	font-weight: 700;
	text-decoration: none;
}

.vb-site-footer__home .vb-brand__mark {
	color: var(--vb-accent-text, #68192e);
	line-height: 0;
}

:root[data-theme="dark"] .vb-site-footer__home .vb-brand__mark {
	color: var(--vb-primary, #d9a9b7);
}

/* Unter dem Namen, nicht unter der Pfote - wie im Kopf. */
.vb-site-footer__claim {
	padding-inline-start: calc( 34px + 0.6rem );
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-caption, 0.78rem);
}

.vb-footer-nav {
	grid-area: nav;
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
}

.vb-footer-nav__list {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	gap: 0 1.3rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* 44 px je Verweis (Aufgabe #257, A10): die Fusszeile ist der letzte Weg zu
   Hilfe, Recht und Einstellungen, und 32 px hohe Ziele dicht nebeneinander
   trafen am Telefon den Nachbarn. */
.vb-footer-nav__list a,
.vb-footer-nav__list .vb-footer-nav__soon {
	display: inline-flex;
	align-items: center;
	min-height: var(--vb-tap-primary, 44px);
}

.vb-footer-nav__col--main .vb-footer-nav__list a {
	color: var(--vb-ink, #30272b);
	font-size: var(--vb-text-label, 0.9rem);
}

/* Die leise Reihe: vollstaendig erreichbar, aber nicht gleich laut wie
   die sechs der Grafik - leiser durch die Farbe, nicht durch die Groesse:
   wesentliche Fussverweise mindestens 14 px (A10), vorher 12,5 px. */
.vb-footer-nav__col--meta .vb-footer-nav__list a,
.vb-footer-nav__col--meta .vb-footer-nav__soon {
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
}

/* Die Spaltenueberschriften sind nur fuer Hilfsmittel da. Die Klasse
   screen-reader-text ist in diesem Projekt nirgends definiert - sie kommt
   aus einem Blatt des Kerns, das dieses Block-Theme im Frontend nicht
   zwingend laedt. */
.vb-footer-nav__title.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Der Fokusring des Projekts statt des blauen des Browsers (FS11). */
.vb-site-footer a:focus-visible,
.vb-site-footer summary:focus-visible {
	outline: 2px solid transparent;
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
	border-radius: var(--vb-radius-xs, 6px);
}

/* ----- Die Merkmalzeile --------------------------------------------------- */
.vb-footer-traits {
	grid-area: traits;
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 11rem, 1fr ) );
	gap: 0.8rem 1.4rem;
	margin: 0;
	padding: 1rem 0 0;
	border-top: 1px solid var(--vb-line, #d9c7c1);
	list-style: none;
}

/* Die Nutzenversprechen deutlich unter den Verweisen (Aufgabe #257, A10):
   kleiner, leise, ohne Fettdruck und ohne gerahmte Marke - sie beschreiben,
   sie fuehren nirgendwohin. */
.vb-footer-traits__item {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-caption, 0.78rem);
	line-height: 1.35;
}

.vb-footer-traits__mark {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 20px;
	height: 20px;
	color: var(--vb-muted, #6b5b60);
}

.vb-footer-traits__mark svg {
	width: 16px;
	height: 16px;
}

.vb-footer-traits__item strong {
	display: block;
	font-weight: 500;
	line-height: 1.3;
}

.vb-footer-traits__item span span {
	color: var(--vb-muted, #6b5b60);
}

.vb-site-footer__langs {
	grid-area: langs;
	justify-self: start;
}

@media (min-width: 62.5rem) {
	.vb-site-footer__langs {
		justify-self: end;
		padding-top: 1rem;
	}
}

/* ----- Kompakter Fuss auf dem Telefon und in der App ----------------------
   Inhaber, 23.09.2026: "Footer zu praesent". Gemessen 620px auf 390px
   Breite - fast ein ganzer Bildschirm unter jeder Seite, aus vier Bloecken:
   Marke in zwei Zeilen, neunzehn Links in sieben Zeilen, vier Merkmal-
   Kacheln, noch einmal die Sprachwahl. Auf dem Telefon und in der App:
   - die Marke in einer Zeile, Zeichen und Name kleiner, der Slogan daneben;
   - die erste Linkreihe bleibt offen, die Transparenz-Zeile klappt hinter
     ihre Ueberschrift (Knopf in der h2, wie ein Akkordeon);
   - die Merkmal-Kacheln entfallen: sie beschreiben Umschalter, die im
     Menue selbst stehen, und sind keine Wege;
   - die Sprachwahl entfaellt bis 59.99rem, wo sie seit KH16 im Kopf steht.
   Wann geklappt wird, entscheidet dieses Blatt; das Skript markiert nur, dass
   geklappt werden kann - ohne JavaScript bleibt die Zeile offen. */
.vb-footer-nav__toggle {
	display: none;
}

@media all and (display-mode: standalone), (max-width: 39.99rem) {
	footer.vb-site-footer {
		padding-block: 1.25rem;
	}

	.vb-site-footer__band {
		gap: 0.6rem;
	}

	.vb-site-footer__home {
		gap: 0.45rem;
		font-size: 1.1rem;
	}

	.vb-site-footer__home .vb-brand__mark svg {
		width: 26px;
		height: 26px;
	}

	.vb-footer-traits {
		display: none;
	}

	.vb-footer-nav {
		gap: 0;
	}

	.vb-footer-nav__col--collapsible .vb-footer-nav__title.screen-reader-text {
		position: static;
		width: auto;
		height: auto;
		margin: 0.4rem 0 0;
		overflow: visible;
		clip-path: none;
		white-space: normal;
		text-transform: none;
		letter-spacing: 0;
	}

	.vb-footer-nav__col--collapsible .vb-footer-nav__label {
		display: none;
	}

	.vb-footer-nav__col--collapsible .vb-footer-nav__toggle {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.75rem;
		box-sizing: border-box;
		inline-size: 100%;
		min-block-size: 44px;
		margin: 0;
		padding: 0;
		border: 0;
		border-top: 1px solid var(--vb-line, #d9c7c1);
		background: transparent;
		color: var(--vb-muted, #6b5b60);
		font: inherit;
		font-size: var(--vb-text-label, 0.9rem);
		font-weight: 600;
		text-align: start;
		cursor: pointer;
	}

	.vb-footer-nav__toggle .vb-ic {
		flex: 0 0 auto;
		transition: transform 0.15s ease;
	}

	.vb-footer-nav__col--collapsible[data-open] .vb-footer-nav__toggle .vb-ic {
		transform: rotate(180deg);
	}

	.vb-footer-nav__col--collapsible:not([data-open]) .vb-footer-nav__list {
		display: none;
	}
}

@media all and (display-mode: standalone), (max-width: 59.99rem) {
	.vb-site-footer__langs {
		display: none;
	}
}

/* Unter der Naht, an der die Marke neben die Links rueckt, steht sie in ihrer
   eigenen Zeile - dort in EINER: Zeichen, Name, Slogan nebeneinander, statt
   den Slogan eingerueckt in eine zweite Zeile zu setzen. */
@media (max-width: 62.49rem) {
	.vb-site-footer__brand {
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
		gap: 0.2rem 0.75rem;
	}

	.vb-site-footer__claim {
		padding-inline-start: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	.vb-footer-nav__toggle .vb-ic {
		transition: none;
	}
}

.vb-footer-nav__soon {
	color: var(--vb-muted);
}

/* ----- Kopfzeile als schwebende Karte (Vorschaugrafik) -------------------
   Die Vorlage zeigt die Kopfzeile auf dem Desktop als abgerundete Karte, die
   vom Seitenrand eingerueckt ueber dem Inhalt schwebt - an beiden Enden sind
   die Rundungen zu sehen. Live war sie ein durchgehender flacher Balken.
   Nur ab 80rem: auf dem Telefon und dem Tablet - auch quer, 1024px - zeigt
   dieselbe Vorlage einen randlosen weissen Streifen. Bis zum 23.09.2026 galt
   die Karte ab 62.5rem, und zwischen 1000 und 1330px lag sie mit Rundung und
   Rand auf der Bildschirmkante, weil nur die 1280er Deckelung sie einrueckte
   (Nachpruefung KH10, KH15). Jetzt traegt sie den Rahmenabstand selbst. */
@media (min-width: 80rem) {
	.vb-site-header .vb-site-header__inner {
		width: min( 100% - 2 * var(--vb-frame-gap), var(--vb-frame-max) );
		max-width: none;
	}

	.vb-site-header {
		background: transparent;
		box-shadow: none;
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		padding-block: 10px;
	}

	.vb-site-header__inner {
		box-sizing: border-box;
		padding-block: 8px;
		background: var(--vb-header-glass, rgba( 255, 255, 255, 0.85 ));
		backdrop-filter: saturate( 180% ) blur( 10px );
		-webkit-backdrop-filter: saturate( 180% ) blur( 10px );
		border: 1px solid var(--vb-line, #d9c7c1);
		border-radius: var(--vb-radius-lg, 22px);
		box-shadow: 0 10px 30px -22px rgba(48, 39, 43, 0.55 );
	}
}

/* Die Vorlage setzt die Navigation MITTIG zwischen Logo und Bedienelemente;
   live klebte sie rechts an den Pillen. Zwei automatische Aussenabstaende
   teilen den freien Platz gleichmaessig - einer vor dem ersten Verweis, einer
   vor dem Konto-Eintrag, mit dem die Bedienelemente beginnen. Faellt der
   Konto-Eintrag weg (ein Betriebsinhaber bekommt stattdessen die Konsole),
   ruecken die Verweise nach links: schlechter zentriert, nie kaputt. */
@media (min-width: 62.5rem) {
	.vb-site-header .vb-nav,
	.vb-site-header .vb-nav__menu {
		flex: 1 1 auto;
	}

	.vb-site-header .vb-nav__menu > .vb-nav__link:first-child {
		margin-inline-start: auto;
	}

	/* Seit dem 23.09.2026 beginnen die Bedienelemente mit der Sprache (Grafik:
	   "DE", Darstellung, "Anmelden", Pille). Der Abstand sitzt deshalb am
	   ersten Bedienelement nach dem letzten Textlink, welches es auch ist -
	   fehlt die Sprachwahl, ist es die Darstellung oder der Konto-Eintrag. */
	.vb-site-header .vb-nav__menu > .vb-nav__link:not(.vb-nav__link--account) + :is(.vb-nav__langs, .vb-nav__prefs, .vb-nav__link--account) {
		margin-inline-start: auto;
	}
}

/* Der Menueknopf traegt in der Vorlage keinen Kasten - die Symbole im
   App-Kopf stehen dort frei. Der Rahmen war einmal noetig, weil der Pruefer
   die Umrandung eines Knopfes als seine Begrenzung misst (1.4.11, 3:1); das
   identifizierende Bild sind aber die drei Balken selbst, und die stehen in
   --vb-ink auf heller Flaeche. Die 44px Zielflaeche bleibt unveraendert. */
.vb-nav__burger {
	border-color: transparent;
}

/* Der Schalter traegt das Kuerzel, das Menue das Endonym. */
.vb-langdrop__code {
	font-weight: 600;
	letter-spacing: 0.02em;
}

.vb-langdrop__menu .vb-langdrop__code {
	display: none;
}

/* Die Zeile unter der Wortmarke steht in der Vorlage nur auf dem Desktop;
   die Telefon- und Tablet-Ansichten derselben Grafik zeigen die Marke
   einzeilig. Die Marke wird dafuer zu einem Raster: Zeichen links ueber
   beide Zeilen, Name oben, Zeile darunter. */
.vb-brand__tagline {
	display: none;
}

@media (min-width: 80rem) {
	.vb-site-header .vb-brand {
		display: grid;
		grid-template-columns: auto 1fr;
		align-items: center;
		column-gap: 0.6rem;
		row-gap: 0;
	}

	.vb-site-header .vb-brand__mark {
		grid-row: 1 / span 2;
	}

	/* KH14: in der Grafik ist die Marke rund ein Drittel groesser - Pfote
	   ~45px bei 66px Kartenhoehe (live 34), Wortmarke ~105px breit (78),
	   Zeile darunter ~122px (108). */
	.vb-site-header .vb-brand__mark svg,
	.vb-site-header .vb-brand__mark img {
		inline-size: 48px;
		block-size: 48px;
	}

	.vb-site-header .vb-brand__tagline {
		display: block;
		grid-column: 2;
		color: var(--vb-muted, #6b5b60);
		font-size: var(--vb-text-small, 0.85rem);
		line-height: 1.2;
	}

	.vb-site-header .wp-block-site-title {
		grid-column: 2;
		font-size: 1.6rem;
		line-height: 1.1;
	}
}

/* ----- Illustrierte Platzhalter (Photo::placeholder) ---------------------
   Inhaber 23.09.2026: wo die Vorschaugrafiken ein Bild zeigen und keines da
   ist, steht eine Platzhaltergrafik. Eine Flaeche im Ton des Ortes, zwei
   weiche Kreise, ein grosses Motiv - kein Etikett "Foto folgt", das auf
   einer oeffentlichen Seite unfertig wirkt.
   Der Ton kommt aus dem Umfeld: die Gewerbefarbe des Profils
   (--vb-cat), sonst die Kachelfarbe (--vb-trade), sonst Markengruen. Die
   Mischung mit --vb-base dreht sich im dunklen Schema von selbst. */
.vb-photo--art {
	--vb-art-tone: var(--vb-cat, var(--vb-trade, var(--vb-accent-text, #68192e)));
	position: relative;
	display: grid;
	place-items: center;
	margin: 0;
	overflow: hidden;
	color: var(--vb-art-tone);
	background:
		radial-gradient( circle at 18% 22%, color-mix( in srgb, var(--vb-art-tone) 16%, transparent ) 0 18%, transparent 18.5% ),
		radial-gradient( circle at 88% 86%, color-mix( in srgb, var(--vb-art-tone) 13%, transparent ) 0 30%, transparent 30.5% ),
		linear-gradient( 140deg, color-mix( in srgb, var(--vb-art-tone) 8%, var(--vb-base, #fbf7f2) ), color-mix( in srgb, var(--vb-art-tone) 20%, var(--vb-base, #fbf7f2) ) );
}

.vb-photo__motif {
	display: grid;
	place-items: center;
	inline-size: clamp( 44px, 30%, 150px );
	aspect-ratio: 1;
	opacity: 0.5;
}

.vb-photo__motif svg {
	inline-size: 100%;
	block-size: 100%;
	stroke-width: 1.4;
}

/* Clash Display setzt das Leerzeichen sehr schmal, und mit der engen
   Laufweite der Ueberschriften lasen "Betreuung fuer" und "Direkt zur" als
   ein Wort (Nachpruefung KH19, gemessen im Bildschirmfoto). Etwas mehr
   Wortabstand, nur wo das Anzeigeschnitt steht. */
.vb-hero__title,
.vb-section__title,
.vb-article .vb-page-head__title,
.vb-article__body h2,
.vb-guide__related-title,
.vb-site-footer__home {
	word-spacing: 0.1em;
}

/* Der aktive Reiter der App-Leiste wie in der Grafik: Markenfarbe und ein
   gefuelltes Zeichen, kein Kasten (Nachpruefung M16). Nach beiden Regeln
   oben, die eine Flaeche setzen - die installierte App trifft beide. */
.vb-appnav .vb-appnav__item.is-active,
.vb-appnav__item.is-active {
	background: transparent;
	font-weight: 700;
}

.vb-appnav .vb-appnav__item.is-active svg {
	fill: currentColor;
	fill-opacity: 0.18;
}

/* Wer per Tab durch die Seite geht, landet nicht unter der klebenden
   Kopfzeile - und auf dem Profil nicht unter der Ankerleiste. Gemessen:
   die "Buchen"-Links der Leistungsliste bekamen den Fokus bei y 91-137,
   genau unter der Leiste (Nachpruefung C3). scroll-padding gilt fuer das
   Scrollen in den Fokus ebenso wie fuer Sprungziele - und ist deshalb die
   EINZIGE Reservierung fuer den Kopf: ein scroll-margin-top in Kopfhoehe
   auf dem Ziel kommt obendrauf (25.09.2026: 99-173px zu tief gemessen).
   Ziele tragen hoechstens ein paar Pixel Luft. */
html {
	scroll-padding-top: calc( var(--vb-header-h, 63px) + var(--vb-progress-h, 0px) + 12px );
}

/* Auf dem Profil (Aufgabe #211) steht oben die Profilleiste - Namenszeile
   und Ankerleiste, dazu eine Adminleiste - und der Seitenkopf scrollt weg.
   Freigehalten wird, was sie hoechstens bedecken (sticky-stack.js misst es),
   das Band der Pfotenspur darunter (--vb-progress-h, #212) und 16 px
   (Analyse 51): auch der Tastaturfokus landet nie darunter. */
html:has( [data-vb-profilebar] ) {
	scroll-padding-top: calc( var(--vb-sticky-reserve, 0px) + var(--vb-progress-h, 0px) + 16px );
}

/* Erhoehter Kontrastwunsch, seitenweit (Nachpruefung Nr. 27): bis zum
   23.09.2026 wirkte prefers-contrast: more an zwei Elementen des Profils.
   Auf der Ebene der Tokens, damit jede Flaeche mitgeht: Haarlinien werden
   zu Feldrahmen, gedaempfter Text wird Vollschrift, Links sind kraeftig
   unterstrichen. Nach dem Dunkel-Block und in beiden Schemata. */
@media (prefers-contrast: more) {
	:root,
	:root[data-theme="dark"] {
		--vb-line: var(--vb-field-line);
		--vb-border: var(--vb-field-line);
		--vb-muted: var(--vb-ink);
	}

	a:not(.vb-btn) {
		text-decoration-thickness: 2px;
	}
}

/* ----- Merken ohne Anmeldung (Inhaber 23.09.2026) ------------------------
   Eine dezente Tafel ueber der App-Leiste: gemerkt fuer diesen Besuch, mit
   den zwei Wegen, die Liste zu behalten. Einmal je Besuch; wer sie schliesst,
   sieht sie in diesem Tab nicht wieder. */
.vb-sessionfav-hint {
	position: fixed;
	inset-inline: 12px;
	bottom: calc( var(--vb-appnav-h, 0px) + var(--vb-stickybar-h, 0px) + 12px );
	z-index: 90;
	box-sizing: border-box;
	max-inline-size: 26rem;
	margin-inline: auto;
	padding: 14px 44px 14px 16px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
	color: var(--vb-ink, #30272b);
	box-shadow: var(--vb-shadow, 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40));
}

.vb-sessionfav-hint p {
	margin: 0;
}

.vb-sessionfav-hint__title {
	font-weight: 700;
}

.vb-sessionfav-hint__text {
	margin-top: 4px !important;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-sessionfav-hint__ways {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 10px !important;
}

.vb-sessionfav-hint__close {
	position: absolute;
	inset: 6px 6px auto auto;
	inline-size: 36px;
	block-size: 36px;
	border: 0;
	border-radius: var(--vb-radius-sm, 11px);
	background: transparent;
	color: var(--vb-muted, #6b5b60);
	font-size: 1.4rem;
	line-height: 1;
	cursor: pointer;
}

.vb-sessionfav-hint__close:hover,
.vb-sessionfav-hint__close:focus-visible {
	background: var(--vb-surface, #f2e7df);
	color: var(--vb-ink, #30272b);
}

@media (min-width: 80rem) {
	.vb-sessionfav-hint {
		inset-inline: auto 24px;
	}
}

/* ----- „Dienstleistungen" im Kopf -----------------------------------------
   Inhaber, 23.09.2026: „Für Betriebe“ und „Ratgeber“ standen allein in der
   Mitte der Kopfkarte und wirkten verloren. Die Vorlage setzt davor ein
   Menü mit den Betriebsarten; jeder Eintrag führt in die Suche nach dieser
   Art. Auf dem Desktop ein schwebendes Feld unter dem Wort, im Burger-Menü
   klappt es in der Spalte auf. */
.vb-navmenu {
	position: relative;
}

.vb-navmenu > summary {
	gap: 4px;
	list-style: none;
	cursor: pointer;
}

.vb-navmenu > summary::-webkit-details-marker {
	display: none;
}

.vb-navmenu__chev {
	transition: transform 0.15s ease;
}

.vb-navmenu[open] .vb-navmenu__chev {
	transform: rotate( 180deg );
}

.vb-navmenu__panel {
	position: absolute;
	top: calc( 100% + 8px );
	left: 50%;
	z-index: 60;
	min-width: 15rem;
	max-width: calc( 100vw - 24px );
	margin: 0;
	padding: 6px;
	list-style: none;
	background: var(--vb-base);
	border: 1px solid var(--vb-line);
	border-radius: var(--vb-radius-sm, 11px);
	box-shadow: var(--vb-shadow-hover, 0 22px 48px -22px rgba(104, 25, 46, 0.32), 0 4px 12px -6px rgba(48, 39, 43, 0.20));
	transform: translateX( -50% );
}

.vb-navmenu__item {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	box-sizing: border-box;
	min-height: 44px;
	padding: 0.45rem 0.7rem;
	border-radius: var(--vb-radius-xs, 6px);
	color: var(--vb-ink);
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 500;
	text-decoration: none;
}

.vb-navmenu__item:hover,
.vb-navmenu__item:focus-visible {
	background: var(--vb-surface);
	color: var(--vb-accent-text, #68192e);
}

.vb-navmenu__icon {
	display: inline-flex;
	color: var(--vb-accent-text, #68192e);
}

/* Das Menü ist jetzt der erste Eintrag der Reihe; der automatische Abstand,
   der die Links mittig stellt, sitzt deshalb an ihm (vgl. die Regel für den
   ersten Link weiter oben). */
@media ( min-width: 62.5rem ) {
	.vb-site-header .vb-nav__menu > .vb-navmenu:first-child {
		margin-inline-start: auto;
	}

	.vb-site-header .vb-nav__menu > .vb-navmenu:first-child + .vb-nav__link {
		margin-inline-start: 0;
	}
}

@media ( max-width: 59.99rem ) {
	.vb-navmenu__panel {
		position: static;
		min-width: 0;
		padding: 0 0 0 8px;
		border: 0;
		box-shadow: none;
		transform: none;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.vb-navmenu__chev {
		transition: none;
	}
}

/* ----- Zurück in der installierten App ----------------------------------- */
.vb-appback {
	display: inline-flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	margin-inline-end: 2px;
	padding: 0;
	color: var(--vb-ink);
	background: transparent;
	border: 0;
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
}

.vb-appback:hover,
.vb-appback:focus-visible {
	background: var(--vb-surface);
}

/* Das Wort für den Screenreader. Nicht auf die Klasse des Kerns verlassen:
   dieses Blatt lädt auf jeder Seite, profile.css nicht. */
.vb-appback .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
}

/* Neben dem Pfeil braucht die Marke ihren Slogan nicht: die Zeile ist die
   einer App, nicht die Visitenkarte. */
.vb-has-appback .vb-brand__tagline {
	display: none;
}

/* ----- Lesefortschritt als Pfotenspur (Inhaber 23.09.2026, #212) ----------
   Zuerst eine 3px-Leiste am oberen Rand nach gnollhof.it; dann der Wunsch:
   unter den Kopf, und als wandernde Pfotenspur statt eines Balkens. Am
   03.10.2026 (#212) gemessen: Abdruecke von ~5px, Zehen unter 2px, auf dem
   Desktop kein Band und Schrift, die durch die Spur lief - "abgeschnitten".
   Neu nach der Vorgabe des Grafik-Leads:

   - ein deckendes Band, 28px samt 1px Unterkante, in der Basisfarbe, ohne
     Glas, ohne Unschaerfe, ohne Maske - Text darunter ist nicht zu sehen;
   - Abdruecke als einzelne 18x18-SVGs auf ganzen Pixeln, 26px Schritt,
     links/rechts abwechselnd (Oberkante 3/7px, jeder zweite gespiegelt):
     die Fuellung reicht von y 1 bis 17 der Box, also bleiben 4px zur Ober-
     und 3px zur Unterkante;
   - ganze Abdruecke statt einer Fuellbreite, die alten 80 %, der juengste
     100 % deckend, kein Verlauf.

   Lage und Anzahl setzt site-theme.js. Das Band nimmt seinen Platz im
   Layout ein (--vb-progress-h unter dem Kopf, in scroll-padding-top und in
   allem, was unter dem Kopf klebt); die Ruhige Ansicht nimmt Band und Platz
   weg, ebenso der Druck und eine Seite ohne Skript (<noscript> in
   SiteChrome). Eine kurze Seite (site-theme.js) zeigt kein Band, behaelt
   aber den Platz: ihn erst nach dem Zeichnen wegzunehmen, verschob die
   ganze Seite - gemessen 0,028 CLS auf der Suche am Telefon (#201-Test). */
:root {
	--vb-progress-h: 28px;
	/* Abdruck und Schritt, von site-theme.js gelesen. */
	--vb-progress-paw: 18px;
	--vb-progress-step: 26px;
	/* #0c7a4a auf Weiss: 5,39:1 (juengster) und 3,67:1 bei 80 % (aeltere) -
	   --vb-primary (#14b870) hielte nur 2,59:1 und verfehlte die 3:1 fuer
	   Grafik (WCAG 1.4.11). */
	--vb-progress-ink: var(--vb-accent-text, #68192e);
}

@media screen {
	/* Helles Rose auf dem dunklen Grund (AniLevia, #408). */
	:root[data-theme="dark"] {
		--vb-progress-ink: #f2b3c3;
	}
}

:root[data-focus="reduced"] {
	--vb-progress-h: 0px;
}

/* Der Platz des Bandes: direkt unter dem Kopf, damit es zu Beginn der Seite
   nichts verdeckt - so gilt er auf jeder Vorlage, und das Band liegt ganz
   oben genau auf ihm. Ein eigener Block hinter dem Kopf, kein Aussenabstand
   des Kopfes: der fiel mit dem oberen Abstand des Inhalts zusammen, und die
   Profilkarte stiess ohne Luft an die Bandkante (gemessen, 390px). Die Huelle
   des Kopfes ist display: contents (oben), ihr ::after steht also im Fluss
   der Seite direkt nach dem Kopf. */
.wp-block-template-part:has( > .vb-site-header )::after {
	content: "";
	display: block;
	height: var(--vb-progress-h);
}

.vb-progress {
	position: fixed;
	z-index: 80;
	top: 0;
	left: 0;
	right: 0;
	box-sizing: border-box;
	height: 28px;
	background: var(--vb-base, #fbf7f2);
	border-bottom: 1px solid var(--vb-border, #d9c7c1);
	opacity: 1;
	color: var(--vb-progress-ink);
	pointer-events: none;
}

.vb-progress__track {
	position: absolute;
	top: 0;
	height: 27px;
}

.vb-progress__paw {
	position: absolute;
	top: 3px;
	display: block;
	width: 18px;
	height: 18px;
	opacity: 0.8;
	transform-origin: 50% 50%;
}

.vb-progress__paw--alt {
	top: 7px;
}

.vb-progress__paw--latest {
	opacity: 1;
}

/* Am Telefon ein 18-px-Band (Aufgabe #222, Grafik-Lead B1, Leistenbudget
   "Pfotenband 18"): oben stehen auf dem Profil Namenszeile 56 + Ankerzeile
   44 + Band, zusammen hoechstens 118 px bei 390x844 - mit 28 waren es 128.
   Dieselben Abdruecke, verkleinert auf 12x12 (Fuellung y 0,7-11,3 der Box),
   Schritt 18, Oberkante 2/4 px: 2,7 px Luft oben, 1,7 px ueber der Linie.
   Das ganze Telefon, nicht nur das Profil: ein Band, eine Hoehe. Die Ruhige
   Ansicht und der Druck setzen --vb-progress-h weiter auf 0 (spezifischer
   bzw. danach). */
@media ( max-width: 47.99rem ) {
	:root {
		--vb-progress-h: 18px;
		--vb-progress-paw: 12px;
		--vb-progress-step: 18px;
	}

	.vb-progress {
		height: 18px;
	}

	.vb-progress__track {
		height: 17px;
	}

	.vb-progress__paw {
		top: 2px;
		width: 12px;
		height: 12px;
	}

	.vb-progress__paw--alt {
		top: 4px;
	}
}

/* Ein kurzer Setzimpuls, nur fuer den juengsten neuen Abdruck und nur ohne
   Bewegungswunsch des Systems (die eigene Taste, data-motion, schaltet
   jede Animation seitenweit ab). Kein "to": der Abdruck endet bei seinem
   eigenen Stand - 100 %, oder 80 %, wenn waehrenddessen ein juengerer kam.
   Geschrumpft bleibt er in seiner Box, nie ueber die Bandkante. */
@media ( prefers-reduced-motion: no-preference ) {
	.vb-progress__paw--setting {
		animation: vb-paw-set 0.18s ease-out;
	}
}

@keyframes vb-paw-set {
	from {
		opacity: 0;
		transform: scale( 0.6 );
	}
}

/* Die Ruhige Ansicht nimmt, was sich von selbst bewegt (Neuroinklusiv-
   Vorgabe 16.9.), samt seinem Platz (oben). Im Druck hat die Spur nichts
   zu sagen. */
:root[data-focus="reduced"] .vb-progress {
	display: none;
}

@media print {
	:root {
		--vb-progress-h: 0px;
	}

	.vb-progress,
	.vb-appback,
	.vb-ptr {
		display: none;
	}
}

/* ----- Die Merkliste des Besuchs (Kontoseite, abgemeldet) -----------------
   Ziel des App-Reiters „Merkliste“ seit dem 23.09.2026. */
.vb-sessionlist {
	margin: 0 0 1.5rem;
	padding: 1rem 1.1rem;
	background: var(--vb-surface);
	border: 1px solid var(--vb-line);
	border-radius: var(--vb-radius, 16px);
	/* Air only: html's scroll-padding-top keeps the header clear. */
	scroll-margin-top: 4px;
}

.vb-sessionlist__title {
	margin: 0 0 0.25rem;
}

.vb-sessionlist__lead {
	margin: 0 0 0.75rem;
	color: var(--vb-muted);
}

.vb-sessionlist__items {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.vb-sessionlist__item {
	display: grid;
	grid-template-columns: minmax( 0, 1fr ) auto;
	grid-template-areas:
		"name remove"
		"place remove";
	align-items: center;
	padding: 0.5rem 0.25rem 0.5rem 0.75rem;
	background: var(--vb-base);
	border: 1px solid var(--vb-line);
	border-radius: var(--vb-radius-sm, 11px);
}

.vb-sessionlist__namebox {
	grid-area: name;
	min-inline-size: 0;
}

.vb-sessionlist__name {
	font-weight: 600;
	color: var(--vb-ink);
	text-decoration: none;
}

.vb-sessionlist__place {
	grid-area: place;
	font-size: var(--vb-text-small, 0.85rem);
	color: var(--vb-muted);
}

.vb-sessionlist__remove {
	grid-area: remove;
	position: relative;
	width: 44px;
	height: 44px;
	font: inherit;
	font-size: 1.3rem;
	color: var(--vb-muted);
	background: transparent;
	border: 0;
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
}

.vb-sessionlist__remove:hover {
	color: var(--vb-ink);
	background: var(--vb-surface);
}

.vb-sessionlist__sr {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
}

/* ----- "Betrieb eintragen" (Inhaber 24.09.2026: "Formular Design nicht
   optimal") -----------------------------------------------------------------
   Das Formular steht auf der Seite fuer Betriebe, und die laedt nur dieses
   Blatt - seine Regeln lebten in profile.css, also liefen Beschriftungen und
   Felder ineinander, und der Knopf war der blaue des Browsers. Hier, damit
   es ueberall so aussieht, wo es steht; profile.css schreibt danach fuer das
   Profil dieselben Werte. */
#vb-register.vb-claim {
	max-width: 36rem;
	margin-block: 2rem 0;
	padding: clamp( 1.25rem, 4vw, 2rem );
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-lg, 22px);
	box-shadow: var(--vb-shadow, 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40));
}

#vb-register .vb-claim__title {
	margin: 0 0 0.35rem;
}

#vb-register .vb-claim__lead {
	margin: 0 0 1.25rem;
	color: var(--vb-muted, #6b5b60);
}

#vb-register .vb-claim__form {
	display: grid;
	gap: 1rem;
}

#vb-register .vb-claim__form label {
	display: grid;
	gap: 6px;
	margin: 0;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	color: var(--vb-ink, #30272b);
}

#vb-register .vb-claim__form input:where(:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])),
#vb-register .vb-claim__form select {
	box-sizing: border-box;
	inline-size: 100%;
	min-block-size: 48px;
	margin: 0;
	padding: 10px 14px;
	font: inherit;
	font-size: 16px;
	font-weight: 400;
	color: var(--vb-ink, #30272b);
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	appearance: none;
}

/* Der Pfeil des Auswahlfelds, gezeichnet statt vom Browser: iOS gibt dem
   <select> sonst eine graue Pille. */
#vb-register .vb-claim__form select {
	padding-inline-end: 40px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2351635a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6l6 -6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 14px center;
}

#vb-register .vb-claim__form input:focus,
#vb-register .vb-claim__form select:focus {
	outline: 2px solid transparent;
	border-color: var(--vb-accent-text, #68192e);
	box-shadow: var(--vb-focus, 0 0 0 2px var(--vb-raised, #fffcf8), 0 0 0 5px var(--vb-focus-color, #205f9c));
}

#vb-register .vb-claim__row {
	display: grid;
	gap: 1rem;
}

@media ( min-width: 30rem ) {
	#vb-register .vb-claim__row {
		grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	}

	#vb-register .vb-claim__row--postal {
		grid-template-columns: minmax( 0, 1fr ) minmax( 0, 2fr );
	}
}

#vb-register .vb-claim__field--hinted + .vb-claim__hint {
	margin: -0.6rem 0 0;
	font-size: var(--vb-text-small, 0.85rem);
	color: var(--vb-muted, #6b5b60);
}

#vb-register .vb-claim__form .vb-claim__consent {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	font-weight: 500;
}

#vb-register .vb-claim__consent input {
	flex: 0 0 auto;
	inline-size: var(--vb-tap-min, 24px);
	block-size: var(--vb-tap-min, 24px);
	margin: 0;
	accent-color: var(--vb-accent-text, #68192e);
}

#vb-register .vb-claim__submit {
	justify-self: start;
	min-block-size: 48px;
	padding-inline: 1.5rem;
}

@media ( max-width: 29.99rem ) {
	#vb-register .vb-claim__submit {
		justify-self: stretch;
	}
}

/* Das Haekchen neben dem Namen in Karten und Listen (Inhaber 03.10.2026:
   ueberall, wo der Name steht). Dieselbe Form und Farbe wie das Zeichen im
   Profil (StatusBadge), aber ein Bild statt eines Knopfs, weil der Name dort
   fast immer ein Link ist. Groesse folgt der Schrift des Namens; senkrecht
   auf die Mitte der Grossbuchstaben gesetzt (1cap), nicht auf die
   Grundlinie - sonst sitzt es tief. Bleibt in der Ruhigen Ansicht stehen:
   Status ist Information. */
/* Geschuetztes Leerzeichen und Zeichen in einem Kasten ohne Umbruch: das
   Leerzeichen allein hielt nicht (Chromium und WebKit brechen davor, gemessen
   03.10.2026); so wandert das letzte Wort mit dem Zeichen in die naechste
   Zeile, statt das Zeichen allein dort stehen zu lassen. */
.vb-namemark {
	white-space: nowrap;
}

.vb-mark {
	--vb-mark-size: clamp( 16px, 0.95em, 22px );
	display: inline-block;
	inline-size: var(--vb-mark-size);
	block-size: var(--vb-mark-size);
	color: var(--vb-accent-text, #68192e);
	line-height: 0;
	vertical-align: calc( ( 0.7em - var(--vb-mark-size) ) / 2 );
	vertical-align: calc( ( 1cap - var(--vb-mark-size) ) / 2 );
}

.vb-mark__icon {
	display: block;
	inline-size: 100%;
	block-size: 100%;
}

.vb-mark .vb-status__shape {
	fill: currentColor;
	stroke: none;
}

.vb-mark .vb-status__mark {
	stroke: #ffffff;
}

:root[data-theme="dark"] .vb-mark {
	color: var(--vb-primary, #d9a9b7);
}

:root[data-theme="dark"] .vb-mark .vb-status__mark {
	stroke: var(--vb-surface, #261e23);
}

@media (forced-colors: active) {
	.vb-mark .vb-status__shape {
		fill: CanvasText;
	}

	.vb-mark .vb-status__mark {
		stroke: Canvas;
	}
}

/* ----- Herunterziehen zum Aktualisieren (installierte App) ----------------
 * Aufgabe #206, Inhaber 03.10.2026. pull-refresh.js setzt .vb-ptr-on nur in
 * der installierten App und nur, wenn die Geste eingeschaltet ist; erst dann
 * darf der Browser die Seite oben nicht zusätzlich federn lassen.
 *
 * Der Knopf kommt unter der Oberkante hervor (translate aus dem Skript) und
 * liegt über dem Kopf. Farben nur aus den Tokens: hell ist der Grund, dunkel
 * nur über [data-theme="dark"]. Bereit und Laden zeigen eine andere Form,
 * nicht nur eine andere Farbe.
 */
:root.vb-ptr-on,
:root.vb-ptr-on body {
	overscroll-behavior-y: contain;
}

.vb-ptr {
	position: fixed;
	top: calc( env( safe-area-inset-top, 0px ) - 44px );
	left: 50%;
	z-index: 1004;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: 50%;
	box-shadow: var(--vb-shadow, 0 2px 6px -2px rgba(48, 39, 43, 0.10), 0 18px 40px -26px rgba(48, 39, 43, 0.40));
	transform: translate3d( -50%, 0, 0 );
	pointer-events: none;
}

.vb-ptr[hidden] {
	display: none;
}

.vb-ptr svg {
	display: block;
}

/* Zwei Klassen, damit die Regel darüber (0-1-1) sie nicht wieder einblendet. */
.vb-ptr .vb-ptr__ready,
.vb-ptr .vb-ptr__spin,
.vb-ptr.is-ready .vb-ptr__arrow,
.vb-ptr.is-loading .vb-ptr__arrow {
	display: none;
}

.vb-ptr.is-ready,
.vb-ptr.is-loading {
	background: var(--vb-primary-soft, #f3e2e7);
	border-color: var(--vb-primary, #b66a80);
}

.vb-ptr.is-ready .vb-ptr__ready,
.vb-ptr.is-loading .vb-ptr__spin {
	display: block;
}

/* Still (reduzierte Bewegung, Ruhige Ansicht): kein Kreisel, der stehende
   Kreispfeil sagt dasselbe. */
.vb-ptr--still.is-loading .vb-ptr__spin {
	display: none;
}

.vb-ptr--still.is-loading .vb-ptr__ready {
	display: block;
}

@media ( prefers-reduced-motion: no-preference ) {
	.vb-ptr:not(.vb-ptr--still).is-loading .vb-ptr__spin {
		animation: vb-ptr-spin 0.8s linear infinite;
	}
}

@keyframes vb-ptr-spin {
	to {
		transform: rotate( 360deg );
	}
}

.vb-ptr__live {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
}

/* Console previews are informative; calm view keeps them one press away. */
.vb-pricing-preview { margin-block: 2.5rem; }
.vb-pricing-preview__note { color: var(--vb-muted); }
.vb-pricing-preview__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; }
.vb-pricing-preview__card { min-width: 0; }
.vb-pricing-preview__link { display: block; border: 1px solid var(--vb-border); border-radius: var(--vb-radius-sm, 11px); overflow: hidden; }
.vb-pricing-preview__link:focus-visible { outline: 3px solid var(--vb-accent); outline-offset: 3px; }
.vb-pricing-preview__photo img { object-fit: contain; }
.vb-pricing-preview__caption { margin: .5rem 0 0; font-weight: 600; }
@media (min-width: 40rem) { .vb-pricing-preview__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 60rem) { .vb-pricing-preview__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
:root[data-focus="reduced"] [data-vb-calm]:not([data-vb-calm-open]) .vb-pricing-preview__grid { display: none; }

/* A signed-in customer's video consultation is near or ready (#369 C):
   a calm card at the bottom, above the app bar and any bar that sticks
   there - never over the sticky site header (review of #385). Out of the
   way while the on-screen keyboard is up. */
.vb-video-notice {
	position: fixed;
	left: 12px;
	right: 12px;
	bottom: calc( max( var( --vb-sticky-bottom, 0px ), var( --vb-notice-lift, 0px ) ) + 12px );
	z-index: 900;
	max-width: 40rem;
	margin: 0 auto;
	padding: 10px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var( --vb-radius-sm, 11px );
	background: var(--vb-surface, #f2e7df);
	color: var(--vb-ink, #30272b);
	box-shadow: 0 4px 16px rgba(48, 39, 43, 0.12 );
}

/* Empty, it stays in the page (a status region announces only what
   changes inside it once it is there) but takes no room. */
.vb-video-notice:empty {
	padding: 0;
	border: 0;
	box-shadow: none;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset( 50% );
}

:root[data-vb-keyboard] .vb-video-notice {
	display: none;
}

/* Above the app bar where it is shown (same query as the bar). */
@media ( max-width: 79.99rem ), all and (display-mode: standalone) {
	body:has( .vb-appnav ) .vb-video-notice {
		--vb-notice-lift: var( --vb-appnav-h );
	}
}

.vb-video-notice.is-ready {
	border-left: 4px solid var(--vb-accent-text, #68192e);
	font-weight: 600;
}

.vb-video-notice__row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 8px 16px;
	max-width: 80rem;
	margin: 0 auto;
}
