/*
 * Patientenportal – Die Urologin
 * Stylesheet des Portalbereichs im Frontend.
 *
 * Alles ist unter .dup-portal verschachtelt, damit keine Regel in das
 * Impreza-Theme ausblutet. Farben kommen aus den Theme-Variablen, jeweils
 * mit Rueckfallwert, falls das Portal einmal ohne Impreza gerendert wird.
 *
 * @package DieUrologin\Patientenportal
 */

/* -------------------------------------------------------------------------
 * 0. Dashboard-Rahmen
 *
 * Auf der Portalseite entfallen Kopf- und Fussbereich des Themes. Damit der
 * Arbeitsbereich wie ein Dashboard wirkt und nicht wie eine Unterseite, werden
 * hier die Abstaende des Themes zurueckgenommen und die Seite auf Bildschirm-
 * hoehe festgesetzt. Alle Regeln haengen an body.dup-portal-page, greifen also
 * ausschliesslich dort.
 * ---------------------------------------------------------------------- */

body.dup-portal-page {
	background: var( --color-content-bg-alt, #f5f5f5 );
	/* Der Arbeitsbereich passt sich der Bildschirmhoehe an, die Seite selbst
	 * scrollt daher nicht. Gescrollt wird nur innerhalb der Spalten. */
	overflow: hidden;
}

/* Die Anmelde- und Passwortkarten sind auf kleinen Schirmen und im
 * Anmelde-Popup laenger als der Ausschnitt - dort MUSS die Seite scrollen
 * koennen, sonst sind Fussbereich und Verweise unerreichbar abgeschnitten. */
body.dup-portal-page.dup-gate-page {
	overflow: auto;
}

body.dup-portal-page .l-canvas,
body.dup-portal-page .l-main,
body.dup-portal-page .l-section,
body.dup-portal-page .l-section-h,
body.dup-portal-page .l-content,
body.dup-portal-page .w-post-elm {
	max-width: none;
	margin: 0;
	padding: 0;
}

body.dup-portal-page .l-section-h {
	width: 100%;
}

/* Impreza gibt die Fusszeile als leere Hülle auch dann aus, wenn kein
 * Baustein zugewiesen ist. Sie würde sonst als Leerstreifen stehen bleiben. */
body.dup-portal-page .l-footer,
body.dup-portal-page .w-toplink,
body.dup-portal-page .l-preloader {
	display: none;
}

/* Kompakte Kopfleiste des Arbeitsbereichs. */
.dup-bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 12px;
	height: 56px;
	padding: 0 20px;
	background: var( --color-content-heading, #121316 );
	color: #fff;
	font-family: inherit;
	font-size: 14px;
	line-height: 1.4;
}

.dup-bar__brand {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
}

.dup-bar__logo {
	display: block;
	width: auto;
	height: 30px;
	border-radius: 4px;
}

.dup-bar__title {
	font-weight: 700;
	letter-spacing: 0.01em;
	white-space: nowrap;
}

.dup-bar__meta {
	display: flex;
	align-items: center;
	gap: 18px;
	min-width: 0;
	font-size: 13px;
}

.dup-bar__user {
	color: rgba( 255, 255, 255, 0.72 );
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.dup-bar__link,
.dup-bar__link:hover,
.dup-bar__link:focus {
	color: rgba( 255, 255, 255, 0.82 );
	text-decoration: none;
	white-space: nowrap;
}

/*
 * Auf der dunklen Leiste kann der Petrol-Akzent nicht wirken - dort waere er
 * Dunkel auf Dunkel. Die Rueckmeldung uebernimmt hier die Helligkeit:
 * ruhend gedaempft, angefasst voll weiss.
 */
.dup-bar__link:hover,
.dup-bar__link:focus {
	color: #fff;
}

.dup-bar__logout {
	font-weight: 600;
}

/* Glocke für Push-Benachrichtigungen. Ausgegraut = aus, gold = aktiv.
 * display nur als :not([hidden]) - sonst gewinnt diese Regel gegen das
 * hidden-Attribut (gleiche Spezifität, spätere Position). */
.dup-bar__bell:not( [hidden] ) {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background: transparent;
	color: rgba( 255, 255, 255, 0.65 );
	cursor: pointer;
	transition: color 160ms ease, background-color 160ms ease;
}

.dup-bar__bell:hover,
.dup-bar__bell:focus-visible {
	color: #fff;
	background: rgba( 255, 255, 255, 0.12 );
	outline: none;
}

/* Aktiv: volle Helligkeit plus dezente Scheibe - so unterscheidet sich
 * "eingeschaltet" auch ohne Farbe vom blossen Ueberfahren. */
.dup-bar__bell[aria-pressed="true"] {
	color: #fff;
	background: rgba( 255, 255, 255, 0.18 );
}

/* Schild: Zwei-Faktor-Einrichtung der Ordination - gleiche Machart wie
 * die Glocke, nur als Verweis statt Schalter. */
.dup-bar__schild {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	border-radius: 999px;
	color: rgba( 255, 255, 255, 0.65 );
	transition: color 160ms ease, background-color 160ms ease;
}

.dup-bar__schild:hover,
.dup-bar__schild:focus-visible {
	color: #fff;
	background: rgba( 255, 255, 255, 0.12 );
	outline: none;
}

/* Ansichtswechsel der Testphase - bewusst als Test-Element erkennbar,
   damit niemand ihn für einen regulären Portalbestandteil hält. */
.dup-bar__switch,
.dup-bar__switch:hover,
.dup-bar__switch:focus {
	padding: 4px 12px;
	border: 1px dashed rgba( 255, 255, 255, 0.55 );
	border-radius: 999px;
	font-size: 0.85rem;
}

.dup-bar__switch:hover,
.dup-bar__switch:focus {
	border-color: rgba( 255, 255, 255, 0.9 );
}

@media ( max-width: 600px ) {
	.dup-bar {
		height: auto;
		padding: 10px 14px;
		/* Marke und Verweise dürfen untereinander rutschen. */
		flex-wrap: wrap;
		row-gap: 6px;
	}

	.dup-bar__title {
		font-size: 13px;
	}

	.dup-bar__user {
		display: none;
	}

	/* Kein Abschneiden mehr: Die Verweise brechen um, statt aus dem Bild zu
	 * laufen - "Abmelden" muss IMMER erreichbar sein. */
	.dup-bar__meta {
		flex-wrap: wrap;
		gap: 6px 14px;
		font-size: 12.5px;
	}

	/* Der Website-Verweis ist am Telefon verzichtbar - die Website ist eine
	 * Adresszeile entfernt, "Abmelden" nicht. */
	.dup-bar__website {
		display: none;
	}

	/* Testphasen-Knopf kompakt, damit er keine ganze Zeile frisst. */
	.dup-bar__switch,
	.dup-bar__switch:hover,
	.dup-bar__switch:focus {
		padding: 3px 9px;
		font-size: 0.75rem;
	}
}

/* -------------------------------------------------------------------------
 * 0b. Anmeldemaske
 *
 * Bewusst zurueckhaltend: fast alles weiss, Farbe nur als schmaler Akzent.
 * Die Hauptfarbe erscheint erst beim Ueberfahren und beim Tastaturfokus - dort
 * dann aber voll, damit klar ist, was gerade angefasst wird.
 * ---------------------------------------------------------------------- */

body.dup-portal-page .dup-gate {
	/*
	 * Akzent des Portals ist das dunkle Petrol der Website-Grundflaeche
	 * (#275765), nicht das Gold. Der Vorteil ist nicht nur optisch: Auf
	 * Petrol traegt weisse Schrift rund 7:1 Kontrast - auf Gold waren es
	 * 1,9:1, weshalb dort ueberall dunkle Schrift noetig war.
	 */
	--dup-gate-accent: #275765;
	/* Dieselbe Farbe traegt auch als Schrift auf Weiss (rund 7:1). */
	--dup-gate-gold-dark: #275765;
	--dup-gate-ink: var( --color-content-heading, #121316 );
	--dup-gate-text: var( --color-content-text, #33353d );
	--dup-gate-border: #e4e4e7;

	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 20px;
	min-height: 100vh;
	padding: 40px 20px;
	background: #fff;
	font-family: inherit;
	font-size: 15px;
	line-height: 1.55;
	color: var( --dup-gate-text );
}

body.dup-portal-page .dup-gate__card {
	width: 100%;
	max-width: 420px;
	padding: 40px 36px 32px;
	background: #fff;
	border: 1px solid var( --dup-gate-border );
	border-radius: 14px;
	box-shadow: 0 1px 2px rgba( 18, 19, 22, 0.03 ), 0 12px 32px rgba( 18, 19, 22, 0.05 );
	text-align: center;
}

body.dup-portal-page .dup-gate__logo {
	display: block;
	width: auto;
	height: 44px;
	margin: 0 auto 22px;
}

body.dup-portal-page .dup-gate__title {
	margin: 0 0 10px;
	font-size: 21px;
	font-weight: 700;
	line-height: 1.3;
	letter-spacing: -0.01em;
	color: var( --dup-gate-ink );
}

body.dup-portal-page .dup-gate__lead {
	margin: 0 0 26px;
	font-size: 14px;
	color: #6b6c74;
	/* Verteilt die Zeilen so, dass am Ende kein Rest allein steht. Browser,
	 * die das nicht kennen, ignorieren es - dort sorgt das geschützte
	 * Leerzeichen vor dem Schlusswort für dasselbe Ergebnis. */
	text-wrap: pretty;
}

/*
 * Frei pflegbarer Hinweis ueber der Anmeldekarte ("noch im Aufbau").
 * Deutlich, aber nicht alarmierend: Es ist eine Ankuendigung, kein Fehler.
 */
body.dup-portal-page .dup-gate__banner {
	max-width: 420px;
	width: 100%;
	margin: 0;
	padding: 12px 18px;
	border: 1px solid var( --dup-gate-accent );
	border-left-width: 4px;
	border-radius: 4px;
	background: #fdf6e3;
	color: var( --dup-gate-ink );
	font-size: 14px;
	line-height: 1.55;
	text-align: center;
	text-wrap: pretty;
}

body.dup-portal-page .dup-gate__alert {
	margin: 0 0 18px;
	padding: 10px 14px;
	/* Der einzige flaechige Farbeinsatz, und auch der nur sehr blass. */
	background: rgba( 39, 87, 101, 0.08 );
	border-left: 3px solid var( --dup-gate-accent );
	border-radius: 4px;
	font-size: 13px;
	text-align: left;
	color: var( --dup-gate-ink );
}

/* Das Formular kommt aus dem WordPress-Kern, deshalb die Kernklassen. */
body.dup-portal-page .dup-gate #dup-loginform {
	text-align: left;
}

body.dup-portal-page .dup-gate .login-username,
body.dup-portal-page .dup-gate .login-password {
	margin: 0 0 16px;
}

body.dup-portal-page .dup-gate label {
	display: block;
	margin: 0 0 6px;
	font-size: 13px;
	font-weight: 600;
	color: var( --dup-gate-ink );
}

body.dup-portal-page .dup-gate input[type="text"],
body.dup-portal-page .dup-gate input[type="password"] {
	width: 100%;
	height: 44px;
	padding: 0 13px;
	background: #fff;
	border: 1px solid var( --dup-gate-border );
	border-radius: 8px;
	box-shadow: none;
	font-family: inherit;
	font-size: 15px;
	line-height: 1.4;
	color: var( --dup-gate-ink );
	transition: border-color 160ms ease, box-shadow 160ms ease;
}

body.dup-portal-page .dup-gate input[type="text"]:hover,
body.dup-portal-page .dup-gate input[type="password"]:hover {
	border-color: #c9cacf;
}

body.dup-portal-page .dup-gate input[type="text"]:focus,
body.dup-portal-page .dup-gate input[type="password"]:focus {
	outline: none;
	border-color: var( --dup-gate-accent );
	box-shadow: 0 0 0 3px rgba( 39, 87, 101, 0.2 );
}

body.dup-portal-page .dup-gate .login-remember {
	margin: 0 0 22px;
	font-size: 13px;
}

body.dup-portal-page .dup-gate .login-remember label {
	display: flex;
	align-items: center;
	gap: 9px;
	margin: 0;
	font-weight: 400;
	color: var( --dup-gate-text );
	cursor: pointer;
}

body.dup-portal-page .dup-gate input[type="checkbox"] {
	width: 17px;
	height: 17px;
	margin: 0;
	accent-color: var( --dup-gate-accent );
	border-radius: 4px;
}

body.dup-portal-page .dup-gate .login-submit {
	margin: 0;
}

/*
 * Ruhig im Normalzustand, beim Ueberfahren voll in der Hauptfarbe.
 *
 * Zum !important: Impreza setzt auf jeden Submit-Button
 * "color:#ffffff!important". Auf hellem Grund waere die Beschriftung damit
 * unsichtbar - weisse Schrift auf Weiss. Ohne Gegenwehr an derselben Stelle
 * ist dagegen nicht anzukommen.
 *
 * Zum Farbton: Das Petrol der Website-Grundflaeche traegt auf Weiss rund
 * 7:1 - lesbar auch fuer Augen, die Kontraste schlechter aufloesen. Beim
 * Ueberfahren kommt es voll als Flaeche, dort mit weisser Schrift (ebenfalls
 * rund 7:1). Mit dem frueheren Gold ging beides nicht: als Schrift zu blass,
 * als Flaeche nur mit dunkler Schrift lesbar.
 */
body.dup-portal-page .dup-gate #dup-wp-submit {
	display: block;
	width: 100%;
	height: 46px;
	padding: 0 18px;
	background: #fff;
	border: 1.5px solid var( --dup-gate-gold-dark );
	border-radius: 8px;
	box-shadow: none;
	font-family: inherit;
	font-size: 15px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.01em;
	color: var( --dup-gate-gold-dark ) !important;
	text-shadow: none;
	cursor: pointer;
	transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

body.dup-portal-page .dup-gate #dup-wp-submit:hover,
body.dup-portal-page .dup-gate #dup-wp-submit:focus-visible {
	background: var( --dup-gate-accent );
	border-color: var( --dup-gate-accent );
	/* Auf dem dunklen Petrol traegt Weiss - dunkle Schrift laege hier bei
	 * 1,9:1 und waere praktisch unlesbar. */
	color: #fff !important;
	outline: none;
}

/*
 * Dieselbe Gestalt fuer die Knoepfe der Passwortkarten ("Link anfordern",
 * "Passwort speichern", "Neuen Link anfordern", "Zur Anmeldung").
 *
 * Sie heissen nicht dup-wp-submit und fielen deshalb durch die Regel oben
 * hindurch - uebrig blieb Imprezas eigene Faerbung: goldene Flaeche mit
 * erzwungen weisser Schrift, rund 1,8:1. Ausgerechnet auf dem Weg, den
 * jemand geht, der nicht mehr hineinkommt. Die Selektoren nennen Element und
 * Klasse mit, um gegen die Theme-Regel anzukommen.
 */
body.dup-portal-page .dup-gate input[type="submit"].button,
body.dup-portal-page .dup-gate a.button {
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 100%;
	height: 46px;
	padding: 0 18px;
	background: #fff;
	border: 1.5px solid var( --dup-gate-accent );
	border-radius: 8px;
	box-shadow: none;
	font-family: inherit;
	font-size: 15px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.01em;
	color: var( --dup-gate-accent ) !important;
	text-align: center;
	text-decoration: none;
	text-shadow: none;
	cursor: pointer;
	transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

body.dup-portal-page .dup-gate input[type="submit"].button:hover,
body.dup-portal-page .dup-gate input[type="submit"].button:focus-visible,
body.dup-portal-page .dup-gate a.button:hover,
body.dup-portal-page .dup-gate a.button:focus-visible {
	background: var( --dup-gate-accent );
	border-color: var( --dup-gate-accent );
	color: #fff !important;
	outline: none;
}

body.dup-portal-page .dup-gate__links {
	margin: 20px 0 0;
	font-size: 13px;
	text-align: center;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* Der Datenschutzverweis in der Karte: im Anmelde-Popup der Website ist der
 * Fussbereich abgeschnitten, hier bleibt er sichtbar. */
body.dup-portal-page .dup-gate__privacy {
	font-size: 12.5px;
}

body.dup-portal-page .dup-gate__links a,
body.dup-portal-page .dup-gate__foot a {
	color: #6b6c74;
	text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: color 160ms ease, border-color 160ms ease;
}

body.dup-portal-page .dup-gate__links a:hover,
body.dup-portal-page .dup-gate__links a:focus-visible,
body.dup-portal-page .dup-gate__foot a:hover,
body.dup-portal-page .dup-gate__foot a:focus-visible {
	color: var( --dup-gate-ink );
	border-bottom-color: var( --dup-gate-accent );
	outline: none;
}

body.dup-portal-page .dup-gate__foot {
	/* Breit genug, dass die laengste der drei Zeilen sicher hineinpasst. */
	max-width: 600px;
	margin: 0;
	font-size: 12.5px;
	line-height: 1.7;
	text-align: center;
	color: #85868d;
}

/* Vertrauenszeile mit Schloss am Fuss der Anmeldekarte. */
body.dup-portal-page .dup-gate__secure {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin: 16px 0 0;
	padding-top: 14px;
	border-top: 1px solid #f0f0f2;
	font-size: 12px;
	line-height: 1.5;
	color: #85868d;
	text-wrap: pretty;
}

body.dup-portal-page .dup-gate__secure-icon {
	flex: 0 0 auto;
	color: var( --dup-gate-gold-dark, #275765 );
}

/* Notfall-Hinweis: etwas abgesetzt und eine Spur dunkler als der Rest
 * des Fussbereichs - lesbar, ohne zu schreien. */
body.dup-portal-page .dup-gate__notfall {
	margin-top: 8px;
	color: #6b6c74;
}

/*
 * Jede der drei Zeilen ist ein eigenes Element. Damit steht der Umbruch fest
 * und es kann kein einzelnes Wort allein auf der letzten Zeile landen.
 * white-space: nowrap waere hier falsch: Wird die Schrift vergroessert oder
 * eine laengere Uebersetzung eingesetzt, liefe die Zeile aus dem Bild.
 */
body.dup-portal-page .dup-gate__line {
	display: block;
}

/* Zu schmal fuer feste Zeilen: dort wieder ein normaler Absatz. */
@media ( max-width: 620px ) {
	body.dup-portal-page .dup-gate__foot {
		max-width: 100%;
		line-height: 1.6;
		text-wrap: pretty;
	}

	body.dup-portal-page .dup-gate__line {
		display: inline;
	}
}

@media ( max-width: 520px ) {
	body.dup-portal-page .dup-gate__card {
		padding: 30px 22px 26px;
		border: 0;
		box-shadow: none;
	}
}

/*
 * Am Telefon MÜSSEN Eingabefelder 16px Schrift haben: Darunter zoomt iOS
 * beim Antippen automatisch heran, die Ansicht springt und bleibt
 * verschoben hängen - genau das soll das Portal nie tun. Gilt auch für
 * die kompakte Popup-Fassung (dort ist das Fenster ohnehin bildfüllend).
 */
@media ( max-width: 600px ) {
	body.dup-portal-page .dup-gate input[type="text"],
	body.dup-portal-page .dup-gate input[type="password"],
	html.dup-popup body.dup-portal-page .dup-gate input[type="text"],
	html.dup-popup body.dup-portal-page .dup-gate input[type="password"] {
		font-size: 16px;
	}

	.dup-portal .dup-composer textarea,
	.dup-portal .dup-list__search,
	.dup-portal .dup-views__search input {
		font-size: 16px;
	}
}

/* Kein Gummiband-Überziehen und kein Doppeltipp-Zoom in der Anwendung. */
body.dup-portal-page {
	overscroll-behavior: none;
}

.dup-portal button,
.dup-portal a,
body.dup-portal-page .dup-gate button,
body.dup-portal-page .dup-gate input,
body.dup-portal-page .dup-gate a {
	touch-action: manipulation;
}

/* Bestätigung nach dem Abmelden: ruhiges Grün statt Warn-Gold. */
body.dup-portal-page .dup-gate__alert--ok {
	background: rgba( 31, 122, 104, 0.08 );
	border-left-color: #1f7a68;
}

/* ---------------------------------------------------------------------
 * Auge im Passwortfeld: Passwort auf Wunsch im Klartext.
 * ------------------------------------------------------------------ */

body.dup-portal-page .dup-gate .dup-passfeld {
	position: relative;
}

/* Platz für das Auge, damit lange Passwörter nicht darunter laufen. */
body.dup-portal-page .dup-gate .dup-passfeld .input {
	padding-right: 46px;
}

body.dup-portal-page .dup-gate .dup-passfeld__auge {
	position: absolute;
	right: 1px;
	bottom: 1px;
	width: 42px;
	height: 42px;
	padding: 0;
	border: 0;
	background: transparent;
	color: #6b6c74;
	display: grid;
	place-items: center;
	cursor: pointer;
	border-radius: 0 8px 8px 0;
}

body.dup-portal-page .dup-gate .dup-passfeld__auge:hover,
body.dup-portal-page .dup-gate .dup-passfeld__auge:focus-visible {
	color: var( --dup-gate-ink, #121316 );
	outline: none;
}

/* Kompakte Popup-Felder sind 40px hoch - das Auge zieht mit. */
html.dup-popup body.dup-portal-page .dup-gate .dup-passfeld__auge {
	width: 38px;
	height: 38px;
}

/*
 * Sicherheitskarte (Zwei-Faktor): Die Wordfence-Oberfläche braucht mehr
 * Platz als die Anmeldekarte und richtet sich links aus.
 */
body.dup-portal-page .dup-gate__card:has( .dup-gate__wfls ) {
	max-width: 620px;
}

body.dup-portal-page .dup-gate__wfls {
	margin-top: 6px;
	text-align: left;
}

/* Notfall-Hinweis unter dem Schreibfeld der Patientenansicht. */
.dup-portal .dup-composer__notfall {
	margin: 6px 4px 0;
	font-size: 11.5px;
	line-height: 1.5;
	color: var( --dup-muted, #85868d );
	text-wrap: pretty;
}

/* Der Verweis zur Sicherheitskarte unter "Meine Daten". */
.dup-portal .dup-form__security {
	margin: 14px 0 0;
	font-size: var( --dup-font-sm, 13px );
}

.dup-portal .dup-form__security a {
	color: var( --dup-muted, #6b6c74 );
	text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: color 160ms ease, border-color 160ms ease;
}

.dup-portal .dup-form__security a:hover,
.dup-portal .dup-form__security a:focus-visible {
	color: var( --dup-ink, #121316 );
	border-bottom-color: var( --dup-accent, #275765 );
	outline: none;
}

/*
 * Die Anmeldekarte im Popup der Website (Klasse dup-popup kommt von der
 * Karte selbst, sobald sie eingebettet läuft).
 *
 * Im Popup ist der weisse Rahmen des Fensters schon die Karte. Alles, was
 * auf der freistehenden Seite die Karte vom Hintergrund abhebt - Rand,
 * Schatten, Eckenrundung, Bildschirmzentrierung - würde hier doppeln und
 * die Proportionen verschieben. Stattdessen: Hinweisband oben, Inhalt mit
 * gleichmässigem Abstand, Fussbereich als heller Abschluss. Die Höhe des
 * Fensters passt sich dem Inhalt an (siehe login-popup.js), deshalb darf
 * hier nichts den Bildschirm füllen wollen.
 */
html.dup-popup body.dup-portal-page .dup-gate {
	display: block;
	min-height: 0;
	padding: 0;
}

/*
 * WICHTIG: Innerhalb des Popups misst jede Media-Query den RAHMEN, nicht
 * den Bildschirm - auf dem Schreibtisch ist der Rahmen immer ~480px breit,
 * am Telefon fuellt er den Schirm (<=540px). Beides ist "schmal", deshalb
 * gibt es hier genau EINEN Satz Regeln fuer schmale Breite und keine
 * eigene Media-Query. Die Banner-Polsterung ist symmetrisch 56px: rechts
 * als Reserve fuer den Schliessknopf, der am Telefon innen liegt - und
 * symmetrisch, damit der mittig gesetzte Text mittig bleibt.
 */
html.dup-popup body.dup-portal-page .dup-gate__banner {
	max-width: none;
	padding: 13px 56px;
	border: 0;
	border-bottom: 1px solid rgba( 39, 87, 101, 0.45 );
	border-radius: 0;
}

html.dup-popup body.dup-portal-page .dup-gate__card {
	max-width: none;
	padding: 22px 28px 18px;
	border: 0;
	border-radius: 0;
	box-shadow: none;
}

/* Im Popup eine Stufe kompakter: kleinere Schrift, engere Abstände -
 * das Fenster soll leicht wirken, nicht wie eine eigene Seite. */
html.dup-popup body.dup-portal-page .dup-gate__logo {
	height: 36px;
	margin-bottom: 14px;
}

html.dup-popup body.dup-portal-page .dup-gate__title {
	font-size: 18px;
	margin-bottom: 8px;
}

html.dup-popup body.dup-portal-page .dup-gate__lead {
	font-size: 12.5px;
	margin-bottom: 16px;
}

html.dup-popup body.dup-portal-page .dup-gate__banner {
	font-size: 12.5px;
	padding: 10px 48px;
	line-height: 1.5;
}

html.dup-popup body.dup-portal-page .dup-gate__alert {
	font-size: 12.5px;
	padding: 9px 12px;
	margin-bottom: 14px;
}

html.dup-popup body.dup-portal-page .dup-gate label {
	font-size: 12.5px;
	margin-bottom: 5px;
}

html.dup-popup body.dup-portal-page .dup-gate input[type="text"],
html.dup-popup body.dup-portal-page .dup-gate input[type="password"] {
	height: 40px;
	font-size: 14px;
}

html.dup-popup body.dup-portal-page .dup-gate .login-username,
html.dup-popup body.dup-portal-page .dup-gate .login-password {
	margin-bottom: 12px;
}

html.dup-popup body.dup-portal-page .dup-gate .login-remember {
	margin-bottom: 16px;
	font-size: 12.5px;
}

html.dup-popup body.dup-portal-page .dup-gate #dup-wp-submit {
	height: 42px;
	font-size: 14px;
}

html.dup-popup body.dup-portal-page .dup-gate__links {
	margin-top: 14px;
	font-size: 12.5px;
	gap: 6px;
}

html.dup-popup body.dup-portal-page .dup-gate__foot {
	padding: 11px 20px 13px;
	font-size: 12px;
	line-height: 1.6;
}

/* Der Fussbereich ist im Popup sichtbar - der doppelte Datenschutzverweis
 * in der Karte darf dort deshalb weg. */
html.dup-popup body.dup-portal-page .dup-gate__privacy {
	display: none;
}

html.dup-popup body.dup-portal-page .dup-gate__foot {
	max-width: none;
	margin: 0;
	padding: 14px 24px 18px;
	background: #fafafa;
	border-top: 1px solid #efeff2;
}


/*
 * Fusszeile der Patientenansicht: Kontaktdaten und Bewertungslink.
 * Bewusst hell und ruhig - sie soll erreichbar sein, nicht auffallen.
 */
/*
 * Der Hinweis ueber dem Stammdatenformular der Patientenansicht. Er klebte
 * am Formular; jetzt eine dezente, akzentuierte Karte mit Luft nach unten.
 */
.dup-portal .dup-form__intro {
	margin: 0 0 22px;
	padding: 11px 16px;
	border-left: 3px solid var( --dup-accent, #275765 );
	border-radius: var( --dup-radius-sm, 8px );
	background: var( --dup-surface-alt, #f5f5f5 );
	font-size: var( --dup-font-sm, 13px );
	line-height: 1.6;
	color: var( --dup-muted, #6b6c74 );
}

body.dup-portal-page .dup-foot {
	/* Drei feste Zonen: Kontakt links, Rechtliches mittig, Copyright rechts.
	 * Die Spalten sind explizit zugewiesen, damit nichts verrutscht, wenn
	 * eine Zone leer bleibt. */
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 6px 22px;
	padding: 14px 20px;
	background: var( --color-content-bg-alt, #f5f5f5 );
	border-top: 1px solid var( --color-content-border, #e8e8e8 );
	font-family: inherit;
	font-size: 13px;
	line-height: 1.6;
	color: #6b6c74;
}

body.dup-portal-page .dup-foot__contact {
	grid-column: 1;
	justify-self: start;
	text-align: left;
}

body.dup-portal-page .dup-foot__links {
	grid-column: 2;
	justify-self: center;
}

body.dup-portal-page .dup-foot__legal {
	grid-column: 3;
	justify-self: end;
	text-align: right;
}

/* Auf schmalen Schirmen untereinander und zentriert - drei Zonen nebeneinander
 * haben dort keinen Platz. */
@media ( max-width: 960px ) {
	body.dup-portal-page .dup-foot {
		grid-template-columns: 1fr;
	}

	body.dup-portal-page .dup-foot__contact,
	body.dup-portal-page .dup-foot__links,
	body.dup-portal-page .dup-foot__legal {
		grid-column: 1;
		justify-self: center;
		text-align: center;
	}
}

body.dup-portal-page .dup-foot p {
	margin: 0;
}

body.dup-portal-page .dup-foot a {
	color: inherit;
	text-decoration: none;
	border-bottom: 1px solid transparent;
}

body.dup-portal-page .dup-foot a:hover,
body.dup-portal-page .dup-foot a:focus-visible {
	color: var( --color-content-heading, #121316 );
	border-bottom-color: var( --dup-gate-accent, #275765 );
	outline: none;
}

body.dup-portal-page .dup-foot__links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 4px 18px;
}

body.dup-portal-page a.dup-foot__review {
	font-weight: 600;
}

/*
 * "Patientenportal" mittig in der Kopfleiste, in Gold - deutlich sichtbar,
 * ohne mit dem Ordinationsnamen zu konkurrieren. Auf dem dunklen Grund
 * (#121316) traegt das Marken-Gold gut lesbaren Kontrast.
 */
.dup-bar {
	position: relative;
}

.dup-bar__badge {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate( -50%, -50% );
	padding: 5px 16px;
	/* Weiss statt Gold: Auf der dunklen Leiste ist das die klarste Marke,
	 * und der Petrol-Akzent des Portals waere hier Dunkel auf Dunkel. Der
	 * Rahmen bleibt zurueckgenommen, damit die Schrift traegt. */
	border: 1px solid rgba( 255, 255, 255, 0.45 );
	border-radius: 999px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: #fff;
	white-space: nowrap;
}

/* Auf schmalen Schirmen fehlt die Mitte - dort weicht das Schildchen dem
 * Ordinationsnamen, der wichtiger ist. */
@media ( max-width: 860px ) {
	.dup-bar__badge {
		display: none;
	}
}

body.dup-portal-page .dup-foot__legal {
	font-size: 12px;
	color: #8a8b91;
}

/*
 * Nachlese der erteilten Einwilligung im Stammdaten-Tab: zurueckhaltend,
 * aber auffindbar. Der Wortlaut behaelt seine Zeilenumbrueche.
 */
.dup-portal .dup-consent-review:not( [hidden] ) {
	display: block;
	margin-top: 26px;
	padding: 14px 18px;
	border: 1px solid var( --dup-border, #e8e8e8 );
	border-radius: var( --dup-radius-sm, 8px );
	background: var( --dup-surface-alt, #f5f5f5 );
	font-size: var( --dup-font-sm, 13px );
	color: var( --dup-muted, #6b6c74 );
}

.dup-portal .dup-consent-review__summary {
	cursor: pointer;
	font-weight: 600;
	color: var( --dup-heading, #121316 );
}

.dup-portal .dup-consent-review__meta {
	margin: 12px 0 8px;
	font-weight: 600;
}

.dup-portal .dup-consent-review__text {
	margin: 0 0 10px;
	white-space: pre-line;
	line-height: 1.6;
}

.dup-portal .dup-consent-review__hint {
	margin: 0;
	font-size: var( --dup-font-xs, 12px );
}

/* Im Dashboard-Rahmen laeuft der Arbeitsbereich randlos bis an die Kanten. */
body.dup-portal-page .dup-portal {
	border-left: 0;
	border-right: 0;
	border-bottom: 0;
	border-radius: 0;
	box-shadow: none;
	min-height: 0;
}

/* -------------------------------------------------------------------------
 * 1. Variablen und Grundlagen
 * ---------------------------------------------------------------------- */

.dup-portal {
	/*
	 * Farben. Akzent ist das dunkle Petrol der Website-Grundflaeche, nicht
	 * das Gold: Auf Petrol traegt weisse Schrift rund 7:1, auf Gold waren
	 * es 1,9:1 - dort musste jede Flaeche dunkle Schrift bekommen.
	 */
	--dup-accent: #275765;
	/* Beim Ueberfahren eine Spur heller (weisse Schrift bleibt bei ~5:1). */
	--dup-accent-soft: #35707f;
	--dup-accent-tint: rgba( 39, 87, 101, 0.1 );
	/* Schrift AUF dem Akzent: jetzt weiss statt dunkel. */
	--dup-accent-ink: #fff;
	--dup-heading: var( --color-content-heading, #121316 );
	--dup-text: var( --color-content-text, #33353d );
	--dup-muted: var( --color-content-faded, #999 );
	--dup-surface: var( --color-content-bg, #fff );
	--dup-surface-alt: var( --color-content-bg-alt, #f5f5f5 );
	--dup-border: var( --color-content-border, #e8e8e8 );
	--dup-border-strong: #d5d6da;
	--dup-bubble-other: var( --color-content-bg-alt, #f5f5f5 );

	/* Masse und Rhythmus. */
	--dup-list-width: 340px;
	/* Wird vom Skript auf die tatsaechlich freie Hoehe gesetzt, damit der
	 * Arbeitsbereich immer genau auf den Bildschirm passt und die Seite nicht
	 * scrollt. Der Wert hier greift nur, solange das Skript nicht geladen hat. */
	--dup-height: calc( 100vh - 64px );
	--dup-radius: 12px;
	--dup-radius-sm: 8px;
	--dup-radius-tail: 4px;
	--dup-radius-pill: 999px;
	--dup-pad: 20px;
	--dup-pad-lg: 24px;
	--dup-gap: 12px;
	--dup-bubble-max: 68%;

	/* Typografie – Impreza setzt 1rem = 20px, deshalb feste Pixelwerte. */
	--dup-font: 15px;
	--dup-font-sm: 13px;
	--dup-font-xs: 12px;
	--dup-line: 1.5;

	/* Wirkung. */
	--dup-shadow: 0 1px 2px rgba( 18, 19, 22, 0.04 ), 0 8px 24px rgba( 18, 19, 22, 0.06 );
	--dup-shadow-inset: inset 0 0 0 1px var( --dup-border );
	--dup-focus: var( --color-content-heading, #121316 );
	--dup-focus-width: 2px;
	--dup-motion: 180ms;
	--dup-ease: cubic-bezier( 0.2, 0, 0.2, 1 );
	--dup-skeleton: #ececee;
	--dup-skeleton-hi: #f7f7f8;

	position: relative;
	display: grid;
	grid-template-columns: var( --dup-list-width ) minmax( 0, 1fr );
	height: var( --dup-height );
	min-height: 440px;
	max-width: 100%;
	overflow: hidden;
	background: var( --dup-surface );
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius );
	box-shadow: var( --dup-shadow );
	/* Impreza vererbt line-height: 28px von html – hier bewusst neu setzen. */
	font-family: inherit;
	font-size: var( --dup-font );
	line-height: var( --dup-line );
	color: var( --dup-text );
	-webkit-text-size-adjust: 100%;
}

.dup-portal *,
.dup-portal *::before,
.dup-portal *::after {
	box-sizing: border-box;
}

/* Impreza setzt globale Abstaende auf p, Listen, Ueberschriften und dl. */
.dup-portal p,
.dup-portal dl,
.dup-portal dd,
.dup-portal dt,
.dup-portal figure,
.dup-portal fieldset {
	margin: 0;
	padding: 0;
}

.dup-portal h1,
.dup-portal h2,
.dup-portal h3,
.dup-portal h4,
.dup-portal h5,
.dup-portal h6 {
	margin: 0;
	padding: 0;
	color: var( --dup-heading );
	line-height: 1.25;
}

.dup-portal ul,
.dup-portal ol {
	margin: 0;
	padding: 0;
	list-style: none;
}

.dup-portal li {
	margin: 0;
}

.dup-portal fieldset {
	border: 0;
}

.dup-portal button {
	font: inherit;
	color: inherit;
	background: none;
	border: 0;
	text-align: left;
	cursor: pointer;
}

.dup-portal button[disabled],
.dup-portal input[disabled],
.dup-portal select[disabled],
.dup-portal textarea[disabled] {
	opacity: 0.55;
	cursor: not-allowed;
}

/* Grundlage fuer alle per hidden-Attribut gesteuerten Bereiche. */
.dup-portal [hidden] {
	display: none;
}

/*
 * Nur fuer Hilfstechnik sichtbarer Text (z. B. Beschriftung der Zurueck-Taste).
 *
 * .dup-sr-only ist derselbe Baustein unter dem Namen, den der DOM-Vertrag fuer
 * die Beschriftung des Suchfelds in der Werkzeugleiste vorsieht. Beide Namen
 * bleiben bestehen, damit vorhandenes Markup nicht umbenannt werden muss. Die
 * Fassung ohne .dup-portal gilt fuer Bausteine ausserhalb des Arbeitsbereichs,
 * etwa in der Kopfleiste.
 */
.dup-portal .dup-visually-hidden,
.dup-portal .dup-sr-only,
body.dup-portal-page .dup-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
	border: 0;
}

/* Dezente Bildlaufleisten in beiden Spalten. */
.dup-portal .dup-list__items,
.dup-portal .dup-overview__items,
.dup-portal .dup-messages,
.dup-portal .dup-files__items,
.dup-portal .dup-tab {
	scrollbar-width: thin;
	scrollbar-color: var( --dup-border-strong ) transparent;
	overscroll-behavior: contain;
}

.dup-portal .dup-list__items::-webkit-scrollbar,
.dup-portal .dup-overview__items::-webkit-scrollbar,
.dup-portal .dup-messages::-webkit-scrollbar,
.dup-portal .dup-files__items::-webkit-scrollbar,
.dup-portal .dup-tab::-webkit-scrollbar {
	width: 10px;
}

.dup-portal .dup-list__items::-webkit-scrollbar-thumb,
.dup-portal .dup-overview__items::-webkit-scrollbar-thumb,
.dup-portal .dup-messages::-webkit-scrollbar-thumb,
.dup-portal .dup-files__items::-webkit-scrollbar-thumb,
.dup-portal .dup-tab::-webkit-scrollbar-thumb {
	background-clip: content-box;
	background-color: var( --dup-border-strong );
	border: 3px solid transparent;
	border-radius: var( --dup-radius-pill );
}

/* -------------------------------------------------------------------------
 * 2. Fokus – muss ueberall sichtbar bleiben
 * ---------------------------------------------------------------------- */

.dup-portal :focus-visible {
	outline: var( --dup-focus-width ) solid var( --dup-focus );
	outline-offset: 2px;
	border-radius: var( --dup-radius-sm );
}

/*
 * Impreza erzwingt outline: none !important auf Eingabefeldern
 * (common/css/base/_general.css, Zeilen 108–112). Ohne eigenes !important
 * gaebe es fuer Tastaturbedienung keinen sichtbaren Fokus.
 */
.dup-portal input:focus-visible,
.dup-portal select:focus-visible,
.dup-portal textarea:focus-visible {
	outline: var( --dup-focus-width ) solid var( --dup-focus ) !important;
	outline-offset: 1px;
}

/* -------------------------------------------------------------------------
 * 3. Formularfelder (Suche, Verfassen, Stammdaten)
 * ---------------------------------------------------------------------- */

.dup-portal input[type="text"],
.dup-portal input[type="email"],
.dup-portal input[type="tel"],
.dup-portal input[type="date"],
.dup-portal input[type="number"],
.dup-portal input[type="search"],
.dup-portal select,
.dup-portal textarea {
	width: 100%;
	/* Impreza setzt min-height/height/line-height auf 3em – hier zuruecknehmen. */
	height: auto;
	min-height: 42px;
	padding: 9px 12px;
	font: inherit;
	font-size: var( --dup-font );
	line-height: 1.4;
	color: var( --dup-heading );
	background: var( --dup-surface );
	border: 1px solid var( --dup-border-strong );
	border-radius: var( --dup-radius-sm );
	transition: border-color var( --dup-motion ) var( --dup-ease ),
		box-shadow var( --dup-motion ) var( --dup-ease );
}

.dup-portal textarea {
	min-height: 84px;
	resize: vertical;
}

.dup-portal input::placeholder,
.dup-portal textarea::placeholder {
	color: var( --dup-muted );
	opacity: 1;
}

.dup-portal input:hover,
.dup-portal select:hover,
.dup-portal textarea:hover {
	border-color: var( --dup-muted );
}

.dup-portal input:focus,
.dup-portal select:focus,
.dup-portal textarea:focus {
	border-color: var( --dup-accent );
	box-shadow: 0 0 0 3px var( --dup-accent-tint );
}

.dup-portal input[aria-invalid="true"],
.dup-portal textarea[aria-invalid="true"],
.dup-portal .dup-field.is-error input,
.dup-portal .dup-field.is-error select,
.dup-portal .dup-field.is-error textarea {
	border-color: #b3261e;
}

.dup-portal select {
	padding-right: 34px;
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2333353d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: right 10px center;
	background-size: 16px 16px;
	-webkit-appearance: none;
	appearance: none;
}

/* -------------------------------------------------------------------------
 * 4. Schaltflaechen
 * ---------------------------------------------------------------------- */

.dup-portal .dup-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 40px;
	padding: 8px 18px;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	line-height: 1.4;
	letter-spacing: 0.01em;
	text-align: center;
	white-space: nowrap;
	color: var( --dup-heading );
	background: var( --dup-surface );
	border: 1px solid var( --dup-border-strong );
	border-radius: var( --dup-radius-pill );
	cursor: pointer;
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease ),
		color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-btn:hover {
	background: var( --dup-surface-alt );
	border-color: var( --dup-muted );
	box-shadow: none;
}

/*
 * Absende-Knoepfe gegen Impreza behaupten.
 *
 * Das Theme faerbt jeden Knopf mit type="submit" selbst, und zwar ueber eine
 * eigene Variable:
 *   [type="submit"]:not(.w-btn):not(.button) {
 *       --btn-background: var(--color-content-primary);   (das Gold)
 *       background: var(--btn-background);
 *       color: #fff !important;
 *   }
 * Diese Regel hat DIESELBE Spezifitaet wie ".dup-portal .dup-btn--primary"
 * und wird als eingebetteter Stil spaeter geladen - bei Gleichstand gewinnt
 * sie. Solange unser Akzent dasselbe Gold war, fiel das nicht auf; der
 * Senden-Knopf trug aber schon immer weisse Schrift auf Gold (1,8:1).
 *
 * Deshalb doppelt abgesichert:
 *   1. eine Klasse mehr im Selektor (.dup-btn UND .dup-btn--primary),
 *   2. die Variable des Themes selbst auf unseren Akzent gesetzt - dann malt
 *      auch die fremde Regel in unserer Farbe.
 * Die Fassung ohne .dup-btn bleibt fuer Knoepfe, die nur die Zusatzklasse
 * tragen.
 */
.dup-portal .dup-btn--primary,
.dup-portal .dup-btn.dup-btn--primary,
.dup-portal .dup-btn.dup-btn--primary:not( .w-btn ) {
	--btn-background: var( --dup-accent );
	--btn-color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-color: var( --dup-accent );
	/* Gegen-!important gegen die Regel oben. */
	color: var( --dup-accent-ink ) !important;
}

.dup-portal .dup-btn--primary:hover,
.dup-portal .dup-btn.dup-btn--primary:hover,
.dup-portal .dup-btn.dup-btn--primary:not( .w-btn ):hover {
	--btn-background: var( --dup-accent-soft );
	background: var( --dup-accent-soft );
	border-color: var( --dup-accent-soft );
}

.dup-portal .dup-btn--quiet {
	background: transparent;
	border-color: transparent;
	color: var( --dup-text );
}

.dup-portal .dup-btn--quiet:hover {
	background: var( --dup-surface-alt );
	border-color: transparent;
}

.dup-portal .dup-btn[disabled]:hover {
	background: var( --dup-surface );
	border-color: var( --dup-border-strong );
}

.dup-portal .dup-btn--primary[disabled]:hover,
.dup-portal .dup-btn.dup-btn--primary[disabled]:hover,
.dup-portal .dup-btn.dup-btn--primary:not( .w-btn )[disabled]:hover {
	--btn-background: var( --dup-accent );
	background: var( --dup-accent );
	border-color: var( --dup-accent );
}

/* Laufender Vorgang: Beschriftung bleibt lesbar, kleiner Ring zeigt Arbeit an. */
.dup-portal .dup-btn.is-loading {
	pointer-events: none;
	color: transparent !important; /* siehe Begruendung bei .dup-btn--primary */
}

.dup-portal .dup-btn.is-loading::after {
	content: "";
	position: absolute;
	width: 16px;
	height: 16px;
	border: 2px solid currentcolor;
	border-color: var( --dup-heading ) transparent var( --dup-heading ) transparent;
	border-radius: 50%;
	animation: dup-spin 700ms linear infinite;
}

/* -------------------------------------------------------------------------
 * 5. Linke Spalte – Patientenliste
 * ---------------------------------------------------------------------- */

.dup-portal .dup-portal__list {
	display: flex;
	flex-direction: column;
	min-width: 0;
	min-height: 0;
	background: var( --dup-surface );
	border-right: 1px solid var( --dup-border );
}

.dup-portal .dup-list__head {
	flex: 0 0 auto;
	padding: var( --dup-pad ) var( --dup-pad ) 14px;
	background: var( --dup-surface );
	border-bottom: 1px solid var( --dup-border );
}

/*
 * Der Elternteil steht bewusst mit im Selektor. Abschnitt 3 spricht das Feld
 * als .dup-portal input[type="search"] an - ein Typselektor mehr und damit
 * staerker als .dup-portal .dup-list__search allein. Ohne den Zusatz gewinnt
 * dort padding: 9px 12px, und der Text startet genau unter der Lupe.
 */
.dup-portal .dup-list__head .dup-list__search {
	padding-left: 38px;
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.6-3.6'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: 12px center;
	background-size: 17px 17px;
	border-radius: var( --dup-radius-pill );
	-webkit-appearance: none;
	appearance: none;
}

.dup-portal .dup-list__search::-webkit-search-cancel-button {
	-webkit-appearance: none;
	appearance: none;
}

.dup-portal .dup-list__filters {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 12px;
}

.dup-portal .dup-filter {
	padding: 5px 12px;
	font-size: var( --dup-font-xs );
	font-weight: 600;
	line-height: 1.4;
	color: var( --dup-text );
	background: var( --dup-surface-alt );
	border: 1px solid transparent;
	border-radius: var( --dup-radius-pill );
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-filter:hover {
	border-color: var( --dup-border-strong );
}

.dup-portal .dup-filter.is-active {
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-color: var( --dup-accent );
}

.dup-portal .dup-list__items {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

.dup-portal .dup-list__empty {
	flex: 0 0 auto;
	padding: 28px var( --dup-pad );
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
	text-align: center;
}

.dup-portal .dup-list__foot {
	flex: 0 0 auto;
	padding: 14px var( --dup-pad );
	background: var( --dup-surface );
	border-top: 1px solid var( --dup-border );
}

.dup-portal .dup-list__new {
	width: 100%;
}

/* --- Einzelner Listeneintrag ------------------------------------------- */

.dup-portal .dup-list__item {
	border-bottom: 1px solid var( --dup-border );
}

.dup-portal .dup-list__item:last-child {
	border-bottom: 0;
}

.dup-portal .dup-item {
	display: grid;
	grid-template-columns: auto minmax( 0, 1fr ) auto;
	align-items: center;
	gap: var( --dup-gap );
	width: 100%;
	padding: 12px 16px 12px 13px;
	background: transparent;
	border-left: 3px solid transparent;
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-item:hover {
	background: var( --dup-surface-alt );
}

.dup-portal .dup-item:focus-visible {
	outline-offset: -3px;
	border-radius: 0;
}

.dup-portal .dup-item__body {
	display: grid;
	gap: 2px;
	min-width: 0;
}

.dup-portal .dup-item__top {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px;
	min-width: 0;
}

.dup-portal .dup-item__name {
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font );
	font-weight: 600;
	color: var( --dup-heading );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-item__time {
	flex: 0 0 auto;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
	font-variant-numeric: tabular-nums;
}

.dup-portal .dup-item__excerpt {
	display: block;
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-item__badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 20px;
	height: 20px;
	padding: 0 6px;
	font-size: 11px;
	font-weight: 700;
	line-height: 1;
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-radius: var( --dup-radius-pill );
	font-variant-numeric: tabular-nums;
}

/* Ungelesen: kraeftigerer Text plus goldene Plakette. */
.dup-portal .dup-list__item.is-unread .dup-item__name,
.dup-portal .dup-item.is-unread .dup-item__name {
	font-weight: 700;
}

.dup-portal .dup-list__item.is-unread .dup-item__excerpt,
.dup-portal .dup-item.is-unread .dup-item__excerpt {
	color: var( --dup-text );
	font-weight: 500;
}

/* Aktive Auswahl: goldene Kante links, sehr helle Flaeche. */
.dup-portal .dup-list__item.is-active .dup-item,
.dup-portal .dup-item.is-active {
	background: var( --dup-accent-tint );
	border-left-color: var( --dup-accent );
}

.dup-portal .dup-list__item.is-active .dup-item:hover,
.dup-portal .dup-item.is-active:hover {
	background: var( --dup-accent-tint );
}

.dup-portal .dup-list__item.is-active .dup-avatar,
.dup-portal .dup-item.is-active .dup-avatar {
	border-color: var( --dup-accent );
}

/* --- Avatar ------------------------------------------------------------ */

.dup-portal .dup-avatar {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	line-height: 1;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border: 1px solid var( --dup-border );
	border-radius: 50%;
	user-select: none;
}

.dup-portal .dup-detail__head .dup-avatar {
	width: 50px;
	height: 50px;
	font-size: 17px;
}

/* -------------------------------------------------------------------------
 * 6. Rechte Spalte – Detailbereich
 * ---------------------------------------------------------------------- */

.dup-portal .dup-portal__detail {
	display: flex;
	flex-direction: column;
	min-width: 0;
	min-height: 0;
	overflow: hidden;
	background: var( --dup-surface );
}

.dup-portal .dup-detail__placeholder {
	margin: auto;
	padding: var( --dup-pad-lg );
	max-width: 34ch;
	font-size: var( --dup-font );
	color: var( --dup-muted );
	text-align: center;
}

/* Sicherheitsnetz, falls das Skript nur den Inhalt und nicht den Platzhalter schaltet. */
.dup-portal .dup-portal__detail:has( .dup-detail__content:not( [hidden] ) ) .dup-detail__placeholder {
	display: none;
}

.dup-portal .dup-detail__content:not( [hidden] ) {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	min-height: 0;
}

.dup-portal .dup-detail__head {
	display: flex;
	flex: 0 0 auto;
	flex-wrap: wrap;
	align-items: center;
	gap: 14px;
	padding: 18px var( --dup-pad-lg );
	border-bottom: 1px solid var( --dup-border );
}

.dup-portal .dup-detail__ident {
	min-width: 0;
}

.dup-portal .dup-detail__name {
	overflow: hidden;
	font-size: 20px;
	font-weight: 600;
	color: var( --dup-heading );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-detail__meta {
	margin-top: 2px;
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
}

.dup-portal .dup-detail__contact {
	display: grid;
	grid-template-columns: max-content minmax( 0, 1fr );
	gap: 2px 12px;
	align-content: center;
	margin-left: auto;
	max-width: 46%;
	font-size: var( --dup-font-sm );
}

.dup-portal .dup-detail__contact dt {
	color: var( --dup-muted );
}

.dup-portal .dup-detail__contact dd {
	overflow: hidden;
	color: var( --dup-text );
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* Zurueck-Schaltflaeche: nur in der mobilen Ebene sichtbar. */
.dup-portal .dup-detail__back {
	display: none;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	padding: 0;
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border: 1px solid var( --dup-border );
	border-radius: 50%;
}

.dup-portal .dup-detail__back::before {
	content: "";
	width: 10px;
	height: 10px;
	border-left: 2px solid currentcolor;
	border-bottom: 2px solid currentcolor;
	transform: rotate( 45deg ) translate( 1px, -1px );
}

/* --- Reiter ------------------------------------------------------------ */

.dup-portal .dup-detail__tabs {
	display: flex;
	flex: 0 0 auto;
	gap: 22px;
	padding: 0 var( --dup-pad-lg );
	border-bottom: 1px solid var( --dup-border );
}

.dup-portal .dup-tab-btn {
	padding: 12px 2px;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	letter-spacing: 0.02em;
	color: var( --dup-muted );
	background: none;
	border-bottom: 2px solid transparent;
	transition: color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-tab-btn:hover {
	color: var( --dup-heading );
}

.dup-portal .dup-tab-btn.is-active {
	color: var( --dup-heading );
	border-bottom-color: var( --dup-accent );
}

.dup-portal .dup-tab-btn:focus-visible {
	outline-offset: -2px;
}

.dup-portal .dup-tab {
	flex: 1 1 auto;
	min-height: 0;
}

.dup-portal .dup-tab:not( [hidden] ) {
	display: flex;
	flex-direction: column;
}

/* -------------------------------------------------------------------------
 * 7. Nachrichtenverlauf
 * ---------------------------------------------------------------------- */

.dup-portal .dup-messages {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	gap: 10px;
	min-height: 0;
	padding: var( --dup-pad ) var( --dup-pad-lg );
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	scroll-behavior: smooth;
}

.dup-portal .dup-message {
	display: flex;
	flex-direction: column;
	max-width: 100%;
}

.dup-portal .dup-message--own {
	align-items: flex-end;
}

.dup-portal .dup-message--other {
	align-items: flex-start;
}

.dup-portal .dup-message__bubble {
	max-width: var( --dup-bubble-max );
	padding: 10px 14px;
	font-size: var( --dup-font );
	line-height: 1.55;
	border-radius: var( --dup-radius );
}

.dup-portal .dup-message__bubble p {
	margin: 0;
	/* Zeilenumbrueche kommen aus dem Text, nie aus <br> – siehe Sicherheitsleitfaden. */
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

.dup-portal .dup-message__bubble p + p {
	margin-top: 0.6em;
}

/* Eigene Nachricht: Gold mit dunkler Schrift, Ecke unten rechts zeigt zum Absender. */
.dup-portal .dup-message--own .dup-message__bubble {
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-radius: var( --dup-radius ) var( --dup-radius ) var( --dup-radius-tail ) var( --dup-radius );
}

.dup-portal .dup-message--other .dup-message__bubble {
	color: var( --dup-text );
	background: var( --dup-bubble-other );
	border-radius: var( --dup-radius ) var( --dup-radius ) var( --dup-radius ) var( --dup-radius-tail );
}

.dup-portal .dup-message__time {
	margin-top: 4px;
	padding: 0 4px;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
	font-variant-numeric: tabular-nums;
}

/* Systemhinweise stehen mittig und ohne Blase. */
.dup-portal .dup-message--system {
	align-items: center;
}

.dup-portal .dup-message--system .dup-message__bubble {
	max-width: 80%;
	padding: 6px 12px;
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
	background: var( --dup-surface-alt );
	border: 1px dashed var( --dup-border-strong );
	border-radius: var( --dup-radius-pill );
	text-align: center;
}

/* Tagestrenner: mittige Beschriftung mit feiner Linie links und rechts. */
.dup-portal .dup-message-daysep {
	display: flex;
	align-items: center;
	gap: 12px;
	margin: 8px 0;
}

.dup-portal .dup-message-daysep::before,
.dup-portal .dup-message-daysep::after {
	content: "";
	flex: 1 1 auto;
	height: 1px;
	background: var( --dup-border );
}

.dup-portal .dup-message-daysep span {
	flex: 0 0 auto;
	font-size: var( --dup-font-xs );
	font-weight: 600;
	letter-spacing: 0.03em;
	color: var( --dup-muted );
}

/* --- Verfassbereich, klebt unten --------------------------------------- */

.dup-portal .dup-composer {
	display: flex;
	/* Umbruch erlaubt, damit die Fehlermeldung eine eigene Zeile bekommt
	 * statt sich neben Textfeld und Schaltfläche zu quetschen. */
	flex-wrap: wrap;
	flex: 0 0 auto;
	align-items: flex-end;
	gap: var( --dup-gap );
	padding: 14px var( --dup-pad-lg ) 16px;
	background: var( --dup-surface );
	border-top: 1px solid var( --dup-border );
}

.dup-portal .dup-composer__error {
	flex: 0 0 100%;
	order: -1;
	margin: 0;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: #b3261e;
}

.dup-portal .dup-composer__error.is-hint {
	font-weight: 400;
	color: var( --dup-muted );
}

.dup-portal .dup-composer__input {
	flex: 1 1 auto;
	min-height: 46px;
	max-height: 180px;
	padding: 11px 14px;
	line-height: 1.5;
	border-radius: var( --dup-radius );
	resize: none;
}

.dup-portal .dup-composer__send {
	flex: 0 0 auto;
	min-height: 46px;
}

/* -------------------------------------------------------------------------
 * 7b. Dateiaustausch
 *
 * Reihenfolge im Verfassbereich: .dup-composer ist ein umbrechender Flex-
 * Container. Alles, was ueber dem Textfeld stehen soll (Fehler, Dateiliste,
 * Fortschritt), belegt deshalb die volle Breite und bekommt order: -1.
 *
 * Farben der Anhaenge laufen ueber eigene Variablen, weil dieselben Bausteine
 * in zwei Umgebungen stehen: in der hellen Blase der Gegenseite und in der
 * goldenen eigenen Blase. Auf Gold ist Weiss verboten (~1.9:1) und --dup-muted
 * zu blass, deshalb werden die Variablen dort neu gesetzt statt die Regeln
 * doppelt zu schreiben.
 * ---------------------------------------------------------------------- */

.dup-portal {
	/* Vorschaubild: feste Kachel, damit object-fit: cover greifen kann. */
	--dup-thumb-w: 220px;
	--dup-thumb-h: 160px;
	--dup-card-w: 250px;

	/* Anhaenge auf hellem Grund. */
	--dup-attach-surface: var( --dup-surface, #fff );
	--dup-attach-border: var( --dup-border-strong, #d5d6da );
	--dup-attach-ink: var( --dup-heading, #121316 );
	--dup-attach-meta: var( --dup-muted, #999 );
	--dup-attach-icon-bg: var( --dup-surface-alt, #f5f5f5 );
	--dup-danger: #b3261e;
}

/* --- Runde Schaltflaechen im Verfassbereich ----------------------------- */

/*
 * Bueroklammer, Kamera und Dateianforderung teilen sich eine Form: gleiche
 * Groesse, gleiche Rundung, gleiches Verhalten beim Ueberfahren. Sie stehen
 * nebeneinander in derselben Zeile, und drei verschiedene Formen dort waeren
 * blosse Unruhe - unterschieden werden sie ueber das Symbol.
 *
 * :not( [hidden] ) ist Pflicht: Die Regel hat dieselbe Spezifitaet wie
 * .dup-portal [hidden] aus Abschnitt 1 und steht weiter unten. Ohne die
 * Verneinung liesse sich keine der drei je verbergen - und genau das tut das
 * Skript, wenn im gesperrten Verfassbereich nichts angehaengt werden darf.
 */
.dup-portal .dup-composer__attach:not( [hidden] ),
.dup-portal .dup-composer__camera:not( [hidden] ),
.dup-portal .dup-composer__request:not( [hidden] ) {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	/* Gleiche Hoehe wie .dup-composer__send, damit die Zeile nicht springt. */
	width: 46px;
	height: 46px;
	min-height: 46px;
	padding: 0;
	color: var( --dup-muted );
	background: transparent;
	border: 1px solid transparent;
	border-radius: 50%;
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-composer__attach > span,
.dup-portal .dup-composer__camera > span,
.dup-portal .dup-composer__request > span {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}

.dup-portal .dup-composer__attach svg,
.dup-portal .dup-composer__camera svg,
.dup-portal .dup-composer__request svg {
	display: block;
	width: 20px;
	height: 20px;
	pointer-events: none;
}

.dup-portal .dup-composer__attach:hover,
.dup-portal .dup-composer__attach:focus-visible,
.dup-portal .dup-composer__camera:hover,
.dup-portal .dup-composer__camera:focus-visible,
.dup-portal .dup-composer__request:hover,
.dup-portal .dup-composer__request:focus-visible {
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border-color: var( --dup-border-strong );
}

.dup-portal .dup-composer__attach[disabled]:hover,
.dup-portal .dup-composer__camera[disabled]:hover,
.dup-portal .dup-composer__request[disabled]:hover {
	color: var( --dup-muted );
	background: transparent;
	border-color: transparent;
}

/*
 * Die Anforderung ist kein Anhang, sondern ein Auftrag an die Gegenseite.
 * Deshalb bekommt sie als einzige der drei beim Ueberfahren die volle
 * Hauptfarbe - dunkle Schrift darauf, auf Gold ist Weiss verboten.
 */
.dup-portal .dup-composer__request:hover,
.dup-portal .dup-composer__request:focus-visible {
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-color: var( --dup-accent );
}

/* Offener Anforderungsblock: Der Schalter bleibt sichtbar eingerastet. */
.dup-portal .dup-composer__request[aria-expanded="true"] {
	color: var( --dup-heading );
	background: var( --dup-accent-tint );
	border-color: var( --dup-accent );
}

/* --- Ausgewaehlte, noch nicht gesendete Dateien ------------------------- */

/* :not( [hidden] ): gleiche Spezifitaet wie .dup-portal [hidden], steht aber
 * spaeter - ohne die Verneinung bliebe die Liste immer sichtbar. */
.dup-portal .dup-composer__files:not( [hidden] ) {
	display: flex;
	flex: 0 0 100%;
	flex-wrap: wrap;
	order: -1;
	gap: 8px;
	margin: 0;
	padding: 0;
}

/* Leer bleibt die Liste unsichtbar, sonst erzeugt sie eine leere Rasterzeile. */
.dup-portal .dup-composer__files:empty {
	display: none;
}

.dup-portal .dup-composer__file {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	max-width: 100%;
	padding: 4px 4px 4px 12px;
	font-size: var( --dup-font-xs );
	line-height: 1.4;
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius-pill );
}

.dup-portal .dup-composer__file.is-error {
	color: var( --dup-danger );
	border-color: var( --dup-danger );
}

.dup-portal .dup-composer__file-name {
	min-width: 0;
	max-width: 24ch;
	overflow: hidden;
	font-weight: 600;
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-composer__file-size {
	flex: 0 0 auto;
	color: var( --dup-muted );
	font-variant-numeric: tabular-nums;
}

.dup-portal .dup-composer__file-remove {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	padding: 0;
	font-size: 16px;
	line-height: 1;
	color: var( --dup-muted );
	background: transparent;
	border: 0;
	border-radius: 50%;
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-composer__file-remove:hover,
.dup-portal .dup-composer__file-remove:focus-visible {
	color: var( --dup-danger );
	background: var( --dup-surface );
}

/* --- Fortschritt beim Hochladen ---------------------------------------- */

/*
 * :not( [hidden] ) ist Pflicht. Die Regel hat dieselbe Spezifitaet wie
 * .dup-portal [hidden] aus Abschnitt 1 und steht weiter unten - ohne die
 * Verneinung stuende die Fortschrittszeile dauerhaft im Verfassbereich, auch
 * wenn gerade nichts uebertragen wird.
 */
.dup-portal .dup-composer__progress:not( [hidden] ) {
	display: flex;
	flex: 0 0 100%;
	flex-wrap: wrap;
	align-items: center;
	order: -1;
	gap: 6px 10px;
	margin: 0;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
}

.dup-portal .dup-composer__progress-text {
	flex: 0 0 auto;
	font-variant-numeric: tabular-nums;
}

/*
 * Bei mehreren Dateien geht ein blosser Prozentwert an der Frage vorbei, die
 * man sich beim Warten stellt: Wie viel steht noch aus? Deshalb daneben die
 * Zaehlung ("Datei 2 von 3"). Sie steht rechts, damit die Prozentzahl links
 * an derselben Stelle bleibt und nicht bei jeder Datei springt.
 */
.dup-portal .dup-composer__progress-count {
	flex: 0 0 auto;
	margin-left: auto;
	font-variant-numeric: tabular-nums;
}

/*
 * Traegt sowohl ein <progress> als auch ein <div> mit innerem Balken: der
 * Browser darf die Fuellung selbst zeichnen, muss aber nicht.
 */
.dup-portal .dup-composer__progress-bar {
	flex: 1 1 auto;
	width: 100%;
	height: 6px;
	overflow: hidden;
	background: var( --dup-surface-alt );
	border: 0;
	border-radius: var( --dup-radius-pill );
	color: var( --dup-accent );
	-webkit-appearance: none;
	appearance: none;
}

.dup-portal progress.dup-composer__progress-bar::-webkit-progress-bar {
	background: var( --dup-surface-alt );
	border-radius: var( --dup-radius-pill );
}

.dup-portal progress.dup-composer__progress-bar::-webkit-progress-value {
	background: var( --dup-accent );
	border-radius: var( --dup-radius-pill );
}

.dup-portal progress.dup-composer__progress-bar::-moz-progress-bar {
	background: var( --dup-accent );
	border-radius: var( --dup-radius-pill );
}

/*
 * Der Stand kommt entweder als eigene Eigenschaft --dup-progress am Element
 * darueber oder weiterhin als style="width: 42%" direkt am Balken. Beides
 * funktioniert: eine Angabe im style-Attribut schlaegt jede Regel von hier.
 */
.dup-portal .dup-composer__progress-fill:not( [hidden] ) {
	display: block;
	width: var( --dup-progress, 0% );
	height: 100%;
	background: var( --dup-accent );
	border-radius: var( --dup-radius-pill );
	transition: width var( --dup-motion ) var( --dup-ease );
}

/* --- Anhaenge in der Nachrichtenblase ---------------------------------- */

.dup-portal .dup-attachments {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 8px 0 0;
	padding: 0;
}

/*
 * Eine Nachricht darf nur aus Anhaengen bestehen. Der leere Absatz des
 * Skripts wuerde sonst eine Leerzeile ueber den Anhaengen erzeugen.
 */
.dup-portal .dup-message__bubble:has( .dup-attachments ) > p:empty {
	display: none;
}

.dup-portal .dup-message__bubble:has( .dup-attachments ) > p:empty + .dup-attachments {
	margin-top: 0;
}

.dup-portal .dup-attachment {
	position: relative;
	display: flex;
	max-width: 100%;
	min-width: 0;
}

.dup-portal .dup-attachment__link {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
	max-width: 100%;
	color: var( --dup-attach-ink );
	text-decoration: none;
	border-radius: var( --dup-radius-sm );
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-attachment__link:hover,
.dup-portal .dup-attachment__link:focus {
	color: var( --dup-attach-ink );
	text-decoration: none;
}

/* Bildvorschau: feste Kachel, Ausschnitt statt Verzerrung. */
.dup-portal .dup-attachment[data-kind="image"] .dup-attachment__link {
	display: block;
	overflow: hidden;
	line-height: 0;
	border-radius: var( --dup-radius-sm );
	/* Innerer Rahmen statt border: kein Groessensprung beim Laden. */
	box-shadow: inset 0 0 0 1px var( --dup-attach-border );
}

.dup-portal .dup-attachment__thumb {
	display: block;
	width: var( --dup-thumb-w );
	max-width: 100%;
	height: var( --dup-thumb-h );
	object-fit: cover;
	background: var( --dup-attach-icon-bg );
	border-radius: var( --dup-radius-sm );
	transition: filter var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-attachment__link:hover .dup-attachment__thumb {
	filter: brightness( 0.94 );
}

/*
 * Bei Bildern traegt die Kachel selbst die Aussage. Name und Groesse bleiben
 * fuer Vorlesewerkzeuge im Baum, verschwinden aber aus der Ansicht.
 */
.dup-portal .dup-attachment[data-kind="image"] .dup-attachment__body {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
	border: 0;
}

/* PDF und uebrige Dateien: waagrechte Karte. */
.dup-portal .dup-attachment[data-kind="pdf"] .dup-attachment__link,
.dup-portal .dup-attachment[data-kind="file"] .dup-attachment__link {
	width: var( --dup-card-w );
	padding: 8px 12px;
	background: var( --dup-attach-surface );
	border: 1px solid var( --dup-attach-border );
}

/* Platz fuer das Loeschkreuz, damit es den Namen nicht ueberdeckt. */
.dup-portal .dup-attachment[data-kind="pdf"]:has( .dup-attachment__delete ) .dup-attachment__link,
.dup-portal .dup-attachment[data-kind="file"]:has( .dup-attachment__delete ) .dup-attachment__link {
	padding-right: 38px;
}

.dup-portal .dup-attachment[data-kind="pdf"] .dup-attachment__link:hover,
.dup-portal .dup-attachment[data-kind="file"] .dup-attachment__link:hover {
	border-color: var( --dup-attach-ink );
}

.dup-portal .dup-attachment__icon {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 38px;
	font-size: 10px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var( --dup-attach-ink );
	background: var( --dup-attach-icon-bg );
	border: 1px solid var( --dup-attach-border );
	border-radius: 4px;
}

.dup-portal .dup-attachment__body {
	display: grid;
	gap: 1px;
	min-width: 0;
}

.dup-portal .dup-attachment__name {
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: var( --dup-attach-ink );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-attachment__link:hover .dup-attachment__name {
	text-decoration: underline;
}

.dup-portal .dup-attachment__size {
	font-size: var( --dup-font-xs );
	color: var( --dup-attach-meta );
	font-variant-numeric: tabular-nums;
}

/* --- Loeschen ---------------------------------------------------------- */

.dup-portal .dup-attachment__delete {
	position: absolute;
	top: 6px;
	right: 6px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	padding: 0;
	font-size: 14px;
	line-height: 1;
	color: var( --dup-attach-ink );
	/* Eigene Flaeche, damit das Kreuz auch auf einem dunklen Foto lesbar ist. */
	background: var( --dup-surface );
	border: 1px solid var( --dup-attach-border );
	border-radius: 50%;
	opacity: 0;
	transition: opacity var( --dup-motion ) var( --dup-ease ),
		color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-attachment__delete svg {
	display: block;
	width: 13px;
	height: 13px;
}

.dup-portal .dup-attachment:hover .dup-attachment__delete,
.dup-portal .dup-attachment:focus-within .dup-attachment__delete {
	opacity: 1;
}

.dup-portal .dup-attachment__delete:hover {
	color: var( --dup-danger );
	border-color: var( --dup-danger );
}

/* Auf einem Bild braucht der Fokusring einen hellen Gegenrand. */
.dup-portal .dup-attachment__delete:focus-visible {
	opacity: 1;
	box-shadow: 0 0 0 2px var( --dup-surface );
}

/* Ohne Zeigegeraet gibt es kein Ueberfahren – dort dauerhaft sichtbar. */
@media ( hover: none ) {

	.dup-portal .dup-attachment__delete {
		opacity: 1;
	}
}

/* --- Entfernter Anhang -------------------------------------------------- */

/*
 * Die Zeile bleibt stehen, damit der Verlauf keine Luecke bekommt. Sie ist
 * bewusst als Rest erkennbar: gestrichelt, kursiv, in der gedaempften Farbe.
 */
.dup-portal .dup-attachment__gone {
	display: inline-block;
	max-width: 100%;
	padding: 6px 12px;
	font-size: var( --dup-font-sm );
	font-style: italic;
	color: var( --dup-attach-meta );
	background: transparent;
	border: 1px dashed var( --dup-attach-border );
	border-radius: var( --dup-radius-sm );
}

/* --- Eigene, goldene Blase --------------------------------------------- */

/*
 * Die eigene Blase ist dunkel (Petrol). Die Anhangskarte wird deshalb mit
 * einem DUNKLEN Schleier abgesetzt, nicht mit einem hellen: Auf einem hellen
 * Schleier waere die weisse Schrift nur noch schemenhaft (~2,8:1), auf dem
 * dunklen traegt sie rund 9:1. Frueher, auf Gold, war es genau umgekehrt -
 * heller Schleier mit dunkler Schrift. Wer den Akzent aendert, muss auch
 * diese fuenf Werte mitdrehen.
 */
.dup-portal .dup-message--own .dup-message__bubble {
	--dup-attach-surface: rgba( 0, 0, 0, 0.2 );
	--dup-attach-border: rgba( 255, 255, 255, 0.28 );
	--dup-attach-ink: #fff;
	--dup-attach-meta: rgba( 255, 255, 255, 0.82 );
	--dup-attach-icon-bg: rgba( 0, 0, 0, 0.32 );
}

/* Loeschkreuz: dunkle Scheibe mit hellem Kreuz - eine weisse Scheibe traegt
 * das jetzt weisse Kreuz nicht mehr. */
.dup-portal .dup-message--own .dup-attachment__delete {
	background: rgba( 0, 0, 0, 0.42 );
}

.dup-portal .dup-message--own .dup-attachment[data-kind="pdf"] .dup-attachment__link:hover,
.dup-portal .dup-message--own .dup-attachment[data-kind="file"] .dup-attachment__link:hover {
	background: rgba( 0, 0, 0, 0.34 );
	border-color: rgba( 255, 255, 255, 0.55 );
}

/* --- Ablegen per Ziehen ------------------------------------------------- */

/*
 * Outline statt border: der Rahmen wird nach innen gezeichnet und verschiebt
 * damit weder Verlauf noch Verfassbereich um zwei Pixel.
 */
.dup-portal .dup-portal__detail.is-dropping {
	background: var( --dup-accent-tint );
	outline: 3px dashed var( --dup-accent );
	outline-offset: -8px;
}

.dup-portal .dup-portal__detail.is-dropping .dup-messages,
.dup-portal .dup-portal__detail.is-dropping .dup-composer {
	background: transparent;
}

/* -------------------------------------------------------------------------
 * 7c. Bild- und PDF-Anzeige
 *
 * Das Skript haengt die Anzeige an document.body, sie liegt also ausserhalb
 * von .dup-portal und erbt dessen Variablen nicht. Deshalb feste Werte und
 * eine eigene Verankerung an body.dup-portal-page.
 *
 * Bild und PDF teilen sich dieselbe Ueberlagerung; getauscht wird nur der
 * Inhalt unter der Kopfzeile. Deshalb ist die Ueberlagerung eine Spalte:
 * oben die Kopfzeile mit Dateiname, Herunterladen und Schliessen, darunter
 * das Bild oder der Rahmen mit der eingebauten PDF-Anzeige des Browsers.
 * ---------------------------------------------------------------------- */

/* Ausserhalb von .dup-portal gilt dessen box-sizing-Regel nicht. */
body.dup-portal-page .dup-lightbox,
body.dup-portal-page .dup-lightbox * {
	box-sizing: border-box;
}

/*
 * Die tragenden Eigenschaften bewusst OHNE body-Vorsatz.
 *
 * Fiele die Klasse dup-portal-page einmal weg – anderes Theme, Seiten-Cache,
 * ein Plugin, das body_class filtert –, laege die Anzeige sonst als normaler
 * Block mitten im Seitenfluss: ein Befund offen sichtbar, ohne Abdeckung. Der
 * gleiche Fehler war beim Sichtschutz schon einmal da.
 */
.dup-lightbox {
	position: fixed;
	inset: 0;
	/* Ueber der Adminleiste von WordPress (99999) und dem Impreza-Header (111). */
	z-index: 100000;
	display: flex;
	background: rgba( 18, 19, 22, 0.88 );
}

.dup-lightbox[hidden] {
	display: none;
}

/*
 * Rechtliches als leichtes Fenster: Impressum und Datenschutz aus der
 * Fusszeile. Bewusst zarter als die Dateianzeige - hellerer Schleier,
 * weiche Kanten, viel Weiss: Man liest kurz nach und ist mit Escape oder
 * einem Klick daneben wieder im Portal.
 */
body.dup-portal-page .dup-legal:not( [hidden] ) {
	position: fixed;
	inset: 0;
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 4vmin;
	background: rgba( 18, 19, 22, 0.45 );
	overscroll-behavior: contain;
	animation: dup-fade-in 160ms cubic-bezier( 0.2, 0, 0.2, 1 ) both;
}

body.dup-portal-page .dup-legal__card {
	position: relative;
	width: min( 760px, 100% );
	height: min( 720px, 88vh );
}

body.dup-portal-page .dup-legal__frame {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
	border-radius: 14px;
	background: #fff;
	box-shadow: 0 18px 60px rgba( 0, 0, 0, 0.28 );
}

body.dup-portal-page .dup-legal__close {
	position: absolute;
	top: -11px;
	right: -11px;
	z-index: 1;
	width: 28px;
	height: 28px;
	padding: 0;
	border: 1px solid #e7e7ea;
	border-radius: 999px;
	background: #fff;
	color: #33353d;
	/* Exakt mittig: Raster statt Zeilenhoehe - das Malzeichen sitzt sonst
	 * je nach Schrift sichtbar zu hoch. */
	display: grid;
	place-items: center;
	font-size: 17px;
	line-height: 1;
	cursor: pointer;
	box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.14 );
}

body.dup-portal-page .dup-legal__close:hover,
body.dup-portal-page .dup-legal__close:focus-visible {
	background: #f2f2f4;
}

body.dup-portal-page .dup-legal__close:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
}

@media ( max-width: 540px ) {
	body.dup-portal-page .dup-legal:not( [hidden] ) {
		padding: 0;
	}

	body.dup-portal-page .dup-legal__card {
		width: 100%;
		height: 100%;
	}

	body.dup-portal-page .dup-legal__frame {
		border-radius: 0;
	}

	/* Auf kleinen Schirmen füllt das Fenster alles - der Knopf wandert von
	 * der Ecke nach innen. */
	body.dup-portal-page .dup-legal__close {
		top: 10px;
		right: 10px;
		width: 32px;
		height: 32px;
		border: 0;
		background: rgba( 18, 19, 22, 0.06 );
		box-shadow: none;
	}

	body.dup-portal-page .dup-legal__close:focus-visible {
		outline: 2px solid #121316;
	}
}

body.dup-portal-page .dup-lightbox {
	position: fixed;
	inset: 0;
	z-index: 100000;
	display: flex;
	/* Spalte statt Zeile: die Kopfzeile steht ueber dem Inhalt, nicht daneben. */
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	padding: 5vmin;
	background: rgba( 18, 19, 22, 0.88 );
	overscroll-behavior: contain;
	animation: dup-fade-in 160ms cubic-bezier( 0.2, 0, 0.2, 1 ) both;
}

/*
 * Muss nach der display-Regel stehen: sonst gewinnt display: flex gegen die
 * Vorgabe des Browsers fuer [hidden] und die Anzeige bliebe immer offen.
 */
body.dup-portal-page .dup-lightbox[hidden] {
	display: none;
}

/* --- Kopfzeile ---------------------------------------------------------- */

body.dup-portal-page .dup-lightbox__bar {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	gap: 10px;
	width: min( 92vw, 1080px );
	max-width: 100%;
	padding: 7px 7px 7px 16px;
	/* Weiss ist hier zulaessig: der Grund ist dunkel, nicht golden. */
	color: #fff;
	background: rgba( 255, 255, 255, 0.08 );
	border: 1px solid rgba( 255, 255, 255, 0.22 );
	border-radius: 10px;
	font-family: inherit;
	font-size: 14px;
	line-height: 1.4;
}

body.dup-portal-page .dup-lightbox__name {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	font-weight: 600;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/*
 * Impreza faerbt Verweise im Inhaltsbereich. Die Anzeige liegt zwar am body,
 * eine Farbangabe an derselben Stelle kostet aber nichts und haelt die
 * Beschriftung auf dem dunklen Grund sicher lesbar.
 */
body.dup-portal-page .dup-lightbox__download,
body.dup-portal-page .dup-lightbox__download:hover,
body.dup-portal-page .dup-lightbox__download:focus {
	color: #fff;
	text-decoration: none;
}

body.dup-portal-page .dup-lightbox__download {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	min-height: 36px;
	padding: 6px 16px;
	font-family: inherit;
	font-size: 13px;
	font-weight: 600;
	line-height: 1.4;
	white-space: nowrap;
	background: rgba( 255, 255, 255, 0.14 );
	border: 1px solid rgba( 255, 255, 255, 0.38 );
	border-radius: 999px;
	transition: background-color 160ms cubic-bezier( 0.2, 0, 0.2, 1 );
}

body.dup-portal-page .dup-lightbox__download:hover,
body.dup-portal-page .dup-lightbox__download:focus-visible {
	background: rgba( 255, 255, 255, 0.28 );
}

/* Sitzt jetzt in der Kopfzeile, nicht mehr frei ueber dem Bild. */
body.dup-portal-page .dup-lightbox__close {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	font: inherit;
	font-size: 24px;
	line-height: 1;
	color: #fff;
	background: rgba( 255, 255, 255, 0.14 );
	border: 1px solid rgba( 255, 255, 255, 0.38 );
	border-radius: 50%;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
	transition: background-color 160ms cubic-bezier( 0.2, 0, 0.2, 1 );
}

body.dup-portal-page .dup-lightbox__close:hover {
	background: rgba( 255, 255, 255, 0.28 );
}

/* Ausserhalb von .dup-portal greift die Fokusregel aus Abschnitt 2 nicht. */
body.dup-portal-page .dup-lightbox__close:focus-visible,
body.dup-portal-page .dup-lightbox__download:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
}

/* --- Inhalt ------------------------------------------------------------- */

/*
 * Bild und PDF wechseln ueber das hidden-Attribut. Deshalb :not( [hidden] ):
 * eine Angabe des Autors zu display schlaegt sonst die Vorgabe des Browsers,
 * und beide Flaechen staenden gleichzeitig da.
 */
body.dup-portal-page .dup-lightbox__image:not( [hidden] ) {
	display: block;
	flex: 0 1 auto;
	width: auto;
	max-width: 92vw;
	height: auto;
	min-height: 0;
	max-height: 88vh;
	object-fit: contain;
	background: #121316;
	border-radius: 8px;
	box-shadow: 0 20px 60px rgba( 0, 0, 0, 0.5 );
}

/*
 * Die eingebaute PDF-Anzeige des Browsers bringt Blaettern, Zoom und Suche
 * mit. Sie fuellt den Rest der Ueberlagerung; die Grenzen 92vw/88vh gelten
 * wie beim Bild, damit die Kopfzeile immer sichtbar bleibt.
 */
body.dup-portal-page .dup-lightbox__pdf:not( [hidden] ) {
	display: block;
	flex: 1 1 auto;
	width: min( 92vw, 1080px );
	max-width: 92vw;
	height: 100%;
	min-height: 0;
	max-height: 88vh;
	/* Weisse Flaeche, damit das Blatt vor dem Laden nicht schwarz aufblitzt. */
	background: #fff;
	border: 0;
	border-radius: 8px;
	box-shadow: 0 20px 60px rgba( 0, 0, 0, 0.5 );
}

/* Sicherheitsnetz fuer alles Weitere, das die Anzeige ueber hidden schaltet. */
body.dup-portal-page .dup-lightbox [hidden] {
	display: none;
}

/* -------------------------------------------------------------------------
 * 7d. Anhaenge in schmalen Fenstern und Bedienpraeferenzen
 * ---------------------------------------------------------------------- */

@media ( max-width: 600px ) {

	.dup-portal {
		--dup-thumb-w: 168px;
		--dup-thumb-h: 126px;
		--dup-card-w: 210px;
	}

	/* Die Verneinung muss mitwandern, sonst gewinnt diese Regel gegen die
	 * Vorgabe des Browsers fuer [hidden] und die Schaltflaeche bliebe stehen. */
	.dup-portal .dup-composer__attach:not( [hidden] ),
	.dup-portal .dup-composer__camera:not( [hidden] ),
	.dup-portal .dup-composer__request:not( [hidden] ) {
		width: 42px;
		height: 42px;
		min-height: 42px;
	}

	body.dup-portal-page .dup-lightbox {
		padding: 3vmin;
	}

	body.dup-portal-page .dup-lightbox__image:not( [hidden] ) {
		max-width: 96vw;
	}

	body.dup-portal-page .dup-lightbox__bar {
		width: 96vw;
		padding-left: 12px;
	}

	body.dup-portal-page .dup-lightbox__pdf:not( [hidden] ) {
		width: 96vw;
		max-width: 96vw;
	}

	/* Enger, damit Dateiname, Herunterladen und Schliessen in eine Zeile passen. */
	body.dup-portal-page .dup-lightbox__download {
		padding: 6px 12px;
	}
}

@media ( prefers-reduced-motion: reduce ) {

	body.dup-portal-page .dup-lightbox,
	body.dup-portal-page .dup-lightbox * {
		animation-duration: 1ms !important; /* Nutzerpraeferenz schlaegt jede Regel. */
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
	}

	.dup-portal .dup-attachment__link:hover .dup-attachment__thumb {
		filter: none;
	}
}

@media ( forced-colors: active ) {

	.dup-portal .dup-composer__file,
	.dup-portal .dup-attachment__link,
	.dup-portal .dup-attachment__icon,
	.dup-portal .dup-attachment__gone,
	.dup-portal .dup-attachment__delete,
	body.dup-portal-page .dup-lightbox__close {
		border: 1px solid CanvasText;
	}

	/* Im Kontrastmodus faellt die Deckkraft weg – sonst waere das Kreuz weg. */
	.dup-portal .dup-attachment__delete {
		opacity: 1;
	}
}

/* -------------------------------------------------------------------------
 * 8. Stammdatenformular
 * ---------------------------------------------------------------------- */

.dup-portal .dup-tab[data-tab="stammdaten"]:not( [hidden] ) {
	display: block;
	padding: var( --dup-pad ) var( --dup-pad-lg ) var( --dup-pad-lg );
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

/* Das Raster sitzt auf dem inneren Wrapper, damit Einleitung und Schaltflächen
 * darunter über die volle Breite laufen statt als Rasterzellen zu landen. */
.dup-portal .dup-form__grid {
	display: grid;
	grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	gap: 16px 20px;
	align-items: start;
	max-width: 780px;
}

.dup-portal .dup-form__grid > * {
	min-width: 0;
}

/* Ein Feld ist entweder .dup-field oder ein direkt eingesetztes <label>/<p>. */
.dup-portal .dup-field,
.dup-portal .dup-patient-form > label,
.dup-portal .dup-patient-form > p:not( .dup-form__actions ) {
	display: flex;
	flex-direction: column;
	gap: 5px;
	min-width: 0;
	margin: 0;
}

.dup-portal .dup-field > span,
.dup-portal .dup-field__label,
.dup-portal .dup-patient-form label > span:first-child {
	font-size: var( --dup-font-xs );
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var( --dup-muted );
}

.dup-portal .dup-field__hint {
	font-size: var( --dup-font-xs );
	font-weight: 400;
	letter-spacing: 0;
	text-transform: none;
	color: var( --dup-muted );
}

/* Textflaechen, Anschrift und Etiketten laufen ueber die volle Breite. */
.dup-portal .dup-field--wide,
.dup-portal .dup-patient-form > .dup-form__actions,
.dup-portal .dup-patient-form fieldset,
.dup-portal .dup-field:has( textarea ) {
	grid-column: 1 / -1;
}

/* Postleitzahl und Ort teilen sich eine Zeile. */
.dup-portal .dup-field--zip {
	max-width: 160px;
}

.dup-portal .dup-patient-form fieldset {
	display: grid;
	grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	gap: 16px 20px;
	padding: 0;
}

.dup-portal .dup-patient-form legend {
	grid-column: 1 / -1;
	padding: 0;
	font-size: var( --dup-font-xs );
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var( --dup-muted );
}

.dup-portal .dup-patient-form textarea[name="notes"] {
	min-height: 110px;
}

.dup-portal .dup-form__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px 14px;
	margin: 6px 0 0;
	padding-top: 16px;
	border-top: 1px solid var( --dup-border );
}

.dup-portal .dup-form__hint {
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
}

.dup-portal .dup-form__hint.is-success {
	color: #1e7a4c;
	font-weight: 600;
}

.dup-portal .dup-form__hint.is-error {
	color: #b3261e;
	font-weight: 600;
}

.dup-portal .dup-form__hint.is-busy {
	color: var( --dup-muted );
	font-style: italic;
}

/* -------------------------------------------------------------------------
 * 8b. Sitzungshinweis
 *
 * Wird vom Skript ganz oben in .dup-portal eingehängt. Da .dup-portal ein
 * zweispaltiges Raster ist, muss der Hinweis ausdrücklich über beide Spalten
 * laufen, sonst landet er in der Spalte der Liste.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-portal__notice {
	grid-column: 1 / -1;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var( --dup-gap );
	padding: 12px var( --dup-pad-lg );
	background: #fdf3d4;
	border-bottom: 1px solid var( --dup-border );
	color: var( --dup-heading );
}

.dup-portal .dup-portal__notice-text {
	margin: 0;
	font-size: var( --dup-font-sm );
	font-weight: 600;
}

.dup-portal .dup-portal__notice-action {
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: var( --dup-heading );
	text-decoration: underline;
}

/* -------------------------------------------------------------------------
 * 9. Ansicht der Patient:innen
 * ---------------------------------------------------------------------- */

/* Patient:innen sehen ausschliesslich die eigene Unterhaltung. */
.dup-portal[data-view="patient"] {
	grid-template-columns: minmax( 0, 1fr );
}

.dup-portal[data-view="patient"] .dup-portal__list,
.dup-portal[data-view="patient"] .dup-detail__back {
	display: none;
}

/* -------------------------------------------------------------------------
 * 10. Ladezustand und Skeleton
 * ---------------------------------------------------------------------- */

.dup-portal.is-loading,
.dup-portal .is-loading {
	cursor: progress;
}

.dup-portal .dup-portal__list.is-loading .dup-list__items,
.dup-portal .dup-list__items.is-loading,
.dup-portal .dup-portal__detail.is-loading .dup-detail__content {
	pointer-events: none;
}

.dup-portal .dup-messages.is-loading {
	opacity: 0.6;
	transition: opacity var( --dup-motion ) var( --dup-ease );
}

/*
 * Skeleton: das Skript setzt fuer Platzhalterzeilen die Klasse
 * dup-list__item--skeleton auf einen normal aufgebauten Listeneintrag.
 * Alle Textknoten bleiben leer, die Breiten kommen von hier.
 */
.dup-portal .dup-skeleton,
.dup-portal .dup-list__item--skeleton .dup-avatar,
.dup-portal .dup-list__item--skeleton .dup-item__name,
.dup-portal .dup-list__item--skeleton .dup-item__time,
.dup-portal .dup-list__item--skeleton .dup-item__excerpt {
	color: transparent;
	background-color: var( --dup-skeleton );
	background-image: linear-gradient(
		90deg,
		var( --dup-skeleton ) 0%,
		var( --dup-skeleton-hi ) 50%,
		var( --dup-skeleton ) 100%
	);
	background-size: 300% 100%;
	border-color: transparent;
	border-radius: 6px;
	animation: dup-shimmer 1.4s ease-in-out infinite;
	user-select: none;
}

.dup-portal .dup-list__item--skeleton {
	pointer-events: none;
}

.dup-portal .dup-list__item--skeleton .dup-avatar {
	border-radius: 50%;
}

.dup-portal .dup-list__item--skeleton .dup-item__name {
	width: 58%;
	height: 12px;
}

.dup-portal .dup-list__item--skeleton .dup-item__time {
	width: 34px;
	height: 10px;
}

.dup-portal .dup-list__item--skeleton .dup-item__excerpt {
	width: 82%;
	height: 10px;
	margin-top: 4px;
}

.dup-portal .dup-list__item--skeleton .dup-item__badge {
	display: none;
}

@keyframes dup-shimmer {
	from {
		background-position: 150% 0;
	}

	to {
		background-position: -150% 0;
	}
}

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

@keyframes dup-fade-in {
	from {
		opacity: 0;
		transform: translateY( 4px );
	}

	to {
		opacity: 1;
		transform: none;
	}
}

.dup-portal .dup-message.is-new {
	animation: dup-fade-in 220ms var( --dup-ease ) both;
}

/* -------------------------------------------------------------------------
 * 11. Schmale Fenster
 * ---------------------------------------------------------------------- */

/* Kontaktdaten passen im Kopf nicht mehr neben den Namen. */
@media ( max-width: 1100px ) {

	.dup-portal .dup-detail__contact {
		flex: 1 0 100%;
		max-width: none;
		margin-left: 0;
		padding-top: 12px;
		border-top: 1px solid var( --dup-border );
	}
}

@media ( max-width: 1024px ) {

	.dup-portal {
		--dup-list-width: 290px;
		--dup-pad-lg: 18px;
		--dup-pad: 16px;
		--dup-bubble-max: 78%;
	}
}

/* Eine Spalte, Detail als Ebene darueber. */
@media ( max-width: 860px ) {

	.dup-portal {
		grid-template-columns: minmax( 0, 1fr );
		/* Das Skript misst den tatsaechlich freien Platz (--dup-height,
		 * Schirm minus Leiste minus Fusszeile) - der Schaetzwert in dvh ist
		 * nur das Netz ohne JavaScript. Ein fester dvh-Wert schob am Telefon
		 * den Senden-Knopf unter die Bildschirmkante. */
		height: var( --dup-height, min( 82dvh, 760px ) );
		min-height: 460px;
	}

	.dup-portal .dup-portal__list {
		border-right: 0;
	}

	/*
	 * Nur die Ordinationsansicht kennt zwei Ebenen. In der Patientenansicht
	 * gibt es keine Liste und damit nie ein --detail-open, der Detailbereich
	 * muss dort im normalen Fluss bleiben.
	 */
	.dup-portal:not( [data-view="patient"] ) .dup-portal__detail {
		position: absolute;
		inset: 0;
		z-index: 2; /* bleibt unter dem Impreza-Header (z-index 111). */
		visibility: hidden;
		opacity: 0;
		transform: translateX( 14px );
		transition: opacity var( --dup-motion ) var( --dup-ease ),
			transform var( --dup-motion ) var( --dup-ease ),
			visibility 0s linear var( --dup-motion );
	}

	.dup-portal.dup-portal--detail-open .dup-portal__detail {
		visibility: visible;
		opacity: 1;
		transform: none;
		transition-delay: 0s;
	}

	/* Die verdeckte Liste darf weder Fokus noch Vorlesereihenfolge stoeren. */
	.dup-portal.dup-portal--detail-open .dup-portal__list {
		visibility: hidden;
	}

	/* Auf der Ebene ist der Platzhalter sinnlos – sie oeffnet nur mit Auswahl. */
	.dup-portal:not( [data-view="patient"] ) .dup-detail__placeholder {
		display: none;
	}

	.dup-portal .dup-detail__back:not( [hidden] ) {
		display: inline-flex;
		/* Die Beschriftung ist fuers Vorlesen gedacht - sichtbar quoll sie
		 * links und rechts aus dem 38px-Kreis und ragte am Telefon aus dem
		 * Bild. Nur der Pfeil (::before) bleibt zu sehen. */
		font-size: 0;
		overflow: hidden;
	}

	.dup-portal .dup-detail__head {
		gap: 12px;
		padding: 14px var( --dup-pad );
	}

	.dup-portal .dup-detail__name {
		font-size: 18px;
	}

	/* In der offenen Unterhaltung hat die Übersichts-Werkzeugleiste
	 * (Ansichtswahl, Suche, Neue Patientin) nichts verloren - der
	 * Zurück-Pfeil führt zur Übersicht, dort ist sie wieder da. */
	.dup-portal.dup-portal--detail-open .dup-views {
		display: none;
	}

	/* Der Kopf muss klein bleiben: Jede Zeile hier fehlt dem Verlauf. */
	.dup-portal .dup-detail__head .dup-avatar {
		width: 38px;
		height: 38px;
		font-size: 14px;
	}

	/* Name NEBEN dem Avatar statt darunter: Ohne flex-Basis 0 bestimmte die
	 * längste Meta-Zeile die Wunschbreite, und der Block rutschte als
	 * eigene Zeile unter Pfeil und Avatar - viel weißer Leerraum. */
	.dup-portal .dup-detail__ident {
		flex: 1 1 0;
	}

	.dup-portal .dup-detail__meta {
		font-size: 12px;
		/* Zwei Zeilen reichen als Überblick - alles Weitere steht im
		 * Stammdaten-Reiter. */
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}

	/* Der Verlauf darf nie auf einen Streifen zusammenfallen ... */
	.dup-portal .dup-messages {
		min-height: 130px;
	}

	/* ... und wenn die Spalte dann doch hoeher ist als der Schirm, rollt die
	 * Ebene, statt den Senden-Knopf unter die Kante zu schieben. */
	.dup-portal:not( [data-view="patient"] ) .dup-portal__detail {
		overflow-y: auto;
		-webkit-overflow-scrolling: touch;
	}

	.dup-portal .dup-patient-form,
	.dup-portal .dup-patient-form fieldset {
		grid-template-columns: minmax( 0, 1fr );
	}

	.dup-portal .dup-field--zip {
		max-width: none;
	}
}

@media ( max-width: 600px ) {

	.dup-portal {
		--dup-bubble-max: 86%;
		--dup-pad: 14px;
		--dup-pad-lg: 14px;
		border-radius: var( --dup-radius-sm );
		/* Gemessene Hoehe auch hier; das dvh-Netz nur ohne JavaScript. */
		height: var( --dup-height, 90dvh );
	}

	/* E-Mail-Zeile raus aus dem Kopf: Sie steht in den Stammdaten, und im
	 * Kopf drueckt sie nur den Verlauf zusammen. */
	.dup-portal .dup-detail__contact {
		display: none;
	}

	.dup-portal .dup-detail__head {
		gap: 10px;
		padding: 10px 14px;
	}

	.dup-portal .dup-detail__name {
		font-size: 16px;
	}

	/* Dateien-, Offen- und Erledigt-Knoepfe eine Stufe kompakter. */
	.dup-portal .dup-files__toggle:not( [hidden] ),
	.dup-portal .dup-detail__done {
		height: 34px;
		padding: 0 10px;
		font-size: 12px;
	}

	.dup-portal .dup-composer {
		gap: 8px;
		/* Engeres Polster: Jede gesparte Zeile kommt dem Verlauf zugute. */
		padding-top: 10px;
		padding-bottom: 12px;
	}

	.dup-portal .dup-composer__send {
		height: 42px;
		min-height: 42px;
	}

	.dup-portal .dup-composer__send {
		padding: 8px 14px;
	}
}

/* -------------------------------------------------------------------------
 * 12. Bedienpraeferenzen
 * ---------------------------------------------------------------------- */

@media ( prefers-reduced-motion: reduce ) {

	.dup-portal,
	.dup-portal *,
	.dup-portal *::before,
	.dup-portal *::after {
		animation-duration: 1ms !important; /* Nutzerpraeferenz schlaegt jede Regel. */
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
		scroll-behavior: auto !important;
	}

	.dup-portal .dup-portal__detail {
		transform: none;
	}
}

@media ( forced-colors: active ) {

	.dup-portal,
	.dup-portal .dup-message__bubble,
	.dup-portal .dup-btn,
	.dup-portal .dup-item__badge,
	.dup-portal .dup-avatar {
		border: 1px solid CanvasText;
	}

	.dup-portal .dup-list__item.is-active .dup-item,
	.dup-portal .dup-item.is-active {
		outline: 2px solid Highlight;
		outline-offset: -2px;
	}
}

/* -------------------------------------------------------------------------
 * 13. Werkzeugleiste der Uebersicht
 *
 * Erstes Kind von .dup-portal__detail und nur in der Ordinationsansicht
 * vorhanden. Eine schmale Zeile, die den Inhalt darunter nicht bedraengt:
 * links die drei Ansichten als Symbolgruppe, in der Mitte die Suche, rechts
 * Sortierfeld, Richtungsschalter und das Anlegen. Die aktive Ansicht ist
 * gefuellt, damit auf einen Blick klar ist, worin man gerade steckt - ein
 * reiner Farbwechsel der Schrift reicht dafuer nicht, weil Gold auf Weiss
 * nicht lesbar ist.
 *
 * Suche und Anlegen stehen hier, weil die Uebersicht der Startbildschirm ist
 * und dort die linke Spalte entfaellt. Ohne sie gaebe es beim Oeffnen des
 * Portals weder eine Suche noch einen Weg zu einem neuen Eintrag.
 * ---------------------------------------------------------------------- */

/*
 * Kein space-between: Die Zeile ordnet sich ueber die Elemente selbst. Die
 * Suche nimmt den freien Platz in der Mitte, die Sortierung schiebt sich mit
 * margin-left: auto nach rechts. So bleibt die Aufteilung auch dann richtig,
 * wenn ein Teil einmal fehlt.
 */
.dup-portal .dup-views {
	display: flex;
	flex: 0 0 auto;
	flex-wrap: wrap;
	align-items: center;
	justify-content: flex-start;
	gap: 10px 12px;
	padding: 10px var( --dup-pad-lg );
	background: var( --dup-surface );
	border-bottom: 1px solid var( --dup-border );
}

/* --- Ansichtsumschalter ------------------------------------------------- */

.dup-portal .dup-views__modes {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	gap: 2px;
	padding: 3px;
	background: var( --dup-surface-alt );
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius-pill );
}

.dup-portal .dup-view-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	color: var( --dup-muted );
	background: transparent;
	border: 0;
	border-radius: var( --dup-radius-pill );
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease );
}

/* Der Klick muss immer die Schaltflaeche treffen, nie das Symbol darin. */
.dup-portal .dup-view-btn svg,
.dup-portal .dup-views__dir svg,
.dup-portal .dup-overview__edit svg {
	display: block;
	width: 18px;
	height: 18px;
	pointer-events: none;
}

.dup-portal .dup-view-btn:hover {
	color: var( --dup-heading );
	background: var( --dup-surface );
}

.dup-portal .dup-view-btn[aria-pressed="true"] {
	/* Auf Gold niemals Weiss – siehe --dup-accent-ink. */
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
}

.dup-portal .dup-view-btn[aria-pressed="true"]:hover {
	background: var( --dup-accent-soft );
}

/*
 * Abschnitt 2 setzt im Fokus border-radius auf --dup-radius-sm. Bei runden
 * Schaltflaechen wuerde der Ring dadurch eckig – deshalb hier zuruecksetzen.
 */
.dup-portal .dup-view-btn:focus-visible {
	border-radius: var( --dup-radius-pill );
	outline-offset: -1px;
}

/* --- Suche -------------------------------------------------------------- */

.dup-portal .dup-views__search:not( [hidden] ) {
	display: flex;
	/* Nimmt den freien Platz zwischen Ansichtswahl und Sortierung. */
	flex: 1 1 200px;
	align-items: center;
	/* Lieber eine Zeile umbrechen als ein Suchfeld, in das nichts hineinpasst. */
	min-width: 150px;
	max-width: 340px;
}

/*
 * Abschnitt 3 gibt jedem Feld 42px Hoehe und volle Breite. In der schmalen
 * Werkzeugleiste waere das zu wuchtig, deshalb hier eigene Masse - wie beim
 * Sortierfeld daneben.
 *
 * Der Elternteil steht mit im Selektor, und zwar zwingend: Abschnitt 3 fasst
 * das Feld als .dup-portal input[type="search"] an. Das ist ein Typselektor
 * mehr und schlaegt eine Klasse mehr nicht auf - aber .dup-portal
 * .dup-views__search-input allein hat genau eine Klasse weniger und verliert.
 * Das Sortierfeld daneben braucht den Zusatz nicht, weil select dort ohne
 * Attributselektor angesprochen wird.
 */
.dup-portal .dup-views__search .dup-views__search-input {
	width: 100%;
	min-width: 0;
	min-height: 34px;
	padding: 5px 12px 5px 34px;
	font-size: var( --dup-font-sm );
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.6-3.6'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: 11px center;
	background-size: 15px 15px;
	border-radius: var( --dup-radius-pill );
	-webkit-appearance: none;
	appearance: none;
}

/* Das eigene Kreuz des Browsers passt weder zur Groesse noch zur Farbe. */
.dup-portal .dup-views__search-input::-webkit-search-cancel-button {
	-webkit-appearance: none;
	appearance: none;
}

/* --- Sortierung --------------------------------------------------------- */

.dup-portal .dup-views__sort:not( [hidden] ) {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	gap: 8px;
	min-width: 0;
	margin-left: auto;
}

.dup-portal .dup-views__sort-label {
	flex: 0 0 auto;
	font-size: var( --dup-font-xs );
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var( --dup-muted );
	white-space: nowrap;
}

/* Abschnitt 3 gibt jedem select volle Breite und 42px Hoehe – in der
 * Werkzeugleiste waere das zu wuchtig. */
.dup-portal .dup-views__sort-select {
	width: auto;
	min-width: 0;
	min-height: 34px;
	padding: 5px 30px 5px 11px;
	font-size: var( --dup-font-sm );
	background-position: right 9px center;
	background-size: 14px 14px;
	border-radius: var( --dup-radius-sm );
}

.dup-portal .dup-views__dir {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	padding: 0;
	color: var( --dup-text );
	background: var( --dup-surface );
	border: 1px solid var( --dup-border-strong );
	border-radius: var( --dup-radius-sm );
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-views__dir:hover {
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border-color: var( --dup-muted );
}

.dup-portal .dup-views__dir svg {
	transition: transform var( --dup-motion ) var( --dup-ease );
}

/*
 * Ein einziges Pfeilsymbol genuegt fuer beide Richtungen: Das Markup traegt
 * einen nach unten zeigenden Pfeil (absteigend), aufsteigend dreht das CSS
 * ihn. Das Skript muss also nur data-dir umschalten und nichts austauschen.
 */
.dup-portal .dup-views__dir[data-dir="asc"] svg {
	transform: rotate( 180deg );
}

/* --- Neue Patientin anlegen --------------------------------------------- */

/*
 * Dieselbe Schaltflaeche wie im Fuss der linken Spalte, nur flacher: mit den
 * 40px aus Abschnitt 4 waere sie das hoechste Stueck der Zeile und die
 * Werkzeugleiste wuerde spuerbar wachsen.
 */
.dup-portal .dup-views__new:not( [hidden] ) {
	flex: 0 0 auto;
	min-height: 34px;
	padding: 6px 16px;
}

/* -------------------------------------------------------------------------
 * 14. Uebersicht – Kacheln und Liste
 *
 * Beide Layouts benutzen dasselbe Markup, umgeschaltet wird nur ueber
 * data-layout am <ul>. Der Streifen fuer Ungelesenes sitzt deshalb auf der
 * Oeffnen-Schaltflaeche und nicht auf der Kachel: so klebt er in beiden
 * Layouts an derselben Kante, ohne dass sich die Masse verschieben.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-overview {
	flex: 1 1 auto;
	min-height: 0;
}

.dup-portal .dup-overview:not( [hidden] ) {
	display: flex;
	flex-direction: column;
}

/* Solange die Uebersicht steht, hat der Platzhalter nichts zu melden. */
.dup-portal .dup-portal__detail:has( .dup-overview:not( [hidden] ) ) .dup-detail__placeholder {
	display: none;
}

.dup-portal .dup-overview__items {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

.dup-portal .dup-overview__empty {
	flex: 0 0 auto;
	padding: 28px var( --dup-pad-lg );
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
	text-align: center;
}

/* --- Gemeinsame Teile eines Eintrags ------------------------------------ */

.dup-portal .dup-overview__item {
	position: relative;
	display: flex;
	align-items: stretch;
	min-width: 0;
	background: var( --dup-surface );
}

.dup-portal .dup-overview__open {
	display: grid;
	flex: 1 1 auto;
	min-width: 0;
	background: transparent;
	border-left: 3px solid transparent;
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-overview__open:focus-visible {
	outline-offset: -3px;
	border-radius: 0;
}

.dup-portal .dup-overview__body {
	display: grid;
	gap: 3px;
	min-width: 0;
}

.dup-portal .dup-overview__name {
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font );
	font-weight: 600;
	color: var( --dup-heading );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-overview__meta,
.dup-portal .dup-overview__contact {
	min-width: 0;
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
	/* Lange Adressen und E-Mail-Adressen duerfen die Kachel nicht sprengen. */
	overflow-wrap: anywhere;
}

/*
 * Setzt das Skript je Angabe ein eigenes Element, kommen die Trennpunkte von
 * hier. Steht dort eine fertig zusammengesetzte Zeichenkette, hat die Regel
 * keine Kinder zum Anfassen und bleibt wirkungslos.
 */
.dup-portal .dup-overview__meta > * + *::before,
.dup-portal .dup-overview__contact > * + *::before {
	content: "·";
	margin: 0 7px;
	color: var( --dup-border-strong );
}

/*
 * Die Schlagwoerter stehen ganz oben im Eintrag, vor Name und Angaben - so
 * gewuenscht. Sie sind ein eigenes Kind von .dup-overview__open und laufen
 * deshalb ueber alle drei Spalten des Rasters. order: -1 haelt sie auch dann
 * an erster Stelle, wenn sie im Markup weiter hinten stehen.
 */
.dup-portal .dup-overview__labels {
	grid-column: 1 / -1;
	order: -1;
	display: flex;
	flex-wrap: wrap;
	gap: 4px 6px;
	min-width: 0;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
}

/*
 * Kleine Plaketten mit blassem Goldton: genug Farbe, um sie von den grauen
 * Angaben darunter zu trennen, aber weit weg von der vollen Hauptfarbe, die
 * der Ungelesen-Plakette vorbehalten bleibt. Schrift bleibt dunkel.
 */
.dup-portal .dup-overview__labels > * {
	display: inline-flex;
	align-items: center;
	max-width: 100%;
	overflow: hidden;
	padding: 1px 8px;
	font-size: 11px;
	font-weight: 600;
	line-height: 1.6;
	color: var( --dup-heading );
	background: var( --dup-accent-tint );
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius-pill );
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* Leere Felder duerfen keine Luecke reissen – muss nach den display-Regeln
 * darueber stehen, sonst gewinnt dort flex gegen none. */
.dup-portal .dup-overview__meta:empty,
.dup-portal .dup-overview__contact:empty,
.dup-portal .dup-overview__labels:empty,
.dup-portal .dup-overview__badge:empty {
	display: none;
}

/* --- Ungelesen ---------------------------------------------------------- */

.dup-portal .dup-overview__item.is-unread .dup-overview__open {
	border-left-color: var( --dup-accent );
}

.dup-portal .dup-overview__item.is-unread .dup-overview__name {
	font-weight: 700;
}

.dup-portal .dup-overview__badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 20px;
	height: 20px;
	padding: 0 6px;
	font-size: 11px;
	font-weight: 700;
	line-height: 1;
	/* Auf Gold niemals Weiss – siehe --dup-accent-ink. */
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-radius: var( --dup-radius-pill );
	font-variant-numeric: tabular-nums;
}

/* --- Stift fuer die Stammdaten ------------------------------------------ */

.dup-portal .dup-overview__edit {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	padding: 0;
	color: var( --dup-muted );
	background: var( --dup-surface );
	border: 1px solid transparent;
	border-radius: 50%;
	opacity: 0;
	transition: opacity var( --dup-motion ) var( --dup-ease ),
		color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-overview__item:hover .dup-overview__edit,
.dup-portal .dup-overview__item:focus-within .dup-overview__edit {
	opacity: 1;
}

.dup-portal .dup-overview__edit:hover,
.dup-portal .dup-overview__edit:focus-visible {
	opacity: 1;
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border-color: var( --dup-border-strong );
}

.dup-portal .dup-overview__edit:focus-visible {
	border-radius: 50%;
	outline-offset: 1px;
}

/* --- Kacheln ------------------------------------------------------------ */

.dup-portal .dup-overview__items[data-layout="cards"] {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 280px, 1fr ) );
	align-content: start;
	gap: 16px;
	padding: 16px var( --dup-pad-lg ) var( --dup-pad-lg );
}

.dup-portal .dup-overview__items[data-layout="cards"] > .dup-overview__item {
	/* Schneidet den Ungelesen-Streifen sauber an der Rundung ab. */
	overflow: hidden;
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius );
	transition: border-color var( --dup-motion ) var( --dup-ease ),
		box-shadow var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-overview__items[data-layout="cards"] > .dup-overview__item:hover {
	border-color: var( --dup-accent );
	box-shadow: var( --dup-shadow );
}

.dup-portal .dup-overview__items[data-layout="cards"] .dup-overview__open {
	grid-template-columns: auto minmax( 0, 1fr ) auto;
	align-items: start;
	gap: 4px 12px;
	/* Rechts bleibt Platz fuer den Stift, der ueber der Kachel schwebt. */
	padding: 16px 46px 16px 13px;
}

.dup-portal .dup-overview__items[data-layout="cards"] .dup-overview__open:focus-visible {
	border-radius: var( --dup-radius );
}

.dup-portal .dup-overview__items[data-layout="cards"] .dup-avatar {
	align-self: start;
}

.dup-portal .dup-overview__items[data-layout="cards"] .dup-overview__badge {
	align-self: start;
	/* Sitzt damit optisch auf der Grundlinie des Namens. */
	margin-top: 2px;
}

/* Etwas Luft zur Zeile mit Bild und Namen darunter. */
.dup-portal .dup-overview__items[data-layout="cards"] .dup-overview__labels {
	margin-bottom: 2px;
}

.dup-portal .dup-overview__items[data-layout="cards"] .dup-overview__edit {
	position: absolute;
	top: 10px;
	right: 10px;
}

/* --- Liste -------------------------------------------------------------- */

.dup-portal .dup-overview__items[data-layout="list"] {
	display: block;
	padding: 0 0 var( --dup-pad );
}

.dup-portal .dup-overview__items[data-layout="list"] > .dup-overview__item {
	align-items: center;
	border-bottom: 1px solid var( --dup-border );
}

.dup-portal .dup-overview__items[data-layout="list"] > .dup-overview__item:last-child {
	border-bottom: 0;
}

/*
 * Der Zeilenabstand ist kleiner als der Spaltenabstand: Er trennt nur die
 * Zeile der Schlagwoerter von der Zeile mit Bild und Namen. Ohne
 * Schlagwoerter bleibt der Eintrag einzeilig und der Wert wirkungslos.
 */
.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__open {
	grid-template-columns: auto minmax( 0, 1fr ) auto;
	align-items: center;
	gap: 5px 12px;
	/* Links drei Pixel weniger, die traegt der Ungelesen-Streifen. */
	padding: 9px var( --dup-pad ) 9px calc( var( --dup-pad-lg ) - 3px );
}

.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__open:hover {
	background: var( --dup-surface-alt );
}

.dup-portal .dup-overview__items[data-layout="list"] .dup-avatar {
	width: 30px;
	height: 30px;
	font-size: var( --dup-font-xs );
}

/*
 * Bewusst center statt baseline: Der Name traegt overflow: hidden fuer die
 * Auslassungspunkte, und ein Kasten mit overflow != visible legt seine
 * Grundlinie auf die Unterkante. Bei baseline wuerde der Name deshalb aus
 * der Zeile nach oben herausrutschen.
 */
.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__body {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 2px 12px;
}

.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__name {
	flex: 0 1 auto;
	max-width: 100%;
}

/* Trennpunkt zwischen Stammdaten und Kontakt, aber nur wenn beides da ist. */
.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__meta:not( :empty ) + .dup-overview__contact:not( :empty )::before {
	content: "·";
	margin-right: 12px;
	color: var( --dup-border-strong );
}

.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__edit {
	margin-right: 8px;
}

/* --- Volle Breite ------------------------------------------------------- */

/*
 * Die Uebersicht braucht den ganzen Schirm. Die linke Spalte faellt weg,
 * der Detailbereich laeuft ueber beide Spalten.
 */
.dup-portal.dup-portal--overview {
	grid-template-columns: minmax( 0, 1fr );
}

.dup-portal.dup-portal--overview .dup-portal__list {
	display: none;
}

.dup-portal.dup-portal--overview .dup-portal__detail {
	grid-column: 1 / -1;
}

/*
 * Sicherheitsnetz: Patient:innen sehen nur die eigene Unterhaltung. Sollte
 * das Markup dort je Werkzeugleiste oder Uebersicht mitbringen, bleiben sie
 * verborgen. Muss nach den display-Regeln oben stehen.
 */
.dup-portal[data-view="patient"] .dup-views,
.dup-portal[data-view="patient"] .dup-overview {
	display: none;
}

/* Ohne Zeigegeraet gibt es kein Ueberfahren – dort ist der Stift immer da. */
@media ( hover: none ) {

	.dup-portal .dup-overview__edit {
		opacity: 1;
	}
}

/* -------------------------------------------------------------------------
 * 15. Sichtschutz
 *
 * Die Ueberlagerung haengt am document.body, also ausserhalb von .dup-portal,
 * und erbt dessen Variablen nicht. Deshalb feste Werte.
 *
 * Anders als bei der Bildanzeige haengen die tragenden Eigenschaften hier
 * NICHT an body.dup-portal-page: Faellt die Klasse einmal weg – ein anderes
 * Zusatzmodul filtert body_class, die Seite wird ohne den Shortcode
 * ausgeliefert, ein Cache liefert altes Markup – waere der Sichtschutz eine
 * unsichtbare, wirkungslose Huelle, und niemand wuerde es bemerken. Lage,
 * Stapelhoehe und Hintergrund muessen deshalb aus sich heraus gelten. Die
 * uebrigen Feinheiten bleiben angebunden, damit nichts in das Theme ausblutet.
 * ---------------------------------------------------------------------- */

/* --- Schaltflaeche in der Kopfleiste ------------------------------------ */

body.dup-portal-page .dup-bar__privacy {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	padding: 0;
	font: inherit;
	color: rgba( 255, 255, 255, 0.78 );
	background: transparent;
	border: 1px solid rgba( 255, 255, 255, 0.24 );
	border-radius: 50%;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
	transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease;
}

body.dup-portal-page .dup-bar__privacy svg {
	display: block;
	width: 18px;
	height: 18px;
	pointer-events: none;
}

/* Auf dem dunklen Grund der Leiste traegt der Petrol-Akzent nicht - dort
 * uebernimmt die Helligkeit die Rueckmeldung, wie bei Glocke und Schild. */
body.dup-portal-page .dup-bar__privacy:hover,
body.dup-portal-page .dup-bar__privacy:focus-visible {
	color: #fff;
	background: rgba( 255, 255, 255, 0.12 );
	border-color: rgba( 255, 255, 255, 0.6 );
}

body.dup-portal-page .dup-bar__privacy:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
}

/* --- Ueberlagerung ------------------------------------------------------ */

/* Ausserhalb von .dup-portal gilt dessen box-sizing-Regel nicht. */
.dup-privacy,
.dup-privacy * {
	box-sizing: border-box;
}

/*
 * Die tragenden Eigenschaften, bewusst ohne jeden Vorsatz - siehe Kopf des
 * Abschnitts. Die Klasse .dup-privacy gibt es nur an dieser einen, vom Skript
 * erzeugten Flaeche; die Regel kann also nichts anderes treffen.
 */
.dup-privacy {
	position: fixed;
	inset: 0;
	/* Ueber der Adminleiste von WordPress (99999) und ueber der Bildanzeige
	 * (100000). Kommt jemand ins Zimmer, darf nichts mehr darueber liegen. */
	z-index: 100001;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 6vmin;
	/*
	 * Deckend, nicht weichgezeichnet: Verschwommene Schrift laesst sich aus
	 * zwei Metern Abstand oft noch entziffern, eine undurchsichtige Flaeche
	 * nicht. Dieselbe Farbe wie die Kopfleiste, damit es zum Portal gehoert.
	 */
	background: #121316;
	color: #fff;
	overscroll-behavior: contain;
}

/* Feinheiten - die duerfen ruhig an der Portalseite haengen. */
body.dup-portal-page .dup-privacy {
	font-family: inherit;
	font-size: 15px;
	line-height: 1.5;
	text-align: center;
	cursor: pointer;
	-webkit-user-select: none;
	user-select: none;
	animation: dup-privacy-in 140ms cubic-bezier( 0.2, 0, 0.2, 1 ) both;
}

/*
 * Muss nach jeder display-Regel stehen und ohne Vorsatz auskommen: sonst
 * gewinnt display: flex gegen die Vorgabe des Browsers fuer [hidden] und der
 * Sichtschutz bliebe immer offen.
 */
.dup-privacy[hidden] {
	display: none;
}

body.dup-portal-page .dup-privacy__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 14px;
	/* Feste Pixelwerte, weil Impreza 1rem auf 20px setzt. */
	max-width: 460px;
}

body.dup-portal-page .dup-privacy__mark {
	display: block;
	max-width: 100%;
	margin-bottom: 4px;
}

/* Ohne hinterlegtes Logo bleibt die Stelle leer statt einen Rahmen zu zeigen. */
body.dup-portal-page .dup-privacy__mark:empty {
	display: none;
}

body.dup-portal-page .dup-privacy__mark img {
	display: block;
	width: auto;
	max-width: 100%;
	height: 48px;
	margin: 0 auto;
}

body.dup-portal-page .dup-privacy__text {
	margin: 0;
	font-size: 22px;
	font-weight: 700;
	line-height: 1.3;
	letter-spacing: 0.01em;
	/* Weiss ist hier zulaessig: der Grund ist dunkel, nicht golden. */
	color: #fff;
}

body.dup-portal-page .dup-privacy__hint {
	margin: 0;
	font-size: 14px;
	/* Die Sichtschutzflaeche ist dunkel - ein gedaempftes Weiss statt der
	 * Akzentfarbe, die hier Dunkel auf Dunkel waere. */
	color: rgba( 255, 255, 255, 0.72 );
}

/* Nichts darf sich unter der Flaeche noch bewegen. */
body.dup-privacy-on {
	overflow: hidden;
}

@keyframes dup-privacy-in {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

/* -------------------------------------------------------------------------
 * 16. Schmale Fenster und Bedienpraeferenzen fuer 13 bis 15
 * ---------------------------------------------------------------------- */

@media ( max-width: 860px ) {

	/*
	 * Abschnitt 11 legt den Detailbereich als verborgene Ebene ueber die
	 * Liste. In der Uebersicht gibt es aber keine Liste, ueber die er sich
	 * legen koennte – dort muss er sichtbar im Fluss bleiben. Diese Regel
	 * steht bewusst nach Abschnitt 11, damit sie bei gleicher Spezifitaet
	 * gewinnt.
	 */
	.dup-portal.dup-portal--overview .dup-portal__detail {
		position: static;
		visibility: visible;
		opacity: 1;
		transform: none;
		transition-delay: 0s;
	}
}

@media ( max-width: 600px ) {

	.dup-portal .dup-views {
		gap: 8px 10px;
		padding: 8px var( --dup-pad );
	}

	.dup-portal .dup-views__sort {
		width: 100%;
		margin-left: 0;
	}

	.dup-portal .dup-views__sort-select {
		flex: 1 1 auto;
	}

	/*
	 * Auf dem Telefon teilen sich Ansichtswahl und Anlegen die erste Zeile,
	 * Suche und Sortierung bekommen je eine eigene. Ohne order stuenden vier
	 * Zeilen untereinander und von der Uebersicht bliebe kaum etwas uebrig.
	 */
	.dup-portal .dup-views__modes {
		order: -2;
	}

	.dup-portal .dup-views__new {
		order: -1;
		margin-left: auto;
	}

	.dup-portal .dup-views__search {
		flex: 1 1 100%;
		max-width: none;
	}

	/*
	 * Unter 280px Kachelbreite liefe das Raster aus dem Bild. Auf dem Telefon
	 * steht deshalb eine Kachel je Zeile.
	 */
	.dup-portal .dup-overview__items[data-layout="cards"] {
		grid-template-columns: minmax( 0, 1fr );
		gap: 12px;
		padding: 12px var( --dup-pad ) var( --dup-pad );
	}

	.dup-portal .dup-overview__items[data-layout="list"] .dup-overview__open {
		padding-left: calc( var( --dup-pad ) - 3px );
		padding-right: 10px;
	}

	body.dup-portal-page .dup-privacy__text {
		font-size: 19px;
	}
}

@media ( prefers-reduced-motion: reduce ) {

	body.dup-portal-page .dup-privacy,
	body.dup-portal-page .dup-privacy * {
		animation-duration: 1ms !important; /* Nutzerpraeferenz schlaegt jede Regel. */
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
	}
}

@media ( forced-colors: active ) {

	.dup-portal .dup-views__modes,
	.dup-portal .dup-view-btn,
	.dup-portal .dup-views__dir,
	.dup-portal .dup-overview__item,
	.dup-portal .dup-overview__badge,
	.dup-portal .dup-overview__edit,
	.dup-portal .dup-overview__labels > *,
	body.dup-portal-page .dup-bar__privacy {
		border: 1px solid CanvasText;
	}

	/* Im Kontrastmodus faellt die Deckkraft weg – sonst waere der Stift weg. */
	.dup-portal .dup-overview__edit {
		opacity: 1;
	}

	.dup-portal .dup-view-btn[aria-pressed="true"] {
		color: HighlightText;
		background: Highlight;
	}

	/* Der Ungelesen-Streifen ist im Kontrastmodus die einzige Kante, die
	 * bleibt – sonst waere kein Unterschied mehr zu sehen. */
	.dup-portal .dup-overview__item.is-unread .dup-overview__open {
		border-left-color: Highlight;
	}

	body.dup-portal-page .dup-privacy {
		background: Canvas;
		color: CanvasText;
	}

	body.dup-portal-page .dup-privacy__text,
	body.dup-portal-page .dup-privacy__hint {
		color: CanvasText;
	}
}

/* -------------------------------------------------------------------------
 * 17. Terminbuchung
 *
 * Nur in der Patientenansicht und oberhalb der Unterhaltung. Eine ruhige
 * Karte: Der Verweis fuehrt aus dem Portal hinaus, das soll erkennbar sein,
 * aber die Unterhaltung nicht verdraengen. Fehlt die Adresse, gibt das Skript
 * den Block gar nicht erst aus - hier ist dafuer nichts vorzusehen.
 *
 * Die Karte steht je nach Einbau im Reiter oder direkt im Detailbereich.
 * Beide sind Flex-Spalten, deshalb flex: 0 0 auto statt fester Hoehen.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-booking {
	display: flex;
	flex: 0 0 auto;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px 16px;
	margin: var( --dup-pad ) var( --dup-pad-lg ) 0;
	padding: 14px 16px;
	background: var( --dup-surface );
	border: 1px solid var( --dup-border );
	/* Die Flaeche bleibt weiss, Farbe traegt nur die schmale Kante links. */
	border-left: 3px solid var( --dup-accent );
	border-radius: var( --dup-radius-sm );
}

.dup-portal .dup-booking__text {
	flex: 1 1 220px;
	min-width: 0;
	margin: 0;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: var( --dup-heading );
}

.dup-portal .dup-booking__link {
	flex: 0 0 auto;
}

/*
 * Der Vertrag gibt dem Verweis nur .dup-btn mit, gewuenscht ist aber das
 * Aussehen der Hauptschaltflaechen. Gold als Flaeche, Schrift dunkel - auf
 * Gold ist Weiss verboten, siehe --dup-accent-ink. Die Farbe steht auch bei
 * :hover und :focus, sonst faerbt das Theme den Verweis dort um.
 */
.dup-portal .dup-booking__link,
.dup-portal .dup-booking__link:hover,
.dup-portal .dup-booking__link:focus {
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-color: var( --dup-accent );
	text-decoration: none;
}

.dup-portal .dup-booking__link:hover,
.dup-portal .dup-booking__link:focus-visible {
	background: var( --dup-accent-soft );
	border-color: var( --dup-accent-soft );
}

.dup-portal .dup-booking__hint {
	flex: 1 1 100%;
	margin: 0;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
}

/*
 * Sicherheitsnetz: Die Ordination bucht ihre Termine nicht ueber den
 * Onlinekalender der Patient:innen. Sollte der Block dort je im Markup
 * landen, bleibt er verborgen.
 */
.dup-portal:not( [data-view="patient"] ) .dup-booking {
	display: none;
}

/* -------------------------------------------------------------------------
 * 18. Dateiuebersicht neben dem Verlauf
 *
 * Das Feld liegt als dritte Spalte rechts neben dem Nachrichtenverlauf. Es
 * ist absolut gesetzt und der Reiter bekommt rechts denselben Innenabstand:
 * So bleibt die Spalte der Nachrichten eine ganz gewoehnliche Flex-Spalte -
 * Verlauf, Verfassbereich und ein etwaiger Terminblock ordnen sich weiter
 * selbst, unabhaengig davon, an welcher Stelle das Feld im Markup steht.
 * Umgeschaltet wird ueber .dup-portal--files, das nur den Innenabstand setzt.
 *
 * Die Stapelhoehen bleiben klein (2 und 3). .dup-portal hat zwar
 * position: relative, aber kein z-index und bildet damit keinen eigenen
 * Stapelkontext - der Sichtschutz auf 100001 liegt also weiterhin sicher
 * ueber diesem Feld.
 * ---------------------------------------------------------------------- */

.dup-portal {
	--dup-files-width: 280px;
}

/* --- Schaltflaeche im Kopf ---------------------------------------------- */

/* :not([hidden]) ist noetig, weil diese Regel dieselbe Spezifitaet hat wie
 * .dup-portal [hidden] { display: none } und weiter unten steht - ohne den
 * Zusatz liesse sich die Schaltflaeche nie verbergen. */
.dup-portal .dup-files__toggle:not( [hidden] ) {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 34px;
	/* Ganz nach rechts, wenn nichts anderes schon dorthin schiebt. */
	margin-left: auto;
	padding: 6px 14px;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	line-height: 1.4;
	white-space: nowrap;
	color: var( --dup-text );
	background: var( --dup-surface );
	border: 1px solid var( --dup-border-strong );
	border-radius: var( --dup-radius-pill );
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

/*
 * Der Kontaktblock traegt bereits margin-left: auto. Zwei solche Elemente
 * teilen sich den freien Platz und stuenden dann beide in der Mitte - hier
 * schiebt deshalb nur eines, und die Schaltflaeche haengt sich daneben.
 */
.dup-portal .dup-detail__contact + .dup-files__toggle {
	margin-left: 0;
}

.dup-portal .dup-files__toggle svg {
	display: block;
	width: 18px;
	height: 18px;
	pointer-events: none;
}

/* Beim Ueberfahren die volle Hauptfarbe, darauf dunkle Schrift. */
.dup-portal .dup-files__toggle:hover {
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
	border-color: var( --dup-accent );
}

/* Offen: blasses Gold, damit der Zustand auch ohne Zeiger zu sehen ist. */
.dup-portal .dup-files__toggle[aria-expanded="true"] {
	color: var( --dup-heading );
	background: var( --dup-accent-tint );
	border-color: var( --dup-accent );
}

.dup-portal .dup-files__toggle[aria-expanded="true"]:hover {
	color: var( --dup-accent-ink );
	background: var( --dup-accent );
}

/* Abschnitt 2 macht den Fokusring eckig – bei einer Pille passt das nicht. */
.dup-portal .dup-files__toggle:focus-visible {
	border-radius: var( --dup-radius-pill );
}

/* --- Das Feld ------------------------------------------------------------ */

/* Traeger fuer das absolut gesetzte Feld daneben. */
.dup-portal .dup-tab[data-tab="nachrichten"]:not( [hidden] ) {
	position: relative;
}

/* Die Klasse macht rechts Platz, mehr braucht die Spaltenaufteilung nicht. */
.dup-portal.dup-portal--files .dup-tab[data-tab="nachrichten"]:not( [hidden] ) {
	padding-right: var( --dup-files-width );
}

/*
 * :not( [hidden] ) ist Pflicht: .dup-portal [hidden] aus Abschnitt 1 hat
 * dieselbe Spezifitaet wie .dup-portal .dup-files und steht weiter oben -
 * ohne die Verneinung waere das Feld dauerhaft offen.
 */
.dup-portal .dup-files:not( [hidden] ) {
	position: absolute;
	inset: 0 0 0 auto;
	z-index: 2;
	display: flex;
	flex-direction: column;
	width: var( --dup-files-width );
	min-height: 0;
	background: var( --dup-surface );
	border-left: 1px solid var( --dup-border );
}

.dup-portal .dup-files__head {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	gap: 8px;
	padding: 11px 10px 11px 16px;
	border-bottom: 1px solid var( --dup-border );
}

.dup-portal .dup-files__title {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font-xs );
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var( --dup-muted );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-files__close {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	padding: 0;
	font-size: 18px;
	line-height: 1;
	color: var( --dup-muted );
	background: transparent;
	border: 1px solid transparent;
	border-radius: 50%;
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-files__close svg {
	display: block;
	width: 16px;
	height: 16px;
	pointer-events: none;
}

.dup-portal .dup-files__close:hover,
.dup-portal .dup-files__close:focus-visible {
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border-color: var( --dup-border-strong );
}

.dup-portal .dup-files__close:focus-visible {
	border-radius: 50%;
}

.dup-portal .dup-files__items {
	flex: 1 1 auto;
	min-height: 0;
	padding: 4px 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

.dup-portal .dup-files__empty {
	flex: 0 0 auto;
	padding: 24px 16px;
	font-size: var( --dup-font-sm );
	color: var( --dup-muted );
	text-align: center;
}

/* --- Ein Eintrag --------------------------------------------------------- */

.dup-portal .dup-file {
	display: flex;
	align-items: center;
	gap: 2px;
	min-width: 0;
	padding-right: 8px;
	transition: background-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-file + .dup-file {
	border-top: 1px solid var( --dup-border );
}

.dup-portal .dup-file:hover {
	background: var( --dup-surface-alt );
}

.dup-portal .dup-file__main {
	display: grid;
	flex: 1 1 auto;
	grid-template-columns: auto minmax( 0, 1fr );
	align-items: center;
	gap: 10px;
	min-width: 0;
	padding: 9px 4px 9px 12px;
	background: transparent;
}

.dup-portal .dup-file__main:focus-visible {
	outline-offset: -2px;
	border-radius: 0;
}

/* Traegt die Endung als Text ("PDF", "JPG") – wie im Verlauf. */
.dup-portal .dup-file__icon {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 34px;
	overflow: hidden;
	font-size: 9px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border: 1px solid var( --dup-border-strong );
	border-radius: 4px;
}

.dup-portal .dup-file__icon svg {
	display: block;
	width: 16px;
	height: 16px;
	pointer-events: none;
}

.dup-portal .dup-file__body {
	display: grid;
	gap: 1px;
	min-width: 0;
}

.dup-portal .dup-file__name {
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: var( --dup-heading );
	white-space: nowrap;
	text-overflow: ellipsis;
}

.dup-portal .dup-file__main:hover .dup-file__name {
	text-decoration: underline;
}

/* Groesse, Datum und Absenderseite in einer Zeile. */
.dup-portal .dup-file__meta {
	min-width: 0;
	overflow: hidden;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
	white-space: nowrap;
	text-overflow: ellipsis;
	font-variant-numeric: tabular-nums;
}

/* --- Aktionen je Eintrag -------------------------------------------------- */

.dup-portal .dup-file__download,
.dup-portal .dup-file__delete {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	padding: 0;
	color: var( --dup-muted );
	background: var( --dup-surface );
	border: 1px solid transparent;
	border-radius: 50%;
	text-decoration: none;
	opacity: 0;
	transition: opacity var( --dup-motion ) var( --dup-ease ),
		color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-file__download svg,
.dup-portal .dup-file__delete svg {
	display: block;
	width: 15px;
	height: 15px;
	pointer-events: none;
}

.dup-portal .dup-file:hover .dup-file__download,
.dup-portal .dup-file:hover .dup-file__delete,
.dup-portal .dup-file:focus-within .dup-file__download,
.dup-portal .dup-file:focus-within .dup-file__delete {
	opacity: 1;
}

.dup-portal .dup-file__download:hover,
.dup-portal .dup-file__download:focus-visible {
	opacity: 1;
	color: var( --dup-heading );
	background: var( --dup-surface-alt );
	border-color: var( --dup-border-strong );
}

.dup-portal .dup-file__delete:hover,
.dup-portal .dup-file__delete:focus-visible {
	opacity: 1;
	color: var( --dup-danger );
	border-color: var( --dup-danger );
}

.dup-portal .dup-file__download:focus-visible,
.dup-portal .dup-file__delete:focus-visible {
	border-radius: 50%;
}

/*
 * Loeschen ist Sache der Ordination. Der Server weist alles andere mit 403
 * ab - die Oberflaeche darf gar nicht erst danach aussehen, als ginge es.
 * Sicherheitsnetz zu can_delete, das schon im Skript greift.
 */
.dup-portal[data-view="patient"] .dup-file__delete,
.dup-portal[data-view="patient"] .dup-attachment__delete {
	display: none;
}

/* --- Entfernter Anhang ---------------------------------------------------- */

/*
 * Geloeschte Anhaenge bleiben in der Liste stehen, sonst wirkte die
 * Unterhaltung nachtraeglich veraendert. Dieselbe Handschrift wie
 * .dup-attachment__gone im Verlauf: gestrichelt, kursiv, gedaempft. Die
 * Klasse is-deleted setzt das Skript, wie schon beim Anhang in der Blase.
 */
.dup-portal .dup-file.is-deleted .dup-file__name,
.dup-portal .dup-file.is-deleted .dup-file__meta {
	font-style: italic;
	font-weight: 400;
	color: var( --dup-muted );
}

.dup-portal .dup-file.is-deleted .dup-file__icon {
	color: var( --dup-muted );
	background: transparent;
	border-style: dashed;
}

.dup-portal .dup-file.is-deleted:hover {
	background: transparent;
}

.dup-portal .dup-file.is-deleted .dup-file__main:hover .dup-file__name {
	text-decoration: none;
}

/* Ohne Zeigegeraet gibt es kein Ueberfahren – dort immer sichtbar. */
@media ( hover: none ) {

	.dup-portal .dup-file__download,
	.dup-portal .dup-file__delete {
		opacity: 1;
	}
}

/* -------------------------------------------------------------------------
 * 19. Hervorhebung nach dem Sprung aus der Dateiuebersicht
 *
 * Ein Klick auf eine Datei rollt zur zugehoerigen Nachricht. Ohne Markierung
 * waere danach unklar, welche der Blasen gemeint war. Der Ring beginnt etwas
 * weiter und faellt in seine Ruhelage zurueck - keine Blinkerei.
 *
 * Bewusst ohne Fuellmodus: Nach dem Ablauf gilt wieder die Regel darunter,
 * die denselben Ring festhaelt, bis das Skript die Klasse nach zwei Sekunden
 * entfernt. Bei reduzierter Bewegung schrumpft die Bewegung auf 1ms und der
 * Ring steht sofort - sichtbar bleibt die Hervorhebung also in jedem Fall.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-message.is-highlighted .dup-message__bubble {
	box-shadow: 0 0 0 1px var( --dup-accent ), 0 0 0 4px var( --dup-accent-tint );
	animation: dup-highlight 520ms var( --dup-ease );
}

@keyframes dup-highlight {
	from {
		box-shadow: 0 0 0 1px var( --dup-accent ), 0 0 0 11px var( --dup-accent-tint );
	}

	to {
		box-shadow: 0 0 0 1px var( --dup-accent ), 0 0 0 4px var( --dup-accent-tint );
	}
}

/* -------------------------------------------------------------------------
 * 20. Schmale Fenster und Bedienpraeferenzen fuer 17 bis 19
 * ---------------------------------------------------------------------- */

@media ( max-width: 1100px ) {

	/*
	 * Der Kontaktblock nimmt hier eine eigene Zeile ueber die volle Breite
	 * und schiebt deshalb nichts mehr - die Schaltflaeche muss selbst.
	 */
	.dup-portal .dup-detail__contact + .dup-files__toggle {
		margin-left: auto;
	}
}

@media ( max-width: 1024px ) {

	.dup-portal {
		--dup-files-width: 250px;
	}
}

/* Unter 860px wird aus der Spalte eine Ebene ueber dem Verlauf. */
@media ( max-width: 860px ) {

	.dup-portal.dup-portal--files .dup-tab[data-tab="nachrichten"]:not( [hidden] ) {
		padding-right: 0;
	}

	/*
	 * Deckt den Reiter vollstaendig ab: Kopf und Reiterleiste bleiben stehen,
	 * damit der Weg zurueck sichtbar ist. Geschlossen wird ueber die
	 * Schaltflaeche im Feld oder erneut ueber die im Kopf.
	 */
	.dup-portal .dup-files:not( [hidden] ) {
		inset: 0;
		z-index: 3;
		width: auto;
		border-left: 0;
	}
}

@media ( max-width: 600px ) {

	.dup-portal .dup-booking {
		margin: var( --dup-pad ) var( --dup-pad ) 0;
		padding: 12px 14px;
	}

	/* Auf dem Telefon eine volle Zeile – da trifft man sicher. */
	.dup-portal .dup-booking__link {
		flex: 1 1 100%;
	}

	.dup-portal .dup-files__toggle {
		padding: 6px 12px;
	}
}

@media ( forced-colors: active ) {

	.dup-portal .dup-booking,
	.dup-portal .dup-files__toggle,
	.dup-portal .dup-file__icon,
	.dup-portal .dup-file__download,
	.dup-portal .dup-file__delete,
	.dup-portal .dup-views__new,
	body.dup-portal-page .dup-lightbox__bar,
	body.dup-portal-page .dup-lightbox__download {
		border: 1px solid CanvasText;
	}

	/* Im Kontrastmodus faellt die Deckkraft weg – sonst waeren sie weg. */
	.dup-portal .dup-file__download,
	.dup-portal .dup-file__delete {
		opacity: 1;
	}

	.dup-portal .dup-files__toggle[aria-expanded="true"] {
		color: HighlightText;
		background: Highlight;
	}

	/* Der Ring ist im Kontrastmodus das Einzige, was die Nachricht abhebt. */
	.dup-portal .dup-message.is-highlighted .dup-message__bubble {
		outline: 2px solid Highlight;
		outline-offset: 2px;
	}
}

/* -------------------------------------------------------------------------
 * Erledigt-Schalter in der Kopfzeile der Unterhaltung
 * ---------------------------------------------------------------------- */

.dup-portal .dup-detail__done:not( [hidden] ) {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	gap: 7px;
	padding: 7px 13px;
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius-pill );
	background: var( --dup-surface );
	color: var( --dup-muted );
	font-family: inherit;
	font-size: var( --dup-font-sm );
	line-height: 1.2;
	cursor: pointer;
	transition: color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease ),
		background var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-detail__done:hover,
.dup-portal .dup-detail__done:focus-visible {
	border-color: var( --dup-accent );
	color: var( --dup-heading );
}

/* Erledigt: gefuellt, damit der Zustand auf einen Blick erkennbar ist.
 * Weisse Schrift auf dem Petrol-Akzent (rund 7:1) - siehe --dup-accent-ink. */
.dup-portal .dup-detail__done.is-done {
	border-color: var( --dup-accent );
	background: var( --dup-accent );
	color: var( --dup-accent-ink );
	font-weight: 600;
}

.dup-portal .dup-detail__done .dup-icon {
	flex: 0 0 auto;
}

/* -------------------------------------------------------------------------
 * 21. Gemeinsame Werte der neuen Bausteine
 *
 * Zwei Farben, die es bisher nur als eingestreute Zahlenwerte gab, bekommen
 * hier einen Namen. Sie werden von den Abschnitten 22 bis 28 gebraucht.
 * ---------------------------------------------------------------------- */

.dup-portal {
	/*
	 * Akzentfarbe fuer SCHRIFT auf hellem Grund - dasselbe Petrol wie
	 * --dup-accent, das auf Weiss rund 7:1 traegt. Der Name stammt aus der
	 * Gold-Zeit; er bleibt, weil ihn acht Regeln benutzen. Derselbe Wert wie
	 * --dup-gate-gold-dark in der Anmeldemaske - dort mit eigenem Namen,
	 * weil die Maske ausserhalb von .dup-portal steht.
	 */
	--dup-gold-ink: #275765;
	/* Bestaetigung. Bereits als Zahlenwert in .dup-form__hint.is-success. */
	--dup-success: #1e7a4c;
}

/*
 * Symbole innerhalb einer Schaltflaeche duerfen weder den Klick abfangen noch
 * die Zeile auseinanderziehen. Fuer .dup-detail__done steht dieselbe Angabe
 * schon weiter oben; hier gilt sie fuer alle uebrigen.
 */
.dup-portal .dup-btn .dup-icon {
	flex: 0 0 auto;
	width: 16px;
	height: 16px;
	pointer-events: none;
}

/* -------------------------------------------------------------------------
 * 22. Einladungsstatus in drei Stufen
 *
 * Kein Zugang -> eingeladen am -> aktiv seit. Der Zustand darf nicht allein
 * an der Farbe haengen: Rot-Gruen-Blindheit betrifft rund acht Prozent der
 * Maenner, und Gold gegen Grau ist auch fuer alle anderen kein starkes
 * Signal. Deshalb unterscheiden sich die drei Plaketten dreifach - in der
 * Form (eckig gestrichelt / Pille / Pille gefuellt), im gezeichneten Zeichen
 * (offener Ring / Raute / Haken) und erst zuletzt in der Farbe.
 *
 * Das Zeichen ist bewusst gezeichnet und kein Schriftzeichen im Text: ein
 * Haken als Zeichen im Inhalt wird vorgelesen ("Haekchen") und fehlt in
 * manchen Schriftarten.
 *
 * Der Baustein ist ortsunabhaengig: Er steht in der Uebersicht, im Kopf der
 * Unterhaltung und in den Stammdaten. Die Spezifitaet 0,4,0 ist mit Absicht
 * hoeher als die der Schlagwort-Plaketten (.dup-overview__labels > *, 0,2,0),
 * damit er auch dort seine eigene Form behaelt.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-status[data-status]:not( [hidden] ) {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	gap: 7px;
	max-width: 100%;
	overflow: hidden;
	padding: 2px 11px 2px 9px;
	font-size: 11px;
	font-weight: 600;
	line-height: 1.7;
	letter-spacing: 0.01em;
	/* Kein text-overflow: Der Text ist hier ein namenloses Flex-Element, und
	 * Auslassungspunkte greifen an einem Flex-Container nicht. nowrap und
	 * overflow halten die Plakette trotzdem in der Zeile. */
	white-space: nowrap;
	color: var( --dup-muted );
	background: var( --dup-surface );
	border: 1px solid var( --dup-border-strong );
	border-radius: var( --dup-radius-pill );
}

.dup-portal .dup-status[data-status]::before {
	content: "";
	flex: 0 0 auto;
	width: 10px;
	height: 10px;
	border: 1.5px solid currentcolor;
	border-radius: 50%;
}

/* Stufe 1 - kein Zugang: offener Ring, gestrichelt, eckig. Die gestrichelte
 * Kante sagt "noch nichts passiert", ganz ohne Farbe. */
.dup-portal .dup-status[data-status="kein-zugang"]:not( [hidden] ) {
	border-style: dashed;
	border-radius: var( --dup-radius-sm );
}

/* Stufe 2 - eingeladen: gefuellte Raute, blasses Gold, dunkles Gold als
 * Schrift. Unterwegs, aber noch nicht angekommen. */
.dup-portal .dup-status[data-status="eingeladen"]:not( [hidden] ) {
	color: var( --dup-gold-ink );
	background: var( --dup-accent-tint );
	border-color: var( --dup-accent );
}

.dup-portal .dup-status[data-status="eingeladen"]::before {
	width: 9px;
	height: 9px;
	background: currentcolor;
	border-radius: 2px;
	transform: rotate( 45deg );
}

/* Stufe 3 - aktiv: Haken aus zwei Kanten, gruen. Angekommen. */
.dup-portal .dup-status[data-status="aktiv"]:not( [hidden] ) {
	color: var( --dup-success );
	background: var( --dup-surface );
	border-color: var( --dup-success );
}

.dup-portal .dup-status[data-status="aktiv"]::before {
	width: 6px;
	height: 10px;
	/* Zwei Kanten eines Rechtecks, um 45 Grad gedreht, ergeben den Haken. */
	border-width: 0 2px 2px 0;
	border-radius: 0;
	transform: translate( 1px, -1px ) rotate( 45deg );
}

/* In der Zeile mit Speichern und Einladen steht die Plakette rechts. */
.dup-portal .dup-form__actions .dup-status[data-status]:not( [hidden] ) {
	margin-left: auto;
}

/* -------------------------------------------------------------------------
 * 23. Dateianforderung
 *
 * Der wichtigste Unterschied zu einer Nachricht ist die Form: Eine Blase ist
 * hoechstens --dup-bubble-max breit und haengt an einer Seite. Die
 * Anforderung laeuft ueber die volle Breite und steht mittig im Verlauf -
 * schon bevor man ein Wort gelesen hat, ist zu sehen, dass hier etwas zu tun
 * ist und nicht etwas gesagt wurde.
 *
 * Drei Zustaende ueber data-status am Traeger:
 *   offen           - goldene Kante links, offenes Kaestchen, Schaltflaechen
 *   erfuellt        - graue Kante, gruener Haken, ruhig und zurueckgenommen
 *   zurueckgezogen  - blass, durchgestrichen, Strich statt Kaestchen
 * ---------------------------------------------------------------------- */

.dup-portal .dup-request:not( [hidden] ) {
	display: flex;
	flex-direction: column;
	gap: 9px;
	max-width: 100%;
	margin: 4px 0;
	padding: 13px 16px;
	background: var( --dup-surface );
	border: 1px solid var( --dup-border );
	/* Die Flaeche bleibt weiss, Farbe traegt nur die schmale Kante links -
	 * dieselbe Handschrift wie beim Terminblock in Abschnitt 17. */
	border-left: 3px solid var( --dup-accent );
	border-radius: var( --dup-radius-sm );
}

/* --- Kopfzeile der Karte ------------------------------------------------ */

.dup-portal .dup-request__kicker:not( [hidden] ) {
	display: flex;
	align-items: center;
	gap: 9px;
	min-width: 0;
	font-size: var( --dup-font-xs );
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var( --dup-gold-ink );
}

/*
 * Ein offenes Kaestchen, gezeichnet statt geschrieben. Es ist dasselbe
 * Zeichen, das jede Aufgabenliste benutzt, und traegt die Aussage "hier
 * fehlt noch etwas" ohne ein einziges Wort.
 */
.dup-portal .dup-request__kicker::before {
	content: "";
	flex: 0 0 auto;
	width: 13px;
	height: 13px;
	border: 1.5px solid currentcolor;
	border-radius: 3px;
}

.dup-portal .dup-request__text {
	min-width: 0;
	margin: 0;
	font-size: var( --dup-font );
	font-weight: 600;
	line-height: 1.5;
	color: var( --dup-heading );
	/* Der Wortlaut kommt aus einem Eingabefeld: Umbrueche stehen im Text,
	 * niemals als Markup - siehe Sicherheitsleitfaden. */
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

.dup-portal .dup-request__meta {
	min-width: 0;
	margin: 0;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
	font-variant-numeric: tabular-nums;
}

/* --- Schaltflaechen ------------------------------------------------------ */

.dup-portal .dup-request__actions:not( [hidden] ) {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 10px;
	min-width: 0;
}

/*
 * Vom Skript erzeugte Teile der Anforderungsliste.
 *
 * Kopfzeile, Titel und Liste kommen aus dem Markup, die einzelnen Eintraege
 * baut das Skript. Beim Zusammenfuehren fehlte fuer diese Auswahl noch die
 * Gestaltung - die Eintraege standen unformatiert im Verlauf.
 */
.dup-portal .dup-requests__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 8px;
}

.dup-portal .dup-requests__title {
	font-size: var( --dup-font-sm );
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var( --dup-muted );
}

.dup-portal .dup-requests__items {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* Der Text der Anforderung ist zugleich die Schaltflaeche zum Springen. */
.dup-portal .dup-request__jump {
	appearance: none;
	border: 0;
	padding: 0;
	background: none;
	font: inherit;
	color: inherit;
	text-align: left;
	cursor: pointer;
}

.dup-portal .dup-request__jump:hover,
.dup-portal .dup-request__jump:focus-visible {
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* Zuruecknehmen ist selten und soll nicht um Aufmerksamkeit buhlen. */
.dup-portal .dup-request__withdraw {
	appearance: none;
	flex: 0 0 auto;
	width: 26px;
	height: 26px;
	padding: 0;
	border: 0;
	border-radius: var( --dup-radius-pill );
	background: none;
	color: var( --dup-muted );
	font-size: 17px;
	line-height: 1;
	cursor: pointer;
}

.dup-portal .dup-request__withdraw:hover,
.dup-portal .dup-request__withdraw:focus-visible {
	background: var( --dup-surface-alt );
	color: var( --dup-heading );
}

/* Ohne Inhalt keine Luecke. Muss nach der display-Regel stehen, sonst
 * gewinnt dort flex gegen none. */
.dup-portal .dup-request__actions:empty {
	display: none;
}

/* Flacher als die Schaltflaechen aus Abschnitt 4: Die Karte steht im
 * Verlauf und soll ihn nicht auseinanderdruecken. */
.dup-portal .dup-request .dup-btn {
	min-height: 34px;
	padding: 6px 16px;
}

/* --- Erfuellt ------------------------------------------------------------ */

/*
 * Erledigtes soll nicht mehr rufen. Die goldene Kante weicht einer grauen,
 * die Schrift wird leichter - stehen bleibt die Karte trotzdem, damit der
 * Verlauf vollstaendig bleibt.
 */
.dup-portal .dup-request[data-status="erfuellt"]:not( [hidden] ) {
	border-left-color: var( --dup-border-strong );
	background: var( --dup-surface-alt );
}

.dup-portal .dup-request[data-status="erfuellt"] .dup-request__kicker {
	color: var( --dup-success );
}

/* Haken statt Kaestchen - dieselbe Zeichnung wie in der Plakette "aktiv". */
.dup-portal .dup-request[data-status="erfuellt"] .dup-request__kicker::before {
	width: 7px;
	height: 12px;
	margin: 0 3px;
	border-width: 0 2px 2px 0;
	border-radius: 0;
	transform: translateY( -1px ) rotate( 45deg );
}

.dup-portal .dup-request[data-status="erfuellt"] .dup-request__text {
	font-weight: 400;
	color: var( --dup-text );
}

/* --- Zurueckgezogen ------------------------------------------------------ */

/*
 * Zurueckgezogen heisst: hat es gegeben, gilt aber nicht mehr. Deshalb blass
 * und durchgestrichen. Die Deckkraft bleibt bei 0,75 - darunter faellt der
 * Text unter den Mindestkontrast, und lesbar muss er bleiben.
 */
.dup-portal .dup-request[data-status="zurueckgezogen"]:not( [hidden] ) {
	border-style: dashed;
	border-left-color: var( --dup-border-strong );
	opacity: 0.75;
}

.dup-portal .dup-request[data-status="zurueckgezogen"] .dup-request__kicker {
	color: var( --dup-muted );
}

/* Ein Strich statt des Kaestchens. */
.dup-portal .dup-request[data-status="zurueckgezogen"] .dup-request__kicker::before {
	width: 13px;
	height: 2px;
	background: currentcolor;
	border: 0;
	border-radius: 1px;
}

.dup-portal .dup-request[data-status="zurueckgezogen"] .dup-request__text {
	font-weight: 400;
	color: var( --dup-muted );
	text-decoration: line-through;
}

/* --- Leiste mit den offenen Anforderungen -------------------------------- */

/*
 * Was noch aussteht, muss auch dann zu sehen sein, wenn die Anforderung
 * selbst weit oben im Verlauf steht. Die Leiste klebt deshalb ueber dem
 * Verfassbereich und scrollt nicht mit.
 */
.dup-portal .dup-requests:not( [hidden] ) {
	display: flex;
	flex: 0 0 auto;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px 12px;
	padding: 9px var( --dup-pad-lg );
	background: var( --dup-accent-tint );
	border-top: 1px solid var( --dup-border );
}

.dup-portal .dup-requests__count:not( [hidden] ) {
	display: flex;
	flex: 1 1 auto;
	align-items: center;
	gap: 9px;
	min-width: 0;
	margin: 0;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: var( --dup-gold-ink );
}

.dup-portal .dup-requests__count::before {
	content: "";
	flex: 0 0 auto;
	width: 12px;
	height: 12px;
	border: 1.5px solid currentcolor;
	border-radius: 3px;
}

.dup-portal .dup-requests__action {
	flex: 0 0 auto;
	min-height: 30px;
	padding: 4px 14px;
	font-size: var( --dup-font-xs );
}

/* --- Anforderung verfassen ----------------------------------------------- */

/*
 * Der Block liegt im Verfassbereich, ist aber ausdruecklich KEIN zweites
 * <form>: .dup-composer ist bereits eines, und ein Formular im Formular ist
 * ungueltiges Markup, das Browser stillschweigend zerlegen. Erwartet wird ein
 * <div role="group">.
 *
 * flex: 0 0 100% mit order: -1 - dieselbe Mechanik wie bei Fehlerzeile,
 * Dateiliste und Fortschritt: alles, was ueber dem Textfeld stehen soll,
 * belegt eine eigene Zeile ganz oben.
 */
.dup-portal .dup-request-form:not( [hidden] ) {
	display: flex;
	flex: 0 0 100%;
	flex-wrap: wrap;
	align-items: center;
	order: -1;
	gap: 8px 10px;
	padding: 10px 12px;
	background: var( --dup-surface );
	border: 1px dashed var( --dup-accent );
	border-radius: var( --dup-radius-sm );
}

.dup-portal .dup-request-form__label {
	flex: 0 0 100%;
	font-size: var( --dup-font-xs );
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var( --dup-gold-ink );
}

/*
 * Abschnitt 3 gibt jedem Feld 42px Hoehe - im Verfassbereich waere das zu
 * wuchtig, dort steht das Textfeld daneben. Der Elternteil steht mit im
 * Selektor: Abschnitt 3 spricht das Feld ueber input[type="text"] an und ist
 * damit um einen Typselektor staerker als eine Klasse allein.
 */
.dup-portal .dup-request-form .dup-request-form__input {
	flex: 1 1 220px;
	width: auto;
	min-width: 0;
	min-height: 38px;
	padding: 7px 12px;
	font-size: var( --dup-font-sm );
}

.dup-portal .dup-request-form__send {
	flex: 0 0 auto;
	min-height: 38px;
	padding: 7px 18px;
}

.dup-portal .dup-request-form__error {
	flex: 0 0 100%;
	margin: 0;
	font-size: var( --dup-font-xs );
	font-weight: 600;
	color: var( --dup-danger );
}

.dup-portal .dup-request-form__error:empty {
	display: none;
}

/* -------------------------------------------------------------------------
 * 24. Antworten freigeschaltet oder gesperrt
 *
 * Durchgesetzt wird die Sperre auf dem Server. Hier geht es nur darum, sie
 * sichtbar zu machen - und zwar sichtbar gesperrt, nicht weggeraeumt: Ein
 * fehlendes Eingabefeld liest sich wie ein Fehler ("wo schreibe ich jetzt
 * hin?"), ein graues Feld mit Begruendung daneben nicht.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-composer.is-locked {
	background: var( --dup-surface-alt );
}

/*
 * pointer-events allein sperrt nichts - mit der Tastatur laesst sich das Feld
 * weiterhin erreichen. Das Skript setzt deshalb zusaetzlich disabled; hier
 * steht nur, wie das aussieht. Die Deckkraft bleibt ueber 0,5, damit der
 * Platzhaltertext lesbar bleibt.
 */
.dup-portal .dup-composer.is-locked .dup-composer__input,
.dup-portal .dup-composer.is-locked .dup-composer__send,
.dup-portal .dup-composer.is-locked .dup-composer__attach,
.dup-portal .dup-composer.is-locked .dup-composer__camera,
.dup-portal .dup-composer.is-locked .dup-composer__request {
	opacity: 0.55;
	pointer-events: none;
}

.dup-portal .dup-composer.is-locked .dup-composer__input {
	background: var( --dup-surface-alt );
	border-style: dashed;
	resize: none;
}

/*
 * Der Grund steht ueber dem gesperrten Feld, nicht daneben: In der Zeile
 * neben dem Textfeld waere er auf schmalen Schirmen das Erste, was
 * verschwindet.
 */
.dup-portal .dup-composer__locked:not( [hidden] ) {
	display: flex;
	flex: 0 0 100%;
	align-items: flex-start;
	order: -1;
	gap: 9px;
	margin: 0;
	font-size: var( --dup-font-sm );
	font-weight: 600;
	color: var( --dup-heading );
}

/*
 * Ein gezeichnetes Schloss als Hintergrundbild statt als Zeichen im Text:
 * Ein Zeichen wuerde vorgelesen und haengt an der Schriftart. Die Farbe ist
 * fest, weil ein Hintergrundbild currentColor nicht kennt - dieselbe Loesung
 * wie bei Lupe und Auswahlpfeil in Abschnitt 3.
 */
.dup-portal .dup-composer__locked::before {
	content: "";
	flex: 0 0 auto;
	width: 16px;
	height: 16px;
	margin-top: 2px;
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2333353d' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4.5' y='10.5' width='15' height='9.5' rx='2'/%3E%3Cpath d='M8 10.5V7.8a4 4 0 0 1 8 0v2.7'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: center;
	background-size: 16px 16px;
}

/* --- Schalter beim Verfassen -------------------------------------------- */

/*
 * Die Ordination entscheidet beim Schreiben, ob geantwortet werden darf.
 * Der Schalter steht links in der Zeile ueber dem Textfeld; margin-right:
 * auto haelt ihn dort, auch wenn spaeter etwas daneben tritt.
 */
.dup-portal .dup-composer__reply:not( [hidden] ) {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	order: -1;
	gap: 8px;
	min-height: 32px;
	margin-right: auto;
	padding: 4px 13px 4px 9px;
	font-size: var( --dup-font-xs );
	font-weight: 600;
	line-height: 1.4;
	white-space: nowrap;
	color: var( --dup-text );
	background: var( --dup-surface );
	border: 1px solid var( --dup-border );
	border-radius: var( --dup-radius-pill );
	cursor: pointer;
	transition: color var( --dup-motion ) var( --dup-ease ),
		background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

/* Die ganze Pille ist anfassbar - das soll man auch merken. */
.dup-portal .dup-composer__reply:not( [hidden] ):hover {
	border-color: var( --dup-border-strong );
	background: var( --dup-surface-alt );
}

/*
 * Eigenes Kaestchen statt des Browser-Hakens: Der native Haken sitzt je nach
 * Betriebssystem unterschiedlich hoch, wirkt neben der zierlichen Pille zu
 * gross und laesst sich in der Groesse nicht sauber ausrichten. Hier ein
 * kleines Quadrat mit gezeichnetem Haken - immer mittig, immer gleich.
 */
.dup-portal .dup-composer__reply input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 17px;
	height: 17px;
	margin: 0;
	padding: 0;
	background: #fff;
	border: 1.5px solid var( --dup-border-strong );
	border-radius: 5px;
	cursor: pointer;
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

/* Der Haken selbst: zwei Kanten eines Quadrats, um 45 Grad gedreht. Der
 * kleine Versatz gleicht aus, dass die Drehung um die Mitte laeuft. */
.dup-portal .dup-composer__reply input[type="checkbox"]::after {
	content: "";
	width: 9px;
	height: 5px;
	margin-top: -3px;
	border-left: 2px solid #fff;
	border-bottom: 2px solid #fff;
	border-radius: 1px;
	transform: rotate( -45deg );
	opacity: 0;
	transition: opacity var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-composer__reply input[type="checkbox"]:checked {
	background: var( --dup-accent );
	border-color: var( --dup-accent );
}

.dup-portal .dup-composer__reply input[type="checkbox"]:checked::after {
	opacity: 1;
}

/*
 * Gesperrt ist der auffaellige Zustand, nicht der erlaubte: Die Regel ist
 * "Antwort moeglich", die Ausnahme muss beim Abschicken ins Auge fallen.
 *
 * Zwei Bauformen sind abgedeckt: ein Etikett mit Kaestchen darin, oder eine
 * Schaltflaeche mit data-reply. Bewusst nicht aria-pressed - dort waere nie
 * eindeutig, ob "gedrueckt" fuer erlaubt oder fuer gesperrt steht.
 */
.dup-portal .dup-composer__reply:has( input:not( :checked ) ),
.dup-portal .dup-composer__reply[data-reply="gesperrt"] {
	color: var( --dup-gold-ink );
	background: var( --dup-accent-tint );
	border-color: var( --dup-accent );
}

/*
 * Den Fokus traegt das Kaestchen, nicht das Etikett darum. Ohne diese Regel
 * saesse der Ring mitten in der Pille und es waere nicht zu sehen, dass die
 * ganze Pille gerade angefasst wird.
 */
.dup-portal .dup-composer__reply:focus-within {
	border-color: var( --dup-heading );
}

/* Abschnitt 2 macht den Fokusring eckig - bei einer Pille passt das nicht. */
.dup-portal .dup-composer__reply:focus-visible {
	border-radius: var( --dup-radius-pill );
}

/* --- Vermerk an der einzelnen Nachricht ---------------------------------- */

/*
 * Steht unter der Blase, nicht darin: In der eigenen goldenen Blase muesste
 * die gedaempfte Farbe eigens neu gesetzt werden, ausserhalb stimmt sie in
 * beiden Faellen.
 */
.dup-portal .dup-message__noreply:not( [hidden] ) {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: 3px;
	padding: 0 4px;
	font-size: var( --dup-font-xs );
	color: var( --dup-muted );
}

.dup-portal .dup-message__noreply::before {
	content: "";
	flex: 0 0 auto;
	width: 13px;
	height: 13px;
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4.5' y='10.5' width='15' height='9.5' rx='2'/%3E%3Cpath d='M8 10.5V7.8a4 4 0 0 1 8 0v2.7'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: center;
	background-size: 13px 13px;
}

/* -------------------------------------------------------------------------
 * 25. Uebergabe per QR-Code
 *
 * Wie die Bildanzeige in Abschnitt 7c: Die tragenden Eigenschaften stehen
 * bewusst OHNE Vorsatz. Faellt die Klasse dup-portal-page einmal weg - anderes
 * Theme, Seiten-Cache, ein Zusatzmodul, das body_class filtert -, laege die
 * Ueberlagerung sonst als normaler Block mitten in der Seite: ein
 * Einladungslink offen sichtbar fuer den, der als Naechster vor dem Schirm
 * steht. Farben sind fest angegeben, weil die Flaeche auch ausserhalb von
 * .dup-portal haengen kann und dessen Variablen dann nicht erbt.
 * ---------------------------------------------------------------------- */

.dup-qr,
.dup-qr * {
	box-sizing: border-box;
}

.dup-qr {
	position: fixed;
	inset: 0;
	/* Ueber der Adminleiste von WordPress (99999), unter dem Sichtschutz
	 * (100001) - kommt jemand ins Zimmer, gehoert der Schirm ihm. */
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 4vmin;
	background: rgba( 18, 19, 22, 0.88 );
	overscroll-behavior: contain;
}

/* Muss nach jeder display-Regel stehen und ohne Vorsatz auskommen: sonst
 * gewinnt display: flex gegen die Vorgabe des Browsers fuer [hidden]. */
.dup-qr[hidden] {
	display: none;
}

body.dup-portal-page .dup-qr {
	animation: dup-fade-in 160ms cubic-bezier( 0.2, 0, 0.2, 1 ) both;
}

/*
 * Alles darunter traegt .dup-qr im Selektor mit, und das ist keine Zierde:
 * Die Ueberlagerung kann als Kind von .dup-portal stehen, und dort gelten
 * .dup-portal button, .dup-portal figure und .dup-portal h2 aus Abschnitt 1.
 * Die sind jeweils um einen Typselektor staerker als eine einzelne Klasse -
 * ohne den Vorsatz naehme .dup-portal figure der Codeflaeche ihren
 * Innenabstand und damit die Ruhezone, und .dup-portal button machte aus der
 * Schaltflaeche wieder einen randlosen Text.
 */
.dup-qr .dup-qr__panel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 14px;
	width: min( 420px, 92vw );
	max-height: 92vh;
	overflow-y: auto;
	padding: 24px;
	background: #fff;
	border-radius: 14px;
	box-shadow: 0 20px 60px rgba( 0, 0, 0, 0.5 );
	font-family: inherit;
	font-size: 15px;
	line-height: 1.5;
	text-align: center;
	color: #33353d;
}

.dup-qr .dup-qr__title {
	margin: 0;
	font-size: 18px;
	font-weight: 700;
	line-height: 1.3;
	color: #121316;
}

/*
 * Zwei Dinge entscheiden darueber, ob ein Telefon den Code vom Schirm liest.
 *
 * Erstens die Groesse: Unter etwa 240px trifft die Kamera aus einer
 * angenehmen Entfernung zu wenige Bildpunkte je Modul. Die Innenflaeche ist
 * deshalb mindestens 240px breit - 272px abzueglich zweimal 16px Rand.
 *
 * Zweitens die Ruhezone: Die Norm verlangt vier Modulbreiten Weiss rundum.
 * Ohne sie findet das Lesegeraet die Begrenzung des Codes nicht und meldet
 * gar nichts - der haeufigste Grund, warum ein selbst erzeugter Code "nicht
 * funktioniert". Erzeugt das SVG die Ruhezone bereits selbst, schadet dieser
 * Rand nicht; fehlt sie, rettet er die Sache.
 */
.dup-qr .dup-qr__code {
	display: block;
	flex: 0 0 auto;
	width: min( 320px, 76vmin );
	min-width: 272px;
	padding: 16px;
	background: #fff;
	border: 1px solid #e4e4e7;
	border-radius: 8px;
}

.dup-qr .dup-qr__code svg,
.dup-qr .dup-qr__code img {
	display: block;
	width: 100%;
	height: auto;
	/*
	 * Keine Weichzeichnung an den Modulkanten: Ein weichgezeichneter Uebergang
	 * zwischen Schwarz und Weiss verschiebt die erkannte Modulgrenze, und
	 * gerade bei kleinen Codes scheitert das Lesegeraet daran.
	 */
	shape-rendering: crispEdges;
	image-rendering: pixelated;
}

.dup-qr .dup-qr__hint {
	margin: 0;
	font-size: 13.5px;
	color: #6b6c74;
	text-wrap: pretty;
}

/*
 * Die Adresse als Rueckfall zum Vorlesen oder Abtippen, wenn die Kamera
 * streikt. Sie ist lang und muss umbrechen duerfen, sonst sprengt sie die
 * Karte.
 */
.dup-qr .dup-qr__link {
	max-width: 100%;
	margin: 0;
	font-size: 12px;
	line-height: 1.5;
	color: #6b6c74;
	overflow-wrap: anywhere;
}

.dup-qr .dup-qr__close {
	min-height: 40px;
	padding: 8px 22px;
	font-family: inherit;
	font-size: 14px;
	font-weight: 700;
	line-height: 1;
	color: #121316;
	background: #fff;
	border: 1.5px solid #275765;
	border-radius: 999px;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
	transition: background-color 160ms cubic-bezier( 0.2, 0, 0.2, 1 ),
		border-color 160ms cubic-bezier( 0.2, 0, 0.2, 1 );
}

/* Beim Ueberfahren die volle Akzentfarbe, darauf weisse Schrift. */
.dup-qr .dup-qr__close:hover,
.dup-qr .dup-qr__close:focus-visible {
	color: #fff;
	background: #275765;
	border-color: #275765;
}

/* Ausserhalb von .dup-portal greift die Fokusregel aus Abschnitt 2 nicht. */
.dup-qr .dup-qr__close:focus-visible {
	outline: 2px solid #121316;
	outline-offset: 2px;
}

/* Sicherheitsnetz fuer alles Weitere, das die Karte ueber hidden schaltet. */
.dup-qr [hidden] {
	display: none;
}

/* -------------------------------------------------------------------------
 * 26. iPhone-Format an der Datei
 *
 * HEIC kommt aus der Kamera jedes neueren iPhones. Laesst es sich auf dem
 * Server nicht nach JPEG wandeln, bleibt es liegen wie es ist - dann zeigt es
 * ausser Safari kein Browser an, und die Datei wird zum Herunterladen
 * angeboten. Genau das gehoert an die Datei geschrieben, sonst klickt in der
 * Ordination jemand dreimal auf eine Vorschau, die nie kommt.
 *
 * Erkennungsmerkmal ist data-format="heic" am Traeger. Die Art bleibt "file":
 * Ein nicht anzeigbares Bild ist im Portal eine Datei wie jede andere.
 * ---------------------------------------------------------------------- */

.dup-portal .dup-attachment__flag:not( [hidden] ),
.dup-portal .dup-file__flag:not( [hidden] ) {
	display: flex;
	align-items: flex-start;
	gap: 6px;
	min-width: 0;
	margin-top: 1px;
	font-size: var( --dup-font-xs );
	font-weight: 600;
	line-height: 1.35;
	color: var( --dup-attach-meta );
	/* Der Hinweis ist ein ganzer Satz - er darf umbrechen. Abgeschnitten mit
	 * Auslassungspunkten waere gerade die Begruendung weg. */
	white-space: normal;
	overflow-wrap: anywhere;
}

.dup-portal .dup-attachment__flag:empty,
.dup-portal .dup-file__flag:empty {
	display: none;
}

/* Ein Telefon als Umriss - das Zeichen sagt "aus der Handykamera", ohne dass
 * dafuer Farbe noetig waere. */
.dup-portal .dup-attachment__flag::before,
.dup-portal .dup-file__flag::before {
	content: "";
	flex: 0 0 auto;
	width: 9px;
	height: 13px;
	margin-top: 2px;
	border: 1.5px solid currentcolor;
	border-radius: 2px;
}

/*
 * Zusaetzlich zum Satz ein Blickfang am Symbol selbst: blasses Gold und eine
 * gestrichelte Kante. In einer langen Dateiliste sieht man damit schon beim
 * Ueberfliegen, welche Datei aus einem iPhone stammt.
 */
.dup-portal .dup-attachment[data-format="heic"] .dup-attachment__icon,
.dup-portal .dup-file[data-format="heic"] .dup-file__icon {
	background: var( --dup-accent-tint );
	border-style: dashed;
	border-color: var( --dup-accent );
}

/* Schon in der Auswahl vor dem Senden erkennbar. */
.dup-portal .dup-composer__file[data-format="heic"] {
	border-style: dashed;
	border-color: var( --dup-accent );
}

/* -------------------------------------------------------------------------
 * 27. Kamera am Telefon
 *
 * Zwei Wege nebeneinander: die Bueroklammer fuer den Dateiweg, daneben "Foto
 * aufnehmen" fuer den geraden Weg in die Kamera. Die Form beider ist in
 * Abschnitt 7b beschrieben, hier steht nur, wo die Kamera keinen Sinn ergibt.
 * ---------------------------------------------------------------------- */

/*
 * An einem Rechner mit Maus oeffnet capture nur denselben Dateidialog wie die
 * Bueroklammer - eine zweite Schaltflaeche, die dasselbe tut, ist dort keine
 * Hilfe, sondern eine Frage mehr. Die Abfrage trifft ausdruecklich nur
 * Geraete, die ueberhaupt zeigen koennen, was unter dem Zeiger liegt; jedes
 * Telefon und jedes Tablett behaelt die Schaltflaeche.
 *
 * Dieselbe Verneinung wie oben, sonst gewinnt diese Regel gegen
 * .dup-portal [hidden] - und die Schaltflaeche liesse sich vom Skript nicht
 * mehr steuern.
 */
@media ( hover: hover ) and ( pointer: fine ) {

	.dup-portal .dup-composer__camera:not( [hidden] ) {
		display: none;
	}
}

/* -------------------------------------------------------------------------
 * 28. Uebertragung: Fortschritt, Abbruch, Wiederholen
 *
 * Auf dem Mobilfunk bricht eine Uebertragung ab, das ist kein Sonderfall.
 * Entscheidend ist, was danach auf dem Schirm steht: Der Text muss noch da
 * sein, der Grund muss dastehen, und ein Weg zurueck muss sichtbar sein -
 * ohne dass jemand die Dateien erneut heraussuchen muss.
 * ---------------------------------------------------------------------- */

/* --- Zustand der einzelnen Datei in der Auswahl -------------------------- */

/*
 * Bei mehreren Dateien reicht ein einziger Balken nicht: Man sieht ihn
 * kriechen, weiss aber nicht, woran es liegt. Deshalb faerbt sich zusaetzlich
 * jedes Plaettchen von links nach rechts, gesteuert ueber die Eigenschaft
 * --dup-progress am Plaettchen selbst (z. B. --dup-progress: 62%). Ohne
 * zusaetzliches Markup, und ohne Wert bleibt alles wie es war.
 */
.dup-portal .dup-composer__file.is-uploading {
	border-color: var( --dup-accent );
	background: linear-gradient(
		to right,
		var( --dup-accent-tint ) 0 var( --dup-progress, 0% ),
		var( --dup-surface-alt ) var( --dup-progress, 0% )
	);
}

/* Fertig uebertragen: ruhig, mit gezeichnetem Haken davor. */
.dup-portal .dup-composer__file.is-done {
	color: var( --dup-muted );
	border-color: var( --dup-border );
}

.dup-portal .dup-composer__file.is-done .dup-composer__file-name::before {
	content: "";
	display: inline-block;
	width: 5px;
	height: 9px;
	margin-right: 7px;
	border: solid var( --dup-success );
	border-width: 0 2px 2px 0;
	transform: translateY( -1px ) rotate( 45deg );
}

/* --- Abgebrochene Uebertragung ------------------------------------------ */

/*
 * Der Balken bleibt stehen, wo er stehen geblieben ist, und wechselt die
 * Farbe. Ein zurueckspringender oder verschwindender Balken laesst offen, ob
 * ueberhaupt etwas angekommen ist.
 */
.dup-portal .dup-composer__progress.is-aborted .dup-composer__progress-fill {
	background: var( --dup-danger );
}

/*
 * Die beiden Fassungen fuer <progress> stehen bewusst je in einer eigenen
 * Regel und nicht in einer gemeinsamen Liste: Ein Browser, der eines dieser
 * herstellereigenen Pseudoelemente nicht kennt, verwirft den GESAMTEN
 * Selektor - und damit auch den Teil, den er verstanden haette. Genau so ist
 * die Aufteilung schon in Abschnitt 7b begruendet.
 */
.dup-portal .dup-composer__progress.is-aborted progress.dup-composer__progress-bar::-webkit-progress-value {
	background: var( --dup-danger );
}

.dup-portal .dup-composer__progress.is-aborted progress.dup-composer__progress-bar::-moz-progress-bar {
	background: var( --dup-danger );
}

.dup-portal .dup-composer__progress.is-aborted .dup-composer__progress-text {
	font-weight: 600;
	color: var( --dup-danger );
}

/*
 * Hinweis in der Fehlerzeile, aber in ruhigerem Ton: Ein Abbruch auf dem
 * Mobilfunk ist kein Bedienfehler. Die Zeile sagt vor allem, dass der Text
 * erhalten geblieben ist - das ist die Frage, die sich in dem Moment stellt.
 */
.dup-portal .dup-composer__error.is-warn:not( [hidden] ) {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	font-weight: 600;
	color: var( --dup-gold-ink );
}

.dup-portal .dup-composer__error.is-warn::before {
	content: "";
	flex: 0 0 auto;
	width: 0;
	height: 0;
	margin-top: 4px;
	border: 6px solid transparent;
	border-top-width: 0;
	border-bottom-color: currentcolor;
}

/* --- Wiederholen im Verfassbereich --------------------------------------- */

.dup-portal .dup-composer__retry:not( [hidden] ) {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	order: -1;
	gap: 8px;
	min-height: 32px;
	margin-right: auto;
	padding: 5px 15px;
	font-size: var( --dup-font-xs );
	font-weight: 700;
	line-height: 1.4;
	white-space: nowrap;
	color: var( --dup-heading );
	background: var( --dup-surface );
	border: 1px solid var( --dup-gold-ink );
	border-radius: var( --dup-radius-pill );
	cursor: pointer;
	transition: background-color var( --dup-motion ) var( --dup-ease ),
		border-color var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-composer__retry:hover,
.dup-portal .dup-composer__retry:focus-visible {
	background: var( --dup-accent );
	border-color: var( --dup-accent );
	color: var( --dup-accent-ink );
}

.dup-portal .dup-composer__retry:focus-visible {
	border-radius: var( --dup-radius-pill );
}

/* Kreisender Pfeil - dasselbe Zeichen, das jeder Browser fuer "neu laden"
 * benutzt. Als Hintergrundbild, siehe Begruendung beim Schloss. */
.dup-portal .dup-composer__retry::before {
	content: "";
	flex: 0 0 auto;
	width: 14px;
	height: 14px;
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23121316' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 11.5a8 8 0 1 1-2.3-5.2'/%3E%3Cpath d='M20 3.5v5h-5'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: center;
	background-size: 14px 14px;
}

/* --- Nachricht in der Warteschlange -------------------------------------- */

/*
 * Die Blase steht schon im Verlauf, waehrend sie noch unterwegs ist. Etwas
 * durchscheinend heisst: da, aber noch nicht bestaetigt.
 */
.dup-portal .dup-message.is-pending .dup-message__bubble {
	opacity: 0.72;
}

.dup-portal .dup-message.is-pending .dup-message__time {
	font-style: italic;
}

/*
 * Fehlgeschlagen: Die Blase bleibt stehen - der Text ist nicht verloren -,
 * bekommt aber eine Kante in der Warnfarbe. Als Schatten und nicht als
 * border, damit die Blase nicht um zwei Pixel wandert und der ganze Verlauf
 * darunter nachrutscht.
 */
.dup-portal .dup-message.is-failed .dup-message__bubble {
	box-shadow: 0 0 0 1px var( --dup-danger );
}

.dup-portal .dup-message.is-failed .dup-message__time {
	font-weight: 600;
	font-style: normal;
	color: var( --dup-danger );
}

.dup-portal .dup-message__retry {
	min-height: 32px;
	margin-top: 6px;
	padding: 5px 15px;
	font-size: var( --dup-font-xs );
}

/*
 * Fortschritt an der Blase selbst, fuer den Fall, dass eine Nachricht mit
 * Dateien bereits im Verlauf haengt. Auch hier zaehlt --dup-progress; eine
 * Breite im style-Attribut schlaegt sie weiterhin.
 */
.dup-portal .dup-message__progress:not( [hidden] ) {
	display: block;
	width: min( 220px, 100% );
	height: 4px;
	margin-top: 6px;
	overflow: hidden;
	background: var( --dup-surface-alt );
	border: 0;
	border-radius: var( --dup-radius-pill );
}

.dup-portal .dup-message__progress-fill:not( [hidden] ) {
	display: block;
	width: var( --dup-progress, 0% );
	height: 100%;
	background: var( --dup-accent );
	border-radius: var( --dup-radius-pill );
	transition: width var( --dup-motion ) var( --dup-ease );
}

.dup-portal .dup-message.is-failed .dup-message__progress-fill {
	background: var( --dup-danger );
}

/* -------------------------------------------------------------------------
 * 29. Schmale Fenster und Bedienpraeferenzen fuer 21 bis 28
 * ---------------------------------------------------------------------- */

@media ( max-width: 600px ) {

	.dup-portal .dup-request:not( [hidden] ) {
		padding: 12px 13px;
	}

	.dup-portal .dup-requests:not( [hidden] ) {
		padding: 8px var( --dup-pad );
	}

	/* Auf dem Telefon eine volle Zeile - da trifft man sicher. */
	.dup-portal .dup-request__actions .dup-btn {
		flex: 1 1 100%;
	}

	.dup-portal .dup-request-form .dup-request-form__input,
	.dup-portal .dup-request-form__send {
		flex: 1 1 100%;
	}

	/*
	 * Auf dem Telefon teilen sich Textfeld, Senden und die runden
	 * Schaltflaechen ohnehin schon eine enge Zeile. Antwortschalter und
	 * Wiederholen bekommen deshalb eine eigene - nebeneinander bliebe vom
	 * Textfeld ein Streifen uebrig.
	 */
	.dup-portal .dup-composer__reply:not( [hidden] ),
	.dup-portal .dup-composer__retry:not( [hidden] ) {
		flex: 0 0 100%;
		justify-content: center;
		margin-right: 0;
	}

	/*
	 * Der Code darf nicht schrumpfen, also schrumpft die Karte um ihn herum:
	 * 96vw abzueglich 2x16px Innenabstand lassen auf einem 320px breiten
	 * Telefon noch 275px - gerade genug fuer die 272px des Codes.
	 */
	.dup-qr .dup-qr__panel {
		width: 96vw;
		gap: 12px;
		padding: 16px;
	}

	.dup-qr .dup-qr__title {
		font-size: 17px;
	}
}

@media ( prefers-reduced-motion: reduce ) {

	.dup-qr,
	.dup-qr * {
		animation-duration: 1ms !important; /* Nutzerpraeferenz schlaegt jede Regel. */
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
	}
}

@media ( forced-colors: active ) {

	.dup-portal .dup-status[data-status],
	.dup-portal .dup-request,
	.dup-portal .dup-requests,
	.dup-portal .dup-request-form,
	.dup-portal .dup-composer__reply,
	.dup-portal .dup-composer__retry,
	.dup-portal .dup-composer__attach,
	.dup-portal .dup-composer__camera,
	.dup-portal .dup-composer__request,
	.dup-portal .dup-message__progress,
	.dup-qr .dup-qr__panel,
	.dup-qr .dup-qr__code,
	.dup-qr .dup-qr__close {
		border: 1px solid CanvasText;
	}

	/*
	 * Im Kontrastmodus faellt jede Hintergrundfarbe weg. Die drei Stufen des
	 * Einladungsstatus bleiben trotzdem unterscheidbar, weil sie sich auch in
	 * Form und Zeichen unterscheiden - genau dafuer sind sie so gebaut. Die
	 * gestrichelte Kante der ersten Stufe muss aber ausdruecklich stehen
	 * bleiben, sie ist dort das einzige verbliebene Merkmal der Flaeche.
	 */
	.dup-portal .dup-status[data-status="kein-zugang"],
	.dup-portal .dup-request[data-status="zurueckgezogen"] {
		border-style: dashed;
	}

	/* Deckkraft wird im Kontrastmodus nicht als Abstufung gelesen. */
	.dup-portal .dup-request[data-status="zurueckgezogen"],
	.dup-portal .dup-message.is-pending .dup-message__bubble {
		opacity: 1;
	}

	.dup-portal .dup-composer.is-locked .dup-composer__input,
	.dup-portal .dup-composer.is-locked .dup-composer__send,
	.dup-portal .dup-composer.is-locked .dup-composer__attach {
		opacity: 1;
	}

	/* Die Warnkante der gescheiterten Nachricht kommt als Schatten - Schatten
	 * zeichnet der Kontrastmodus nicht. */
	.dup-portal .dup-message.is-failed .dup-message__bubble {
		outline: 2px solid CanvasText;
		outline-offset: 1px;
	}

	.dup-qr {
		background: Canvas;
	}

	.dup-qr .dup-qr__panel {
		background: Canvas;
		color: CanvasText;
	}

	/*
	 * Der Code selbst muss schwarz auf weiss bleiben. forced-color-adjust:
	 * none ist hier keine Bequemlichkeit: Ein umgefaerbter oder invertierter
	 * QR-Code wird von vielen Lesegeraeten nicht mehr erkannt, und dann ist
	 * die ganze Ueberlagerung wertlos.
	 */
	.dup-qr .dup-qr__code,
	.dup-qr .dup-qr__code svg,
	.dup-qr .dup-qr__code img {
		forced-color-adjust: none;
		background: #fff;
	}
}

/* -------------------------------------------------------------------------
 * 30. Einwilligung zur Datenverarbeitung
 *
 * Ohne gespeicherte Zustimmung liegt diese Ebene ueber dem ganzen Portal.
 * Durchgesetzt wird die Sperre auf dem Server - hier steht nur, dass vor der
 * Entscheidung nichts anderes bedienbar aussieht.
 *
 * Dieselbe Bauart wie der Sichtschutz in Abschnitt 15: Die tragenden
 * Eigenschaften haengen bewusst NICHT an body.dup-portal-page, damit die
 * Ebene auch dann deckt, wenn die Klasse einmal fehlt. Der z-index bleibt
 * unter dem Sichtschutz (100001): Kommt jemand ins Zimmer, hat der
 * Sichtschutz das letzte Wort - auch ueber der Einwilligung.
 *
 * :not( [hidden] ) an jeder display-Regel: Die Ebene wird per hidden
 * geschaltet, und eine display-Angabe des Autors schluege sonst die Vorgabe
 * des Browsers fuer [hidden] - die Ebene liesse sich nie schliessen.
 * ---------------------------------------------------------------------- */

.dup-consent:not( [hidden] ) {
	position: fixed;
	inset: 0;
	/* Ueber der Adminleiste von WordPress (99999), unter dem Sichtschutz
	 * (100001) - wie die QR-Karte in Abschnitt 25. */
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 4vmin;
	background: rgba( 18, 19, 22, 0.88 );
	overscroll-behavior: contain;
}

/* Muss nach der display-Regel stehen und ohne Vorsatz auskommen - dasselbe
 * Sicherheitsnetz wie bei Sichtschutz, Bildanzeige und QR-Karte. */
.dup-consent[hidden] {
	display: none;
}

/*
 * Die Karte: weiss, mittig, hoechstens 560px breit. Der Wortlaut kann lang
 * sein und waechst mit jeder Fassung - auf kleinen Schirmen rollt deshalb
 * die Karte selbst, nie die Seite dahinter.
 */
.dup-portal .dup-consent__card {
	display: flex;
	flex-direction: column;
	gap: 14px;
	width: min( 560px, 100% );
	max-width: 560px;
	max-height: 85vh;
	overflow-y: auto;
	padding: 26px 28px;
	background: #fff;
	border-radius: var( --dup-radius, 12px );
	box-shadow: 0 20px 60px rgba( 0, 0, 0, 0.5 );
	font-size: 15px;
	line-height: 1.6;
	text-align: left;
	color: var( --dup-text, #33353d );
	scrollbar-width: thin;
	scrollbar-color: var( --dup-border-strong, #d5d6da ) transparent;
	overscroll-behavior: contain;
}

.dup-portal .dup-consent__title {
	margin: 0;
	font-size: 18px;
	font-weight: 700;
	line-height: 1.3;
	color: var( --dup-heading, #121316 );
}

/*
 * Das Skript fuellt den Wortlaut per textContent. Umbrueche stehen im Text,
 * niemals als Markup - pre-wrap macht sie sichtbar, siehe
 * Sicherheitsleitfaden. 15px bei Zeilenhoehe 1.6: Der Text ist der Kern der
 * Ebene und muss ohne Anstrengung lesbar sein.
 */
.dup-portal .dup-consent__text {
	margin: 0;
	font-size: 15px;
	line-height: 1.6;
	color: var( --dup-text, #33353d );
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

.dup-portal .dup-consent__link {
	margin: 0;
	font-size: var( --dup-font-sm, 13px );
}

/* Farbe auch bei :hover und :focus, sonst faerbt das Theme den Verweis um. */
.dup-portal .dup-consent__link a,
.dup-portal .dup-consent__link a:hover,
.dup-portal .dup-consent__link a:focus {
	color: var( --dup-text, #33353d );
	text-decoration: underline;
	text-underline-offset: 2px;
}

.dup-portal .dup-consent__link a:hover,
.dup-portal .dup-consent__link a:focus-visible {
	color: var( --dup-heading, #121316 );
}

/* Ohne hinterlegte Datenschutz-Adresse bleibt der Verweis leer - dann darf
 * die Zeile keine Luecke in der Karte reissen. */
.dup-portal .dup-consent__link:empty,
.dup-portal .dup-consent__link:has( a:empty ) {
	display: none;
}

.dup-portal .dup-consent__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px 12px;
	margin: 4px 0 0;
}

/*
 * Ablehnen ist ein Verweis (der Abmeldelink), traegt aber .dup-btn und soll
 * wie die ruhige Schaltflaeche aus Abschnitt 4 aussehen. Farbe und
 * text-decoration stehen auch bei :hover und :focus, sonst greift dort die
 * Verweisfarbe des Themes - dieselbe Begruendung wie beim Terminverweis.
 */
.dup-portal .dup-consent__decline,
.dup-portal .dup-consent__decline:hover,
.dup-portal .dup-consent__decline:focus {
	color: var( --dup-heading, #121316 );
	text-decoration: none;
}

/*
 * Rueckmeldung unter den Schaltflaechen ("Wird gespeichert ...", Fehler).
 * Kraeftig genug zum Lesen, aber ohne eigene Farbe: Was zu tun ist, steht
 * im Text selbst.
 */
.dup-portal .dup-consent__hint {
	margin: 0;
	font-size: var( --dup-font-sm, 13px );
	font-weight: 600;
	color: var( --dup-heading, #121316 );
}

/* Leer darf die Zeile keine Luecke im Kartenraster lassen. */
.dup-portal .dup-consent__hint:empty {
	display: none;
}

/* --- Schmale Fenster ------------------------------------------------------ */

@media ( max-width: 600px ) {

	.dup-consent:not( [hidden] ) {
		padding: 3vmin;
	}

	.dup-portal .dup-consent__card {
		padding: 20px 16px;
	}

	/* Auf dem Telefon eine volle Zeile je Schaltflaeche - da trifft man sicher. */
	.dup-portal .dup-consent__actions .dup-btn {
		flex: 1 1 100%;
	}
}

/* --- Kontrastmodus -------------------------------------------------------- */

@media ( forced-colors: active ) {

	.dup-consent:not( [hidden] ) {
		background: Canvas;
	}

	.dup-portal .dup-consent__card {
		border: 1px solid CanvasText;
	}
}

/* =====================================================================
 * Anmeldekarte am Telefon: alles auf EINEM Schirm.
 *
 * Steht bewusst am Dateiende: Die Regeln müssen die Popup-Kompaktwerte
 * (html.dup-popup ...) bei gleicher Spezifität durch spätere Position
 * schlagen. Jede Gruppe nennt deshalb beide Fassungen - Portalseite
 * direkt UND im eingebetteten Popup (das am Telefon bildfüllend ist).
 *
 * dvh statt vh: Die ein- und ausfahrende Adressleiste von iOS ändert
 * die vh-Höhe - mit dvh bemisst sich die Karte am tatsächlich
 * sichtbaren Ausschnitt und nichts rutscht aus dem Bild.
 * =================================================================== */
@media ( max-width: 600px ) {
	body.dup-portal-page .dup-gate {
		min-height: 100dvh;
		padding: 12px 12px 10px;
		gap: 10px;
	}

	html.dup-popup body.dup-portal-page .dup-gate {
		min-height: 100dvh;
		display: flex;
		flex-direction: column;
		justify-content: center;
	}

	body.dup-portal-page .dup-gate__banner,
	html.dup-popup body.dup-portal-page .dup-gate__banner {
		padding: 8px 12px;
		font-size: 11.5px;
		line-height: 1.45;
	}

	body.dup-portal-page .dup-gate__card,
	html.dup-popup body.dup-portal-page .dup-gate__card {
		padding: 14px 16px 12px;
	}

	body.dup-portal-page .dup-gate__logo,
	html.dup-popup body.dup-portal-page .dup-gate__logo {
		height: 30px;
		margin-bottom: 10px;
	}

	body.dup-portal-page .dup-gate__title,
	html.dup-popup body.dup-portal-page .dup-gate__title {
		font-size: 17px;
		margin-bottom: 6px;
	}

	body.dup-portal-page .dup-gate__lead,
	html.dup-popup body.dup-portal-page .dup-gate__lead {
		font-size: 12px;
		line-height: 1.5;
		margin-bottom: 12px;
	}

	body.dup-portal-page .dup-gate__alert,
	html.dup-popup body.dup-portal-page .dup-gate__alert {
		font-size: 12px;
		padding: 8px 10px;
		margin-bottom: 10px;
	}

	body.dup-portal-page .dup-gate label,
	html.dup-popup body.dup-portal-page .dup-gate label {
		font-size: 12px;
		margin-bottom: 4px;
	}

	/* 16px Schrift bleibt Pflicht (iOS-Zoom) - nur die Höhe schrumpft. */
	body.dup-portal-page .dup-gate input[type="text"],
	body.dup-portal-page .dup-gate input[type="password"],
	html.dup-popup body.dup-portal-page .dup-gate input[type="text"],
	html.dup-popup body.dup-portal-page .dup-gate input[type="password"] {
		height: 42px;
		font-size: 16px;
	}

	body.dup-portal-page .dup-gate .dup-passfeld__auge,
	html.dup-popup body.dup-portal-page .dup-gate .dup-passfeld__auge {
		width: 40px;
		height: 40px;
	}

	body.dup-portal-page .dup-gate .login-username,
	body.dup-portal-page .dup-gate .login-password,
	html.dup-popup body.dup-portal-page .dup-gate .login-username,
	html.dup-popup body.dup-portal-page .dup-gate .login-password {
		margin-bottom: 10px;
	}

	body.dup-portal-page .dup-gate .login-remember,
	html.dup-popup body.dup-portal-page .dup-gate .login-remember {
		margin-bottom: 12px;
		font-size: 12.5px;
	}

	body.dup-portal-page .dup-gate #dup-wp-submit,
	html.dup-popup body.dup-portal-page .dup-gate #dup-wp-submit {
		height: 44px;
	}

	/* Die beiden Verweise nebeneinander statt untereinander: eine Zeile. */
	body.dup-portal-page .dup-gate__links,
	html.dup-popup body.dup-portal-page .dup-gate__links {
		margin-top: 10px;
		flex-direction: row;
		flex-wrap: wrap;
		justify-content: center;
		gap: 4px 14px;
		font-size: 12px;
	}

	body.dup-portal-page .dup-gate__foot,
	html.dup-popup body.dup-portal-page .dup-gate__foot {
		font-size: 11px;
		line-height: 1.5;
		padding: 0 4px 4px;
	}
}

/* Niedrigere Schirme (iPhone SE/8-Klasse, kleine Androids): eine Stufe
 * enger. Der Einleitungstext weicht - Titel und Felder sagen alles
 * Nötige. Der Datenschutz-Verweis in der Karte entfällt ebenfalls: Er
 * steht eine Handbreit tiefer wortgleich in der Fußzeile. */
@media ( max-width: 600px ) and ( max-height: 700px ) {
	body.dup-portal-page .dup-gate,
	html.dup-popup body.dup-portal-page .dup-gate {
		gap: 8px;
	}

	body.dup-portal-page .dup-gate__lead,
	html.dup-popup body.dup-portal-page .dup-gate__lead {
		display: none;
	}

	body.dup-portal-page .dup-gate__privacy,
	html.dup-popup body.dup-portal-page .dup-gate__privacy {
		display: none;
	}

	body.dup-portal-page .dup-gate__title,
	html.dup-popup body.dup-portal-page .dup-gate__title {
		font-size: 16px;
		margin-bottom: 4px;
	}

	body.dup-portal-page .dup-gate .login-username,
	body.dup-portal-page .dup-gate .login-password,
	html.dup-popup body.dup-portal-page .dup-gate .login-username,
	html.dup-popup body.dup-portal-page .dup-gate .login-password {
		margin-bottom: 8px;
	}

	body.dup-portal-page .dup-gate__logo,
	html.dup-popup body.dup-portal-page .dup-gate__logo {
		height: 24px;
		margin-bottom: 8px;
	}

	body.dup-portal-page .dup-gate__foot,
	html.dup-popup body.dup-portal-page .dup-gate__foot {
		font-size: 10.5px;
	}
}
