/* Practice profile sections - mobile-first. Tokens from site.css. */

/* Open-now indicator badge (profile opening-hours heading). */
.vb-badge {
	display: inline-block;
	margin-left: 0.5rem;
	padding: 0.1em 0.6em;
	border-radius: var(--vb-radius-pill, 999px);
	font-size: 0.7em;
	font-weight: 600;
	vertical-align: middle;
}

/* Tokens, not literals: these two pills are the only status the profile
   shows at a glance, and hard-coded they stayed light under
   [data-theme="dark"] - two lit rectangles on a dark page. */
.vb-badge--open { background: var(--vb-success-soft, #e6f3ea); color: var(--vb-success-ink, #176849); }

/* Closed on a regular day is a state, not an alarm (Aufgabe #257, A6): the
   neutral role with its word; red is for emergencies and errors only. */
.vb-badge--closed { background: var(--vb-profile-neutral-bg, #eef1f0); color: var(--vb-profile-neutral-ink, #51635a); }


/* Public services & prices list. */
/* Two columns for the whole list (Aufgabe #219): the text on the left, the
   buttons on the right in ONE column. The rows wrapped as flex lines before,
   and measured live on a phone (WebKit 390/320) the button of a row with a
   long meta line ("nur für Bestandskunden", "Preis auf Anfrage") - and every
   button at 320px - dropped onto a line of its own at the LEFT edge, x = 24
   instead of 308: the buttons no longer stood in a column. A long word ran
   into the price beside it as well.
   Each row is a subgrid of the list, so the button column is as wide as the
   widest button and every button starts at the same x, "Book" and "Ask
   about this" alike. Without subgrid each row is its own grid with the
   button at the right edge. Before that (boarding profile, 390px) the row
   would not wrap at all and its only button ended 70px past the screen:
   the text column may shrink to nothing, the button column never does. */
.vb-services {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	column-gap: 1rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.vb-services__item {
	grid-column: 1 / -1;
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	column-gap: 1rem;
	row-gap: 0.15rem;
	align-items: center;
	padding: 0.5rem 0;
	border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

@supports (grid-template-columns: subgrid) {
	.vb-services__item {
		grid-template-columns: subgrid;
	}
}

/* The text breaks, never the button: a name or a word longer than the
   column wraps inside it instead of running under the price or the button. */
.vb-services__name {
	grid-column: 1;
	grid-row: 1;
	min-width: 0;
	font-weight: 500;
	overflow-wrap: anywhere;
}

.vb-services__meta {
	grid-column: 1;
	grid-row: 2;
	min-width: 0;
	color: var(--vb-muted, #6b5b60);
	overflow-wrap: anywhere;
}


.vb-profile {
	display: flex;
	flex-direction: column;
	gap: 4px;
}


/* Editorial sections: separated by a hairline rather than boxed (keeps the
   long-form profile calm; the booking widget + team/service cards carry the
   dimensional weight). */
.vb-profile__section {
	margin: 0;
	padding: 26px 0;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
}


.vb-profile__section:last-child {
	border-bottom: 0;
}


/* .vb-profile__sechead: a part of a main section that looks like one - the
   two level-3 parts of "Overview" (Aufgabe #221). The outline changed, the
   page did not. */
.vb-profile__section .vb-profile__sechead,
.vb-profile__section h2 {
	display: flex;
	align-items: center;
	gap: 11px;
	margin: 0 0 16px;
	font-size: 1.45rem;
	font-weight: 600;
	letter-spacing: -0.01em;
}


.vb-profile__section .vb-profile__sechead::before,
.vb-profile__section h2::before {
	content: "";
	flex: none;
	width: 5px;
	height: 22px;
	border-radius: 3px;
	/* The section mark is the second place the business's own tone appears,
	   and the one that carries it down the whole page: a reader who scrolls
	   past the hero should still be able to tell a boarding house from a
	   veterinary practice. Lime stays the fallback for anything outside a
	   provider profile, and the calm view folds all five back into one. */
	background: var(--vb-cat, var(--vb-lime, #c99b84));
}


/* The booking widget is the conversion moment: a card with a 1 px line and
   the one small neutral lift on the page - no green frame, no glow
   (Aufgabe #257, A5). */
.vb-profile__section.vb-booking {
	background: var(--vb-base, #fbf7f2);
	border-width: 1px;
	border-style: solid;
	border-color: var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	padding: 24px;
	box-shadow: var(--vb-profile-shadow-box, 0 4px 16px rgba(48, 39, 43, 0.08));
}


@media (max-width: 37.5rem) {
	.vb-profile__section.vb-booking {
		padding: 18px;
		border-radius: var(--vb-radius, 16px);
	}
}


/* Two-column profile: long-form content + a sticky booking aside on desktop.
   Below the breakpoint it is a single column with the booking (source-first)
   leading — conversion-first on mobile. */
.vb-profile__aside {
	margin: 0 0 4px;
}

/* The columns start 24 px under the head (Aufgabe #211; owner, 03.10.2026:
   "Direkt unter dem Betriebsheader anfangen"). Until then the description
   and the anchor bar stood full width in between, and the booking box began
   290 px under the head on the vet demo at 1280 x 720. .vb-profile is a
   column flex container with a 4 px gap; the rest is this margin. */
.vb-profile__cols {
	margin-block-start: 20px;
}

@media (min-width: 62.5rem) {
	/* Measured widths of the analysis (item 52-55): 608 | 24 | 320 at
	   1000 px, 760 | 32 | 360 at 1280, 808 | 32 | 360 at 1440 - the page is
	   952, 1152 and 1200 wide there (.vb-profile's own width, below). */
	.vb-profile__cols {
		display: grid;
		grid-template-columns: minmax(0, 1fr) 320px;
		column-gap: 24px;
		align-items: start;
	}

	.vb-profile__main {
		grid-column: 1;
		grid-row: 1;
		min-width: 0;
	}

	.vb-profile__aside {
		grid-column: 2;
		grid-row: 1;
		/* The whole row's height, so the box at its top can stick for the
		   whole length of the main column. The column itself does not
		   stick (Aufgabe #194): at 900 to 1,250 px it was taller than every
		   laptop screen and stuck at its BOTTOM. */
		align-self: stretch;
		margin: 0;
	}

	/* Only the booking box sticks: under the profile bar, at the height the
	   bars take when they are up (--vb-sticky-reserve, sticky-stack.js:
	   admin bar, name row and anchor row), the paw trail's band under them
	   (--vb-progress-h, #212) and 16 px - the same top before
	   and after the bar shows, so nothing moves when it does. Its height
	   follows the window in three stages (profile-aside.js, booking.js) and
	   it never scrolls inside itself; where not even the smallest stage
	   fits, .is-static lets it scroll with the page. No ancestor scrolls or
	   clips (MDN: a sticky box sticks to its nearest scrolling ancestor). */
	.vb-profile__bookbox {
		--vb-bookbox-top: calc(var(--vb-sticky-reserve, 0px) + var(--vb-progress-h, 0px) + 16px);
		position: sticky;
		top: var(--vb-bookbox-top);
		z-index: 2;
	}

	.vb-profile__bookbox.is-static {
		position: static;
	}
}

@media (min-width: 80rem) {
	.vb-profile__cols {
		grid-template-columns: minmax(0, 1fr) 360px;
		column-gap: 32px;
	}
}


/* What kind of business this is - shown for every non-veterinary type. */
.vb-profile__type {
	display: inline-block;
	/* align-self, not align-items on the parent: .vb-profile is a column
	   flex container, so every direct child is blockified and stretched -
	   measured at 1440px, this chip was 1080px wide with a computed display
	   of block, a pill the width of the page. Setting align-items on the
	   parent would shrink .vb-profile__cols and the cover with it. */
	align-self: flex-start;
	margin: 0 0 8px;
	padding: 3px 10px;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-tint, #f3e2e7);
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	letter-spacing: 0.02em;
}

.vb-profile__verified {
	display: inline-flex;
	align-self: flex-start;
	align-items: center;
	gap: 6px;
	margin: 0;
	padding: 5px 14px;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-success-soft, #e6f3ea);
	color: var(--vb-success, #176849);
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 700;
}


.vb-profile__verified::before {
	content: "✓";
	font-weight: 900;
}


/* Contact ------------------------------------------------------------------- */
.vb-profile__contact-list {
	list-style: none;
	margin: 12px 0 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}


.vb-profile__contact-list a {
	display: inline-flex;
	align-items: center;
	padding: 8px 14px;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	text-decoration: none;
	font-weight: 600;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-accent-text, #68192e);
	transition: background-color 0.15s ease, border-color 0.15s ease;
}


.vb-profile__contact-list a:hover {
	background: var(--vb-primary-soft, #f3e2e7);
	border-color: var(--vb-primary, #b66a80);
}


/* Directions (mini map + route link) ----------------------------------------- */
/* The address is the answer to "where is this", so it reads as an answer and
   not as the first line of a paragraph. */
.vb-profile__address {
	margin: 0 0 10px;
	font-weight: 600;
	color: var(--vb-ink, #30272b);
}


/* The map is a block inside "Getting here", not a section of its own any
   more. It keeps its own spacing so the heading above it and the
   accessibility note below it do not sit against the canvas. */
.vb-profile__map {
	margin: 18px 0;
}


.vb-profile__map h3 {
	margin-top: 0;
}


.vb-profile__map-canvas {
	/* Leaflet stacks its panes at 400 and its controls at 1000; without a
	   stacking context of its own the map slid over the sticky profile
	   head and the tab bar on scroll. */
	isolation: isolate;
	height: 300px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	overflow: hidden;
	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-profile__directions {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: 12px;
	padding: 9px 16px;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	text-decoration: none;
	font-weight: 600;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-accent-text, #68192e);
	transition: background-color 0.15s ease, border-color 0.15s ease;
}


/* The arrow is a drawn icon in the markup now (M-105): a text glyph renders
   in whatever the device has, beside stroke icons that all match. */


.vb-profile__directions:hover {
	background: var(--vb-primary-soft, #f3e2e7);
	border-color: var(--vb-primary, #b66a80);
}


/* Opening hours ------------------------------------------------------------- */
.vb-profile__hours table {
	width: 100%;
	max-width: 420px;
	border-collapse: collapse;
}


.vb-profile__hours tr {
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
}


.vb-profile__hours th {
	text-align: left;
	padding: 9px 16px 9px 0;
	font-weight: 600;
	color: var(--vb-ink, #30272b);
}


.vb-profile__hours td {
	text-align: right;
	padding: 9px 0;
	color: var(--vb-muted, #6b5b60);
	font-variant-numeric: tabular-nums;
}


/* Facts + badges ------------------------------------------------------------ */
.vb-profile__facts p {
	margin: 0 0 6px;
}


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

/* Die Ueberschrift ueber einer eigenen Reihe: als Flex-Kind stand sie mit
   den ersten Merkmalen in einer Zeile, und die waren auf ihre Hoehe
   gestreckt (Nachpruefung P13). */
.vb-profile__badges > h2,
.vb-profile__badges > h3 {
	flex: 0 0 100%;
}

.vb-profile__badges > .vb-profile__badge {
	align-self: center;
}


.vb-profile__badge {
	display: inline-block;
	padding: 5px 12px;
	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: 600;
}


.vb-profile__phone-only {
	margin: 0;
	padding: 12px 16px;
	background: var(--vb-surface, #f2e7df);
	border-radius: var(--vb-radius-sm, 11px);
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-body, 1rem);
}


/* The way in, inside the sentence that names it: "takes appointments by
   phone" used to be the end of the column, with the number two screens
   further down under its own heading. */
.vb-profile__call {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: 10px;
	padding: 8px 16px;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 700;
	text-decoration: none;
}


/* The handset, too, is drawn in the markup - and only on the telephone
   branch, never on the website link that shares this class. */


/* On a phone the submit is the end of the form and should look like it -
   it measured 119px in a 335px form. `> button[type="submit"]`, never a
   bare `> button`: the password reveal is a direct sibling of the same
   parent and would have been stretched to the full width with it. */
@media ( max-width: 40rem ) {

	.vb-account .vb-callback__form > button[type="submit"] {
		box-sizing: border-box;
		width: 100%;
		min-height: var(--vb-tap-primary, 44px);
	}
}


/* Ranked under the booking button in weight as well as in order: this is
   the pointer to the call-back form at the foot of the page, not a second
   thing to press. */
/* Said only to the reader it is about (M-116): quiet, above the fold, in
   the weight of a note rather than of a claim. */
.vb-profile__visited {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin: 0 0 10px;
	padding: 4px 12px;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-pill, 999px);
}

/* One animal, one symbol (M-105). The profile does not load search.css, so
   .vb-chip would arrive here unstyled - these are the profile's own. */
.vb-profile__facts-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
}

.vb-profile__tag {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	padding: 3px 10px;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-ink, #30272b);
	background: var(--vb-surface, #f2e7df);
	border-radius: var(--vb-radius-pill, 999px);
}

.vb-profile__directions svg,
.vb-profile__call svg {
	margin-right: 6px;
	vertical-align: -0.18em;
}

.vb-profile__demandcount {
	display: block;
	margin-top: 2px;
	font-size: var(--vb-text-small, 0.85rem);
	color: var(--vb-muted, #6b5b60);
}

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


.vb-profile__secondary a {
	color: var(--vb-muted, #6b5b60);
	text-decoration: underline;
}


/* Callback + claim cards ---------------------------------------------------- */
.vb-callback,
.vb-claim,
.vb-correction {
	max-width: 32.5rem;
	margin: 0;
	padding: 22px;
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-callback h2,
.vb-callback h3,
.vb-claim summary,
.vb-correction summary {
	margin: 0 0 14px;
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--vb-ink, #30272b);
}


.vb-claim__details {
	border: none;
	padding: 0;
}


.vb-claim summary {
	cursor: pointer;
	margin: 0;
}


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


/* The auth and claim forms declare their own layout, because they sit inside
   the .vb-account wrapper on the SIGNED-OUT account page - so the console's
   generic form rule (flex, wrap, fields at 260px asking to share a row) was
   laying out the sign-in card. Two credential fields side by side on a login
   card is not a login card, and the submit button was being placed by
   wrapping rather than sitting on its own row. One column, explicitly. */
.vb-callback__form,
.vb-claim__form,
.vb-correction__form {
	display: block;
}


.vb-callback__form label,
.vb-claim__form label,
.vb-correction__form label {
	display: block;
	margin-bottom: 14px;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	color: var(--vb-ink, #30272b);
}


/* Text-entry controls only. A tick box stretched to 100% is a 335px band
   with a sentence under it, and the code box beside it measured 212px
   because a later rule of the same weight won - so the exclusion is
   wrapped in :where(), which adds nothing: the rule stays at 0-1-1 and
   source order goes on deciding, exactly as profile.css:2050 documents. */
.vb-callback__form input:where(:not([type="checkbox"]):not([type="radio"])),
.vb-callback__form select,
.vb-callback__form textarea,
.vb-claim__form input:where(:not([type="checkbox"]):not([type="radio"])),
.vb-correction__form input:where(:not([type="checkbox"]):not([type="radio"])),
.vb-correction__form select {
	display: block;
	width: 100%;
	box-sizing: border-box;
	margin-top: 6px;
	padding: 10px 12px;
	font: inherit;
	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);
}


.vb-callback__form input:focus,
.vb-callback__form select:focus,
.vb-callback__form textarea:focus,
.vb-claim__form input: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));
}


/* Account safety net: several account forms (pets, e-mail change, reviews,
   adopt, GDPR) do not use .vb-callback__form, so enforce box-sizing here too —
   every text control sits cleanly inside its card and never overflows. */
/* The :not() chain used to carry its own specificity - four attribute
   selectors' worth - which made this box-model rule outrank every later,
   more specific component rule that only wanted a narrower field. :where()
   matches exactly the same elements and contributes nothing, so ordering
   decides again. */
.vb-account input:where(:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"])),
.vb-account select,
.vb-account textarea {
	box-sizing: border-box;
	width: 100%;
	max-width: 100%;
	margin-top: 6px;
}
.vb-callback__form button,
.vb-claim__form button {
	padding: 11px 22px;
	font: inherit;
	font-weight: 600;
	color: #fff;
	background: var(--vb-primary-dark, #68192e);
	border: none;
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
	transition: background-color 0.15s ease, transform 0.15s ease;
}


.vb-callback__form button:hover,
.vb-claim__form button:hover {
	background: var(--vb-primary-dark, #68192e);
	transform: translateY(-1px);
}


/* The same form class serves two worlds: on the public profile its submit is
   the page's green CTA; inside the console the patients panel reuses it for
   per-row note and recall forms, where four green fills crowded out the one
   promoted primary. 0-2-1 outranks the public 0-1-1 rule above; width and
   style are restated because the public rule declares border: none.
   :not(.vb-btn) because other console forms reuse the class too - team's
   "Create login" sits in one, and without the guard this rule (0-3-1 with
   the :not counted) would out-rank .vb-dashboard .vb-btn (0-2-0) and quiet
   the very button a renderer just promoted. */
.vb-dashboard .vb-callback__form button:not(.vb-btn),
.vb-account .vb-callback__form button:not(.vb-btn) {
	color: var(--vb-ink, #30272b);
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-field-line, #8c716e);
}


.vb-dashboard .vb-callback__form button:not(.vb-btn):hover,
.vb-account .vb-callback__form button:not(.vb-btn):hover {
	color: var(--vb-ink, #30272b);
	background: var(--vb-base, #fbf7f2);
	border-color: var(--vb-accent-text, #68192e);
	transform: none;
}


/* Honeypot: visually removed, still present for bots.
   .vb-hp was rendered by DemandSignal and styled by nobody, so it sat on the
   page as a 201x19 input labelled "Website". Worse than the trust problem the
   analysis called it: a honeypot a person can see is one a person can fill in,
   and filling it rejects their booking as spam. Every honeypot class belongs
   in this rule; HoneypotHiddenTest keeps the list honest. */
.vb-hp,
.vb-hp input,
.vb-callback__hp,
.vb-claim__hp {
	position: absolute !important;
	left: -9999px !important;
	height: 1px;
	overflow: hidden;
}


.vb-callback__notice,
.vb-claim__notice {
	margin: 0 0 14px;
	padding: 10px 14px;
	border-radius: var(--vb-radius-sm, 11px);
	font-weight: 600;
}


.vb-callback__notice--ok,
.vb-claim__notice--ok {
	background: var(--vb-success-soft, #e6f3ea);
	color: var(--vb-success, #176849);
}


.vb-callback__notice--error,
.vb-claim__notice--error {
	background: rgba( 207, 34, 46, 0.1 );
	color: var(--vb-danger-text-strong, #b52216);
}


/* Personalization (Task 14): header image, tagline, awards, references, gallery. */
.vb-profile__header {
	margin: 0;
	border-radius: var(--vb-radius, 16px);
	overflow: hidden;
	aspect-ratio: 16 / 6;
	background: var(--vb-surface, #f2e7df);
}


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


.vb-profile__awards-list {
	margin: 0;
	padding-left: 1.2rem;
}


.vb-profile__awards-list li {
	margin-bottom: 4px;
}


/* --- Credentials -------------------------------------------------------- *
   One component for two places: what a business has learned (the profile's
   own section) and what a person has learned (every team page). They sit on
   the same page, and before this they were a bordered card on one and a plain
   bullet list on the other.

   The year is a column rather than a suffix in the title, because a column
   can be scanned: six lines of "... (2022)" have to be read to be compared.
   When no entry carries a year the column is dropped in the markup, so a list
   without years does not start 3.75rem to the right of everything else. */

.vb-creds {
	display: flex;
	flex-direction: column;
	gap: 20px;
}


.vb-creds__legend {
	/* The same 18px the list indents by, so the label sits over the year
	   column rather than hanging to the left of the rule that carries it. */
	margin: 0 0 8px;
	padding-left: 18px;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--vb-cat, var(--vb-accent-text, #68192e));
}


.vb-creds__list {
	list-style: none;
	margin: 0;
	padding: 0 0 0 16px;
	/* A real border and not a shadow: forced colours keep the first and
	   delete the second, and this rule is what holds the group together. */
	border-left: 2px solid var(--vb-cat-soft, var(--vb-primary-soft, #f3e2e7));
}


.vb-creds__item {
	display: grid;
	grid-template-columns: 3.75rem 1fr;
	grid-template-areas:
		"year title"
		"year org";
	column-gap: 14px;
	padding: 9px 0;
}


.vb-creds__item + .vb-creds__item {
	border-top: 1px solid var(--vb-line, #d9c7c1);
}


.vb-creds__year {
	grid-area: year;
	align-self: start;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	font-size: var(--vb-text-label, 0.9rem);
	line-height: 1.6;
	color: var(--vb-muted, #6b5b60);
}


.vb-creds__title {
	grid-area: title;
	font-weight: 600;
	color: var(--vb-ink, #30272b);
}


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


.vb-creds__list--flush .vb-creds__item {
	grid-template-columns: 1fr;
	grid-template-areas:
		"title"
		"org";
}


/* On a narrow phone the year column costs a fifth of the line; above the
   title it costs one short line and the title keeps the full width. */
@media ( max-width: 30rem ) {

	.vb-creds__item {
		grid-template-columns: 1fr;
		grid-template-areas:
			"year"
			"title"
			"org";
	}
}


/* Measured 16.09.2026 with auto-fill and a 140px floor: four columns of 162px
   on the desktop and four of 141-155px on a tablet, where the specification
   asks for two. auto-fill answers "how many fit" - which on a tablet is the
   wrong question, because the answer is "four thumbnails nobody can make out".
   Explicit counts per width instead, and they step down rather than up. */
.vb-profile__gallery-grid {
	display: grid;
	grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	gap: 10px;
}

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

/* The grid's children are links now, so the picture can open over the page
   instead of navigating away from it. Without this the anchor shrink-wraps
   and the cell collapses around it. */
.vb-profile__gallery-grid > a {
	display: block;
	border-radius: var(--vb-radius-sm, 11px);
	overflow: hidden;
}

.vb-profile__gallery-grid img {
	width: 100%;
	/* height: auto, or the height attribute wp_get_attachment_image() writes
	   (512 for medium_large) beats aspect-ratio and every tile stands 512px
	   tall - four photos, 1158 to 1616px of gallery (re-audit P2, PB1).
	   Landscape, as the reference graphic draws the tiles. */
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: var(--vb-radius-sm, 11px);
	display: block;
}

/* Same for the photo in the head: it became a link in the same change. */
.vb-profile__shot > a {
	display: block;
	height: 100%;
}

.vb-profile__shot > a:focus-visible {
	outline: 3px solid var(--vb-cat, var(--vb-focus-color, #205f9c));
	outline-offset: -3px;
}


/* Reviews (P7). */
.vb-stars {
	/* Amber at #f59e0b measures 2.15:1 - the rating is carried visually by
	   these glyphs, so they are content, not decoration. #b45309 is the amber
	   this codebase already uses for sponsored and favourite marks (5.02:1). */
	color: var(--vb-warn-text, #86520a);
	letter-spacing: 0.08em;
}


.vb-reviews__summary {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 0 0 16px;
	font-size: var(--vb-text-lead, 1.0625rem);
}


.vb-review {
	margin: 0 0 16px;
	padding: 14px 18px;
	background: var(--vb-surface, #f2e7df);
	border-radius: var(--vb-radius-sm, 11px);
}


.vb-review__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin: 0 0 6px;
}


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


.vb-review__text {
	margin: 0;
	white-space: pre-line;
}


.vb-review__reply {
	margin: 10px 0 0;
	padding: 10px 14px;
	border-left: 3px solid var(--vb-primary, #b66a80);
	background: var(--vb-base, #fbf7f2);
	border-radius: 0 var(--vb-radius-sm, 11px) var(--vb-radius-sm, 11px) 0;
	font-size: var(--vb-text-body, 1rem);
}


.vb-reviews__pagination {
	display: flex;
	gap: 14px;
	margin-bottom: 16px;
}


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


.vb-review-form {
	max-width: 32.5rem;
	margin-top: 10px;
	padding: 18px;
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-review-form h3 {
	margin: 0 0 12px;
}


/* CSS-only star picker: inputs render right-to-left so hover/checked
   highlights every star up to the pointer. */
.vb-review-form__stars {
	display: flex;
	flex-direction: row-reverse;
	justify-content: flex-end;
	gap: 2px;
	margin: 0 0 12px;
	padding: 0;
	border: none;
}


.vb-review-form__stars input {
	position: absolute;
	opacity: 0;
	width: 1px;
	height: 1px;
}


.vb-review-form__stars label {
	/* The unfilled stars ARE the control - at #cbd5d1 (1.50:1) you could not
	   see how many you had left to click. 3.20:1 keeps them clearly the
	   quieter half of the pair. */
	cursor: pointer;
	font-size: 1.7rem;
	color: #82948b;
	transition: color 0.1s ease;
}


.vb-review-form__stars input:checked ~ label,
.vb-review-form__stars label:hover,
.vb-review-form__stars label:hover ~ label {
	color: var(--vb-warn-text, #86520a);
}


.vb-review-form__stars input:focus-visible + label {
	outline: 2px solid var(--vb-focus-color, #205f9c);
	outline-offset: 2px;
	border-radius: var(--vb-radius-xs, 6px);
}


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


.vb-review-login {
	padding: 12px 16px;
	background: var(--vb-surface, #f2e7df);
	border-radius: var(--vb-radius-sm, 11px);
}


.vb-practice-card__rating {
	margin: 0;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	color: var(--vb-ink, #30272b);
}


/* Customer account (P6). */
.vb-account {
	display: flex;
	flex-direction: column;
	gap: 28px;
}


.vb-account__auth {
	display: grid;
	grid-template-columns: 1fr;
	gap: 22px;
}


@media ( min-width: 48rem ) {
	.vb-account__auth {
		grid-template-columns: 1fr 1fr;
		align-items: start;
	}
}


.vb-account__card {
	max-width: none;
}


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

/* Which appointment a pre-filled "attach" link carries - said before the
   button, because a link can come from anybody (review 25.09.2026). */
.vb-account__facts {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 4px;
}


/* The box beside its sentence, not above it: the field rule above makes
   every label a block, so a bare consent label put the tick box on its own
   line and the sentence under it. 0-2-0 beats that rule; the stay widget
   has done it this way since it was built (see .vb-staybook__consent). */
.vb-callback__form .vb-account__consent,
.vb-claim__form .vb-claim__consent {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}


.vb-account__consent {
	font-weight: 500 !important;
}


.vb-push__consent {
	/* The push opt-in renders on the account page AND in the practice
	   console, so its rule lives beside the other consent boxes rather than
	   in either surface's own sheet. */
	margin: 12px 0;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-surface, #f2e7df);
}


.vb-push__consent p {
	margin: 0 0 8px;
}


.vb-push__consent p:last-child {
	margin-bottom: 0;
}


.vb-push__consent label {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	font-weight: 500;
}


.vb-account__consent input,
.vb-claim__consent input,
.vb-push__consent input {
	/* 24 px is the WCAG 2.5.8 minimum, and it applied here all along. The
	   design audit reported zero undersized targets because it walks the
	   public pages; the booking form does not render without a Turnstile key
	   and the console sits behind a login, so neither was ever measured. */
	width: var(--vb-tap-min, 24px);
	height: var(--vb-tap-min, 24px);
	/* The gap above carries the distance now, and a 24px box must not be
	   allowed to shrink beside a long sentence. */
	flex: 0 0 auto;
	accent-color: var(--vb-accent-text, #68192e);
}


.vb-account__welcome {
	margin: 0;
}


.vb-account__logout {
	margin-left: 10px;
	font-weight: 600;
}


.vb-account__section h2 {
	margin: 0 0 12px;
	font-size: 1.25rem;
}


.vb-account__bookings {
	width: 100%;
	border-collapse: collapse;
}


.vb-account__bookings th,
.vb-account__bookings td {
	text-align: left;
	padding: 9px 12px 9px 0;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
}


.vb-account__bookings th {
	font-size: var(--vb-text-small, 0.85rem);
	color: var(--vb-muted, #6b5b60);
}


.vb-account__cancel {
	padding: 6px 12px;
	font: inherit;
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	color: var(--vb-danger-text-strong, #b52216);
	background: transparent;
	/* On a transparent button the border IS the control's boundary, so
	   1.4.11 asks 3:1 of it. At alpha 0.35 it measured 1.80:1 - visible to
	   someone who already knows it is there. 0.7 measures 3.42:1. */
	border: 1px solid rgba( 207, 34, 46, 0.7 );
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
}


.vb-account__cancel:hover {
	background: rgba( 207, 34, 46, 0.08 );
}


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


/* Calendar manager (Task 13). */
.vb-calendar {
	display: flex;
	flex-direction: column;
	gap: 28px;
}


.vb-calendar__section {
	padding: 20px;
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-calendar__section h2 {
	margin: 0 0 14px;
	font-size: 1.2rem;
}


.vb-calendar__section h3 {
	margin: 16px 0 8px;
	font-size: var(--vb-text-body, 1rem);
}


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


.vb-calendar__list li {
	display: flex;
	align-items: center;
	gap: 10px;
}


.vb-calendar__form {
	max-width: none;
	margin: 0 0 10px;
	padding: 14px;
	background: var(--vb-base, #fbf7f2);
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	gap: 12px;
}


.vb-calendar__form label {
	margin: 0;
}


.vb-calendar__form input[type="time"],
.vb-calendar__form input[type="date"],
.vb-calendar__form input[type="number"],
.vb-calendar__form input[type="text"],
.vb-calendar__form select {
	padding: 8px 10px;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-base, #fbf7f2);
	font: inherit;
}


.vb-calendar__inline {
	display: inline-flex;
	margin: 0 0 0 8px;
}


.vb-calendar__del {
	background: transparent !important;
	color: var(--vb-danger-text-strong, #b52216) !important;
	border: 1px solid rgba( 207, 34, 46, 0.7 ) !important;
	padding: 5px 12px !important;
}


.vb-calendar__grid {
	width: 100%;
	border-collapse: collapse;
	margin-bottom: 12px;
}


.vb-calendar__grid th {
	text-align: left;
	padding: 6px 12px 6px 0;
	font-weight: 600;
	white-space: nowrap;
}


.vb-calendar__grid td {
	padding: 4px 0;
}


/* Waitlist (Task 34) */
.vb-waitlist {
	margin-top: 24px;
	padding: 20px;
	background: var(--vb-surface, #f2e7df);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-waitlist h2 {
	margin: 0 0 10px;
	font-size: 1.15rem;
}


.vb-waitlist__hint,
.vb-waitlist__on {
	margin: 0 0 12px;
	color: var(--vb-muted, #6b5b60);
}


.vb-waitlist__leave {
	padding: 8px 16px;
	font: inherit;
	font-weight: 600;
	color: var(--vb-muted, #6b5b60);
	background: transparent;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
}


/* Favorite toggle (Task 36) */
.vb-favorite {
	margin: 0 0 16px;
}


.vb-favorite__btn {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 8px 14px;
	font: inherit;
	font-weight: 600;
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border: 1px solid transparent;
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
}


.vb-favorite__btn.is-active {
	color: var(--vb-warn-text, #86520a);
	/* The colour was already a token and the background was not, so in dark
	   the amber text sat on a light amber field it was never measured on. */
	background: var(--vb-warn-soft, #fff0d8);
}


.vb-favorite__icon {
	font-size: var(--vb-text-lead, 1.0625rem);
	line-height: 1;
}


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


.vb-account__reschedule {
	margin-left: 10px;
}


.vb-account__reschedule:hover {
	text-decoration: underline;
}


/* Die Plakette "Notdienst" (T43) trug hier bis zum 23.09.2026 dieselbe
   Klasse wie der Notfallblock der Seitenspalte - Versalien, 12px, fett -, und
   der ganze Block las sich in Grossbuchstaben. Die Plakette gibt es im Markup
   nicht mehr; der Notfall steht als Vertrauenszeile im Kopf. */


/* Each group used to scroll sideways on a phone. Two things were wrong with
   that: half of every group sat off-screen with no indication it was there -
   a section you cannot see is a section you will not use - and the last
   visible entry was cut mid-word, which simply reads as broken. Wrapping
   shows all twenty, costs about the same height as the scrollers did, and
   nothing is hidden or clipped. The chips shrink to pay for it. */



/* Visually-hidden helper (the dashboard panel headings use it; not all
   themes define it, so the plugin ships its own copy). */
.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* Focused, it shows: WordPress's own rule for the skip link lifts only
   clip-path, and this copy's legacy clip kept "Skip to content" invisible
   on every profile after the first Tab (re-audit Nr. 7). */
.screen-reader-text:focus {
	clip: auto;
	overflow: visible;
	white-space: normal;
}


/* When JS is active, panels are toggled via the hidden attribute. */

.vb-card-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 14px;
}


@media ( min-width: 35rem ) {
	.vb-card-grid {
		/* minmax(min(100%, 260px), …), not minmax(260px, …): a hard 260px
		   floor cannot shrink, so on a narrow container the tracks overflowed
		   and the cards ran off the right edge. */
		grid-template-columns: repeat( auto-fill, minmax( min(100%, 260px), 1fr ) );
	}
}


.vb-card {
	display: flex;
	flex-direction: column;
	gap: 8px;
	/* A grid item defaults to min-width: auto, so anything with a wide
	   min-content - a file input's "Choose file / no file selected" row -
	   pushed straight through the card's edge. */
	min-width: 0;
	padding: 18px;
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	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));
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}


.vb-card:hover {
	transform: translateY(-3px);
	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));
}


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


.vb-card__when {
	font-weight: 700;
	color: var(--vb-ink, #30272b);
	font-variant-numeric: tabular-nums;
}


.vb-card__practice {
	font-size: var(--vb-text-body, 1rem);
}


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


.vb-card__status {
	align-self: flex-start;
	padding: 2px 10px;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-primary-soft, #f3e2e7);
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
}


.vb-card__status.is-cancelled,
.vb-card__status.is-noshow {
	background: rgba(207, 34, 46, 0.1);
	color: var(--vb-danger-text-strong, #b52216);
}


.vb-card__actions {
	margin-top: 4px;
	display: flex;
	gap: 10px;
	align-items: center;
	flex-wrap: wrap;
}


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

/* Visit-questions editor (service rows). */
/* Who the contract is with. A block and not a tooltip: it has to be read
   before the contract, and it sits at the top of the column where every
   profile offers its booking, its stay request or its phone number. A real
   left border, so forced colours keep it. */
/* The register line sits under the word it qualifies and reads as a note,
   not as a second fact of the same weight. */
.vb-profile__registry {
	margin: 2px 0 10px;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
}


.vb-trader-notice {
	margin: 0 0 14px;
	padding: 10px 14px;
	border-left: 3px solid var(--vb-cat, var(--vb-accent-text, #68192e));
	background: var(--vb-surface, #f2e7df);
	border-radius: 0 var(--vb-radius-sm, 11px) var(--vb-radius-sm, 11px) 0;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-ink, #30272b);
}


/* A packing list, not a paragraph. It sits under its own label and keeps the
   indent of the row above it. */
.vb-profile__bring {
	margin: 2px 0 10px;
	padding-left: 1.2em;
}

.vb-profile__bring li {
	margin: 2px 0;
}


.vb-dashboard__trade,
.vb-dashboard__trader {
	margin: 12px 0;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-dashboard__trade legend,
.vb-dashboard__trader legend {
	padding: 0 6px;
	font-weight: 700;
}


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


/* An unanswered question stays visibly unanswered. */
.vb-dashboard__warn {
	margin: 10px 0 0;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	color: var(--vb-danger-text, #b52216);
}


.vb-calendar__quals {
	margin: 12px 0;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
}


.vb-calendar__quals legend {
	padding: 0 6px;
	font-weight: 700;
}


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


.vb-calendar__questions {
	width: 100%;
	margin: 0.35rem 0;
}

.vb-calendar__questions summary {
	cursor: pointer;
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-calendar__questions textarea {
	width: 100%;
	margin-top: 0.35rem;
}


/* Practice bookings overview: filters + pager (SUB-2). */
.vb-bookings__filters {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 0.6rem;
	margin: 0 0 0.5rem;
}

.vb-bookings__filters label {
	display: flex;
	flex-direction: column;
	gap: 0.2rem;
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-bookings__export {
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-accent-text, #68192e);
}

.vb-bookings__pager {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-top: 0.75rem;
}


/* Customer review edit/delete + e-mail change (SUB-4). */
.vb-own-review {
	margin-bottom: 0.6rem;
}

.vb-own-review__edit summary {
	cursor: pointer;
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-own-review__edit .vb-callback__form {
	margin-top: 0.5rem;
}

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


/* ==========================================================================
   Design refresh: lighter card/panel contours. Grouping containers lean on
   whitespace + a soft shadow instead of a hard 1px line; buttons, inputs and
   the map frame keep their visible borders for affordance.
   ========================================================================== */
.vb-card,
.vb-calendar__section,
.vb-day__item,
.vb-review-form,
.vb-waitlist,
.vb-account__card {
	border-color: rgba(48, 39, 43, 0.06);
}


/* Practice week view (read-only weekly agenda). */
.vb-week__nav {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 1rem;
}


/* The nav links carry class="button", which nothing styles - they rendered
   as bare links in the theme's green link colour, four green things before
   the list even starts. Same quiet pill as .vb-day__nav-btn, so all three
   calendar views navigate with one visual language. */
.vb-week__nav a {
	display: inline-flex;
	align-items: center;
	padding: 0.4rem 0.85rem;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-surface, #f2e7df);
	color: var(--vb-ink, #30272b);
	text-decoration: none;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	line-height: 1.2;
}


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

.vb-week__grid {
	display: grid;
	grid-template-columns: repeat(7, minmax(0, 1fr));
	gap: 0.5rem;
}

.vb-week__col {
	background: var(--vb-surface, #f2e7df);
	border: 1px solid rgba(48, 39, 43, 0.08);
	border-radius: var(--vb-radius, 16px);
	padding: 0.5rem;
	min-height: 4rem;
}

.vb-week__day {
	margin: 0 0 0.5rem;
	font-size: var(--vb-text-small, 0.85rem);
	display: flex;
	flex-direction: column;
	line-height: 1.2;
}

.vb-week__date {
	font-weight: 400;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-caption, 0.78rem);
}

.vb-week__empty {
	color: var(--vb-muted, #c9b7be);
	margin: 0.25rem 0;
}

.vb-week__card {
	display: flex;
	flex-direction: column;
	gap: 0.05rem;
	border-left: 4px solid #8a94a6;
	background: var(--vb-surface, #f2e7df);
	border-radius: var(--vb-radius-xs, 6px);
	padding: 0.3rem 0.45rem;
	margin-bottom: 0.4rem;
	font-size: var(--vb-text-caption, 0.78rem);
}


/* Same trap as the day rows: opacity 0.55 on the card dragged the customer
   and staff names to 3.15:1. The line-through already says "cancelled". */
.vb-week__card--cancelled { text-decoration: line-through; }

.vb-week__card--cancelled .vb-week__cust,
.vb-week__card--cancelled .vb-week__staff,
.vb-week__card--noshow .vb-week__cust,
.vb-week__card--noshow .vb-week__staff { color: var( --vb-muted, #6b5b60 ); }

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

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

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

.vb-week__staff { color: var(--vb-muted, #6b5b60); font-size: var(--vb-text-caption, 0.78rem); }

@media (max-width: 56rem) {
	.vb-week__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* ===== Week time grid (D2): cards sit at their start time ================ */
.vb-week__grid--timed .vb-week__col {
	display: flex;
	flex-direction: column;
	padding: 0.5rem 0.35rem 0.35rem;
}


.vb-week__col.is-today {
	border-color: var(--vb-field-line, #8c716e);
	box-shadow: inset 0 2px 0 var(--vb-accent-text, #68192e);
}


/* Each day column is its own inner grid: one row per 15 minutes (14px, an
   hour is 56px), hour ruling as a repeating gradient - no gutter column,
   so print keeps its seven columns. */
.vb-week__lanes {
	position: relative;
	flex: 1;
	display: grid;
	grid-template-rows: repeat(var(--vb-week-rows, 44), 14px);
	grid-template-columns: minmax(0, 1fr);
	background: repeating-linear-gradient(
		to bottom,
		transparent 0,
		transparent 55px,
		rgba(48, 39, 43, 0.08) 55px,
		rgba(48, 39, 43, 0.08) 56px
	);
	border-radius: var(--vb-radius-xs, 6px);
}


.vb-week__grid--timed .vb-week__card {
	grid-column: 1;
	margin-bottom: 0;
	min-height: 0;
	overflow: hidden;
	z-index: 1;
	font-size: var(--vb-text-caption, 0.78rem);
	line-height: 1.15;
	padding: 0.1rem 0.3rem;
	gap: 0;
	box-shadow: 0 1px 2px rgba(48, 11, 23, 0.12);
}


.vb-week__grid--timed .vb-week__card:hover {
	z-index: 3;
	overflow: visible;
	min-height: -webkit-fit-content;
	min-height: fit-content;
	background: var(--vb-base, #fbf7f2);
}


.vb-week__card.is-moving {
	outline: 2px dashed var(--vb-accent-text, #68192e);
	outline-offset: 1px;
}


.vb-week__more {
	grid-column: 1;
	justify-self: end;
	z-index: 2;
	align-self: start;
	background: var(--vb-primary-dark, #68192e);
	color: #fff;
	border-radius: var(--vb-radius-pill, 999px);
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	padding: 0.05rem 0.4rem;
	text-decoration: none;
}


.vb-week__legend {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem 1rem;
	margin: 0 0 0.75rem;
	font-size: var(--vb-text-caption, 0.78rem);
	color: var(--vb-muted, #6b5b60);
}


.vb-week__legenditem {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
}


.vb-week__legenddot {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	display: inline-block;
}


.vb-week__filter {
	margin-left: auto;
	display: flex;
	align-items: center;
	gap: 0.5rem;
}


.vb-week__filter select {
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	padding: 0.35rem 0.5rem;
	font-size: var(--vb-text-small, 0.85rem);
	background: var(--vb-surface, #f2e7df);
}


@media (max-width: 56rem) {
	/* The grid needs width: fall back to the stacked list. display:block
	   makes the inline grid-row inert; the lane widths are inline styles,
	   so the two !important lines below are the one honest way to win. */
	.vb-week__lanes {
		display: block;
		background: none;
	}

	.vb-week__lanes .vb-week__card {
		width: auto !important;
		margin-left: 0 !important;
		margin-bottom: 0.4rem;
		font-size: var(--vb-text-caption, 0.78rem);
		overflow: visible;
	}

	.vb-week__more {
		display: inline-block;
		margin-bottom: 0.4rem;
	}
}


/* ===== Public team section (§10.2.x) ===================================== */
/* Two columns only where each card gets 300 px (Aufgabe #211, graphics lead
   A3): with the main column beside the booking box, 240 px tracks made
   three of them at 1280 and left each card 243 px, its details 131 px
   beside the avatar. Now 1000 px (608 px column) shows one column, 1280 and
   1440 two of 372 and 396 px. min() keeps a 288 px phone column from
   overflowing. */
.vb-team__grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
	gap: 1rem;
}

/* A head (avatar, name, role) and the details under it at the card's full
   width, on every screen - beside the avatar they were a narrow column of
   short lines (A3, B8). */
.vb-team__card {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	min-width: 0;
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	padding: 1rem;
	/* A card at rest is its line, nothing more (Aufgabe #257, A5): the
	   shadow and the lift on hover made a person look like something to
	   press, and competed with the booking box. */
	box-shadow: none;
}

.vb-team__head {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	min-width: 0;
}

.vb-team__avatar {
	flex: 0 0 auto;
	line-height: 0;
}

.vb-team__avatar img,
.vb-team__avatar--empty {
	width: 64px;
	height: 64px;
	border-radius: 50%;
	object-fit: cover;
	flex: 0 0 auto;
}

@media (max-width: 39.99rem) {
	.vb-team__avatar img,
	.vb-team__avatar--empty {
		width: 48px;
		height: 48px;
	}
}

.vb-team__avatar--empty {
	display: flex;
	align-items: center;
	justify-content: center;
	/* Quieter than the initial it replaces: this is the absence of a photo,
	   not a thing to look at. */
	background: var(--vb-neutral-soft, #efe9e6);
	color: var(--vb-muted, #6b5b60);
}

.vb-team__ident,
.vb-team__body { min-width: 0; }

.vb-team__name { margin: 0 0 0.1rem; font-size: var(--vb-text-body, 1rem); }

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

.vb-team__langs { margin: 0 0 0.35rem; font-size: var(--vb-text-small, 0.85rem); }

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

.vb-team__quals { margin: 0 0 0.35rem; padding-left: 1.1rem; font-size: var(--vb-text-small, 0.85rem); }

.vb-team__quals li { margin: 0.1rem 0; }

/* Its own line, always. Inline it sat beside the short titles and wrapped
   under the long ones, so three entries on one card had the year in two
   different places. */
.vb-team__qualyear { display: block; color: var(--vb-muted, #6b5b60); font-variant-numeric: tabular-nums; }

/* The card shows three; this is the door to the rest, and it sits flush with
   them rather than carrying a bullet of its own. */
.vb-team__quals-more { list-style: none; margin-left: -1.1rem; }

.vb-team__quals-more a { font-weight: 600; }

.vb-team__body > :last-child { margin-bottom: 0; }

.vb-team__bio { margin: 0; font-size: var(--vb-text-small, 0.85rem); color: var(--vb-text, #30272b); }



/* ----- Account pet manager (was unstyled: native details + stray buttons) -- */
.vb-pet {
	margin: 0 0 12px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
	box-shadow: 0 1px 2px rgba(48, 39, 43, 0.04);
}


.vb-pet__summary {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 16px;
	cursor: pointer;
	font-weight: 600;
	list-style: none;
	border-radius: var(--vb-radius, 16px);
}


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


.vb-pet__summary::after {
	content: "";
	margin-left: auto;
	width: 0;
	height: 0;
	border-left: 5px solid transparent;
	border-right: 5px solid transparent;
	border-top: 6px solid var(--vb-muted, #6b5b60);
	transition: transform 0.15s ease;
}


.vb-pet[open] > .vb-pet__summary::after {
	transform: rotate(180deg);
}


.vb-pet > form,
.vb-pet__body {
	padding: 0 16px 14px;
}


.vb-pet__delete {
	padding: 0 16px 14px;
}


.vb-pet__delete button {
	background: transparent;
	border: 1px solid var(--vb-danger-text, #b52216);
	color: var(--vb-danger-text, #b52216);
	padding: 0.4rem 0.9rem;
	border-radius: var(--vb-radius-sm, 11px);
	font-weight: 600;
	cursor: pointer;
}


.vb-pet__delete button:hover {
	background: #c0392b;
	color: #fff;
}


/* ----- Pet health record (weight curve + vaccination pass, T-G4) ------------ */
.vb-pet__health {
	padding: 0 16px 14px;
	display: grid;
	gap: 10px;
}


.vb-pet__healthhead {
	margin: 0;
	font-size: var(--vb-text-body, 1rem);
}


.vb-pet__weights {
	margin: 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 6px;
	font-size: var(--vb-text-label, 0.9rem);
}


.vb-pet__weights li {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
}


.vb-pet__recdel {
	display: inline;
	margin: 0;
}


.vb-pet__recdel button {
	background: transparent;
	border: 0;
	color: var(--vb-danger-text, #b52216);
	font-size: var(--vb-text-caption, 0.78rem);
	padding: 2px 6px;
	cursor: pointer;
	text-decoration: underline;
}


.vb-pet__weightform {
	display: flex;
	align-items: flex-end;
	gap: 10px;
	flex-wrap: wrap;
}


.vb-pet__weightform label {
	display: grid;
	gap: 4px;
	font-size: var(--vb-text-small, 0.85rem);
	margin: 0;
}


.vb-pet__weightform input {
	width: 8.5em;
}


.vb-pet .vb-record__pass {
	padding: 0 16px 14px;
	margin: 0;
}


.vb-pet .vb-record__pass h3 {
	margin: 0 0 8px;
	font-size: var(--vb-text-body, 1rem);
}


/* ----- Invalid form fields (validation flash) ------------------------------- */
.vb-account input[aria-invalid="true"],
.vb-account textarea[aria-invalid="true"] {
	border-color: var(--vb-danger-text, #b52216);
	box-shadow: 0 0 0 1px #c0392b;
}


/* ----- Onboarding wizard (console activation checklist) --------------------- */
.vb-wizard {
	margin: 0 0 18px;
	padding: 16px 18px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-lg, 22px);
	background: linear-gradient(160deg, var(--vb-tint, #f3e2e7), var(--vb-base, #fbf7f2) 70%);
}


.vb-wizard__head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 8px;
	font-size: var(--vb-text-lead, 1.0625rem);
}


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


.vb-wizard__bar {
	height: 8px;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-neutral-soft, #efe9e6);
	overflow: hidden;
	margin-bottom: 12px;
}


.vb-wizard__bar span {
	display: block;
	height: 100%;
	border-radius: var(--vb-radius-pill, 999px);
	/* Fortschrittsbalken: Länge ist die Aussage, 3:1 gilt. */
	background: var(--vb-accent-text, #68192e);
	transition: width 0.4s ease;
}


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


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


.vb-wizard__step.is-done .vb-wizard__label {
	color: var(--vb-muted, #6b5b60);
	text-decoration: line-through;
}


.vb-wizard__mark {
	flex: 0 0 auto;
	width: 22px;
	height: 22px;
	display: inline-grid;
	place-items: center;
	border-radius: 50%;
	background: var(--vb-neutral-soft, #efe9e6);
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	color: var(--vb-muted, #6b5b60);
}


/* A step done is a success, so green with its tick (#410). */
.vb-wizard__step.is-done .vb-wizard__mark {
	background: var(--vb-success-fill, #176849);
	color: #fff;
}


.vb-wizard__go {
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
}


.vb-wizard__dismiss button {
	background: transparent;
	border: 0;
	padding: 0;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-small, 0.85rem);
	text-decoration: underline;
	cursor: pointer;
}


/* ----- Pet health timeline --------------------------------------------------- */
.vb-timeline {
	margin: 12px 0 4px;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-surface, #f2e7df);
}


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


.vb-timeline__item {
	display: flex;
	gap: 10px;
	align-items: baseline;
	font-size: var(--vb-text-label, 0.9rem);
}


.vb-timeline__date {
	flex: 0 0 auto;
	min-width: 86px;
	font-variant-numeric: tabular-nums;
	color: var(--vb-muted, #6b5b60);
}


.vb-timeline__item--recall .vb-timeline__label {
	color: var(--vb-warn-text, #86520a);
	font-weight: 600;
}


/* ----- Practitioner profile page --------------------------------------------- */
/* The person's page, in the shape the business's page has.
 *
 * None of these rules had ever applied: profile.css was not enqueued on this
 * route, so the page fell back to bare theme styles - a 300px portrait as the
 * largest thing on the screen, and everything under it a loose column of
 * paragraphs. Measured on the live page before the fix.
 *
 * It is the same identity card as the business hero, which is what makes the
 * two read as one site: a bordered block, a tinted head in the practice's own
 * accent, the portrait at a size that says "this is a person" rather than
 * "this is the page". */
.vb-staffprofile__head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 18px;
	margin-bottom: 20px;
	padding: 18px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-lg, 22px);
	background:
		linear-gradient(180deg, var(--vb-cat-soft, var(--vb-primary-soft, #f3e2e7)) 0%, transparent 120px),
		var(--vb-base, #fbf7f2);
}

/* A portrait, not a poster. 128px is the size the team card uses on the
   business profile plus a third - large enough to recognise somebody, small
   enough that the words beside it are still the point. */
.vb-staffprofile__portrait {
	flex: none;
	width: 128px;
	height: 128px;
	margin: 0;
	border-radius: var(--vb-radius-lg, 22px);
	overflow: hidden;
	background: var(--vb-surface, #f2e7df);
}

.vb-staffprofile__intro {
	flex: 1 1 16ch;
	min-width: 0;
}



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


.vb-staffprofile__role {
	margin: 0 0 6px;
	font-weight: 600;
	/* The practice's own accent, so a salon's page and a practice's page are
	   told apart here the same way they are told apart everywhere else. */
	color: var(--vb-cat, var(--vb-accent-text, #68192e));
}


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


.vb-staffprofile__bio {
	max-width: 65ch;
}


.vb-staffprofile__creds {
	margin-top: 20px;
}


.vb-staffprofile__practice {
	margin: 16px 0;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-surface, #f2e7df);
}


/* Passwordless login — OTP input (EML-1) */
/* input.vb-otp-input, not .vb-otp-input: the account box-model rule near the
   top is `.vb-account input:where(…)` = one class + one element, which
   outranks a bare class no matter where it sits. Matching its weight and
   coming later is enough - the six-digit code box was stretching to the full
   width of the sign-in card with 0.6em letter-spacing between the digits. */
input.vb-otp-input {
	font-size: 1.6rem;
	letter-spacing: 0.6em;
	text-align: center;
	font-variant-numeric: tabular-nums;
	width: auto;
	max-width: 12ch;
}


/* B2B-1 — pricing conversion + console sign-in gate */
.vb-pricing__note { font-size: var(--vb-text-small, 0.85rem); color: var(--vb-muted, #6b5b60); margin: 2px 0 10px; }

.vb-console-gate { display: flex; justify-content: center; padding: clamp(20px, 5vw, 48px) 0; }

.vb-console-gate__card {
	width: 100%;
	max-width: 26rem;
	background: var(--vb-base, #fbf7f2);
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-lg, 22px);
	padding: clamp(22px, 5vw, 32px);
	box-shadow: 0 24px 48px -32px rgba(48, 39, 43, 0.45);
}

.vb-console-gate__card h2 { margin: 0 0 18px; font-size: clamp(1.3rem, 1.1rem + 1vw, 1.6rem); line-height: 1.15; }

/* One message for every failed sign-in, in the page the reader was on -
   see Security\WpLoginGuard. Tokens, not literals: the box has to hold in
   both themes, and --vb-danger-ink on --vb-danger-soft is the pair the
   audit already measures. */
.vb-console-gate__error {
	margin: 0 0 16px;
	padding: 10px 14px;
	border: 1px solid var(--vb-danger-text, #b52216);
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-danger-soft, #ffece6);
	color: var(--vb-danger-ink, #b52216);
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-console-gate__form { display: flex; flex-direction: column; gap: 14px; margin-bottom: 18px; }

.vb-console-gate__form label { display: flex; flex-direction: column; gap: 6px; font-size: var(--vb-text-label, 0.9rem); font-weight: 600; color: var(--vb-ink, #30272b); }

.vb-console-gate__form input {
	width: 100%;
	padding: 12px 14px;
	border: 1px solid var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-base, #fbf7f2);
	font: inherit;
	font-weight: 400;
}

.vb-console-gate__form input:focus { border-color: var(--vb-focus-color, #205f9c); 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)); }

.vb-console-gate__form button {
	margin-top: 4px;
	padding: 13px;
	border: 0;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-ink, #30272b);
	color: #fff;
	font: inherit;
	font-weight: 700;
	cursor: pointer;
	transition: background-color 0.15s ease;
}

.vb-console-gate__form button:hover { background: var(--vb-primary-dark, #68192e); }

.vb-console-gate__alt { font-size: var(--vb-text-label, 0.9rem); color: var(--vb-muted, #6b5b60); margin: 0 0 12px; padding-top: 16px; border-top: 1px solid var(--vb-line, #d9c7c1); }

/* .vb-btn--ghost (M-109). */


/* B2B-1 — ROI / no-show calculator */
.vb-roi { max-width: 35rem; margin: 24px auto; padding: 20px; border: 1px solid var(--vb-border, #d9c7c1); border-radius: var(--vb-radius, 16px); background: var(--vb-surface, #f2e7df); }

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

.vb-roi__inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }

.vb-roi__field { display: flex; flex-direction: column; gap: 4px; font-size: var(--vb-text-label, 0.9rem); }

.vb-roi__field input { padding: 9px 11px; border: 1px solid var(--vb-field-line, #8c716e); border-radius: var(--vb-radius-xs, 6px); font-variant-numeric: tabular-nums; }

.vb-roi__out { margin: 16px 0 6px; padding: 14px; border-radius: var(--vb-radius-sm, 11px); background: var(--vb-primary-soft, #f3e2e7); }

.vb-roi__loss strong { font-size: 1.4rem; color: var(--vb-ink, #30272b); }

.vb-roi__save strong { color: var(--vb-accent-text, #68192e); font-weight: 700; }

.vb-roi__note { font-size: var(--vb-text-caption, 0.78rem); color: var(--vb-muted, #6b5b60); margin: 4px 0 0; }


/* B2B-1 — read-only demo console */
.vb-demo__banner { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 16px; background: var(--vb-primary-soft, #f3e2e7); border: 1px solid var(--vb-primary, #b66a80); border-radius: var(--vb-radius-sm, 11px); font-size: var(--vb-text-label, 0.9rem); }

.vb-demo__badge { background: var(--vb-primary-dark, #68192e); color: #fff; font-weight: 700; font-size: var(--vb-text-caption, 0.78rem); letter-spacing: 0.04em; text-transform: uppercase; padding: 3px 8px; border-radius: var(--vb-radius-pill, 999px); }

.vb-demo__cta {
	/* A .vb-btn--sm (M-109); only its place in the row is its own. */
	margin-left: auto;
	white-space: nowrap;
}

.vb-demo__title { margin: 8px 0 4px; }


/* CLAIM-1 — outreach banner + demand signal */
.vb-profile__claimhint.is-outreach { border: 2px solid var(--vb-primary, #b66a80); border-radius: var(--vb-radius-sm, 11px); padding: 18px; }

.vb-profile__claimbadge { display: inline-block; margin: 0 0 6px; padding: 3px 9px; border-radius: var(--vb-radius-pill, 999px); background: var(--vb-primary-dark, #68192e); color: #fff; font-size: var(--vb-text-caption, 0.78rem); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }

.vb-demand { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 12px 0 24px; }

.vb-demand__btn {
	/* .vb-btn--ghost .vb-btn--sm (M-109): the one darker green that measures
	   5.39:1 as text and past 3:1 as the edge now comes from the variant. */
	margin: 0;
}

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


/* ===== Month calendar ==================================================== */
.vb-month__title { margin: 12px 0 10px; font-size: 1.15rem; }

.vb-month__grid { border: 1px solid var(--vb-line, #d9c7c1); border-radius: var(--vb-radius, 16px); overflow: hidden; background: var(--vb-base, #fbf7f2); }

.vb-month__head { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); background: var(--vb-surface, #f2e7df); }

.vb-month__dow { padding: 8px 6px; text-align: center; font-size: var(--vb-text-caption, 0.78rem); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--vb-muted, #6b5b60); }

.vb-month__weeks { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }

.vb-month__cell { min-height: 6.5rem; padding: 6px; border-top: 1px solid var(--vb-line, #d9c7c1); border-right: 1px solid var(--vb-line, #d9c7c1); }

.vb-month__cell:nth-child(7n) { border-right: 0; }

.vb-month__cell.is-outside { background: color-mix(in srgb, var(--vb-surface, #f2e7df) 55%, #fff); }

/* These stay real links, so they are active controls and 1.4.3 applies.
   opacity 0.45 measured 2.72:1; a declared colour measures 4.85:1 and still
   reads as the quieter neighbour month. */
.vb-month__cell.is-outside .vb-month__day { color: var(--vb-muted, #6b5b60); }

.vb-month__cell.is-today { box-shadow: inset 0 0 0 2px var(--vb-accent-text, #68192e); }

.vb-month__day { display: inline-block; min-width: 1.6rem; margin-bottom: 4px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--vb-ink, #30272b); text-decoration: none; }

.vb-month__day:hover { text-decoration: underline; }

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

.vb-month__item { font-size: var(--vb-text-caption, 0.78rem); font-variant-numeric: tabular-nums; padding: 1px 5px; border-radius: var(--vb-radius-xs, 6px); background: var(--vb-primary-soft, #f3e2e7); color: var(--vb-accent-text, #68192e); }

.vb-month__item.is-internal { background: var(--vb-neutral-soft, #efe9e6); color: var(--vb-neutral-ink, #5a5348); }

.vb-month__more { display: block; margin-top: 3px; font-size: var(--vb-text-caption, 0.78rem); color: var(--vb-muted, #6b5b60); }


/* A seven-column grid is unreadable on a phone: the weeks become a list of
   days that actually have something in them. */
@media (max-width: 46rem) {
	.vb-month__head { display: none; }
	.vb-month__weeks { grid-template-columns: 1fr; }
	.vb-month__cell { display: flex; align-items: baseline; gap: 10px; min-height: 0; border-right: 0; }
	.vb-month__cell.is-empty { display: none; }
	.vb-month__cell.is-outside { display: none; }
	.vb-month__day::after { content: "."; }
	.vb-month__items { flex-direction: row; flex-wrap: wrap; }
}


/* --- Trial strip (practice console) ----------------------------------- */
.vb-trial {
	margin: 0 0 20px;
	padding: 16px 18px;
	background: linear-gradient(135deg, var(--vb-primary-soft, #f3e2e7), var(--vb-base, #fbf7f2));
	border: 1px solid var(--vb-primary, #b66a80);
	border-left-width: 4px;
	border-radius: var(--vb-radius, 16px);
}

.vb-trial.is-urgent {
	background: linear-gradient(135deg, var(--vb-warning-soft, #fff0d8), var(--vb-base, #fbf7f2));
	border-color: var(--vb-gold, #e0a82e);
}

.vb-trial__head {
	margin: 0;
	font-size: var(--vb-text-body, 1rem);
}

.vb-trial__head strong {
	font-size: 1.15rem;
}

.vb-trial__lead {
	margin: 10px 0 6px;
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	color: var(--vb-muted, #6b5b60);
}

.vb-trial__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 4px 20px;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-trial__list li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 6px;
	padding: 3px 0;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
}

.vb-trial__feature {
	flex: 1 1 auto;
}

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

.vb-trial__plan {
	padding: 1px 8px;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-primary-soft, #f3e2e7);
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	white-space: nowrap;
}

.vb-trial__more,
.vb-trial__actions {
	margin: 10px 0 0;
	font-size: var(--vb-text-small, 0.85rem);
}


.vb-claim__trial {
	margin: 0 0 10px;
	padding: 10px 14px;
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-sm, 11px);
	color: var(--vb-ink, #30272b);
	font-weight: 600;
	font-size: var(--vb-text-label, 0.9rem);
}



/* ==========================================================================
   Practice console: forms and controls

   The console had exactly two rules in the whole codebase - a focus ring and
   a max-width on inputs - so every editor in it rendered as raw browser
   defaults: `<label>Text<input></label>` with no display rule runs inline, so
   labels and fields ran into each other in one paragraph. This is the layout
   the markup always assumed.

   Scoped under .vb-dashboard AND .vb-account: the customer account page has
   the same markup written against the same absent stylesheet, so "Neue
   Nachricht an [select] [Schreiben]" ran together on one line there too.

   The console composes markup from
   a dozen editors that were written against no stylesheet at all, so the
   rules key off ELEMENTS rather than classes those editors do not emit.
   ========================================================================== */

.vb-dashboard,
.vb-account {
	font-family: var(--vb-body, system-ui, sans-serif);
	color: var(--vb-ink, #30272b);
}


.vb-dashboard h2,
.vb-account h2 {
	font-size: 1.35rem;
	line-height: 1.2;
	margin: 0 0 4px;
}


.vb-dashboard h3,
.vb-account h3 {
	font-size: var(--vb-text-lead, 1.0625rem);
	line-height: 1.25;
	margin: 24px 0 8px;
}


.vb-dashboard p,
.vb-account p {
	line-height: 1.55;
	max-width: 68ch;
}


/* ----- Form rhythm -------------------------------------------------------- */

/* Flex-wrap, not a grid of equal columns. Half the forms in this console are
   not forms at all but toolbars - a date and a Go button, a delay select and
   a Notify button - and an auto-fit grid spread those three controls across
   a 1400px page with a gulf between the label and its field. Wrapping keeps
   a toolbar a toolbar and still gives a 14-field editor two or three columns,
   because each field asks for ~280px and takes what is left. */
:where(.vb-dashboard, .vb-account) form {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 14px 18px;
	margin: 14px 0 0;
}


/* A field asks for a comfortable width and shares what is there. */
:where(.vb-dashboard, .vb-account) form > label {
	/* min-width, NOT flex-basis. `flex: 1 1 260px` sets the basis on the
	   container's MAIN axis - which is the width in a row and the HEIGHT in a
	   column. Every form in this console that lays itself out as a column got
	   260px-tall labels from it: the practice sign-in gate rendered two
	   fields over 606px with a void between them. min-width gives the same
	   wrapping behaviour in a row (items wrap once they cannot be 260 wide)
	   and means nothing in a column, so one rule stops fighting the other. */
	flex: 1 1 auto;
	min-width: min(260px, 100%);
	/* Capped, or a field that happens to be alone on its row stretches to the
	   full width while its neighbours above sit at 270 - the same form in
	   three different column widths, which reads as broken rather than
	   responsive. Anything that genuinely needs the room asks for it. */
	max-width: 430px;
}


:where(.vb-dashboard, .vb-account) form > label:has( textarea ),
:where(.vb-dashboard, .vb-account) form > .vb-dashboard__wide {
	max-width: 100%;
}


/* Controls that are their own row: they need the width or they need the
   break after them. */
:where(.vb-dashboard, .vb-account) form > :is(p, fieldset, table) {
	flex: 1 1 100%;
}


/* Each label owns its field: the text sits above the control, and the pair
   moves as one. This single rule is what the screenshot was missing. */
:where(.vb-dashboard, .vb-account) form > label,
:where(.vb-dashboard, .vb-account) fieldset > label,
:where(.vb-dashboard, .vb-account) fieldset > label {
	display: grid;
	gap: 5px;
	align-content: start;
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	color: var(--vb-ink, #30272b);
	min-width: 0;
}


/* Text areas want the room; a lone button does not - it belongs beside the
   field it submits, which is what makes a toolbar read as one. */
:where(.vb-dashboard, .vb-account) form > :is(label:has( textarea ), .vb-dashboard__wide) {
	flex: 1 1 100%;
}


:where(.vb-dashboard, .vb-account) form > button {
	flex: 0 0 auto;
}


:where(.vb-dashboard, .vb-account) fieldset,
:where(.vb-dashboard, .vb-account) fieldset {
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	padding: 14px 16px 16px;
	margin: 0;
	min-width: 0;
}


:where(.vb-dashboard, .vb-account) legend,
:where(.vb-dashboard, .vb-account) legend {
	font-size: var(--vb-text-body, 1rem);
	padding: 0 6px;
}


/* ----- Controls ----------------------------------------------------------- */

:where(.vb-dashboard, .vb-account) input:where(:not([type="checkbox"]):not([type="radio"]):not([type="file"])),
:where(.vb-dashboard, .vb-account) select,
:where(.vb-dashboard, .vb-account) textarea {
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	padding: 9px 12px;
	font: inherit;
	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);
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}


:where(.vb-dashboard, .vb-account) textarea {
	min-height: 96px;
	resize: vertical;
	line-height: 1.5;
}


:where(.vb-dashboard, .vb-account) :is(input, textarea)::placeholder {
	color: var(--vb-muted, #6b5b60);
	opacity: 1;
}


/* 44px comfortable target on touch, without making the desk view chunky. */
@media (pointer: coarse) {
:where(.vb-dashboard, .vb-account) :where(input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), select, button) {
		min-height: 44px;
	}
}


/* Checkbox and radio rows read as a sentence, so they lay out as one - and
   emphatically do NOT stretch to the field width, which turns a checkbox into
   a giant pill. */
:where(.vb-dashboard, .vb-account) label:has( > input:is([type="checkbox"], [type="radio"]) ) {
	display: flex;
	flex-direction: row;
	align-items: center;
	gap: 9px;
	font-weight: 400;
	line-height: 1.4;
}


:where(.vb-dashboard, .vb-account) input[type="checkbox"],
:where(.vb-dashboard, .vb-account) input[type="radio"],
:where(.vb-dashboard, .vb-account) input[type="radio"] {
	flex: 0 0 auto;
	/* 24 px is the WCAG 2.5.8 minimum, and it applied here all along. The
	   design audit reported zero undersized targets because it walks the
	   public pages; the booking form does not render without a Turnstile key
	   and the console sits behind a login, so neither was ever measured. */
	width: var(--vb-tap-min, 24px);
	height: var(--vb-tap-min, 24px);
	margin: 0;
	accent-color: var(--vb-accent-text, #68192e);
}


:where(.vb-dashboard, .vb-account) input[type="file"],
:where(.vb-dashboard, .vb-account) input[type="file"] {
	font: inherit;
	font-size: var(--vb-text-label, 0.9rem);
	padding: 8px 0;
}


:where(.vb-dashboard, .vb-account) input[type="file"]::file-selector-button,
:where(.vb-dashboard, .vb-account) input[type="file"]::file-selector-button {
	font: inherit;
	font-weight: 600;
	margin-right: 10px;
	padding: 8px 14px;
	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;
}


:where(.vb-dashboard, .vb-account) input:focus-visible,
:where(.vb-dashboard, .vb-account) select:focus-visible,
:where(.vb-dashboard, .vb-account) textarea:focus-visible,
:where(.vb-dashboard, .vb-account) textarea:focus-visible {
	border-color: var(--vb-accent-text, #68192e);
}


/* ----- Buttons ------------------------------------------------------------ */

/* Structure is shared; colour is not. The old version painted EVERY button
   in the console as a green filled pill - the census counted eight green
   fills on the calendar tab alone and seven on patients. When everything is
   green, .vb-btn means nothing and the eye has nowhere to land. So the
   DEFAULT is now the quiet one, and green is what a renderer says on the
   one action per panel it promotes to .vb-btn.
   border-width/style here, border-color per branch below - the shorthand
   would also set border-color and fight the branches. */
:where(.vb-dashboard, .vb-account) button,
.vb-dashboard .vb-btn,
.vb-account .vb-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	justify-self: start;
	padding: 10px 20px;
	font: inherit;
	font-weight: 600;
	line-height: 1.2;
	border-width: 1px;
	border-style: solid;
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
	transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}


/* The quiet default: ink on the page ground, --vb-field-line as the 3:1
   control boundary. :where() keeps this at 0-0-1 so every component class
   still wins. */
:where(.vb-dashboard, .vb-account) button {
	color: var(--vb-ink, #30272b);
	background: var(--vb-base, #fbf7f2);
	border-color: var(--vb-field-line, #8c716e);
}


/* The one deliberate primary per panel. --vb-primary-dark, not --vb-primary:
   white on #14b870 measures 2.59:1 and fails AA outright, while #0c7a4a
   measures 5.39:1. */
.vb-dashboard .vb-btn,
.vb-account .vb-btn {
	color: #fff;
	background: var(--vb-primary-dark, #68192e);
	border-color: var(--vb-accent-text, #68192e);
}


/* A generic hover has to be conservative, because it lands on every button
   in the console - including ones whose own :hover rule changes only the
   TEXT colour, on the assumption that the background stays light. Painting a
   near-black fill from here produced exactly that collision on "+ Neues
   Dashboard": this rule supplied the dark background, .vb-board__tab:hover
   supplied dark ink, and the label became unreadable on its own button.
   Components that want a filled hover say so themselves. */
:where(.vb-dashboard, .vb-account) button:hover {
	border-color: var(--vb-accent-text, #68192e);
	transform: translateY(-1px);
}


/* .vb-btn is the deliberate filled button, so it keeps the strong hover. */
.vb-dashboard .vb-btn:hover,
.vb-account .vb-btn:hover {
	background: var(--vb-primary-darker, #300b17);
	color: #fff;
	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));
}


/* Modifier variants. Same 0-2-0 specificity as .vb-btn, so source order
   decides - these sit after the base on purpose.
   --secondary: everyday scheduling ops ("Slot blocken") that share a form
   with a promoted primary; visually the quiet default.
   --link: restrictive actions behind a confirm ("Von Online-Buchung
   sperren"). Blocking a patient belongs to the red family, not the green
   one - a green button that punishes is a lie. */
.vb-dashboard .vb-btn--secondary,
.vb-account .vb-btn--secondary {
	color: var(--vb-ink, #30272b);
	background: var(--vb-base, #fbf7f2);
	border-color: var(--vb-field-line, #8c716e);
}


.vb-dashboard .vb-btn--secondary:hover,
.vb-account .vb-btn--secondary:hover {
	color: var(--vb-ink, #30272b);
	background: var(--vb-base, #fbf7f2);
	border-color: var(--vb-accent-text, #68192e);
	box-shadow: none;
}


.vb-dashboard .vb-btn--link,
.vb-account .vb-btn--link {
	color: var(--vb-danger-text-strong, #b52216);
	background: transparent;
	border-color: rgba( 207, 34, 46, 0.7 );
}


.vb-dashboard .vb-btn--link:hover,
.vb-account .vb-btn--link:hover {
	color: #fff;
	background: #cf222e;
	border-color: var(--vb-danger-text-strong, #b52216);
	box-shadow: none;
}


@media (prefers-reduced-motion: reduce) {
	:where(.vb-dashboard, .vb-account) button:hover,
	.vb-dashboard .vb-btn:hover,
	.vb-account .vb-btn:hover {
		transform: none;
	}
}


:where(.vb-dashboard, .vb-account) button:disabled {
	color: var(--vb-muted, #6b5b60);
	background: var(--vb-surface, #f2e7df);
	border-color: var(--vb-line, #d9c7c1);
	cursor: not-allowed;
	transform: none;
	box-shadow: none;
}


.vb-dashboard .vb-btn:disabled,
.vb-account .vb-btn:disabled {
	color: #fff;
	background: var(--vb-muted, #6b5b60);
	border-color: var(--vb-muted, #6b5b60);
}


/* is-secondary is gone: no renderer ever emitted the class, and with the
   quiet default it described nothing the bare element does not already do.
   (Its history: .vb-account__cancel was once grouped in here and rendered
   green - the destructive treatment lives with the destructive class.) */
.vb-dashboard__actions,
.vb-account__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	align-items: center;
	margin-top: 4px;
}


/* ----- Hints and helpers -------------------------------------------------- */

.vb-dashboard__hint,
.vb-account__hint,
:where(.vb-dashboard, .vb-account) small,
:where(.vb-dashboard, .vb-account) small {
	display: block;
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 400;
	line-height: 1.45;
	color: var(--vb-muted, #6b5b60);
}


.vb-dashboard__upsell,
.vb-account__upsell {
	margin: 12px 0 0;
	padding: 12px 14px;
	font-size: var(--vb-text-label, 0.9rem);
	background: linear-gradient(135deg, var(--vb-warning-soft, #fff0d8), var(--vb-base, #fbf7f2));
	border: 1px solid var(--vb-gold, #e0a82e);
	border-left-width: 4px;
	border-radius: var(--vb-radius-sm, 11px);
}


/* ----- Tables ------------------------------------------------------------- */

:where(.vb-dashboard, .vb-account) table,
:where(.vb-dashboard, .vb-account) table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--vb-text-label, 0.9rem);
}


:where(.vb-dashboard, .vb-account) th,
:where(.vb-dashboard, .vb-account) td,
:where(.vb-dashboard, .vb-account) td {
	text-align: left;
	padding: 9px 10px;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
	vertical-align: top;
}


:where(.vb-dashboard, .vb-account) th,
:where(.vb-dashboard, .vb-account) th {
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--vb-muted, #6b5b60);
}


/* ----- Appointment card: actions and attachments -------------------------- */

/* The three things a customer can do with an appointment used to render as
   three different kinds of object - an outlined pill, a bare underlined link
   and a browser-default button - stacked with no shared rhythm. They are one
   row of controls, so they look like one row of controls. */
/* Each action that posts brings its own <form>. display: contents keeps the
   form out of the layout so the three controls sit in one row rather than
   stacking as three blocks. */
.vb-card__actions > form {
	display: contents;
}


.vb-card__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	margin: 2px 0 0;
}


.vb-account__cancel,
.vb-account__reschedule,
.vb-account__rebook {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 8px 15px;
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	line-height: 1.2;
	border-radius: var(--vb-radius-pill, 999px);
	border: 1px solid var(--vb-field-line, #8c716e);
	text-decoration: none;
	cursor: pointer;
	white-space: nowrap;
	transition: background-color 0.15s ease, color 0.15s ease;
}


.vb-account__reschedule,
.vb-account__rebook {
	color: var(--vb-accent-text, #68192e);
	background: transparent;
}


.vb-account__reschedule:hover,
.vb-account__rebook:hover {
	color: #fff;
	background: var(--vb-primary-dark, #68192e);
}


/* Cancelling is the one that cannot be undone, so it reads as the exception:
   outlined in the error colour rather than filled, which would make the most
   destructive control the loudest thing on the card. */
.vb-account__cancel {
	color: var(--vb-danger-text-strong, #b52216);
	background: transparent;
	border-color: var(--vb-danger-text-strong, #b52216);
}


.vb-account__cancel:hover {
	color: #fff;
	background: #cf222e;
}


/* ----- Photo and attachment lightbox (Aufgabe #184, #220) ----------------- */

/* A light album, not a dark stage (Aufgabe #220, direction A of the graphics
   lead): one opaque warm-white surface, the photo fitted whole with small
   corners and nothing around it, and quiet square buttons beside it. On a
   phone the dialog is the whole screen and previous, the count and next are
   one row under the photo, so no control covers the picture's edges; from
   40rem it is a centred card over the dimmed page with the arrows left and
   right of the photo. The same colours whatever the page theme: the album is
   light either way. Corners come from the site's radius scale (the
   nearest steps to the 12/8/4px of the sketch: 11px card, 6px controls
   and photo). Its own colours, so the page's dark theme never reaches
   in; every text at least 6:1, every edge and the focus ring at least 3:1.
   The only motion is the photo fading in over 120ms once it has loaded, and
   only for a reader who has not asked for less (system setting, the motion
   switch or the calm view). The dialog itself never moves. */
.vb-lightbox {
	box-sizing: border-box;
	position: fixed;
	inset: 0;
	width: 100%;
	max-width: none;
	height: 100vh;
	height: 100dvh;
	max-height: none;
	margin: auto;
	padding: calc(12px + env(safe-area-inset-top, 0px)) 12px calc(12px + env(safe-area-inset-bottom, 0px));
	color: #30272b;
	background: #f7f6f2;
	border-width: 0;
	border-radius: 0;
	color-scheme: light;
	overflow: hidden;
	animation: none;
	transition: none;
}


.vb-lightbox[open] {
	display: grid;
	grid-template-columns: 48px minmax(0, 1fr) 48px;
	grid-template-rows: auto minmax(0, 1fr) auto auto;
	grid-template-areas:
		"top top top"
		"stage stage stage"
		"caption caption caption"
		"prev count next";
	column-gap: 12px;
}


.vb-lightbox::backdrop {
	background: rgba(48, 39, 43, 0.6);
}


/* The attribute is the switch, whatever display a rule below gives. */
.vb-lightbox [hidden] {
	display: none !important;
}


.vb-lightbox__bar--top {
	grid-area: top;
	display: flex;
	align-items: center;
	gap: 16px;
	min-height: 48px;
	padding: 0 0 12px;
}


/* "Photos of <business>" - one line; a long name ends in an ellipsis
   rather than pushing the way out off the screen. */
.vb-lightbox__title {
	flex: 1;
	min-width: 0;
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	line-height: 20px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}


/* Figure and the arrow bar take part in the dialog's grid: the stage, the
   caption, the arrows and the count each get their own cell. */
.vb-lightbox__figure,
.vb-lightbox__bar--nav {
	display: contents;
}


.vb-lightbox__stage {
	grid-area: stage;
	position: relative;
	min-width: 0;
	min-height: 0;
	overflow: hidden;
	touch-action: pan-y pinch-zoom;
}


/* The stage has its size before the photo arrives, so nothing jumps while
   it loads. The picture is centred in it at its own proportions, portrait
   or landscape, never cropped and never blown up past its own pixels; its
   box is the picture itself, so the corners are the picture's corners. */
.vb-lightbox__img {
	display: block;
	position: absolute;
	inset: 0;
	width: auto;
	height: auto;
	max-width: 100%;
	max-height: 100%;
	margin: auto;
	border-radius: var(--vb-radius-xs, 6px);
}


@keyframes vb-lightbox-photo-in {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}


/* lightbox.js sets is-entering anew each time a photo has loaded. The
   motion switch and the calm view hold it still too: site.css, on every
   page, stops every animation under data-motion and data-focus. */
@media (prefers-reduced-motion: no-preference) {
	.vb-lightbox__img.is-entering {
		animation: vb-lightbox-photo-in 120ms ease-out;
	}
}


.vb-lightbox__state {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	margin: 0;
	padding: 24px;
	color: #526057;
	font-size: 16px;
	line-height: 24px;
	text-align: center;
}


.vb-lightbox__state.is-failed {
	font-weight: 600;
	color: #a3261b;
}


/* At most a third of the screen, so a long caption never squeezes the
   photo away; beyond that it scrolls on its own. lightbox.js then puts it
   in the Tab loop, and a focused scroll box scrolls with the arrow and
   page keys - the rest is never out of the keyboard's reach (review of
   PR #196). Without a caption the row is not there at all. */
.vb-lightbox__caption {
	grid-area: caption;
	max-height: 30vh;
	max-height: 30dvh;
	margin: 0;
	padding: 12px 4px 0;
	color: #526057;
	font-size: 14px;
	line-height: 20px;
	text-align: center;
	overflow-y: auto;
	overflow-wrap: anywhere;
}


.vb-lightbox__caption:empty {
	display: none;
}


.vb-lightbox__count {
	grid-area: count;
	display: grid;
	place-items: center;
	min-height: 48px;
	margin-top: 12px;
	font-size: 14px;
	font-weight: 600;
	line-height: 20px;
	font-variant-numeric: tabular-nums;
}


/* One shape for every control: a 48px square with the small corners of
   the site's scale, a thin grey edge on the album's own colour. */
.vb-lightbox__close,
.vb-lightbox__nav {
	box-sizing: border-box;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-width: 48px;
	min-height: 48px;
	padding: 0;
	font: inherit;
	color: #30272b;
	background: #f7f6f2;
	border: 1px solid #738078;
	border-radius: var(--vb-radius-xs, 6px);
	box-shadow: none;
	cursor: pointer;
	transition: none;
}


.vb-lightbox__close svg,
.vb-lightbox__nav svg {
	display: block;
	flex-shrink: 0;
	width: 24px;
	height: 24px;
}


/* On a phone only the cross shows; the word stays the button's name. */
.vb-lightbox__close {
	flex-shrink: 0;
	width: 48px;
	height: 48px;
}


.vb-lightbox__close-label {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}


.vb-lightbox__nav {
	width: 48px;
	height: 48px;
	margin-top: 12px;
	align-self: center;
}


.vb-lightbox__prev {
	grid-area: prev;
}


.vb-lightbox__next {
	grid-area: next;
}


@media (hover: hover) {
	.vb-lightbox__close:hover,
	.vb-lightbox__nav:hover {
		color: #30272b;
		background: #efe9e6;
		border-color: #8c716e;
	}
}


/* One plain blue ring, for the keyboard only. */
.vb-lightbox__close:focus-visible,
.vb-lightbox__nav:focus-visible,
.vb-lightbox__caption:focus-visible {
	outline: 3px solid #1d4ed8;
	outline-offset: 3px;
}


/* Opened with a pointer: focus is on the way out, the ring waits for the
   first key (lightbox.js; Safari rings any focus a script sets). See-
   through rather than none, so forced colours still draw it. */
.vb-lightbox[data-vb-pointer] .vb-lightbox__close:focus-visible {
	outline-color: transparent;
}


/* Screen-reader only: announces "Photo 2 of 4" when the photo changes. */
.vb-lightbox__live {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}


@media (min-width: 40.0625rem) {
	.vb-lightbox {
		width: calc(100% - 48px);
		max-width: 1240px;
		height: calc(100vh - 48px);
		height: calc(100dvh - 48px);
		max-height: 860px;
		padding: 24px;
		border-radius: var(--vb-radius-sm, 11px);
	}

	.vb-lightbox[open] {
		grid-template-areas:
			"top top top"
			"prev stage next"
			". caption ."
			". count .";
	}

	.vb-lightbox__close {
		width: auto;
		padding: 0 14px 0 10px;
		font-size: 15px;
		font-weight: 600;
		line-height: 20px;
	}

	.vb-lightbox__close-label {
		position: static;
		width: auto;
		height: auto;
		margin: 0;
		overflow: visible;
		clip: auto;
	}

	.vb-lightbox__nav {
		margin-top: 0;
	}

	/* The card stops at 860px, a third of the screen does not: on a very
	   tall screen (2560px) 30dvh were 768px and pressed the photo's row
	   down to nothing. 258px is 30 % of the card's own 860px, so the photo
	   keeps 422px of a full card (Aufgabe #225). */
	.vb-lightbox__caption {
		max-height: min(30vh, 258px);
		max-height: min(30dvh, 258px);
	}
}


/* ----- Week view: an appointment is a control, not a label ---------------- */

.vb-week__card {
	display: grid;
	gap: 2px;
	text-decoration: none;
	color: inherit;
	cursor: pointer;
	transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}


.vb-week__card:hover {
	background: var(--vb-primary-soft, #f3e2e7);
	transform: translateY(-1px);
	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-week__card: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));
}


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


@keyframes vb-day-arrive {
	0%, 55% {
		background: var(--vb-primary-soft, #f3e2e7);
		box-shadow: inset 0 0 0 2px var(--vb-accent-text, #68192e);
	}
	100% {
		background: transparent;
		box-shadow: none;
	}
}


/* No keyboard, no keyboard hint. */
@media (pointer: coarse) {
	.vb-msg__kbd {
		display: none;
	}
}


/* ==========================================================================
   Console + account: the sections go beside the work, not on top of it

   Twenty sections rendered as a cloud of chips cost 186px of band, and with
   the page title above them the first line of actual content began 444px
   down a 697px viewport - roughly two thirds of the opening screen spent on
   furniture, on a tool somebody opens twenty times a day.

   On a wide screen they move into a sticky column instead. The list is the
   same list; it simply stops competing with the content for the top of the
   page, it scales past twenty entries without getting taller, and where you
   are stays visible while you scroll. Below 1088px nothing changes - the
   horizontal grouping is right for a narrow screen.
   ========================================================================== */

@media ( min-width: 68rem ) {
	/* No max-width here. It used to say 1340px and never took effect - the
	   block theme's constrained-layout rule matches at equal weight and sits
	   later in the head, so the console renders at 1080px. Measured, not
	   assumed: with the 210px column and its 30px gap that leaves an 840px
	   work column, which is the number the board grid below is sized
	   against. A rule that quietly loses is worse than no rule, because the
	   next person sizing something will trust it. */




	/* The title is a signpost in a tool, not a headline. Two thirds smaller,
	   set beside the navigation so it stops pushing the work down.
	   Keyed on the section list rather than on .vb-account, because the
	   account page emits that wrapper when signed OUT as well - so the rule
	   was shrinking the heading of the public sign-in and registration page
	   from ~56px to 24px, on a screen that is a landing page and wants its
	   headline. No tab list, no console, no shrink. */
}


/* One orchestrated arrival rather than a dozen scattered twitches: the row
   settles in sequence on load and is never seen again. */
@media ( prefers-reduced-motion: no-preference ) {
	.vb-board__tile {
		animation: vb-tile-in 0.34s cubic-bezier( 0.22, 0.61, 0.36, 1 ) both;
	}

	.vb-board__tile:nth-child(1) { animation-delay: 0.02s; }
	.vb-board__tile:nth-child(2) { animation-delay: 0.06s; }
	.vb-board__tile:nth-child(3) { animation-delay: 0.10s; }
	.vb-board__tile:nth-child(4) { animation-delay: 0.14s; }
	.vb-board__tile:nth-child(5) { animation-delay: 0.18s; }
	.vb-board__tile:nth-child(6) { animation-delay: 0.22s; }
	.vb-board__tile:nth-child(n+7) { animation-delay: 0.26s; }

	/* A tile being dragged must not also be arriving. */
	.vb-board__grid.is-sorting .vb-board__tile {
		animation: none;
	}
}


@keyframes vb-tile-in {
	from {
		opacity: 0;
		transform: translateY(7px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}


/* ----- The section list: legible movement, no jitter --------------------- */
@media ( min-width: 68rem ) {
	/* A bar rather than a filled block: it marks the row without turning a
	   quarter of the sidebar into a green slab. */

}


/* ----- Vertical rhythm inside a panel ------------------------------------ */

/* Sections carried no margin at all, so an empty "Kommende" ran straight
   into the next heading and the two read as one confused block. */
.vb-account__section > h2,
.vb-day > h2 {
	margin: 0 0 10px;
	font-size: var(--vb-text-lead, 1.0625rem);
	line-height: 1.25;
}


.vb-account__section > h2 + p,
.vb-account__section > h2 + .vb-account__muted {
	margin-top: 0;
}


/* ----- One shape for the section list on both sides ---------------------- */

@media ( min-width: 68rem ) {
	/* The account list was rendering as outlined pills while the console's
	   was flat rows - same component, two personalities. Twenty (or four)
	   outlined boxes stacked in a column is twenty borders to read past; in
	   a list, the row itself is the affordance. */

}


/* ----- Nothing wider than its box ---------------------------------------- */

/* A button whose label is long enough sets its own width and pushes the
   document sideways with it: "Luxemburger Feiertage vorbefüllen" measured
   372px and ran the calendar tab 17px past a 402px phone. Buttons wrap and
   stay inside their column now. */
:where(.vb-dashboard, .vb-account) button,
:where(.vb-dashboard, .vb-account) .vb-btn {
	max-width: 100%;
	overflow-wrap: break-word;
}


/* Same for anything holding a long unbroken value - a URL, an embed snippet,
   a booking code - that is rendered as text rather than in a field. */
:where(.vb-dashboard, .vb-account) code,
:where(.vb-dashboard, .vb-account) pre,
:where(.vb-dashboard, .vb-account) .vb-account__feed {
	max-width: 100%;
	overflow-wrap: anywhere;
}


/* The stats table overflowed its wrapper by only 21px on a tablet, so it
   scrolled for the sake of one column. A tighter cell fits it. */
@media ( max-width: 67.99rem ) {
	.vb-stats__table-wrap table th,
	.vb-stats__table-wrap table td {
		padding-left: 7px;
		padding-right: 7px;
		font-size: var(--vb-text-small, 0.85rem);
	}
}


/* ----- Reviews: the verification, said once and shown per review ---------- */

/* The platform's strongest review claim is that every one of them is
   attached to a completed appointment, chosen server-side. It had never been
   said anywhere - and under Directive (EU) 2019/2161 saying whether and how
   you verify is not optional for anyone displaying consumer reviews. */
.vb-reviews__verified-note {
	margin: 0 0 18px;
	padding: 10px 14px;
	font-size: var(--vb-text-small, 0.85rem);
	line-height: 1.5;
	color: var(--vb-muted, #6b5b60);
	background: var(--vb-surface, #f2e7df);
	border-left: 3px solid var(--vb-accent-text, #68192e);
	border-radius: var(--vb-radius-sm, 11px);
}


/* Quiet by design. A badge on every review is a badge on none if it shouts;
   what carries here is that it is present on all of them, not how loud any
   one is. */
.vb-review__verified {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 2px 8px;
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 600;
	line-height: 1.5;
	white-space: nowrap;
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-pill, 999px);
}


.vb-review__verified-icon {
	flex: 0 0 auto;
}


@media ( prefers-reduced-motion: no-preference ) {
	/* The mark settles in with the review rather than appearing stamped on. */
	.vb-review__verified {
		animation: vb-verified-in 0.28s ease-out both;
	}
}


@keyframes vb-verified-in {
	from {
		opacity: 0;
		transform: scale(0.94);
	}
	to {
		opacity: 1;
		transform: none;
	}
}


/* NOTE: these live in profile.css, not booking.css, because the confirmation
   page does not load booking.css - it enqueues vb-profile only
   (BookingPage.php:159). Putting them next to the widget's own styles looked
   right and reached nothing; the buttons stayed browser-grey on the page every
   customer sees after booking. */
/* ----- Booking confirmation page ------------------------------------------
   The page a customer lands on straight after booking, and the one the
   reminder mail links to. Its two primary buttons had no styling reaching
   them: .vb-booking__submit is declared only as a descendant of .vb-booking,
   which this page is not, and .vb-booking-confirm__yes had no rule at all -
   so both rendered as raw browser buttons in grey with square corners, next
   to a properly styled Cancel. The most-seen page in the whole flow looked
   half-finished. */

.vb-booking-confirm__yes,
.vb-booking-confirm__confirm .vb-booking__submit {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 44px;
	padding: 11px 22px;
	font-family: inherit;
	font-size: var(--vb-text-body, 1rem);
	font-weight: 700;
	line-height: 1.2;
	color: #fff;
	background: var(--vb-primary-dark, #68192e);
	border: 1px solid var(--vb-accent-text, #68192e);
	border-radius: var(--vb-radius-pill, 999px);
	cursor: pointer;
	transition: background-color 0.16s ease, transform 0.16s ease, box-shadow 0.16s ease;
}


.vb-booking-confirm__yes:hover,
.vb-booking-confirm__confirm .vb-booking__submit:hover {
	background: var(--vb-primary-darker, #300b17);
	border-color: var(--vb-primary-darker, #300b17);
	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));
}


@media ( prefers-reduced-motion: reduce ) {
	.vb-booking-confirm__yes:hover,
	.vb-booking-confirm__confirm .vb-booking__submit:hover {
		transform: none;
	}
}


/* The check-in button sits in its own form (.vb-booking-confirm__checkin),
   not inside the confirm form - so a sibling selector reached it only by
   accident of order and a descendant of the confirm form never did. Named
   directly. It is secondary to confirming and should not compete with it. */
.vb-booking-confirm__checkin .vb-booking__submit,
.vb-booking-confirm .vb-booking__submit {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	padding: 11px 22px;
	font-family: inherit;
	font-size: var(--vb-text-body, 1rem);
	font-weight: 700;
	line-height: 1.2;
	border-radius: var(--vb-radius-pill, 999px);
	border-width: 1px;
	border-style: solid;
	cursor: pointer;
	color: var(--vb-accent-text, #68192e);
	background: transparent;
	border-color: var(--vb-field-line, #8c716e);
}


.vb-booking-confirm__checkin .vb-booking__submit:hover,
.vb-booking-confirm .vb-booking__submit:hover {
	color: #fff;
	background: var(--vb-primary-dark, #68192e);
	border-color: var(--vb-accent-text, #68192e);
}


/* The forms stack, so give them air rather than letting the buttons collide. */
.vb-booking-confirm__confirm,
.vb-booking-confirm__cancel,
.vb-booking-confirm__checkin {
	margin: 12px 0 0;
}

/* What the page says about the appointment itself (25.09.2026): a request
   is not yet an appointment; past the cancellation window there is a
   sentence and a phone number instead of a vanished button; and the link
   in the address bar is the customer's key to it. */
.vb-booking-confirm__pending,
.vb-booking-confirm__late {
	margin: 12px 0 0;
	padding: 10px 14px;
	border-radius: var(--vb-radius-sm, 11px);
}

.vb-booking-confirm__pending {
	color: var(--vb-warn-ink, #86520a);
	background: var(--vb-warn-soft, #fff0d8);
}

/* Video consultation on the appointment page (#298). */
.vb-video-room {
	margin: 14px 0;
	padding: 12px 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-surface, #f2e7df);
}

.vb-video-room h3 {
	margin: 0 0 8px;
}

.vb-video-room__frame {
	display: flex;
	flex-direction: column;
	height: min(80vh, 640px);
	min-height: 420px;
	margin-top: 10px;
	overflow: hidden;
	border-radius: var(--vb-radius-sm, 11px);
}

.vb-video-room__frame iframe {
	display: block;
	flex: 1 1 auto;
	width: 100%;
	min-height: 0;
	border-width: 0;
}

/* The way out of the room, shown only while it fills a phone screen (#369 A). */
.vb-video-room__bar {
	display: none;
}

/* On a phone the joined room fills the screen: above the app bar, below
   the cookie notice. */
.vb-video-room__frame.is-full {
	position: fixed;
	inset: 0;
	z-index: 1002;
	height: 100vh;
	height: 100dvh;
	min-height: 0;
	margin: 0;
	border-radius: 0;
	background: var(--vb-base, #fbf7f2);
}

.vb-video-room__frame.is-full .vb-video-room__bar {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: max(6px, env(safe-area-inset-top)) 12px 6px;
	border-bottom: 1px solid var(--vb-line, #d9c7c1);
	background: var(--vb-surface, #f2e7df);
	color: var(--vb-ink, #30272b);
	font-weight: 600;
}

:root.vb-video-full,
:root.vb-video-full body {
	overflow: hidden;
}

.vb-video-room__left,
.vb-video-room__help {
	margin: 10px 0 0;
}

.vb-video-room__help p {
	margin: 0;
}

.vb-video-room__left p {
	margin: 0 0 8px;
}

.vb-video-room__frame .vb-video-room__help {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 12px;
	flex: 0 0 auto;
	margin: 0;
	border-radius: 0;
}

.vb-video-room__help {
	padding: 10px 12px;
	border-radius: var(--vb-radius-sm, 11px);
	color: var(--vb-warn-ink, #86520a);
	background: var(--vb-warn-soft, #fff0d8);
}

.vb-booking-confirm__late {
	color: var(--vb-ink, #30272b);
	background: var(--vb-neutral-soft, #efe9e6);
}

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

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

/* The stay form: the boarding house's booking panel. Same field language as
   the slot widget; the price line answers before the form asks for a name. */
/* The stay form is the conversion moment of a boarding profile, exactly as
   the appointment widget is of a veterinary one - and it was a plain 1px box
   with a bare h3 sitting above a glowing green card (M-054). Same card, same
   heading weight; the difference between the two profiles is which form
   appears, not which one looks like it matters. */
/* Since Aufgabe #257 (A5) both are the same calm card: a 1 px line and the
   small neutral lift of the booking box, no green frame and no glow. */
.vb-staybook {
	margin: 0 0 16px;
	padding: 24px;
	background: var(--vb-base, #fbf7f2);
	border-width: 1px;
	border-style: solid;
	border-color: var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	box-shadow: var(--vb-profile-shadow-box, 0 4px 16px rgba(48, 39, 43, 0.08));
}

.vb-staybook h2 {
	margin: 0 0 12px;
	font-size: var(--vb-profile-h2, 1.375rem);
	line-height: var(--vb-profile-h2-lh, 1.2727);
}

@media (max-width: 37.5rem) {
	.vb-staybook {
		padding: 18px;
		border-radius: var(--vb-radius, 16px);
	}
}

/* The heading and, on a phone, the way to fold the form away again
   (Aufgabe #256, stay.js) - the booking area's close button, the same look. */
.vb-staybook__head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 12px;
}

.vb-staybook__head h2 {
	min-width: 0;
	scroll-margin-top: 4px;
}

.vb-staybook__close {
	flex: none;
	min-height: var(--vb-tap-primary, 44px);
	padding: 8px 16px;
	font: inherit;
	font-weight: 600;
	color: var(--vb-muted, #6b5b60);
	background: transparent;
	border-width: 1px;
	border-style: solid;
	border-color: var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	cursor: pointer;
}

.vb-staybook__close:hover {
	color: var(--vb-ink, #30272b);
	border-color: var(--vb-field-line, #8c716e);
}

/* While a request is on its way it cannot be closed (stay.js): the line
   under the head says why. */
.vb-staybook__close[aria-disabled="true"] {
	cursor: not-allowed;
}

.vb-staybook__wait {
	margin: 0 0 12px;
	color: var(--vb-muted, #6b5b60);
}
.vb-staybook__form { display: grid; gap: 10px; }
.vb-staybook__form label { display: grid; gap: 4px; font-size: var(--vb-text-label, 0.9rem); }
.vb-staybook__form input,
.vb-staybook__form select,
.vb-staybook__form textarea { box-sizing: border-box; min-width: 0; max-width: 100%; min-height: var(--vb-tap-primary, 44px); border: 1px solid var(--vb-field-line, #8c716e); border-radius: var(--vb-radius-sm, 11px); padding: 8px 10px; font: inherit; background: var(--vb-base, #fbf7f2); color: inherit; }
/* minmax(0, 1fr): a date field's min-content width held each track at its
   own size, and at 320px the form ran 51px past the screen (re-audit A9). */
.vb-staybook__dates { display: grid; grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1fr ); gap: 10px; }
.vb-staybook__dates > * { min-width: 0; }
.vb-staybook__price { margin: 0; font-weight: 600; color: var(--vb-accent-text, #68192e); }
.vb-staybook__consent { display: flex !important; gap: 8px; align-items: flex-start; }
.vb-staybook__notice { margin: 0; min-height: 1em; font-size: var(--vb-text-label, 0.9rem); }
.vb-staybook__notice.is-ok { color: var(--vb-success, #176849); }
.vb-staybook__notice.is-error { color: var(--vb-danger-text, #b52216); }

/* The destructive action: cancel a stay. Danger tokens exist in both schemes. */
.vb-btn--danger { background: var(--vb-danger-soft, #ffece6); color: var(--vb-danger-text, #b52216); border: 1px solid var(--vb-danger-text, #b52216); }


/* Reported corrections, and the honest empty state that leads to them.
   Measured: 153 of the 176 real entries carry neither a phone number nor a
   website, and the profile told the visitor the business "takes appointments
   by phone" above an empty contact list. The route out is deliberately open
   on unclaimed entries - claiming needs a document, and Art. 14 owes a
   correction route to anyone listed without being asked. */
.vb-profile__nocontact {
	margin: 0 0 12px;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-body, 1rem);
	line-height: 1.5;
}


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


/* Where an unclaimed entry's details came from, on the page that shows
   them (Art. 14 GDPR). Sits directly above the correction form, because
   the sentence ends by pointing at it. */
.vb-profile__provenance {
	margin: 16px 0 0;
	padding: 12px 16px;
	border-left: 3px solid var(--vb-line, #d9c7c1);
	background: var(--vb-surface, #f2e7df);
	border-radius: 0 var(--vb-radius-sm, 11px) var(--vb-radius-sm, 11px) 0;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
	line-height: 1.6;
}


.vb-profile__provenance-label {
	display: inline-block;
	margin-right: 6px;
	padding: 2px 9px;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-tint, #f3e2e7);
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-caption, 0.78rem);
	font-weight: 700;
	letter-spacing: 0.02em;
}


.vb-correction {
	margin-top: 16px;
}


.vb-correction summary {
	cursor: pointer;
	font-size: var(--vb-text-body, 1rem);
}

/* Der Korrekturweg ist einer der Wege, die gesammelt erreichbar bleiben
   (Aufgabe #245, B33; Trefferflaeche B6): 44 px wie jede andere Falte. */
.vb-correction > summary {
	box-sizing: border-box;
	min-height: 44px;
	padding-block: 10px;
}


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


.vb-correction__notice {
	margin: 0 0 12px;
	padding: 10px 12px;
	border-radius: var(--vb-radius-sm, 11px);
	font-size: var(--vb-text-label, 0.9rem);
}


.vb-correction__notice--ok {
	background: var(--vb-success-soft, #e6f3ea);
	color: var(--vb-success, #176849);
}


.vb-correction__notice--error {
	background: var(--vb-warn-soft, #fff0d8);
	color: var(--vb-warn-ink, #86520a);
}


/* The two verification paths, said before anybody fills anything in. They
   differ by days, so they are two items rather than one sentence. */
.vb-claim__paths {
	margin: 0 0 16px;
	padding-left: 1.1rem;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-label, 0.9rem);
	line-height: 1.55;
}


.vb-claim__paths li + li {
	margin-top: 8px;
}


.vb-claim__paths strong {
	color: var(--vb-ink, #30272b);
}

/* The one door on a service row. A list of prices with no way to act on it
   is the end of the road - "Price on request" and nothing to request with. */
.vb-services__do {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Die rechte Spalte der Leistungsliste, neben Name und Meta-Zeile
	   (Aufgabe #219): jeder Knopf beginnt am selben x und ist so breit wie
	   der breiteste der Liste. */
	grid-column: 2;
	grid-row: 1 / span 2;
	justify-self: stretch;
	box-sizing: border-box;
	/* Die Buchungshandlung je Leistung, also ein ganzes Fingerziel - sie
	   war 24px hoch, auf dem Telefon wie auf dem Desktop (Nachtrag der
	   Gegenpruefung, A19). */
	min-height: var(--vb-tap-primary, 44px);
	padding: 0 0.55rem;
	text-align: center;
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	color: var(--vb-accent-text, #68192e);
	background: var(--vb-primary-soft, #f3e2e7);
	border-radius: var(--vb-radius-sm, 11px);
	text-decoration: none;
	white-space: nowrap;
}

.vb-services__do:hover,
.vb-services__do:focus-visible {
	background: var(--vb-primary-dark, #68192e);
	color: #fff;
}

/* The way back. Two rungs and a chevron: a profile opened from the results
   had no route to them, and the browser's back button is gone the moment
   somebody switches language - which on this site is what a German visitor
   does the second they land on a Luxembourgish profile. */
.vb-crumbs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem;
	margin: 0 0 0.9rem;
	font-size: var(--vb-text-label, 0.9rem);
	color: var(--vb-muted, #6b5b60);
}

.vb-crumbs a {
	min-height: var(--vb-tap-min, 24px);
	display: inline-flex;
	align-items: center;
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 2px;
}

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


/* ----- The one action, within reach ---------------------------------------
   A profile is around 2,400px on a phone and its booking widget or number
   sits in the first screen; past that, nothing on the page offered the
   action again. The bar is printed hidden and uncovered by
   profile-stickybar.js once the aside has scrolled off the top.

   The seam is the profile's own, not the app bar's: the aside goes sticky at
   1000px (above), and between 1000px and the app bar's 1280px it never
   leaves the screen, so the bar would be dead code there. Base rule
   display:none so it cannot become a third grid child on wide screens; the
   [hidden] rule in site.css outranks both and stays the switch.

   Static reserve below the page, never toggled: padding that appears when
   the bar does is a layout shift on scroll. z-index 65 - under the report
   offer (70) and the app bar (1000), above the language menu (60). */
.vb-profile__stickybar {
	display: none;
}

@media (max-width: 62.49rem) {
	.vb-profile__stickybar {
		position: fixed;
		inset: auto 0 var(--vb-appnav-h) 0;
		z-index: 65;
		display: flex;
		justify-content: center;
		/* 64 px with its line: 1 + 7 + 48 + 8 (graphics lead, #211 B7 -
		   it was 81, a 64 px button in 16 px of padding). Grows with the
		   text, never cut. */
		padding: 7px 12px 8px;
		/* Opaque like the app bar it stands on (Aufgabe #222, B2): no glass,
		   no text showing through. */
		background: var(--vb-appnav-bg, #fffcf8);
		border-top: 1px solid var(--vb-line, #d9c7c1);
	}

	.vb-profile__stickybar-link {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		box-sizing: border-box;
		width: min( 100%, 32rem );
		min-height: 48px;
		padding: 10px 18px;
		/* In der Gewerbefarbe und der Form des Hauptknopfs im Kopf, dessen
		   Handlung er wiederholt (Nachpruefung P7). */
		border-radius: var(--vb-radius-sm, 11px);
		background: var(--vb-cat, var(--vb-primary-dark, #68192e));
		color: var(--vb-cat-ink, #fff);
		font-weight: 600;
		text-decoration: none;
	}

	.vb-profile {
		padding-bottom: 64px;
	}
}

/* ----- The map in the dark scheme -------------------------------------------
   The tiles are OpenStreetMap's own raster and arrive light; a CSS filter
   turns them dark in the browser, so the recipient of the tile request stays
   the OpenStreetMap Foundation named in ADR 0007 - no second tile provider,
   no second processor. The classic "dark OSM" recipe: darken, invert, pull
   the contrast back, rotate the hue so water stays blue-ish, desaturate.
   Leaflet's own chrome (zoom buttons, attribution, popups) gets the page's
   tokens instead of staying a white island. */
:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-tile-pane {
	filter: brightness( 0.6 ) invert( 1 ) contrast( 3 ) hue-rotate( 200deg ) saturate( 0.3 ) brightness( 0.7 );
}

:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-control-zoom a,
:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-control-attribution,
:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-popup-content-wrapper,
:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-popup-tip {
	background: var(--vb-base, #191418);
	color: var(--vb-ink, #f8f0ea);
}

:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-control-zoom a {
	border-color: var(--vb-line, #56434d);
}

/* Leaflet's attribution links keep their own #0078a8, and the site's dark
   link rule leaves .leaflet-container alone on purpose. On the page's dark
   ground that blue measured 3.05:1 - two of the only two findings on the
   search and the profile once the tiles were dark. */
:root[data-theme="dark"] .vb-profile__map-canvas .leaflet-control-attribution a {
	color: var(--vb-accent-text, #f2b3c3);
}


/* ----- The moment a business went live (M-058) ------------------------------
   The other end of the checklist above: it appears exactly when the wizard
   stops, so the two are never on the screen together. */
.vb-golive {
	margin: 0 0 18px;
	padding: 16px 18px;
	border: 1px solid var(--vb-primary, #b66a80);
	border-radius: var(--vb-radius-lg, 22px);
	background: linear-gradient(160deg, var(--vb-tint, #f3e2e7), var(--vb-base, #fbf7f2) 70%);
}

.vb-golive__head {
	margin: 0 0 6px;
	font-size: var(--vb-text-lead, 1.0625rem);
}

.vb-golive__lead {
	margin: 0 0 14px;
	max-width: 62ch;
	color: var(--vb-muted, #6b5b60);
}

.vb-golive__field {
	display: block;
	margin: 0 0 6px;
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	color: var(--vb-muted, #6b5b60);
}

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

.vb-golive__url {
	flex: 1 1 22rem;
	min-width: 0;
	padding: 10px 12px;
	font-size: var(--vb-text-body, 1rem);
	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);
}

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

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

.vb-golive__done button {
	font-size: var(--vb-text-small, 0.85rem);
	color: var(--vb-muted, #6b5b60);
	text-decoration: underline;
	cursor: pointer;
	background: none;
	border: 0;
	/* The tap target the rest of the site is measured against. */
	min-height: 44px;
	padding: 0;
}

.vb-golive__done button:hover {
	color: var(--vb-ink, #30272b);
}

/* ===========================================================================
 * Profile hero and the per-industry accent
 * ---------------------------------------------------------------------------
 * Two things live here. The accent is one variable, --vb-cat, set on the
 * profile wrapper from the business's type; every rule below reads that
 * variable rather than naming a colour, so a type is one line and not a
 * stylesheet. The hero is the block the whole redesign is about: name, type,
 * place, what is true about the business and the one thing a reader can do,
 * all in the first screen.
 *
 * The seven tones are computed against the surface they actually sit on, not
 * picked: light with white text 5.2:1 to 7.1:1, dark against --vb-surface
 * 6.6:1 to 11.4:1. The brighter tones of the visual reference fall below AA in
 * light mode and are deliberately not used. Numbers and the rule for where an
 * accent may appear at all: docs/DESIGN-SYSTEM.md.
 * ======================================================================== */

.vb-profile {
	/* The fallback is the brand green: an unknown type is a veterinary
	   practice everywhere else in the code, and it is one here too. */
	--vb-cat: var(--vb-primary-dark, #68192e);
	--vb-cat-soft: var(--vb-primary-soft, #f3e2e7);
	--vb-cat-hover: var(--vb-primary-darker, #300b17);
	--vb-cat-ink: #ffffff;
}

.vb-profile[data-provider-type="vet"]      { --vb-cat: #68192e; --vb-cat-soft: #f3e2e7; --vb-cat-hover: #300b17; }
.vb-profile[data-provider-type="groomer"]  { --vb-cat: #68192e; --vb-cat-soft: #f2e7df; --vb-cat-hover: #300b17; }
.vb-profile[data-provider-type="walker"]   { --vb-cat: #68192e; --vb-cat-soft: #f5e6da; --vb-cat-hover: #300b17; }
.vb-profile[data-provider-type="sitter"]   { --vb-cat: #68192e; --vb-cat-soft: #efe6ec; --vb-cat-hover: #300b17; }
.vb-profile[data-provider-type="boarding"] { --vb-cat: #68192e; --vb-cat-soft: #f2ebe3; --vb-cat-hover: #300b17; }
.vb-profile[data-provider-type="therapy"]  { --vb-cat: #68192e; --vb-cat-soft: #f3e2e7; --vb-cat-hover: #300b17; }
.vb-profile[data-provider-type="trainer"]  { --vb-cat: #68192e; --vb-cat-soft: #f2e7df; --vb-cat-hover: #300b17; }

/* Dark mode turns the relationship round: the tone carries dark text, because
   a 5:1 tone that was readable under white becomes a bruise on #0b1d16. */
:root[data-theme="dark"] .vb-profile {
	--vb-cat: var(--vb-primary, #d9a9b7);
	--vb-cat-soft: #432530;
	--vb-cat-hover: #f2b3c3;
	--vb-cat-ink: var(--vb-surface, #261e23);
}

:root[data-theme="dark"] .vb-profile[data-provider-type="vet"]   { --vb-cat: #d9a9b7; --vb-cat-soft: #432530; --vb-cat-hover: #f2b3c3; }
:root[data-theme="dark"] .vb-profile[data-provider-type="groomer"] { --vb-cat: #d9a9b7; --vb-cat-soft: #3a2c26; --vb-cat-hover: #f2b3c3; }
:root[data-theme="dark"] .vb-profile[data-provider-type="walker"] { --vb-cat: #d9a9b7; --vb-cat-soft: #3d2d25; --vb-cat-hover: #f2b3c3; }
:root[data-theme="dark"] .vb-profile[data-provider-type="sitter"] { --vb-cat: #d9a9b7; --vb-cat-soft: #3a2a36; --vb-cat-hover: #f2b3c3; }
:root[data-theme="dark"] .vb-profile[data-provider-type="boarding"] { --vb-cat: #d9a9b7; --vb-cat-soft: #3a3026; --vb-cat-hover: #f2b3c3; }
:root[data-theme="dark"] .vb-profile[data-provider-type="therapy"] { --vb-cat: #d9a9b7; --vb-cat-soft: #432530; --vb-cat-hover: #f2b3c3; }
:root[data-theme="dark"] .vb-profile[data-provider-type="trainer"] { --vb-cat: #d9a9b7; --vb-cat-soft: #3a2c26; --vb-cat-hover: #f2b3c3; }

/* The calm view keeps one accent for the whole site (part C §38): the colour
   is what tells five industries apart, and telling them apart is not what
   somebody who asked for less stimulation came for. */
:root[data-focus="reduced"] .vb-profile,
:root[data-focus="reduced"][data-theme="dark"] .vb-profile,
:root[data-focus="reduced"] .vb-profile[data-provider-type] {
	--vb-cat: var(--vb-primary-dark, #68192e);
	--vb-cat-soft: var(--vb-primary-soft, #f3e2e7);
	--vb-cat-hover: var(--vb-primary-darker, #300b17);
}

:root[data-focus="reduced"][data-theme="dark"] .vb-profile,
:root[data-focus="reduced"][data-theme="dark"] .vb-profile[data-provider-type] {
	--vb-cat: var(--vb-primary, #d9a9b7);
	--vb-cat-soft: #432530;
	--vb-cat-hover: #f2b3c3;
}


/* --- The head: one card, a picture band on top (03.10.2026) --------------
   Variant A of Aufgabe #184 part 2: one frame around the picture and the
   identity, the band clipped to the card's top corners, the identity on a
   flat surface directly below - no gradient behind the place, no logo
   lifted onto the seam, nothing printed over a picture. Until then a 282px
   photo and a 234px card stood apart (528px at 1280); on a phone the primary
   action started at y=737, under the app bar.

   Heights are presets, not free values (ProfileDisplay::HEAD_CHOICES): an
   own photo 220px on the desktop and 120px on a phone, "compact" 180/100.
   A category picture and its interim surface stay at 220/120 whatever the
   preset: the masters keep their motif in y=365-650 of 1536 x 1024, and a
   1080 x 180 crop shows only y=384-640 (docs/ASSET-REQUESTS.md). */
.vb-profile__hero {
	--vb-band-h: 220px;
	--vb-cat-band-h: 220px;
	--vb-head-pad: 24px;
	display: flex;
	flex-direction: column;
	/* The 24 px to the columns are .vb-profile's 4 px gap and the columns'
	   own 20 px (Aufgabe #211), one sum in one place. */
	margin: 0;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-lg, 22px);
	background: var(--vb-base, #fbf7f2);
}

.vb-profile__hero--compact {
	--vb-band-h: 180px;
}

@media (max-width: 39.99rem) {
	.vb-profile__hero {
		--vb-band-h: 120px;
		--vb-cat-band-h: 120px;
		--vb-head-pad: 16px;
	}

	.vb-profile__hero--compact {
		--vb-band-h: 100px;
	}
}

/* Only the band is clipped: the toggletip of the status mark and every
   focus ring in the identity stay free of the card's corners. */
.vb-profile__band {
	position: relative;
	margin: 0;
	overflow: hidden;
	border-radius: var(--vb-radius-lg, 22px) var(--vb-radius-lg, 22px) 0 0;
	background: var(--vb-surface, #f2e7df);
}

.vb-profile__band--photo .vb-profile__shot {
	margin: 0;
}

.vb-profile__band--photo .vb-profile__shot img {
	display: block;
	inline-size: 100%;
	block-size: var(--vb-band-h);
	object-fit: cover;
}

.vb-profile__bandmedia {
	position: relative;
	block-size: var(--vb-cat-band-h);
}

.vb-profile__bandimg {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

/* The category picture is a neutral master; its type colour comes from
   here (briefing #179): an ordinary overlay of 10% in the light scheme, in
   the dark one a neutral shade of 22% under at most 7% of the colour. The
   overlay never covers text: the chip sits above it, the caption below. */
.vb-profile__band--category[data-media="category"] .vb-profile__bandmedia::after {
	content: "";
	position: absolute;
	inset: 0;
	background: color-mix( in srgb, var(--vb-cat, #68192e) 10%, transparent );
	pointer-events: none;
}

:root[data-theme="dark"] .vb-profile__band--category[data-media="category"] .vb-profile__bandmedia::after {
	background:
		linear-gradient( color-mix( in srgb, var(--vb-cat, #d9a9b7) 7%, transparent ), color-mix( in srgb, var(--vb-cat, #d9a9b7) 7%, transparent ) ),
		rgba( 8, 11, 13, 0.22 );
}

/* Tint switched off in the admin, and the calm view: no colour at all; the
   dark scheme keeps its neutral shade. */
.vb-profile__hero--untinted .vb-profile__band--category[data-media="category"] .vb-profile__bandmedia::after,
:root[data-focus="reduced"] .vb-profile__band--category[data-media="category"] .vb-profile__bandmedia::after {
	background: transparent;
}

:root[data-theme="dark"] .vb-profile__hero--untinted .vb-profile__band--category[data-media="category"] .vb-profile__bandmedia::after,
:root[data-focus="reduced"][data-theme="dark"] .vb-profile__band--category[data-media="category"] .vb-profile__bandmedia::after {
	background: rgba( 8, 11, 13, 0.22 );
}

/* The calm view takes the colour out of the picture as well; its large
   shapes carry the kind of business in grey. */
:root[data-focus="reduced"] .vb-profile__bandimg {
	filter: grayscale( 1 );
}

/* Until a category picture is set: the calm colour of the kind and two
   overlapping circles in its ink at 6 % and 12 %, cut by the right edge
   (Aufgabe #232, graphics lead 04.10.2026). No icon, no drawing, no
   gradient, nothing that reads as a photo; the left stays free for the
   chip. 4:1 on the desktop, 3:1 on a phone, and never taller than the
   240px of the brief's 960 x 240: on a 1200px head 4:1 would be 300px of
   colour, the large empty surface the brief set out to remove. The width
   is set, so the cap shortens the band instead of narrowing it. The
   colours are the kind's tone (business-media.css), light and dark. */
.vb-profile__band[data-media="interim"] .vb-profile__bandmedia {
	overflow: hidden;
	inline-size: 100%;
	block-size: auto;
	max-block-size: 240px;
	aspect-ratio: 4 / 1;
	background: var(--vb-tone-bg, #f3e2e7);
}

.vb-profile__band[data-media="interim"] .vb-profile__bandmedia::before,
.vb-profile__band[data-media="interim"] .vb-profile__bandmedia::after {
	content: "";
	position: absolute;
	inset: auto;
	aspect-ratio: 1 / 1;
	border-radius: 50%;
	background: var(--vb-tone-ink, #68192e);
	pointer-events: none;
}

.vb-profile__band[data-media="interim"] .vb-profile__bandmedia::before {
	inset-block-start: -45%;
	inset-inline-end: -8%;
	block-size: 165%;
	opacity: 0.06;
}

.vb-profile__band[data-media="interim"] .vb-profile__bandmedia::after {
	inset-block-start: 18%;
	inset-inline-end: 13%;
	block-size: 125%;
	opacity: 0.12;
}

@media (max-width: 39.99rem) {
	.vb-profile__band[data-media="interim"] .vb-profile__bandmedia {
		aspect-ratio: 3 / 1;
	}
}

/* The same surface where a picture failed to load (data-media switched
   to "interim" by business-media.js): the category picture's caption goes
   with the picture, and the own photo's box makes room for the surface,
   which waits empty after it until then. */
.vb-profile__band[data-media="interim"] .vb-profile__symbol,
.vb-profile__band[data-media="interim"] > .vb-profile__shot,
.vb-profile__band--photo[data-media="own"] > .vb-profile__bandmedia {
	display: none;
}

/* Tint switched off in the admin, and the calm view: one neutral surface,
   no shapes. */
.vb-profile__hero--untinted .vb-profile__band[data-media="interim"] .vb-profile__bandmedia,
:root[data-focus="reduced"] .vb-profile__band[data-media="interim"] .vb-profile__bandmedia {
	background: var(--vb-surface, #f2e7df);
}

.vb-profile__hero--untinted .vb-profile__band[data-media="interim"] .vb-profile__bandmedia::before,
.vb-profile__hero--untinted .vb-profile__band[data-media="interim"] .vb-profile__bandmedia::after,
:root[data-focus="reduced"] .vb-profile__band[data-media="interim"] .vb-profile__bandmedia::before,
:root[data-focus="reduced"] .vb-profile__band[data-media="interim"] .vb-profile__bandmedia::after {
	content: none;
}

/* "Basisprofil": the band's statement about an entry nobody manages, on an
   opaque surface of its own - never text on a picture. */
.vb-profile__basic {
	position: absolute;
	z-index: 1;
	inset: auto auto 12px var(--vb-head-pad);
	margin: 0;
	padding: 0.2rem 0.75rem;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-base, #fbf7f2);
	color: var(--vb-ink, #30272b);
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	line-height: 1.4;
}

/* "Symbolbild", directly under the picture on an opaque line (#175, owner
   03.10.2026) - visible without a script and in the calm view. */
.vb-profile__symbol {
	box-sizing: border-box;
	min-block-size: 24px;
	margin: 0;
	padding: 0.2rem var(--vb-head-pad);
	border-block-end: 1px solid var(--vb-line, #d9c7c1);
	background: var(--vb-base, #fbf7f2);
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-small, 0.85rem);
	line-height: 1.4;
}

/* The identity. A grid, so the reading order can stay name, action, states
   - on a phone the button then stands right under the name, above the app
   bar - while the desktop puts the action on the right. Items are placed
   explicitly rather than by template areas: an area with no item still
   costs its row gap, and most heads have no slogan and no visit line. */
.vb-profile__ident {
	position: relative;
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	gap: 10px 12px;
	align-items: start;
	padding: var(--vb-head-pad);
}

.vb-profile__ident.has-mark {
	grid-template-columns: auto minmax( 0, 1fr );
}

.vb-profile__ident > * {
	grid-column: 1 / -1;
}

.vb-profile__ident.has-mark > .vb-profile__logo {
	grid-column: 1;
	grid-row: 1;
}

.vb-profile__ident.has-mark > .vb-profile__idtext {
	grid-column: 2;
	grid-row: 1;
}

.vb-profile__idtext {
	display: flex;
	flex-direction: column;
	gap: 6px;
	min-inline-size: 0;
}

@media (min-width: 64rem) {
	.vb-profile__ident {
		grid-template-columns: minmax( 0, 1fr ) auto;
		column-gap: 24px;
	}

	.vb-profile__ident > * {
		grid-column: 1;
	}

	.vb-profile__ident > .vb-profile__idtext {
		grid-row: 1;
	}

	.vb-profile__ident > .vb-profile__trust {
		grid-row: 2;
	}

	.vb-profile__ident > .vb-profile__act {
		grid-column: 2;
		grid-row: 1 / span 2;
		align-self: center;
	}

	.vb-profile__ident.has-mark {
		grid-template-columns: auto minmax( 0, 1fr ) auto;
		column-gap: 16px;
	}

	.vb-profile__ident.has-mark > * {
		grid-column: 2;
	}

	.vb-profile__ident.has-mark > .vb-profile__logo {
		grid-column: 1;
		grid-row: 1 / span 2;
	}

	.vb-profile__ident.has-mark > .vb-profile__act {
		grid-column: 3;
		grid-row: 1 / span 2;
		margin-inline-start: 8px;
	}
}

/* The business's own mark, beside the name: 56px on a phone, 64 on the
   desktop, padding and frame included, on its own light plate so a mark
   drawn for white paper keeps its contrast in both schemes. contain, not
   cover: a logo cropped to fill a square is a different logo, and it is
   somebody else's mark to crop. */
.vb-profile__logo {
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	inline-size: 56px;
	block-size: 56px;
	margin: 0;
	overflow: hidden;
	padding: 4px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	background: #ffffff;
}

.vb-profile__logo img {
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
}

@media (min-width: 64rem) {
	.vb-profile__logo {
		inline-size: 64px;
		block-size: 64px;
	}
}

/* No logo, or one that failed to load: the business's initials in the
   colour of its kind (Aufgabe #232) - 64px on a phone and tablet, 88 on the
   desktop, radius 16, a 2px frame in the card's surface colour. Text, not
   a picture; no "Symbolbild". */
.vb-profile__logo > .vb-initials {
	display: none;
}

.vb-profile__logo[data-media="initials"] {
	inline-size: 64px;
	block-size: 64px;
	padding: 0;
	border: 2px solid var(--vb-base, #fbf7f2);
	border-radius: var(--vb-radius, 16px);
	background: transparent;
}

.vb-profile__logo[data-media="initials"] > .vb-initials {
	display: flex;
	border-radius: var(--vb-radius-sm, 11px);
	font-size: 26px;
	line-height: 32px;
}

:root[data-theme="dark"] .vb-profile__logo[data-media="initials"] {
	border-color: var(--vb-base, #191418);
}

@media (min-width: 64rem) {
	.vb-profile__logo[data-media="initials"] {
		inline-size: 88px;
		block-size: 88px;
	}

	.vb-profile__logo[data-media="initials"] > .vb-initials {
		font-size: 34px;
		line-height: 40px;
	}
}

.vb-profile__hero .vb-profile__type {
	display: inline-flex;
	align-self: flex-start;
	align-items: center;
	gap: 0.3rem;
	margin: 0;
	padding: 0.15rem 0.7rem;
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-cat);
	color: var(--vb-cat-ink, #fff);
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
	letter-spacing: 0.01em;
}

/* 28px on a phone, 36 at 1280, at most 38. No balancing: it spread short
   names over two even lines. A word longer than the line breaks rather than
   running out of the card (measured 03.10.2026: "Kleintierpraxisgemeinschaft"
   at 390px). The mark stays glued to the last word (.vb-namemark). */
.vb-profile__name {
	margin: 0;
	font-family: var(--vb-display, system-ui);
	/* The one heading in Clash Display (Aufgabe #257, token table C):
	   28/34 px on a phone, 36/40 px from 1000 px, weight 600. */
	font-size: var(--vb-profile-h1, 1.75rem);
	line-height: var(--vb-profile-h1-lh, 1.2143);
	font-weight: 600;
	overflow-wrap: break-word;
}

/* The place, in the ink of the name and the weight of a lead: 15.4:1 light,
   14.8:1 dark (#184 B). The pin is decoration; "Cared for by" shares the
   line where one person is the business. */
.vb-profile__where {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 2px 16px;
	margin: 0;
	color: var(--vb-ink, #30272b);
	font-family: var(--vb-body, system-ui);
	font-size: var(--vb-text-lead, 1.0625rem);
	font-weight: 600;
	line-height: 1.4;
}

.vb-profile__place {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

.vb-profile__place svg {
	flex: none;
	color: var(--vb-cat, #68192e);
}

.vb-profile__person {
	font-weight: 400;
}

/* The states, quieter than the action and told apart by more than colour:
   the next appointment is the one filled chip (it is a way in), the
   emergency service the one red one; opening status and rating are text
   with a mark. */
.vb-profile__trust {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px 16px;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-profile__trustitem {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	min-block-size: 28px;
	color: var(--vb-ink, #30272b);
}

.vb-profile__trustitem a {
	display: inline-flex;
	align-items: center;
	min-block-size: var(--vb-tap-min, 24px);
	color: inherit;
	text-underline-offset: 0.15em;
}

/* Never colour alone: each state carries a mark as well, so it survives a
   dark scheme, a forced-colours mode and a reader who sees no difference
   between the green and the red one. */
.vb-profile__trustitem::before { font-weight: 700; }

.vb-profile__trustitem.is-next {
	padding: 0.1rem 0.7rem;
	border: 1px solid var(--vb-cat, var(--vb-accent-text, #68192e));
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-cat-soft, var(--vb-primary-soft, #f3e2e7));
	font-weight: 600;
}

.vb-profile__trustitem.is-next::before { content: "\25F7"; color: var(--vb-cat, var(--vb-accent-text, #68192e)); }

/* Eine Offline-Kopie des Service Workers (data-vb-offline-copy, Review von
   PR #264) zeigt ihren alten naechsten freien Termin nicht - auch ohne
   Skript. booking.js nimmt die Zeile zusaetzlich weg. */
html[data-vb-offline-copy] .vb-profile__trustitem.is-next {
	display: none;
}


.vb-profile__trustitem.is-open { color: var(--vb-success-ink, #176849); }

.vb-profile__trustitem.is-open::before { content: "\2713"; color: var(--vb-success, #176849); }

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

.vb-profile__trustitem.is-closed::before { content: "\2014"; }

.vb-profile__trustitem.is-emergency {
	padding: 0.1rem 0.7rem;
	border: 1px solid var(--vb-danger-text, #b52216);
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-danger-soft, #ffece6);
	color: var(--vb-danger-ink, #b52216);
}

.vb-profile__trustitem.is-emergency::before { content: "\26A0"; }

.vb-profile__trustitem.is-video {
	padding: 0.1rem 0.7rem;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-surface, #f2e7df);
	color: var(--vb-ink, #30272b);
}

.vb-profile__trustitem.is-rating::before { content: "\2605"; color: var(--vb-warn-text, #86520a); }

/* The slogan, where the admin put it in the head; by default it opens the
   description (.vb-profile__slogan). Body size and weight: it is the
   business speaking, not a heading. */
.vb-profile__tagline,
.vb-profile__slogan {
	margin: 0;
	color: var(--vb-text, #30272b);
	font-size: var(--vb-text-body, 1rem);
	font-weight: 400;
}

.vb-profile__intro > .vb-profile__slogan {
	margin-block-end: 0.75rem;
}

/* The action: the one button, then two quiet ones - favourites and the
   way into the photos, no filled pill beside the primary. */
.vb-profile__act {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 4px;
	margin: 0;
}

/* The paint comes from the base button, which reads --vb-cat with the brand
   green as its fallback - written THERE rather than overridden here, because
   profile.css is enqueued before site.css and a rule of equal specificity in
   this file loses. What stays here is the reach. */
a.vb-profile__primary,
button.vb-profile__primary,
.vb-profile__primary {
	min-height: var(--vb-tap-primary, 44px);
}

.vb-profile__act > a.vb-profile__primary {
	display: flex;
	inline-size: 100%;
}

@media (max-width: 63.99rem) {
	.vb-profile__act > a.vb-profile__primary {
		min-height: 48px;
	}
}

.vb-profile__minor {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0 16px;
}

.vb-profile__minor:empty {
	display: none;
}

.vb-profile__minor .vb-favorite {
	margin: 0;
}

.vb-profile__minor .vb-favorite__btn,
.vb-profile__allphotos {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-block-size: var(--vb-tap-primary, 44px);
	padding: 0 4px;
	border: 0;
	border-radius: var(--vb-radius-sm, 11px);
	background: transparent;
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-label, 0.9rem);
	font-weight: 600;
}

.vb-profile__minor .vb-favorite__btn.is-active {
	background: transparent;
	color: var(--vb-warn-text, #86520a);
}

.vb-profile__allphotos {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.vb-profile__allphotos svg {
	flex: none;
}

.vb-profile__visited {
	justify-self: start;
	margin: 0;
}

.vb-profile__ident > .vb-calm-more {
	justify-self: start;
}

/* Calm view: the photo flatter, the identity as it is. What folds is the
   additional states and a slogan in the head - by meaning (.is-extra), not
   by position: "Open now" was the third chip and disappeared behind "Show
   more" (Aufgabe #146). The place, the status mark and its explanation, the
   action, favourites and "Symbolbild" are never folded. "Show more" brings
   the rest back. */
:root[data-focus="reduced"] .vb-profile__hero {
	--vb-band-h: 160px;
}

@media (max-width: 39.99rem) {
	:root[data-focus="reduced"] .vb-profile__hero {
		--vb-band-h: 100px;
	}
}

:root[data-focus="reduced"] [data-vb-calm]:not([data-vb-calm-open]) .vb-profile__trustitem.is-extra {
	display: none;
}

:root[data-focus="reduced"] [data-vb-calm]:not([data-vb-calm-open]) .vb-profile__tagline {
	display: none;
}

:root[data-focus="reduced"] .vb-profile__primary:hover {
	filter: none;
}

:root[data-theme="dark"] .vb-profile__logo {
	/* The plate stays light in both schemes: a mark drawn for white paper
	   disappears on #161b1f, and this is the one surface on the page that
	   belongs to the business rather than to us. */
	border-color: var(--vb-line, #56434d);
}

/* A reader who asked for more contrast gets the borders drawn rather than
   suggested. */
@media (prefers-contrast: more) {
	.vb-profile__hero,
	.vb-profile__basic,
	.vb-profile__trustitem.is-next,
	.vb-profile__trustitem.is-emergency,
	.vb-profile__trustitem.is-video {
		border-width: 2px;
		border-color: currentColor;
	}
}


/* --- The business's own text -------------------------------------------- */

.vb-profile__intro {
	max-width: 68ch;
	margin-bottom: 1.5rem;
}

.vb-profile__intro > :first-child { margin-top: 0; }

.vb-profile__intro > :last-child { margin-bottom: 0; }


/* Today's row in the opening hours. Marked in the browser, because a page
   copy cached at 23:55 and served at 00:05 would point at yesterday. Weight
   and tint carry it for the eye, the word "today" for everyone else - colour
   alone tells a third of readers nothing. */
.vb-profile__hours tr.is-today th,
.vb-profile__hours tr.is-today td {
	font-weight: 600;
	background: var(--vb-cat-soft, var(--vb-primary-soft, #f3e2e7));
}

.vb-hours__today {
	font-weight: 400;
	font-size: var(--vb-text-small, 0.85rem);
	color: var(--vb-muted, #6b5b60);
}

/* Forced colours: the 5px mark beside every section heading is drawn out of a
   background on an empty element, so this mode erases all thirteen of them
   and the page loses its structure. Measured on the profile before the rule
   existed. */
@media (forced-colors: active) {
	.vb-profile__section .vb-profile__sechead::before,
	.vb-profile__section h2::before {
		background: CanvasText;
	}

	/* The check is drawn white on the green square; forced colours turn the
	   square into CanvasText, which is white on a dark scheme. */
	.vb-status--verified .vb-status__shape {
		fill: CanvasText;
	}

	.vb-status--verified .vb-status__mark {
		stroke: Canvas;
	}

	.vb-profile__logo {
		outline: 1px solid CanvasText;
	}
}

/* The rest of the page: the same measured reading width the article body has,
   so a bio does not run the full width of a desktop. */
.vb-staffprofile__bio,
.vb-staffprofile__creds {
	max-width: 68ch;
}


.vb-staffprofile__creds h2 {
	margin-bottom: 14px;
}

/* Where they work, as a card rather than two loose lines under the bio. */
.vb-staffprofile__practice {
	margin-top: 20px;
	padding: 14px 16px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
}

.vb-staffprofile__practice p {
	margin: 0;
}

.vb-staffprofile__practice p + p {
	margin-top: 4px;
	color: var(--vb-muted, #6b5b60);
}

/* The accent is the practice's, so the page carries the same five tones the
   profile does. Set here rather than repeated: .vb-profile already declares
   them, and this is the only other place a business's colour appears. */
.vb-staffprofile[data-provider-type="vet"]      { --vb-cat: #68192e; --vb-cat-soft: #f3e2e7; }
.vb-staffprofile[data-provider-type="groomer"]  { --vb-cat: #68192e; --vb-cat-soft: #f2e7df; }
.vb-staffprofile[data-provider-type="walker"]   { --vb-cat: #68192e; --vb-cat-soft: #f5e6da; }
.vb-staffprofile[data-provider-type="sitter"]   { --vb-cat: #68192e; --vb-cat-soft: #efe6ec; }
.vb-staffprofile[data-provider-type="boarding"] { --vb-cat: #68192e; --vb-cat-soft: #f2ebe3; }
.vb-staffprofile[data-provider-type="therapy"]  { --vb-cat: #68192e; --vb-cat-soft: #f3e2e7; }
.vb-staffprofile[data-provider-type="trainer"]  { --vb-cat: #68192e; --vb-cat-soft: #f2e7df; }

:root[data-theme="dark"] .vb-staffprofile[data-provider-type="vet"]      { --vb-cat: #d9a9b7; --vb-cat-soft: #432530; }
:root[data-theme="dark"] .vb-staffprofile[data-provider-type="groomer"]  { --vb-cat: #d9a9b7; --vb-cat-soft: #3a2c26; }
:root[data-theme="dark"] .vb-staffprofile[data-provider-type="walker"]   { --vb-cat: #d9a9b7; --vb-cat-soft: #3d2d25; }
:root[data-theme="dark"] .vb-staffprofile[data-provider-type="sitter"]   { --vb-cat: #d9a9b7; --vb-cat-soft: #3a2a36; }
:root[data-theme="dark"] .vb-staffprofile[data-provider-type="boarding"] { --vb-cat: #d9a9b7; --vb-cat-soft: #3a3026; }
:root[data-theme="dark"] .vb-staffprofile[data-provider-type="therapy"]  { --vb-cat: #d9a9b7; --vb-cat-soft: #432530; }
:root[data-theme="dark"] .vb-staffprofile[data-provider-type="trainer"]  { --vb-cat: #d9a9b7; --vb-cat-soft: #3a2c26; }

:root[data-focus="reduced"] .vb-staffprofile__head {
	background: var(--vb-base, #fbf7f2);
}

/* ----- Ankerleiste (§6, §12) ---------------------------------------------
   Die Vorlage zeigt an dieser Stelle eine Reiterzeile. Reiter sind es
   trotzdem nicht: §60 verbietet ausdruecklich, fuer die Buchungsentscheidung
   wesentliche Inhalte zu verstecken, und ein Reiter verbirgt vier Fuenftel
   der Seite schon von Bauart. Die Leiste springt, sie verdeckt nichts - wer
   sie nie beruehrt, scrollt wie bisher an allem vorbei. */
.vb-profile__anchors {
	/* Klebt nicht mehr selbst (Aufgabe #211): am Beginn der Hauptspalte steht
	   sie im Fluss; sobald der Name oben verschwunden ist, steht dieselbe
	   Leiste als zweite Zeile der Profilleiste fest (.vb-pbar-on, unten). */
	margin: 0;
	background: var(--vb-base, #fbf7f2);
	border-block: 1px solid var(--vb-line, #d9c7c1);
	/* Auf dem Telefon passen sechs Woerter nicht nebeneinander. Die Leiste
	   scrollt seitlich, die Seite nicht - overscroll-behavior haelt die
	   Geste in der Leiste. */
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
}

.vb-profile__anchors::-webkit-scrollbar {
	display: none;
}

.vb-profile__anchorlist {
	display: flex;
	gap: 2px;
	min-width: max-content;
	margin: 0;
	padding: 0;
	list-style: none;
}

.vb-profile__anchorlink {
	display: flex;
	align-items: center;
	box-sizing: border-box;
	/* §47: das Ziel ist der Finger, nicht die Schriftgroesse. */
	min-height: 44px;
	padding-inline: 14px;
	color: var(--vb-muted, #6b5b60);
	text-decoration: none;
	white-space: nowrap;
	/* Der Strich liegt immer da, nur durchsichtig: sonst waechst die Leiste
	   um 3px, sobald der erste Punkt aktiv wird, und die ganze Seite rutscht. */
	border-block-end: 3px solid transparent;
}

.vb-profile__anchorlink:hover {
	color: var(--vb-ink, #30272b);
}

/* Die Farbe des Gewerbes, wie sie die Vorlage unter dem aktiven Reiter
   zeigt - dasselbe --vb-cat, das schon den Hauptknopf faerbt. */
.vb-profile__anchorlink[aria-current="location"] {
	color: var(--vb-cat, var(--vb-accent-text, #68192e));
	border-block-end-color: var(--vb-cat, var(--vb-accent-text, #68192e));
	/* Unterstreichung und kraeftigere Schrift, nicht die Farbe allein
	   (Aufgabe #211, Analyse 49); aria-current="location" sagt es dem
	   Vorleseprogramm. */
	font-weight: 700;
}

/* Das Sprungziel liegt unter Kopfzeile UND Leiste. Beides haelt
   html:has( .vb-profile__anchors ) mit scroll-padding-top frei (site.css);
   hier steht nur noch die Luft darueber. Die Hoehe beider stand bis zum
   25.09.2026 zusaetzlich hier - die Ueberschrift landete 145-173px unter
   der Leiste statt knapp darunter. */
.vb-profile__anchorpoint {
	scroll-margin-top: 4px;
}

/* ----- Kontakt und Anfahrt (§3, §8; Aufgabe #226) ------------------------
   Anrufen, Nachricht und Route lagen bei y=897, y=1.871 und y=4.054 eines
   5.722px langen Dokuments verstreut. Seit #211 stehen sie am Anfang von
   "Kontakt & Anfahrt", seit #226 in zwei Gruppen (Spezifikation des
   Grafik-Leads, 04.10.2026): "Kontakt" (Anrufen, Nachricht) und "Anfahrt"
   (Ziel, "Wegbeschreibung oeffnen mit:", Google Maps und Waze gleich
   gewichtet, Apple Karten als Textlink). Die gemeinsame Viererreihe liess
   in 358 px nur 83,5 px je Knopf.

   Spalten nach Platz, nicht nach Fensterbreite: zwei Knoepfe nebeneinander
   nur ab 10rem je Knopf (358 px Gruppe: 2 x 175), sonst einer je Zeile;
   zwei Gruppen nebeneinander nur, wenn jede zwei Spalten hat. Ein einzelner
   Kontaktknopf spannt seine Gruppe. Farben aus der Spezifikation, hell als
   Standard, dunkel nur ueber [data-theme="dark"]. Radius 11 px statt der
   12 der Spezifikation: die naechste Stufe der Radius-Skala. */
.vb-reach {
	--vb-reach-bg: #fffcf8;
	--vb-reach-ink: #30272b;
	--vb-reach-accent: #68192e;
	--vb-reach-line: #8c716e;
	--vb-reach-hover: #f3e2e7;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(20rem + 8px)), 1fr));
	gap: 16px;
	align-items: start;
	margin: 12px 0 0;
	color: var(--vb-reach-ink);
	font-size: var(--vb-text-body, 1rem);
	line-height: 1.5rem;
}

:root[data-theme="dark"] .vb-reach {
	--vb-reach-bg: #30262c;
	--vb-reach-ink: #f8f0ea;
	--vb-reach-accent: #f2b3c3;
	--vb-reach-line: #a68a96;
	--vb-reach-hover: #432530;
}

.vb-reach__group {
	min-inline-size: 0;
	max-inline-size: 24rem;
}

.vb-reach__title,
.vb-reach__dest,
.vb-reach__lead,
.vb-reach__note,
.vb-reach__more {
	margin: 0;
	color: var(--vb-reach-ink);
	font-size: var(--vb-text-body, 1rem);
	line-height: 1.5rem;
	overflow-wrap: anywhere;
	-webkit-hyphens: auto;
	hyphens: auto;
}

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

.vb-reach__dest,
.vb-reach__lead,
.vb-reach__note {
	margin-block-start: 4px;
}

/* Eine Adresse wird nicht getrennt ("Luxem-bourg"); nur ein Wort, das
   allein nicht passt, bricht. */
.vb-reach__dest {
	-webkit-hyphens: manual;
	hyphens: manual;
}

.vb-reach__dest > span {
	display: block;
}

.vb-reach__btns {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
	gap: 8px;
	margin: 8px 0 0;
	padding: 0;
	list-style: none;
}

.vb-reach__btns > li {
	min-inline-size: 0;
	margin: 0;
}

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

	.vb-reach__group--directions .vb-reach__btns:has( > li:last-child:nth-child(2) ) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.vb-reach__group--directions .vb-reach__btns:has( > li:only-child ) {
		grid-template-columns: minmax(0, 1fr);
	}
}

a.vb-reach__btn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	box-sizing: border-box;
	block-size: 100%;
	min-block-size: 48px;
	padding: 10px 12px;
	color: var(--vb-reach-ink);
	font-size: var(--vb-text-body, 1rem);
	font-weight: 400;
	line-height: 1.5rem;
	text-align: center;
	text-decoration: none;
	overflow-wrap: anywhere;
	-webkit-hyphens: auto;
	hyphens: auto;
	background: var(--vb-reach-bg);
	border-width: 1px;
	border-style: solid;
	border-color: var(--vb-reach-line);
	border-radius: var(--vb-radius-sm, 11px);
	box-shadow: none;
}

a.vb-reach__btn:hover,
a.vb-reach__btn:active {
	color: var(--vb-reach-ink);
	text-decoration: none;
	background: var(--vb-reach-hover);
}

a.vb-reach__btn svg {
	flex: none;
	inline-size: 20px;
	block-size: 20px;
	color: var(--vb-reach-accent);
}

.vb-reach__more {
	margin-block-start: 4px;
}

a.vb-reach__link {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
	min-inline-size: 44px;
	color: var(--vb-reach-accent);
	text-decoration: underline;
	text-underline-offset: 2px;
}

a.vb-reach__link:hover,
a.vb-reach__link:active {
	color: var(--vb-reach-accent);
	text-decoration-thickness: 2px;
}

a.vb-reach__btn:focus-visible,
a.vb-reach__link:focus-visible {
	outline: 2px solid var(--vb-reach-accent);
	outline-offset: 2px;
	box-shadow: none;
}

/* ----- Fingerziele, die keine Regel hatten (§47, WCAG 2.5.8) -------------
   Gemessen auf 390px: der Teamname 106x20, "Diesen Inhalt melden" 151x20.
   Beides sind eigenstaendige Ziele, keine Verweise mitten im Satz - fuer die
   gilt die Abstandsausnahme, fuer diese nicht. 24px ist das Minimum der
   Richtlinie; mehr wuerde die Zeilen auseinanderziehen, die sie tragen. */
.vb-team__name a,
.vb-report-link {
	display: inline-flex;
	align-items: center;
	min-height: var(--vb-tap-min, 24px);
}

/* Das Einwilligungskaestchen der Aufenthaltsbuchung mass 12x12 - das letzte
   Ziel unter 24px auf den fuenf Profilen. Ein Kaestchen ist kein Verweis im
   Satz, also greift die Abstandsausnahme nicht; und an dieser Stelle steht
   die Einwilligung zwischen dem Leser und der Buchung. */
.vb-staybook__consent input[type="checkbox"] {
	flex: none;
	inline-size: var(--vb-tap-min, 24px);
	block-size: var(--vb-tap-min, 24px);
}

/* ----- Das zweite Buchungssystem (§32) -----------------------------------
   Eine Tierpension bucht Aufenthalte. Stand daneben der Terminkalender
   offen, forderten zwei gleich laute Formulare dieselbe Entscheidung -
   gemessen bei y=705 und y=1.788 desselben Profils. Der Kalender bleibt,
   aber leiser und zu: eine zweite Tuer, nicht ein zweiter Eingang. */
.vb-profile__alsobooks {
	margin-block-start: 14px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
}

.vb-profile__alsobooks > summary {
	display: flex;
	align-items: center;
	box-sizing: border-box;
	min-height: 44px;
	padding-inline: 14px;
	color: var(--vb-muted, #6b5b60);
	cursor: pointer;
	list-style: none;
}

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

/* Ein Pfeil, der sich dreht - sonst sieht eine Zusammenfassung ohne Marker
   wie ein Satz aus, den man nicht anfassen kann. */
.vb-profile__alsobooks > summary::after {
	content: "";
	flex: none;
	margin-inline-start: auto;
	inline-size: 9px;
	block-size: 9px;
	border-inline-end: 2px solid currentColor;
	border-block-end: 2px solid currentColor;
	transform: rotate(45deg) translate(-2px, -2px);
}

.vb-profile__alsobooks[open] > summary::after {
	transform: rotate(-135deg) translate(-2px, -2px);
}

.vb-profile__alsobooks > summary:hover {
	color: var(--vb-ink, #30272b);
}

/* Der Kalender innen traegt seinen eigenen Rahmen; zwei ineinander sind
   die "Box in Box", die schon die Suchkarte einmal eingesammelt hat. */
.vb-profile__alsobooks .vb-booking {
	border: 0;
	margin-block: 0;
}

/* ----- Notfallblock (§28, §45) -------------------------------------------
   Eigener Block, oben in der Entscheidungsspalte, vor jedem Kalender. Rot
   und nicht gruen: §45 verlangt ausdruecklich, dass ein Notfallhinweis
   nicht wie eine gewoehnliche Handlungsaufforderung aussieht - gruen heisst
   auf dieser Seite "verfuegbar, offen, erfolgreich".

   Ohne eigene Dunkelregeln: die drei Gefahr-Token drehen sich im Schema
   schon selbst (--vb-danger-soft wird #401812, --vb-danger-text #ff9a86).
   Eine zweite Kopie derselben Farben waere genau der Fehler, den der Fuss
   einmal gemacht hat. Der Rand wird aus dem Textton gemischt, statt ein
   Token zu erfinden, das niemand sonst braucht. */
.vb-profile__emergency {
	margin-block-end: 16px;
	padding: 14px 16px;
	background: var(--vb-danger-soft, #ffece6);
	border: 1px solid color-mix( in srgb, var(--vb-danger-text, #b52216) 40%, var(--vb-base, #fbf7f2) );
	border-radius: var(--vb-radius, 16px);
}

.vb-profile__emergency-title {
	margin: 0 0 6px;
	font-size: 1.1rem;
	color: var(--vb-danger-text, #b52216);
}

.vb-profile__emergency p {
	margin: 0 0 12px;
	color: var(--vb-ink, #30272b);
}

a.vb-profile__emergency-call {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	box-sizing: border-box;
	min-height: 48px;
	inline-size: 100%;
	/* Die Nummer ist die Handlung, also traegt sie Ziffernbreite - sonst
	   tanzt sie beim Vorlesen und beim Abtippen. */
	font-variant-numeric: tabular-nums;
	font-weight: 600;
}

/* Gefuellt, nicht umrandet: .vb-btn--danger traegt als Flaeche genau das
   --vb-danger-soft, auf dem dieser Block schon steht - der Knopf verschwand
   also in seinem eigenen Grund und blieb als Umriss zurueck. Fuer die eine
   Handlung, auf die es in einem Notfall ankommt, zu wenig.

   Spezifitaet bewusst 0-3-1: die Grundregel steht in site.css, und welche
   der beiden Dateien zuletzt geladen wird, haengt an der Seite. Ein Rennen
   um die Reihenfolge hat dieses Projekt schon einmal verloren.

   Im dunklen Schema dreht sich das Verhaeltnis um, wie ueberall hier: ein
   Ton, der unter Weiss lesbar war, traegt auf #0b1d16 dunkle Schrift. */
.vb-profile .vb-profile__emergency a.vb-profile__emergency-call,
.vb-profile .vb-profile__emergency a.vb-profile__emergency-call:hover {
	--vb-emergency-ink: #ffffff;
	color: var(--vb-emergency-ink);
	background: var(--vb-danger-text, #b52216);
	border-color: var(--vb-danger-text, #b52216);
}

:root[data-theme="dark"] .vb-profile .vb-profile__emergency a.vb-profile__emergency-call,
:root[data-theme="dark"] .vb-profile .vb-profile__emergency a.vb-profile__emergency-call:hover {
	--vb-emergency-ink: var(--vb-surface, #261e23);
}

/* ----- Faltbare Gruppen (Neuroinklusives Mandat) -------------------------
   Galerie und Umgebungsliste sind zusammenklappbar. Offen in der
   gewoehnlichen Ansicht und offen ohne jedes Skript - ein Leser ohne
   JavaScript sieht alles. profile-focus.js schliesst sie nur, wenn die
   ruhige Ansicht gewaehlt ist ("lange Galerien werden zu einer Vorschau",
   "Related Listings werden standardmaessig eingeklappt").

   Die Ueberschrift bleibt eine h2 IM summary: die Gliederung der Seite
   aendert sich dadurch nicht, und aria-labelledby zeigt weiter auf sie. */
.vb-profile__fold > summary {
	display: flex;
	align-items: center;
	gap: 8px;
	box-sizing: border-box;
	min-height: 44px;
	cursor: pointer;
	list-style: none;
}

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

.vb-profile__fold > summary > h2,
.vb-profile__fold > summary > h3 {
	margin: 0;
}

.vb-profile__fold > summary::after {
	content: "";
	flex: none;
	inline-size: 9px;
	block-size: 9px;
	border-inline-end: 2px solid var(--vb-muted, #6b5b60);
	border-block-end: 2px solid var(--vb-muted, #6b5b60);
	transform: rotate(-135deg) translate(-2px, -2px);
}

.vb-profile__fold:not([open]) > summary::after {
	transform: rotate(45deg) translate(-2px, -2px);
}

/* Die Ankerleiste zeigt, wohin sie weitergeht (A18, P14): die Kante, hinter
   der noch Punkte liegen, blendet aus. profile-anchors.js setzt data-edge. */
.vb-profile__anchors[data-edge="start"] {
	-webkit-mask-image: linear-gradient( to right, #000 calc( 100% - 40px ), transparent );
	mask-image: linear-gradient( to right, #000 calc( 100% - 40px ), transparent );
}

.vb-profile__anchors[data-edge="end"] {
	-webkit-mask-image: linear-gradient( to left, #000 calc( 100% - 40px ), transparent );
	mask-image: linear-gradient( to left, #000 calc( 100% - 40px ), transparent );
}

.vb-profile__anchors[data-edge="middle"] {
	-webkit-mask-image: linear-gradient( to right, transparent, #000 40px, #000 calc( 100% - 40px ), transparent );
	mask-image: linear-gradient( to right, transparent, #000 40px, #000 calc( 100% - 40px ), transparent );
}

/* Das Rueckrufformular gefaltet (A15): eine Zeile mit Weg, das Formular erst
   auf Wunsch. */
.vb-callback__fold > summary {
	display: flex;
	align-items: center;
	min-height: var(--vb-tap-primary, 44px);
	font-weight: 600;
	color: var(--vb-accent-text, #68192e);
	cursor: pointer;
}

.vb-callback__fold[open] > summary {
	margin-block-end: 0.75rem;
}

.vb-callback__fold > summary h2,
.vb-callback__fold > summary h3 {
	margin: 0;
	font: inherit;
	color: inherit;
}

/* Wie im Buchungsformular: WebKit rechnet min-height auf einem nativen
   select nicht mit - das Unterkunftsfeld stand auf iOS 27px hoch (A8). */
.vb-staybook__form select {
	appearance: none;
	-webkit-appearance: none;
	padding-inline-end: 2.2rem;
	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 0.75rem center;
	background-size: 16px 16px;
}

:root[data-theme="dark"] .vb-staybook__form select {
	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='%23a3b1b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6l6 -6'/%3E%3C/svg%3E");
}

/* Unterkuenfte und weitere Leistungen als zwei benannte Gruppen (PB5). */
.vb-services__group {
	margin: 1rem 0 0.4rem;
	font-size: var(--vb-text-body, 1rem);
	color: var(--vb-muted, #6b5b60);
}

.vb-services__group:first-of-type {
	margin-top: 0;
}

/* Abschnittsueberschriften nach der Tabelle (Nr. 24): H2 20-22 / 22-25 /
   24-28px, H3 ab 18px. Vorher ueberall 23,2 und 16px. */
.vb-profile .vb-profile__section .vb-profile__sechead,
.vb-profile .vb-profile__section h2 {
	font-size: clamp( 1.25rem, 1.1rem + 0.5vw, 1.6rem );
}

.vb-profile .vb-profile__section h3,
.vb-profile .vb-profile__section .vb-profile__subhead {
	font-size: 1.15rem;
}

/* "Anfahrt & Parken" and "Barrierefreiheit" are level 4 inside "Kontakt &
   Anfahrt" (Aufgabe #221) and level 3 on a basic entry: one look for both,
   the browser's h4 margins are a third larger. */
.vb-profile .vb-profile__section .vb-profile__subhead {
	margin-block: 1em;
}

/* Land und Postleitzahl nebeneinander, der Hinweis darunter ueber beide. */
.vb-account__homeplace {
	display: grid;
	grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1fr );
	gap: 0 12px;
}

.vb-account__homeplace .vb-account__hint {
	grid-column: 1 / -1;
	margin-top: 4px;
}


/* ----- Status am Namen (02.10.2026) -----------------------------------------
   Ein Zeichen, drei Formen: Haekchen auf eigenem, leicht gedrehtem Quadrat
   (verifiziert), "i" im Kreis (Verzeichniseintrag), Uhr (in Pruefung). Die
   Form unterscheidet, nicht die Farbe; der Name kommt aus dem versteckten
   Text im Knopf, die Erklaerung aus dem Toggletip (profile-status.js).
   Bleibt in der Ruhigen Ansicht stehen: Status ist Information. */
.vb-profile__namerow {
	position: relative;
}

.vb-status {
	/* Groesser und auf Hoehe der Schrift (Inhaber 03.10.2026): 0,85 em des
	   Namens, mindestens 24, hoechstens 34 px; die Mitte des Zeichens auf
	   der Mitte der Grossbuchstaben (1cap), nicht unter der Grundlinie. */
	--vb-status-size: clamp( 24px, 0.85em, 34px );
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: calc( var(--vb-status-size) + 4px );
	block-size: calc( var(--vb-status-size) + 4px );
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: var(--vb-radius-sm, 11px);
	background: transparent;
	color: var(--vb-muted, #6b5b60);
	font: inherit;
	line-height: 1;
	vertical-align: calc( ( 0.7em - var(--vb-status-size) - 4px ) / 2 );
	vertical-align: calc( ( 1cap - var(--vb-status-size) - 4px ) / 2 );
	cursor: pointer;
}

/* Trefferflaeche mindestens 44 x 44, ohne die Zeile hoeher zu machen. */
.vb-status::after {
	content: "";
	position: absolute;
	inset: min( -6px, calc( ( var(--vb-status-size) - 40px ) / 2 ) );
}

.vb-status: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));
}

.vb-status:hover,
.vb-status[aria-expanded="true"] {
	background: color-mix( in srgb, currentColor 10%, transparent );
}

.vb-status__icon {
	display: block;
	inline-size: var(--vb-status-size);
	block-size: var(--vb-status-size);
}

.vb-status__shape,
.vb-status__mark {
	stroke: currentColor;
}

.vb-status__dot {
	fill: currentColor;
}

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

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

.vb-status--verified .vb-status__mark {
	stroke: #ffffff;
}

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

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

.vb-status__name {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	overflow: hidden;
	white-space: nowrap;
	clip-path: inset( 50% );
}

.vb-status__tip {
	position: absolute;
	z-index: 20;
	box-sizing: border-box;
	inset-block-start: calc( 100% + 0.35rem );
	inset-inline-start: 0;
	inline-size: max-content;
	max-inline-size: min( 24rem, 100% );
	padding: 0.7rem 0.9rem;
	border: 1px solid var(--vb-line-strong, #c7b1aa);
	border-inline-start: 4px solid var(--vb-muted, #6b5b60);
	border-radius: var(--vb-radius-sm, 11px);
	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));
	font-family: var(--vb-body, system-ui);
	font-size: var(--vb-text-small, 0.9rem);
	font-weight: 400;
	line-height: 1.5;
}

.vb-status__tip--verified {
	border-inline-start-color: var(--vb-accent-text, #68192e);
}

:root[data-theme="dark"] .vb-status__tip--verified {
	border-inline-start-color: var(--vb-primary, #d9a9b7);
}

.vb-status__text,
.vb-status__more {
	margin: 0;
}

.vb-status__more {
	margin-block-start: 0.4rem;
}

.vb-status__more a {
	color: var(--vb-accent-text, #68192e);
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

/* ----- Basisprofil nach der Vorlage des Inhabers (02.10.2026, #175 Teil 2) --
   Drei Bausteine fuer Eintraege, die niemand verwaltet: der Text ueber den
   Eintrag, der Leistungs-Abschnitt mit seinem Leer-Satz und der Uebernahme-Kasten in der
   Seitenspalte. Nur Tokens, damit Dunkelmodus und Ruhige Ansicht sie mit
   umstellen; nichts davon wird in der Ruhigen Ansicht versteckt. */

/* Der Text spricht ueber den Eintrag, nicht fuer den Betrieb - die Zeile
   darunter sagt, von wem er stammt. Ruhig, in Lesebreite. */
.vb-profile__about-text {
	margin: 0;
	max-width: 68ch;
	line-height: 1.6;
}

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

.vb-profile__services-pending {
	margin: 12px 0 0;
	color: var(--vb-muted, #6b5b60);
}

.vb-profile__services--basic h2 + .vb-profile__services-pending {
	margin-top: 0;
}

/* Die Adresse mit ihrem Zeichen, wie im Standort-Kasten der Vorlage. */
.vb-profile__access .vb-profile__address {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}

.vb-profile__access .vb-profile__address svg {
	flex: none;
	margin-top: 2px;
	color: var(--vb-accent-text, #68192e);
}

.vb-profile__mapslink {
	margin: 0 0 6px;
}

/* Der Uebernahme-Kasten: eine Karte in der Seitenspalte, Knopf ueber die
   ganze Breite, drei Vorteile mit Haken. Die Karte traegt keinen Schatten
   und keine Bewegung - sie steht ruhig neben dem Inhalt. */
.vb-profile__claimbox {
	margin: 16px 0 0;
	padding: 20px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-base, #fbf7f2);
}

.vb-profile__claimbox-title {
	margin: 0 0 8px;
	font-size: 1.15rem;
	font-weight: 700;
	line-height: 1.3;
}

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

.vb-profile__claimbox-act {
	margin: 16px 0 0;
}

.vb-profile__claimbox-btn {
	box-sizing: border-box;
	width: 100%;
	justify-content: center;
}

.vb-profile__claimbox-list {
	display: grid;
	gap: 8px;
	margin: 16px 0 0;
	padding: 0;
	list-style: none;
	font-size: var(--vb-text-label, 0.9rem);
}

.vb-profile__claimbox-list li {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}

.vb-profile__claimbox-list svg {
	flex: none;
	margin-top: 2px;
	color: var(--vb-accent-text, #68192e);
}

.vb-profile__claimbox .vb-profile__claimmore {
	margin: 14px 0 0;
	font-size: var(--vb-text-small, 0.85rem);
}


/* ----- Profilleiste (Aufgabe #211) ----------------------------------------
   Inhaber 03.10.2026: "Beim Runterscrollen z. B. den Namen des Betriebs oben
   fixieren". Der Seitenkopf scrollt auf dem Profil weg; sobald die
   Unterkante des Namens den oberen Rand passiert, steht oben eine
   Namenszeile (56 px) und darunter die Ankerleiste der Seite (44 px) -
   zusammen 100 px, deckend, je eine 1-px-Linie, kein Glas, keine Bewegung.
   Die Hoehen meldet profile-bar.js an den gemeinsamen Hoehenvertrag
   (sticky-stack.js); Box, Sprungziele, Ankerverfolgung und Pfotenspur
   lesen nur ihn. */

/* Auf dem Profil scrollt der Seitenkopf weg; die Profilleiste uebernimmt
   die Orientierung (Analyse C.36). */
.single-vb_practice .vb-site-header {
	position: relative;
	top: auto;
}

/* Breite der Seite (Analyse 52-55): bis 1279 px die volle Breite zwischen
   den Raendern (952 bei 1000), ab 80rem 90 % und hoechstens 1200 px
   (1152 bei 1280, 1200 bei 1440). Nur auf dem Profil; das Theme haelt
   sonst 1080. Spezifischer als die Layout-Regel von WordPress. */
@media (min-width: 80rem) {
	.vb-main.vb-practice > .entry-content.wp-block-post-content {
		max-width: min( 90vw, 1200px );
	}

	.vb-main.vb-practice > .entry-content.wp-block-post-content > * {
		max-width: none;
	}
}

/* 86, one above the anchor row (85) under it: the name row's status
   explanation hangs down over that row, and with equal values the row,
   later in the document, painted over its top 40 px - the tip's own
   z-index cannot leave this stacking context (review of PR #216). Below the
   menus and the open navigation (90 and up), above the paw trail (80). */
.vb-profilebar {
	position: fixed;
	inset-block-start: var(--vb-sticky-admin, 0px);
	inset-inline: 0;
	z-index: 86;
	box-sizing: border-box;
	block-size: 56px;
	background: var(--vb-base, #fbf7f2);
	box-shadow: inset 0 -1px 0 var(--vb-line, #d9c7c1);
}

.vb-profilebar__inner {
	position: relative;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	gap: 10px;
	block-size: 100%;
	padding-inline: var(--vb-pbar-inset, 16px);
	min-inline-size: 0;
}

.vb-profilebar__logo,
.vb-profilebar__icon {
	flex: none;
	box-sizing: border-box;
	inline-size: 32px;
	block-size: 32px;
}

.vb-profilebar__logo {
	padding: 2px;
	object-fit: contain;
	background: #ffffff;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
}

.vb-profilebar__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: var(--vb-cat-soft, #f3e2e7);
	color: var(--vb-cat, #68192e);
}

.vb-profilebar__namerow {
	display: flex;
	align-items: center;
	gap: 6px;
	min-inline-size: 0;
	margin: 0;
	font-size: 1.125rem;
	line-height: 1.25;
}

/* Eine Zeile; ein langer Name endet mit Auslassung, der Link traegt ihn
   ganz (Vorlesen, Tooltip des Browsers bleibt der Text). 44 px Trefferhoehe. */
.vb-profilebar__name {
	display: block;
	min-inline-size: 0;
	padding-block: 11px;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	color: var(--vb-ink, #30272b);
	font-family: var(--vb-display, inherit);
	font-weight: 700;
	text-decoration: none;
}

.vb-profilebar__name:hover {
	text-decoration: underline;
}

.vb-profilebar .vb-status {
	--vb-status-size: 24px;
	flex: none;
	vertical-align: middle;
}

/* Die Erklaerung haengt unter der Leiste, nicht darin: die Zeile bleibt
   56 px hoch. */
.vb-profilebar .vb-status__tip {
	inset-block-start: calc( 100% + 4px );
	inset-inline-start: var(--vb-pbar-inset, 16px);
	max-inline-size: min( 24rem, calc( 100vw - 32px ) );
}

.vb-profilebar__chip {
	flex: none;
	padding: 2px 10px;
	border: 1px solid var(--vb-line-strong, #c7b1aa);
	border-radius: var(--vb-radius-pill, 999px);
	background: var(--vb-surface, #f2e7df);
	color: var(--vb-ink, #30272b);
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	white-space: nowrap;
}

.vb-profilebar__open {
	flex: none;
	color: var(--vb-muted, #6b5b60);
	font-size: var(--vb-text-small, 0.9rem);
	white-space: nowrap;
}

/* Telefon: Name und Kennzeichnung; der Oeffnungsstatus und - unter 360 px -
   Zeichen und Logo nur, wo Platz ist (Analyse 39). */
@media (max-width: 39.99rem) {
	.vb-profilebar__open {
		display: none;
	}
}

@media (max-width: 22.49rem) {
	.vb-profilebar__logo,
	.vb-profilebar__icon,
	.vb-profilebar .vb-status {
		display: none;
	}
}

/* Der Platz der Ankerleiste im Dokument bleibt, waehrend sie oben steht:
   nichts darunter rutscht nach (Analyse 48). 44 px Ziele plus je 1 px Linie. */
.vb-profile__anchorslot {
	min-block-size: 46px;
	margin-block: 0 clamp( 1rem, 2vw, 1.75rem );
}

html.vb-pbar-on .vb-profile__anchorbar {
	position: fixed;
	inset-block-start: calc( var(--vb-sticky-admin, 0px) + 56px );
	inset-inline: 0;
	z-index: 85;
	box-sizing: border-box;
	block-size: 44px;
	padding-inline: var(--vb-pbar-inset, 16px);
	background: var(--vb-base, #fbf7f2);
	box-shadow: inset 0 -1px 0 var(--vb-line, #d9c7c1);
}

/* Bei offener Bildschirmtastatur (Aufgabe #222, B9) geht die feste
   Ankerzeile; die Namenszeile mit dem Zurueck-Knopf bleibt. Ihr Platz im
   Dokument (.vb-profile__anchorslot) bleibt ebenso - nichts rutscht. */
html.vb-pbar-on[data-vb-keyboard] .vb-profile__anchorbar {
	display: none;
}

/* Oben deckt die Zeile, nicht die Leiste: deren seitliches Ausblenden
   (data-edge) maskiert dann nur die Woerter, nie die Flaeche dahinter. */
html.vb-pbar-on .vb-profile__anchors {
	border-block: 0;
	background: transparent;
}

/* Der Aufenthalts-Starter einer Pension (Aufgabe #211): eine Aktion in voller
   Breite der Box; das Formular steht nach "Unterkuenfte & Preise". */
.vb-profile__staystart {
	margin: 0 0 8px;
}

.vb-profile__staystart-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	inline-size: 100%;
	min-block-size: var(--vb-tap-primary, 44px);
}


/* ----- Abschnitte ordnen (Aufgabe #245) ------------------------------------
   Analyse des Grafik-Leads vom 03.10.2026, B18-B35. Alles mit Tokens, also
   hell als Standard und dunkel ueber [data-theme="dark"] ohne eigene Regel;
   keine Bewegung, damit die Ruhige Ansicht nichts zu daempfen hat. */

/* "Mehr lesen" unter einer langen Beschreibung. profile-more.js setzt
   .is-clamped nur, wenn der Text laenger ist als die Klammer; ohne Skript
   steht er ganz da. Die Blende ist eine Flaeche in der Seitenfarbe, kein
   Glas. */
.vb-profile__intro.is-clamped {
	position: relative;
	max-height: 10.5em;
	overflow: hidden;
}

.vb-profile__intro.is-clamped::after {
	content: "";
	position: absolute;
	inset: auto 0 0;
	block-size: 3em;
	background: linear-gradient( to bottom, transparent, var(--vb-base, #fbf7f2) );
	pointer-events: none;
}

.vb-profile__intro.is-clamped + .vb-profile__more {
	margin-block-start: 4px;
}

.vb-profile__more {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	margin: 0 0 1.5rem;
	padding: 0;
	border: 0;
	background: none;
	color: var(--vb-accent-text, #68192e);
	font: inherit;
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 0.15em;
	cursor: pointer;
}

.vb-profile__intro:has( + .vb-profile__more:not([hidden]) ) {
	margin-bottom: 0;
}

@media print {
	.vb-profile__intro.is-clamped {
		max-height: none;
		overflow: visible;
	}

	.vb-profile__intro.is-clamped::after,
	.vb-profile__more {
		display: none;
	}
}

/* Ausstattung als letzter Teil der Uebersicht. */
.vb-profile__overview > .vb-profile__badges {
	margin-block-start: 8px;
}

/* Qualifikationen je Person, eingeklappt: eine Zeile mit Zahl, darunter
   alle Eintraege. Trefferflaeche 44 px. */
.vb-team__qualfold {
	margin: 0.35rem 0 0;
}

.vb-team__qualfold > summary {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: 44px;
	color: var(--vb-accent-text, #68192e);
	font-size: var(--vb-text-small, 0.85rem);
	font-weight: 600;
	cursor: pointer;
	list-style: none;
}

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

.vb-team__qualfold > summary::after {
	content: "";
	inline-size: 7px;
	block-size: 7px;
	border-inline-end: 2px solid currentColor;
	border-block-end: 2px solid currentColor;
	transform: rotate(45deg) translate(-2px, -2px);
}

.vb-team__qualfold[open] > summary::after {
	transform: rotate(-135deg) translate(-2px, -2px);
}

/* Weiterbildung und Auszeichnungen unter den Teamkarten. */
.vb-team > .vb-creds__fold,
.vb-team > .vb-team__awards {
	margin-block-start: 24px;
}

.vb-team__awards > h3 {
	margin: 0 0 8px;
}

/* Bring- und Abholzeiten der Pension bei den Unterkuenften, sichtbar
   getrennt von den Oeffnungszeiten: eigene Flaeche, eigener Titel, ein Satz
   dazu. */
.vb-stayfacts {
	margin: 16px 0 24px;
	padding: 16px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius, 16px);
	background: var(--vb-surface, #f2e7df);
}

.vb-stayfacts__title {
	margin: 0 0 4px;
	font-size: 1.15rem;
	font-weight: 700;
}

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

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

.vb-stayfacts .vb-profile__facts-row {
	margin: 0 0 6px;
}

.vb-stayfacts .vb-profile__bring {
	margin: 0;
}

/* Bewertungen ohne Bewertungen: eine Statuszeile statt eines Abschnitts. */
.vb-reviews__status {
	margin: 0;
	color: var(--vb-muted, #6b5b60);
}

.vb-reviews__status .vb-reviews__rules {
	margin-inline-start: 6px;
}

.vb-reviews__status--demo {
	display: inline-block;
	padding: 6px 12px;
	border: 1px solid var(--vb-line, #d9c7c1);
	border-radius: var(--vb-radius-sm, 11px);
	color: var(--vb-ink, #30272b);
	font-weight: 600;
}

/* Das Uebernahme-Formular im Kasten: keine Karte in der Karte, die Zeile
   zum Aufklappen sieht aus wie der Knopf, der sie vorher war. */
.vb-profile__claimbox .vb-claim {
	max-width: none;
	margin: 16px 0 0;
	padding: 0;
	border: 0;
	background: none;
}

.vb-profile__claimbox .vb-claim__details > summary {
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	min-height: 48px;
	margin: 0;
	padding: 10px 16px;
	border: 1px solid var(--vb-cat, var(--vb-accent-text, #68192e));
	border-radius: var(--vb-radius-sm, 11px);
	background: var(--vb-cat, var(--vb-primary-dark, #68192e));
	color: var(--vb-cat-ink, #fff);
	font-weight: 600;
	text-align: center;
	list-style: none;
}

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

.vb-profile__claimbox .vb-claim__details[open] > summary {
	margin-block-end: 12px;
}

.vb-profile__claimbox .vb-claim__details > summary:focus-visible {
	outline: 3px solid var(--vb-focus-color, #205f9c);
	outline-offset: 2px;
}


/* ===== Profil-Design-System (Aufgabe #257) ================================
   Paket 5 der Gesamtanalyse (#211), Token-Tabelle der Detailanalyse vom
   04.10.2026 (Abschnitt C). Eine Quelle fuer Schriftstufen, Abstaende,
   Karten, Statusfarben, Zeichen und Trefferflaechen auf dem Profil; die
   Regeln darunter lesen nur noch diese Tokens. Hell ist der Standard, dunkel
   dreht ueber [data-theme="dark"]. Bewusste Abweichungen von der Tabelle:
   - Fliesstext bleibt die Seitengroesse (WordPress "medium": 16 px am
     Telefon, 17 px am Desktop) statt fest 16 px - wesentlicher Text wird
     nicht kleiner, und das Profil waere sonst die eine Seite mit kleinerer
     Schrift. Die Zeilenhoehe ist die der Tabelle, proportional (26/16).
   - Meta 14 px und Caption 12 px liegen auf den naechsten Stufen der
     Kleinskala (--vb-text-label 14,4 px, --vb-text-caption 12,48 px): die
     Skala hat fuenf Stufen und ist per Test verbindlich (SurfaceScaleTest).
   - Radien: Feld/Knopf 11 px (--vb-radius-sm) statt 12, wie ueberall.
   Zeilenhoehen sind Verhaeltnisse, damit sie mit der Textgroesse wachsen. */
.vb-profile,
.vb-profilebar {
	/* Schriftstufen: Groesse und Zeilenhoehe, Telefon. */
	--vb-profile-h1: 1.75rem;
	--vb-profile-h1-lh: 1.2143;
	--vb-profile-h2: 1.375rem;
	--vb-profile-h2-lh: 1.2727;
	--vb-profile-h3: 1.125rem;
	--vb-profile-h3-lh: 1.4444;
	--vb-profile-text: var(--wp--preset--font-size--medium, var(--vb-text-body, 1rem));
	--vb-profile-text-lh: 1.625;
	--vb-profile-meta: var(--vb-text-label, 0.9rem);
	--vb-profile-meta-lh: 1.3889;
	--vb-profile-caption: var(--vb-text-caption, 0.78rem);
	--vb-profile-caption-lh: 1.2821;
	/* Abstandsskala 4/8/12/16/24/32/48 px. */
	--vb-space-1: 0.25rem;
	--vb-space-2: 0.5rem;
	--vb-space-3: 0.75rem;
	--vb-space-4: 1rem;
	--vb-space-5: 1.5rem;
	--vb-space-6: 2rem;
	--vb-space-7: 3rem;
	/* Rhythmus: 8 in einer Angabe, 16 zwischen Gruppen, 24 (Telefon) bzw.
	   32 (Desktop) zwischen Abschnitten - die Haelfte steht ueber, die
	   Haelfte unter der Trennlinie. Ueberschrift zu Inhalt 16. */
	--vb-profile-gap-info: var(--vb-space-2);
	--vb-profile-gap-group: var(--vb-space-4);
	--vb-profile-gap-head: var(--vb-space-4);
	--vb-profile-section-half: var(--vb-space-3);
	--vb-profile-measure: 65ch;
	/* Karten: Rahmen statt Schatten; nur die Terminbox ist leicht angehoben. */
	--vb-profile-card-pad: var(--vb-space-4);
	--vb-profile-card-gap: var(--vb-space-4);
	--vb-profile-shadow-box: 0 4px 16px rgba(48, 39, 43, 0.08);
	/* Farbrollen: neutral (geschlossen), Demo, Notfall/Fehler. Die
	   Kategorienfarbe ist --vb-cat (oben). */
	--vb-profile-neutral-ink: #51635a;
	--vb-profile-neutral-bg: #eef1f0;
	--vb-profile-demo-ink: var(--vb-warn-ink, #86520a);
	--vb-profile-demo-bg: var(--vb-warn-soft, #fff0d8);
	--vb-profile-alert-ink: var(--vb-danger-ink, #b52216);
	--vb-profile-alert-bg: var(--vb-danger-soft, #ffece6);
	/* Masse: Chips, Abzeichen, Zeichen, Trefferflaeche. */
	--vb-profile-chip-h: 28px;
	--vb-profile-badge-h: 24px;
	--vb-profile-icon: 20px;
	--vb-profile-icon-gap: 8px;
	--vb-profile-icon-badge: 14px;
	--vb-profile-target: var(--vb-tap-primary, 44px);
	/* Kein gruener Lichtschein und kein Anheben beim Zeigen auf dem Profil
	   (A5): jede Regel, die diese beiden Tokens liest, bleibt flach. */
	--vb-glow: none;
	--vb-shadow-hover: none;
}

/* Desktop ab der Naht, an der das Profil zweispaltig wird (1000 px). */
@media (min-width: 62.5rem) {
	.vb-profile,
	.vb-profilebar {
		--vb-profile-h1: 2.25rem;
		--vb-profile-h1-lh: 1.1111;
		--vb-profile-h2: 1.5rem;
		--vb-profile-h2-lh: 1.3333;
		--vb-profile-section-half: var(--vb-space-4);
		--vb-profile-card-pad: var(--vb-space-5);
	}
}

:root[data-theme="dark"] .vb-profile,
:root[data-theme="dark"] .vb-profilebar {
	--vb-profile-shadow-box: 0 4px 16px rgba(0, 0, 0, 0.24);
	--vb-profile-neutral-ink: #c5ced3;
	--vb-profile-neutral-bg: #222a2f;
}

/* ----- Schrift (A1, Rang 9) ------------------------------------------------
   Clash Display nur fuer den Namen; jede funktionale Ueberschrift General
   Sans, 600. H2 je Hauptabschnitt, H3 fuer Teile und Karten, H4 im Gewicht
   einer Beschriftung. Die Ueberschrift der Notfall-Box und des
   Uebernahme-Kastens sind Kartentitel und tragen die H3-Stufe. */
.vb-profile {
	line-height: var(--vb-profile-text-lh);
}

/* The exclusion sits in :where() so the rule stays as light as the
   selectors it replaces (0-1-1 and 0-2-1): a card title below can take it
   back with one class. */
.vb-profile h2:where(:not(.screen-reader-text)),
.vb-profile .vb-profile__section h2:where(:not(.screen-reader-text)),
.vb-profile .vb-profile__section h2.vb-profile__sechead {
	font-family: var(--vb-body, inherit);
	font-size: var(--vb-profile-h2);
	line-height: var(--vb-profile-h2-lh);
	font-weight: 600;
}

.vb-profile h3,
.vb-profile .vb-profile__section h3,
.vb-profile .vb-profile__section h3.vb-profile__sechead,
.vb-profile .vb-profile__section h3.vb-profile__subhead {
	font-family: var(--vb-body, inherit);
	font-size: var(--vb-profile-h3);
	line-height: var(--vb-profile-h3-lh);
	font-weight: 600;
}

.vb-profile h4,
.vb-profile .vb-profile__section h4.vb-profile__subhead {
	font-family: var(--vb-body, inherit);
	font-size: var(--vb-profile-text);
	line-height: var(--vb-profile-text-lh);
	font-weight: 600;
}

/* Ueberschrift zu Inhalt 16 px (A4); Untertitel 16 px nach der Gruppe davor
   und 8 px zu ihrer Angabe. */
.vb-profile .vb-profile__section h2,
.vb-profile .vb-profile__section .vb-profile__sechead {
	margin-block: 0 var(--vb-profile-gap-head);
}

.vb-profile .vb-profile__section .vb-profile__subhead {
	margin-block: var(--vb-profile-gap-group) var(--vb-profile-gap-info);
}

/* Kartentitel tragen die H3-Stufe, auch wo die Gliederung eine H2 verlangt:
   Notfall-Box, Uebernahme-Kasten, Warteliste im Buchungsbereich und der
   Titel des eigenstaendigen Rueckrufs, wenn sein Formular gesperrt ist
   (dann steht die H2 frei in der Karte). Vorher hob die Abschnittsregel die
   beiden letzten von 18,4 auf 22/24 px. Steht die Ueberschrift des
   Rueckrufs in der Zeile seiner Falte, bleibt sie diese Zeile
   (`font: inherit`), auf Stufe 2 wie auf Stufe 3. */
.vb-profile h2.vb-profile__emergency-title,
.vb-profile h2.vb-profile__claimbox-title,
.vb-profile .vb-waitlist > h2,
.vb-profile .vb-callback > h2 {
	font-family: var(--vb-body, inherit);
	font-size: var(--vb-profile-h3);
	line-height: var(--vb-profile-h3-lh);
	font-weight: 600;
}

.vb-profile .vb-waitlist > h2,
.vb-profile .vb-callback > h2 {
	margin-block: 0 var(--vb-space-3);
}

/* Die Abschnittsmarke traegt die Kategorienfarbe weiter durch die Seite;
   sie ist so hoch wie die Grossbuchstaben ihrer Ueberschrift, nicht hoeher. */
.vb-profile .vb-profile__section .vb-profile__sechead::before,
.vb-profile .vb-profile__section h2::before {
	width: 4px;
	height: 0.9em;
}

/* Fliesstext: Beschreibung, Bewertungen, Leistungszeilen samt Preis und
   der Schwerpunkt einer Person - entscheidungsrelevant, also nie Meta. */
.vb-profile .vb-profile__intro,
.vb-profile .vb-review__text,
.vb-profile .vb-services__name,
.vb-profile .vb-services__meta,
.vb-profile .vb-team__bio,
.vb-profile .vb-reviews__summary {
	font-size: var(--vb-profile-text);
	line-height: var(--vb-profile-text-lh);
}

/* Textmass nur fuer die Beschreibung (A2): etwa 65 Zeichen je Zeile. */
.vb-profile .vb-profile__intro {
	max-width: var(--vb-profile-measure);
}

/* Meta: Rolle, Sprachen, Qualifikationen, Jahre, Datum. */
.vb-profile .vb-team__role,
.vb-profile .vb-team__langs,
.vb-profile .vb-team__quals,
.vb-profile .vb-team__qualfold > summary,
.vb-profile .vb-review__date,
.vb-profile .vb-creds__year {
	font-size: var(--vb-profile-meta);
	line-height: var(--vb-profile-meta-lh);
}

.vb-profile .vb-team__name {
	margin: 0;
}

/* ----- Karten und Schatten (A5, Rang 10) ----------------------------------
   Normale Karten: 1 px Rahmen, kein Schatten, 24/16 px innen, 16 px
   Abstand, Radius 16. */
.vb-profile .vb-team__card,
.vb-profile .vb-review {
	padding: var(--vb-profile-card-pad);
	border-radius: var(--vb-radius, 16px);
	box-shadow: none;
}

.vb-profile .vb-team__grid {
	gap: var(--vb-profile-card-gap);
}

.vb-profile .vb-review {
	margin-block: 0 var(--vb-profile-card-gap);
	border-width: 1px;
	border-style: solid;
	border-color: var(--vb-line, #d9c7c1);
}

.vb-profile .vb-review__meta {
	margin-block-end: var(--vb-profile-gap-info);
}

/* ----- Rhythmus (A4, Rang 6) ------------------------------------------------
   Jeder Abschnitt der Hauptspalte beginnt mit derselben Linie, 24/32 px
   zwischen zwei Abschnitten, die Haelfte ueber, die Haelfte unter ihr.
   Vorher trug nur, wer zufaellig nicht das letzte Kind seines Ankerpunkts
   war, eine Linie unten, und 26 + 26 px lagen zwischen allen; ohne
   Ausstattung stand zwischen Beschreibung und Leistungen gar keine. Der
   erste Teil der Uebersicht steht unter der Ankerleiste, die schon eine
   Linie hat. Der Buchungsbereich ist eine Karte. */
.vb-profile__main .vb-profile__section:not(.vb-booking) {
	padding-block: var(--vb-profile-section-half);
	border-bottom-width: 0;
	border-top-width: 1px;
	border-top-style: solid;
	border-top-color: var(--vb-line, #d9c7c1);
}

.vb-profile__main .vb-profile__overview > .vb-profile__section:first-of-type {
	border-top-width: 0;
}

/* Karten zwischen zwei Abschnitten - der Rueckruf nach "Kontakt & Anfahrt",
   der Uebernahme-Kasten vor den Bewertungen - stehen nicht auf der Linie des
   naechsten Abschnitts: dieselbe halbe Luecke darunter, die ein Abschnitt
   ueber seiner Linie hat. Darueber sorgt der Abschnitt davor. */
.vb-profile__main .vb-callback,
.vb-profile__main > .vb-claim {
	margin-block-end: var(--vb-profile-section-half);
}

/* Die letzte Zeile einer Liste schliesst nicht mit einer eigenen Linie,
   wenn der naechste Abschnitt mit einer beginnt. */
.vb-profile .vb-services__item:last-child {
	border-bottom-width: 0;
}

.vb-profile .vb-profile__badges {
	margin-block-start: 0;
}

.vb-profile .vb-profile__intro:not(:has( + .vb-profile__more:not([hidden]) )) {
	margin-bottom: var(--vb-profile-gap-group);
}

.vb-profile .vb-profile__more {
	margin-block-end: var(--vb-profile-gap-group);
}

.vb-profile .vb-team__card {
	gap: var(--vb-profile-gap-info);
}

.vb-profile .vb-team__awards,
.vb-profile .vb-creds__fold {
	margin-block-start: var(--vb-profile-gap-group);
}

/* ----- Farbsemantik (A6, Rang 11) -------------------------------------------
   Kategorienfarbe fuer Angebot und Hauptaktion: Ausstattungs-Chips und die
   Akzente der Terminbox folgen der Art, nicht mehr fest Gruen. Fuellflaechen
   mit fest weisser Schrift tauschen nur hell ihre Farbe; dunkel bleibt die
   Fuellung Markengruen, Text und Grund folgen der Art. */
/* Text in der Kategorienfarbe, auch auf ihrem hellen Grund: gemessen
   erreicht #c2410c auf #fdead8 (Dogwalker) nur 4,42:1, also traegt diese
   Art dort ihren dunkleren Ton #9a3412 (6,24:1). Alle anderen Paare liegen
   bei 4,58 bis 5,79:1. Dunkel und in der Ruhigen Ansicht die Farbe selbst. */
.vb-profile {
	--vb-cat-text: var(--vb-cat, #68192e);
}

.vb-profile[data-provider-type="walker"] {
	--vb-cat-text: #68192e;
}

:root[data-theme="dark"] .vb-profile[data-provider-type],
:root[data-focus="reduced"] .vb-profile[data-provider-type] {
	--vb-cat-text: var(--vb-cat, #68192e);
}

.vb-profile .vb-profile__badge {
	background: var(--vb-cat-soft, var(--vb-primary-soft, #f3e2e7));
	color: var(--vb-cat-text, var(--vb-accent-text, #68192e));
}

.vb-profile .vb-booking,
.vb-profile .vb-staybook,
.vb-profile .vb-services {
	--vb-accent-text: var(--vb-cat-text, #68192e);
	--vb-primary-soft: var(--vb-cat-soft, #f3e2e7);
	--vb-primary-dark: var(--vb-cat, #68192e);
	--vb-primary-darker: var(--vb-cat-hover, #300b17);
	--vb-primary: var(--vb-cat, #b66a80);
}

:root[data-theme="dark"] .vb-profile .vb-booking,
:root[data-theme="dark"] .vb-profile .vb-staybook,
:root[data-theme="dark"] .vb-profile .vb-services {
	--vb-primary-dark: #68192e;
	--vb-primary-darker: #300b17;
	--vb-primary: #d9a9b7;
}

/* Die Hauptaktion des Buchens in der Kategorienfarbe (Token-Tabelle:
   "Hauptaktion"): "Termin auswaehlen" im Telefon-Starter (#256), "Zeit
   waehlen"/"Angaben vervollstaendigen" in der Box und das Absenden im
   Bereich - flach statt des dunklen Verlaufs mit Lichtschein, hell weisse
   Schrift auf der Farbe, dunkel die helle Farbe mit dunkler Schrift, wie
   der Kopfknopf. Die Ruhige Ansicht hat wie ueberall eine Farbe. */
.vb-profile .vb-booking .vb-booking__submit,
.vb-profile .vb-booking__actions button[type="submit"] {
	background: var(--vb-cat, #68192e);
	color: var(--vb-cat-ink, #fff);
	border-radius: var(--vb-radius-sm, 11px);
	box-shadow: none;
}

.vb-profile .vb-booking .vb-booking__submit:hover:not(:disabled),
.vb-profile .vb-booking__actions button[type="submit"]:hover:not(:disabled) {
	background: var(--vb-cat-hover, #300b17);
	transform: none;
	filter: none;
	box-shadow: none;
}

/* "Schliessen" am Buchungsbereich und am Aufenthaltsformular (#256): ein
   leiser Knopf, 44 px, Rahmen in der Farbe eines Bedienelements. Der Satz,
   warum eine laufende Anfrage nicht geschlossen werden kann, ist Fliesstext
   in gedaempfter Farbe, keine Fussnote. */
.vb-profile .vb-booking__areaclose,
.vb-profile .vb-staybook__close {
	box-sizing: border-box;
	min-block-size: var(--vb-profile-target);
	padding: var(--vb-space-2) var(--vb-space-4);
	border-color: var(--vb-field-line, #8c716e);
	border-radius: var(--vb-radius-sm, 11px);
	font-size: var(--vb-profile-text);
	line-height: var(--vb-profile-meta-lh);
}

.vb-profile .vb-booking__areaclose:hover,
.vb-profile .vb-staybook__close:hover {
	border-color: var(--vb-ink, #30272b);
}

.vb-profile .vb-staybook__wait {
	margin-block: 0 var(--vb-space-3);
	font-size: var(--vb-profile-text);
	line-height: var(--vb-profile-text-lh);
}

/* ----- Chips und Abzeichen: Mindesthoehen, 14 px Text --------------------- */
.vb-profile .vb-profile__tag,
.vb-profile .vb-profile__badge {
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	min-block-size: var(--vb-profile-chip-h);
	font-size: var(--vb-profile-meta);
	line-height: var(--vb-profile-meta-lh);
}

.vb-profile .vb-badge,
.vb-profile .vb-demo-badge,
.vb-profilebar .vb-demo-badge,
.vb-profilebar .vb-profilebar__chip,
.vb-profile .vb-profile__basic {
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	min-block-size: var(--vb-profile-badge-h);
	font-size: var(--vb-profile-meta);
	line-height: var(--vb-profile-meta-lh);
}

.vb-profile .vb-badge {
	padding-block: 0;
	vertical-align: middle;
}

/* ----- Zeichen (A7) -----------------------------------------------------------
   UI-Zeichen 20 px, 2 px Strich, 8 px zum Text; Abzeichen 14 px. Signet
   (Status-Zeichen) und Fotos bleiben, wie sie sind. */
.vb-profile .vb-profile__place,
.vb-profile .vb-profile__minor .vb-favorite__btn,
.vb-profile .vb-profile__allphotos,
.vb-profile .vb-profile__address,
.vb-profile .vb-profile__access .vb-profile__address {
	gap: var(--vb-profile-icon-gap);
}

.vb-profile .vb-profile__place svg,
.vb-profile .vb-profile__minor .vb-favorite__btn svg,
.vb-profile .vb-profile__allphotos svg,
.vb-profile .vb-profile__address svg,
.vb-profile .vb-profile__access .vb-profile__address svg,
.vb-profile .vb-profile__directions svg,
.vb-profile .vb-profile__call svg {
	flex: none;
	inline-size: var(--vb-profile-icon);
	block-size: var(--vb-profile-icon);
	stroke-width: 2;
}

/* Both are flex rows: the gap is the distance, the old margin beside it
   would add up to 14 px. */
.vb-profile .vb-profile__directions,
.vb-profile .vb-profile__call {
	gap: var(--vb-profile-icon-gap);
}

.vb-profile .vb-profile__directions svg,
.vb-profile .vb-profile__call svg {
	margin-right: 0;
}

.vb-profile .vb-review__verified-icon {
	inline-size: var(--vb-profile-icon-badge);
	block-size: var(--vb-profile-icon-badge);
}

/* ----- Trefferflaechen (Rang 12, B6) --------------------------------------
   Eigenstaendige Aktionen mindestens 44 x 44 px. Wo die Zeile nicht hoeher
   werden soll (Namen im Team, Zustaende im Kopf), waechst nur die
   unsichtbare Flaeche um den Verweis herum; sonst die Hoehe selbst. */
.vb-profile .vb-team__name a,
.vb-profile .vb-profile__trustitem a {
	position: relative;
}

.vb-profile .vb-team__name a::after,
.vb-profile .vb-profile__trustitem a::after {
	content: "";
	position: absolute;
	inset: calc( ( 100% - var(--vb-profile-target) ) / 2 ) 0;
	min-inline-size: var(--vb-profile-target);
}

/* The status mark in the profile bar is 28 px drawn and 44 to press: its
   area reaches 8 px each way, and 6 px of gap left 2 px of it over the end
   of the name link beside it. 4 px more room between them. */
.vb-profilebar .vb-status {
	margin-inline-start: var(--vb-space-1);
}

/* Wraps the row of states (a phone, a long rating): the next row starts
   where the 44 px of the one above end, so two hit areas never overlap. */
.vb-profile .vb-profile__trust {
	row-gap: calc( var(--vb-profile-target) - var(--vb-profile-chip-h) );
}

.vb-profile .vb-profile__contact-list a,
.vb-profile .vb-profile__directions {
	box-sizing: border-box;
	min-block-size: var(--vb-profile-target);
}

/* In der Terminbox: der Weg zum Notfallsatz und zur Warteliste, je 44 px
   ohne die Flaeche eines Nachbarn zu bedecken (eine unsichtbare Flaeche
   rundum nahm vorher dem Titel, dem Feld darunter und "Weitere Zeiten" die
   Klicks ab). Die Box hat ein Hoehenbudget je Stufe (#211):
   - Die Notfallzeile ist selbst 44 px hoch, ihr Verweis fuellt die Hoehe;
     dafuer entfallen der Abstand unter dem Titel und unter der Zeile
     (8 + 24 + 8 px wurden 44 px, die Box 4 px hoeher).
   - Der Wartelisten-Verweis steht immer zuletzt in der Box; seine Flaeche
     beginnt an der Unterkante des Knopfs darueber (die 8 px Lueckenabstand
     der Stufe) und reicht in den Innenabstand der Box, der frei ist.
   Der Titelabstand entfaellt nur, wenn die Notfallzeile folgt (#263): Ohne
   sie (Groomer, Walker, Sitter, Therapie, Training) stuende das erste Feld
   bzw. der Starterknopf sonst direkt am Titel. Ein Browser ohne :has()
   behaelt den Abstand auch ueber der Notfallzeile - 8 px mehr, nichts
   klebt. */
.vb-profile .vb-booking--box .vb-booking__boxtitle {
	margin-block-end: var(--vb-space-2);
}

.vb-profile .vb-booking--box .vb-booking__boxtitle:has( + .vb-booking__boxsos ) {
	margin-block-end: 0;
}

.vb-profile .vb-booking__boxsos {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	column-gap: 0.3em;
	box-sizing: border-box;
	min-block-size: var(--vb-profile-target);
	margin-block: 0;
}

.vb-profile .vb-booking__boxsos a {
	display: inline-flex;
	align-items: center;
	align-self: stretch;
	min-block-size: var(--vb-profile-target);
}

.vb-profile .vb-booking__boxlink a {
	position: relative;
}

.vb-profile .vb-booking__boxlink a::after {
	content: "";
	position: absolute;
	inset: calc( -1 * var(--vb-space-2) ) 0 auto;
	block-size: var(--vb-profile-target);
	min-inline-size: var(--vb-profile-target);
}

.vb-profile .vb-team__qualfold > summary,
.vb-profile .vb-callback__fold > summary {
	min-block-size: var(--vb-profile-target);
}

/* "Melden" an einer Bewertung war ein Knopf ohne jede Regel: 60 x 21 px im
   Aussehen des Browsers. Jetzt ein leiser Textknopf in voller Hoehe. */
.vb-profile .vb-review__report {
	margin: var(--vb-profile-gap-info) 0 0;
}

.vb-profile .vb-review__report-btn {
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	min-block-size: var(--vb-profile-target);
	min-inline-size: var(--vb-profile-target);
	margin-inline-start: calc( -1 * var(--vb-space-2) );
	padding: 0 var(--vb-space-2);
	border: 0;
	border-radius: var(--vb-radius-sm, 11px);
	background: transparent;
	color: var(--vb-muted, #6b5b60);
	font: inherit;
	font-size: var(--vb-profile-meta);
	text-decoration: underline;
	text-underline-offset: 0.15em;
	cursor: pointer;
}

.vb-profile .vb-review__report-btn:hover {
	color: var(--vb-ink, #30272b);
}

.vb-profile .vb-review__report-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));
}

/* ----- "Mehr in dieser Gegend" tertiaer (A11) ----------------------------------
   Kleiner als jede Abschnittsueberschrift, die Liste kompakt; die Falte und
   ihre 44 px bleiben. */
.vb-profile .vb-profile__nearby h3 {
	font-size: var(--vb-profile-text);
	line-height: var(--vb-profile-text-lh);
	color: var(--vb-muted, #6b5b60);
}

.vb-profile .vb-profile__nearby ul {
	margin: var(--vb-profile-gap-info) 0 0;
	padding-inline-start: 1.1rem;
	font-size: var(--vb-profile-meta);
	line-height: var(--vb-profile-meta-lh);
}

.vb-profile .vb-profile__nearby li + li {
	margin-block-start: var(--vb-profile-gap-info);
}
