/* ===== Tokens ===== */

/* Shared palette below = verbatim copy of the `:root` block in myroster-next
 * src/styles/globals.css @ 11e8320 (2026-08-29); keep names, values and order
 * identical so drift stays diffable. Bare HSL parts: `hsl(var(--primary))`. */

:root {
	--background: 210 40% 98%;
	--foreground: 222 47% 11%;

	--card: 0 0% 100%;
	--card-foreground: 222 47% 11%;

	--popover: 0 0% 100%;
	--popover-foreground: 222 47% 11%;

	--primary: 221 83% 53%;
	--primary-foreground: 210 40% 98%;

	--secondary: 210 40% 96.1%;
	--secondary-foreground: 222 47% 11.2%;

	--muted: 210 40% 96.1%;
	--muted-foreground: 215.4 16.3% 46.9%;

	--accent: 210 40% 96.1%;
	--accent-foreground: 222.2 47.4% 11.2%;

	--destructive: 0 84.2% 60.2%;
	--destructive-foreground: 210 40% 98%;

	--border: 214.3 31.8% 91.4%;
	--input: 214.3 31.8% 91.4%;
	--ring: 221 83% 53%;

	--radius: 0.5rem;

	/* Below: id-provider's own tokens — add new values here, not to the copy above.
	 * Every one of them is re-stated in the dark block; a value that only exists
	 * here is a value that breaks when the OS switches. */

	/* Darker steps of --primary; the link shades must clear 4.5:1 on white. */
	--primary-hover: 221 83% 47%;
	--primary-active: 221 83% 42%;
	--primary-link: 221 83% 42%;
	--primary-link-hover: 221 83% 36%;

	/* The header is its own surface, not `--primary` directly: in dark mode a bar
	 * in the primary blue would be the brightest thing on the page. */
	--header-bg: 221 83% 53%;
	--header-foreground: 210 40% 98%;
	--header-border: 221 83% 40%;
	--header-hover-bg: 0 0% 100% / 0.14;
	--header-focus-ring: 0 0% 100%;

	/* Card and input surfaces. Light: both white on a tinted page. Dark: lifted off
	 * the page, because the shared palette gives --card and --background the same
	 * value and a card that matches the page behind it reads as no card at all. */
	--surface-card: 0 0% 100%;
	--surface-input: 0 0% 100%;
	--surface-secondary-hover: 210 40% 92%;

	--danger-surface: 0 84% 97%;
	--danger-border: 0 84% 90%;
	--danger-foreground: 0 62% 30%;
	--success-surface: 145 60% 96%;
	--success-border: 145 45% 82%;
	--success-foreground: 152 55% 24%;

	--font-sans: "Inter", ui-sans-serif, system-ui, -apple-system,
		BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
		sans-serif, "Apple Color Emoji", "Segoe UI Emoji";

	--text-xs: 0.75rem;
	--text-sm: 0.875rem;
	--text-body: 1rem; /* 16px — body copy AND every form control; see THE iOS ZOOM FIX */
	--text-prose: 0.9375rem;
	--text-lg: 1.125rem;
	--text-h2: 1.25rem;
	--text-h1: clamp(1.375rem, 1.15rem + 1.1vw, 1.625rem);

	--leading-tight: 1.25;
	--leading-normal: 1.5;
	--leading-relaxed: 1.6;
	--tracking-tight: -0.02em;

	--weight-normal: 400;
	--weight-semibold: 600;

	--space-2xs: 0.25rem;
	--space-xs: 0.5rem;
	--space-sm: 0.75rem;
	--space-md: 1rem;
	--space-lg: 1.5rem;
	--space-xl: 2rem;
	--space-2xl: 3rem;

	--radius-md: calc(var(--radius) - 2px);
	--radius-lg: var(--radius);

	/* Spelled out rather than hsl(var(--foreground)/…): --foreground is near-white
	 * in dark mode, and a shadow tinted with it is a glow. */
	--shadow-card: 0 1px 3px hsl(222 47% 11% / 0.06),
		0 12px 40px -12px hsl(var(--primary) / 0.15);
	--shadow-button: 0 4px 14px hsl(var(--primary) / 0.35);

	/* Layout */
	--container-max: 71.25rem; /* 1140px — matches the old header width */
	--card-max: 28rem;
	--card-max-wide: 42rem;

	/* WCAG 2.2 target size / Apple HIG minimum. */
	--tap-min: 2.75rem;

	/* Motion & focus */
	--transition-fast: 0.15s ease;
	--focus-ring: 0 0 0 3px hsl(var(--ring) / 0.35);
	--focus-outline-width: 2px;
	--focus-outline-offset: 2px;

	/* Both, so the UA renders its own furniture — scrollbars, the caret, autofill
	 * dropdowns, the password-reveal eye — to match whichever theme is active,
	 * instead of handing back a white rectangle on a dark page. */
	color-scheme: light dark;
}

/* Dark mode follows the OS: this server has no theme toggle and no place to keep
 * a preference, and a login screen is the wrong place to ask for one.
 *
 * The first block is the `.dark` block from myroster-next globals.css @ 11e8320,
 * verbatim and in the same order as the light copy above — same reason: drift
 * between login and portal should show up in a diff. Everything after it is
 * id-provider's own, and every token from the light block appears here. */

@media (prefers-color-scheme: dark) {
	:root {
		--background: 222.2 84% 4.9%;
		--foreground: 210 40% 98%;

		--card: 222.2 84% 4.9%;
		--card-foreground: 210 40% 98%;

		--popover: 222.2 84% 4.9%;
		--popover-foreground: 210 40% 98%;

		--primary: 217.2 91.2% 59.8%;
		--primary-foreground: 222.2 47.4% 11.2%;

		--secondary: 217.2 32.6% 17.5%;
		--secondary-foreground: 210 40% 98%;

		--muted: 217.2 32.6% 17.5%;
		--muted-foreground: 215 20.2% 65.1%;

		--accent: 217.2 32.6% 17.5%;
		--accent-foreground: 222.2 47.4% 11.2%;

		--destructive: 0 62.8% 30.6%;
		--destructive-foreground: 210 40% 98%;

		--border: 217.2 32.6% 17.5%;
		--input: 217.2 32.6% 17.5%;
		--ring: 212.7 26.8% 83.9%;

		/* id-provider's own, from here down. Contrast measured against the page
		 * background above; the link shades hold the same ratios as light mode
		 * (7.2:1 and 9.5:1) by going lighter rather than darker. */
		--primary-hover: 217.2 91.2% 65%;
		--primary-active: 217.2 91.2% 70%;
		--primary-link: 217.2 91.2% 68%;
		--primary-link-hover: 217.2 91.2% 76%;

		/* A dark bar, not the primary blue: at 59.8% lightness that would be the
		 * brightest surface on the page, which is how a dark theme ends up looking
		 * like a light theme someone inverted. */
		--header-bg: 222.2 47.4% 11.2%;
		--header-foreground: 210 40% 98%;
		--header-border: 217.2 32.6% 17.5%;
		--header-hover-bg: 0 0% 100% / 0.08;
		--header-focus-ring: 212.7 26.8% 83.9%;

		/* Lifted off the page: the shared palette gives --card the same value as
		 * --background, so without this the card is a border around nothing. */
		--surface-card: 222.2 47.4% 9%;
		--surface-input: 222.2 47.4% 13%;
		--surface-secondary-hover: 217.2 32.6% 22%;

		--danger-surface: 0 63% 14%;
		--danger-border: 0 63% 26%;
		--danger-foreground: 0 90% 82%;
		--success-surface: 152 45% 12%;
		--success-border: 152 40% 24%;
		--success-foreground: 145 60% 78%;

		/* Black, and deeper: a shadow tinted with --foreground would glow. */
		--shadow-card: 0 1px 3px hsl(0 0% 0% / 0.5),
			0 12px 40px -12px hsl(0 0% 0% / 0.7);
		--shadow-button: 0 4px 14px hsl(var(--primary) / 0.25);
	}

	/* The brand wash is a light-on-dark glow here; at the light-mode opacities it
	 * turns the top of the page into a blue haze. */
	.idp-auth-shell::before {
		opacity: 0.55;
	}

}

/* ===== Base ===== */

/* Inter 4.001, latin subset, SIL OFL 1.1 — the family myroster-next also loads,
 * so login and portal match. `swap`: never hide a login form behind a font. */

@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("/assets/fonts/inter-latin-400-normal-f837d382a885a07c34a3d4bf4f49373d.woff2") format("woff2");
}

@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("/assets/fonts/inter-latin-600-normal-75fb76ec35595f04717e8b3f1dc3ae2a.woff2") format("woff2");
}

/* Reset */

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

body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
figure,
blockquote,
dl,
dd {
	margin: 0;
}

ul,
ol {
	margin: 0;
	padding: 0;
}

/* Document */

html {
	/* Pinned: iOS Safari/Chrome otherwise inflate text after an orientation change. */
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
	overflow-wrap: break-word;
}

body {
	min-height: 100dvh;
	margin: 0;
	font-family: var(--font-sans);
	font-size: var(--text-body);
	font-weight: var(--weight-normal);
	line-height: var(--leading-normal);
	color: hsl(var(--foreground));
	background-color: hsl(var(--background));
	text-align: left;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	font-feature-settings: "rlig" 1, "calt" 1;
}

/* Typography */

h1,
h2,
h3,
h4,
h5,
h6 {
	font-weight: var(--weight-semibold);
	line-height: var(--leading-tight);
	color: hsl(var(--foreground));
	text-wrap: balance;
}

h1 {
	font-size: var(--text-h1);
	letter-spacing: var(--tracking-tight);
}

h2 {
	font-size: var(--text-h2);
}

h3,
h4 {
	font-size: var(--text-lg);
}

h5,
h6 {
	font-size: var(--text-body);
}

p {
	text-wrap: pretty;
}

p + p,
p + ul,
p + ol,
ul + p,
ol + p,
h1 + p,
h2 + p,
h3 + p,
h4 + p {
	margin-top: var(--space-md);
}

ul,
ol {
	padding-left: 1.25rem;
}

li + li {
	margin-top: var(--space-2xs);
}

a {
	color: hsl(var(--primary-link));
	text-decoration: none;
}

a:hover,
a:focus {
	color: hsl(var(--primary-link-hover));
	text-decoration: underline;
}

strong,
b {
	font-weight: var(--weight-semibold);
}

small {
	font-size: var(--text-sm);
}

hr {
	height: 0;
	margin: var(--space-lg) 0;
	overflow: visible;
	border: 0;
	border-top: 1px solid hsl(var(--border));
}

img,
svg,
video,
canvas,
iframe {
	max-width: 100%;
}

img,
svg,
video {
	vertical-align: middle;
}

/* Forms — THE iOS ZOOM FIX, at its cause. Mobile Safari zooms the page when a
 * focused control renders below 16px, and the zoomed layout is then wider than
 * the viewport. Every control is 16px at every width; do not lower it. */

input,
button,
select,
optgroup,
textarea {
	margin: 0;
	font-family: inherit;
	font-size: var(--text-body);
	line-height: inherit;
	color: inherit;
}

button,
[type="button"],
[type="reset"],
[type="submit"] {
	-webkit-appearance: button;
	appearance: button;
	cursor: pointer;
}

button:disabled,
input:disabled {
	cursor: default;
}

textarea {
	resize: vertical;
}

label {
	display: inline-block;
}

/* Firefox's inner focus ring — we draw our own below. */
::-moz-focus-inner {
	padding: 0;
	border-style: none;
}

/* Focus */

:focus-visible {
	outline: var(--focus-outline-width) solid hsl(var(--ring));
	outline-offset: var(--focus-outline-offset);
}

:focus:not(:focus-visible) {
	outline: none;
}

/* Motion — every transition here uses var(--transition-fast); collapsing that
 * one token disables all of them at once. */

@media (prefers-reduced-motion: reduce) {
	:root {
		--transition-fast: 0s;
	}

	html {
		scroll-behavior: auto;
	}
}

/* Utilities */

[hidden] {
	display: none;
}

/* ===== Layout ===== */

.app {
	display: flex;
	flex-direction: column;
	/* dvh, not vh: `100vh` is mobile Safari's LARGE viewport and overflows. */
	min-height: 100dvh;
	min-width: 0;
}

/* Container — the inline padding carries the safe-area inset so content clears
 * a notch in landscape. */
.container {
	width: 100%;
	max-width: var(--container-max);
	margin-inline: auto;
	padding-inline: max(var(--space-md), env(safe-area-inset-left))
		max(var(--space-md), env(safe-area-inset-right));
	min-width: 0;
}

/* Header */

.app-header {
	flex: none;
	background: hsl(var(--header-bg));
	border-bottom: 1px solid hsl(var(--header-border));
	padding-top: env(safe-area-inset-top);
}

/* Sticky, not fixed + a hard-coded body offset: sticky reserves its own space,
 * so the header can grow without ever covering the content beneath it. */
.header-fixed .app-header {
	position: sticky;
	top: 0;
	z-index: 1020;
}

.app-header > .container {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-xs);
	padding-block: var(--space-xs);
}

.header-logo {
	display: flex;
	align-items: center;
	min-width: 0;
}

.header-logo a {
	display: flex;
	align-items: center;
	min-height: var(--tap-min);
	padding-inline: 0;
}

.header-logo img {
	display: block;
	height: 3.125rem; /* 50px — keeps step with height="50px" in the template */
	width: auto;
}

/* Must stay a plain wrapping row: the old off-canvas overlay was unreachable. */
.menu-wrapper {
	display: flex;
	align-items: center;
	min-width: 0;
	margin-left: auto;
}

.app-body {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	/* Load-bearing: a long unbreakable child otherwise widens the whole column. */
	min-width: 0;
	width: 100%;
}

/* Error pages put a bare .container in the content slot. */
.app-body > .container {
	padding-block: var(--space-xl) var(--space-2xl);
}

/* Auth shell — centres a card in whatever height is left over */

.idp-auth-shell {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	align-items: center;
	/* Centring is done by the child's auto margins, NOT justify-content: center.
	 * Once the card is taller than the shell — a phone in landscape, or a 200%
	 * browser zoom — centred content overflows equally in both directions and the
	 * top of the card scrolls off past the start edge, where it cannot be reached.
	 * Auto margins collapse to zero instead of going negative. */
	justify-content: flex-start;
	min-width: 0;
	width: 100%;
	position: relative;
	background: hsl(var(--background));
	/* top | right | bottom | left */
	padding: var(--space-xl) max(var(--space-md), env(safe-area-inset-right))
		calc(var(--space-2xl) + env(safe-area-inset-bottom))
		max(var(--space-md), env(safe-area-inset-left));
}

/* Decorative brand wash; a pseudo-element so it never intercepts a tap. */
.idp-auth-shell::before {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: radial-gradient(
			ellipse 480px 480px at 85% -10%,
			hsl(var(--primary) / 0.12),
			transparent 55%
		),
		radial-gradient(
			ellipse 420px 420px at -15% 45%,
			hsl(var(--primary) / 0.06),
			transparent 50%
		),
		radial-gradient(
			ellipse 400px 400px at 70% 100%,
			hsl(var(--primary) / 0.05),
			transparent 45%
		);
}

.idp-auth-shell > * {
	position: relative;
	z-index: 1;
	width: 100%;
	min-width: 0;
	margin-block: auto;
}

/* Narrow phones (iPhone SE and down): the card's own 2rem padding costs a
 * quarter of a 320px viewport, so give it back to the content. */
@media (max-width: 22.5rem) {
	.form-signin,
	.idp-auth-shell--wide > .container {
		padding: var(--space-lg) var(--space-md);
	}
}

/* Phone in landscape: ~375px of height for header, card and footer together.
 * Trim the shell's vertical breathing room rather than let the card scroll. */
@media (max-height: 30rem) and (orientation: landscape) {
	.idp-auth-shell {
		padding-block: var(--space-md)
			calc(var(--space-md) + env(safe-area-inset-bottom));
	}

	.form-header {
		margin-bottom: var(--space-md);
	}
}

/* Footer — no fixed height, ever. Pinned at `flex: 0 0 50px` its items wrapped
 * to three lines on a phone and overflowed into the page above. */

.app-footer {
	flex: none;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--space-2xs) var(--space-md);
	/* top | right | bottom | left */
	padding: var(--space-sm) max(var(--space-md), env(safe-area-inset-right))
		calc(var(--space-sm) + env(safe-area-inset-bottom))
		max(var(--space-md), env(safe-area-inset-left));
	font-size: var(--text-sm);
	color: hsl(var(--muted-foreground));
	background: hsl(var(--surface-card));
	border-top: 1px solid hsl(var(--border));
	text-align: center;
}

@media (min-width: 40rem) {
	.app-footer {
		justify-content: space-between;
		text-align: left;
	}
}

/* ===== Components ===== */

/* Class names are Bootstrap-shaped with purpose-built implementations behind
 * them. This is not Bootstrap: no rule below is here for a class, state or
 * element the templates never produce. The converse is not promised — a
 * template may carry a class nothing styles. */

/* The standard auth card; the second selector is that card at consent's width. */
.form-signin,
.idp-auth-shell--wide > .container {
	width: 100%;
	max-width: var(--card-max);
	margin-inline: auto;
	padding: var(--space-xl);
	background: hsl(var(--surface-card));
	color: hsl(var(--card-foreground));
	border: 1px solid hsl(var(--border));
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-card);
	min-width: 0;
}

.idp-auth-shell--wide > .container {
	max-width: var(--card-max-wide);
}

.form-header {
	margin-bottom: var(--space-lg);
	text-align: center;
}

.form-header h1 {
	margin-bottom: var(--space-sm);
}

.form-header p {
	margin-top: 0;
	font-size: var(--text-prose);
	line-height: var(--leading-relaxed);
	color: hsl(var(--muted-foreground));
}

.form-aside {
	margin-top: var(--space-md);
	font-size: var(--text-body);
	font-weight: var(--weight-normal);
	text-align: center;
}

/* The registration line: present, not competing. As a full-width button it carried the
 * same weight as "Anmelden" on a screen with one job. */
.form-aside--muted {
	font-size: var(--text-prose);
	color: hsl(var(--muted-foreground));
}

.form-aside--muted a {
	font-weight: var(--weight-semibold);
}

/* The interstitial's subject, before its headline. One offer, one image. */
.form-icon {
	display: flex;
	justify-content: center;
	margin-bottom: var(--space-lg);
}

.form-icon svg {
	width: 2.125rem;
	height: 2.125rem;
	padding: 1.0625rem;
	box-sizing: content-box;
	fill: none;
	stroke: hsl(var(--primary));
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
	border-radius: 999px;
	background: hsl(var(--primary) / 0.1);
}

/* The reassurance under the two actions: present, subordinate to both. */
.form-note {
	display: flex;
	align-items: flex-start;
	gap: var(--space-xs);
	margin: var(--space-lg) 0 0;
	padding-top: var(--space-md);
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
	color: hsl(var(--muted-foreground));
	border-top: 1px solid hsl(var(--border));
	text-align: left;
}

.form-note-icon {
	flex: none;
	width: 1rem;
	height: 1rem;
	margin-top: 0.125rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.75;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* Sits under its field, not in a paragraph above the form. */
.form-hint {
	margin: calc(-1 * var(--space-xs)) 0 var(--space-md);
	padding-inline: var(--space-2xs);
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
	color: hsl(var(--muted-foreground));
}

/* Labelled rule between the passkey offer and the password form. */
.form-divider {
	display: flex;
	align-items: center;
	gap: var(--space-sm);
	margin-block: var(--space-md);
	color: hsl(var(--muted-foreground));
	font-size: var(--text-sm);
}

.form-divider::before,
.form-divider::after {
	content: "";
	flex: 1 1 auto;
	height: 1px;
	background: hsl(var(--border));
}

/* Inherits currentColor, so one markup shape serves both button variants. */
.btn-icon {
	flex: none;
	width: 1.25rem;
	height: 1.25rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.75;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.btn-outline-secondary .btn-icon {
	stroke: hsl(var(--primary));
}

.form-aside a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: var(--tap-min);
	padding-inline: var(--space-xs);
}

.text-muted {
	color: hsl(var(--muted-foreground));
}

/* Buttons */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-xs);
	min-height: var(--tap-min);
	padding: var(--space-xs) var(--space-md);
	font-family: inherit;
	font-size: var(--text-body);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-normal);
	text-align: center;
	text-decoration: none;
	vertical-align: middle;
	cursor: pointer;
	background: none;
	border: 1px solid transparent;
	border-radius: var(--radius-lg);
	user-select: none;
	transition: color var(--transition-fast), background-color var(--transition-fast),
		border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.btn:hover,
.btn:focus {
	text-decoration: none;
}

.btn:focus-visible {
	outline: var(--focus-outline-width) solid hsl(var(--ring));
	outline-offset: var(--focus-outline-offset);
}

.btn:disabled {
	opacity: 0.65;
	cursor: default;
}

.btn-primary {
	color: hsl(var(--primary-foreground));
	background: hsl(var(--primary));
	border-color: hsl(var(--primary-active));
}

.btn-primary:hover,
.btn-primary:focus {
	color: hsl(var(--primary-foreground));
	background: hsl(var(--primary-hover));
	border-color: hsl(var(--primary-active));
	box-shadow: var(--shadow-button);
}

.btn-primary:active {
	background: hsl(var(--primary-active));
}

.btn-secondary {
	color: hsl(var(--secondary-foreground));
	background: hsl(var(--secondary));
	border-color: hsl(var(--border));
}

.btn-secondary:hover,
.btn-secondary:focus {
	color: hsl(var(--secondary-foreground));
	background: hsl(var(--surface-secondary-hover));
}

.btn-outline-secondary {
	color: hsl(var(--secondary-foreground));
	background: transparent;
	border-color: hsl(var(--border));
}

.btn-outline-secondary:hover,
.btn-outline-secondary:focus {
	color: hsl(var(--secondary-foreground));
	background: hsl(var(--secondary));
	border-color: hsl(var(--muted-foreground) / 0.4);
}

/* The way out of an offer, not a second offer. Borderless so it cannot compete with the
 * primary action, but still a full-height target. */
.btn-quiet {
	color: hsl(var(--muted-foreground));
	background: transparent;
	border-color: transparent;
	font-weight: var(--weight-normal);
}

.btn-quiet:hover,
.btn-quiet:focus {
	color: hsl(var(--foreground));
	background: hsl(var(--secondary));
}

/* Deliberately does NOT raise the font size (16px floor); height only. */
.btn-lg {
	padding: var(--space-sm) var(--space-lg);
}

.btn-block {
	display: flex;
	width: 100%;
}

.btn-block + .btn-block {
	margin-top: var(--space-xs);
}

/* Out-specifies `.btn-block { display: flex }`; the bare `[hidden]` above ties
 * on specificity and loses on source order. */
.btn[hidden] {
	display: none;
}

.form-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-xs);
	margin-top: var(--space-lg);
}

/* Inputs with floating labels. `:placeholder-shown` drives the float, so every
 * field REQUIRES a placeholder attribute in the markup. */

.form-label-group {
	position: relative;
	margin-bottom: var(--space-md);
}

.form-control {
	display: block;
	width: 100%;
	min-width: 0;
	padding: 1.35rem var(--space-sm) 0.45rem; /* top pad = room for the floated label */
	font-size: var(--text-body); /* 16px, unconditionally — see THE iOS ZOOM FIX */
	line-height: var(--leading-normal);
	color: hsl(var(--foreground));
	background-color: hsl(var(--surface-input));
	background-clip: padding-box;
	border: 1px solid hsl(var(--input));
	border-radius: var(--radius-lg);
	transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.form-control::placeholder {
	color: transparent;
}

.form-control:focus {
	outline: none;
	border-color: hsl(var(--ring));
	box-shadow: var(--focus-ring);
}

.form-label-group > label {
	position: absolute;
	top: 0;
	left: 0;
	display: flex;
	align-items: center;
	width: 100%;
	height: 100%;
	margin-bottom: 0;
	padding: 0 var(--space-sm);
	font-size: var(--text-body);
	line-height: var(--leading-normal);
	color: hsl(var(--muted-foreground));
	/* Clicks belong to the input underneath; <label for> still names it. */
	pointer-events: none;
	border: 1px solid transparent;
	border-radius: var(--radius-lg);
	transition: font-size var(--transition-fast), padding var(--transition-fast),
		height var(--transition-fast), color var(--transition-fast);
}

.form-label-group > .form-control:not(:placeholder-shown) ~ label,
.form-label-group > .form-control:focus ~ label {
	height: auto;
	padding-top: 0.35rem;
	padding-bottom: 0;
	font-size: var(--text-xs);
	color: hsl(var(--muted-foreground));
}

/* The reveal control, inside the field's box. The input reserves room for it with a
 * right padding wide enough that a long password never runs underneath. */
.input-affix {
	position: absolute;
	top: 50%;
	right: var(--space-2xs);
	transform: translateY(-50%);
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--tap-min);
	height: var(--tap-min);
	padding: 0;
	color: hsl(var(--muted-foreground));
	background: none;
	border: 0;
	border-radius: var(--radius-md);
	cursor: pointer;
}

.input-affix:hover {
	color: hsl(var(--foreground));
}

.input-affix:focus-visible {
	outline: var(--focus-outline-width) solid hsl(var(--ring));
	outline-offset: calc(-1 * var(--focus-outline-width));
}

/* Showing the password is the state worth marking, since it is the one with a cost. */
.input-affix.is-on {
	color: hsl(var(--primary));
}

.form-label-group:has(.input-affix) > .form-control {
	padding-right: calc(var(--tap-min) + var(--space-2xs));
}

/* Set by th:classappend on forcePasswordChange when binding fails. */
.form-label-group.has-error > .form-control {
	border-color: hsl(var(--destructive));
}

.form-label-group.has-error > .form-control:focus {
	box-shadow: 0 0 0 3px hsl(var(--destructive) / 0.25);
}

/* Alerts */

.alert {
	position: relative;
	margin-bottom: var(--space-md);
	padding: var(--space-sm) var(--space-md);
	font-size: var(--text-prose);
	line-height: var(--leading-relaxed);
	border: 1px solid transparent;
	border-radius: var(--radius-lg);
	text-align: left;
}

.alert-danger {
	color: hsl(var(--danger-foreground));
	background: hsl(var(--danger-surface));
	border-color: hsl(var(--danger-border));
}

.alert-info {
	color: hsl(var(--foreground));
	background: hsl(var(--secondary));
	border-color: hsl(var(--border));
}

.alert-success {
	color: hsl(var(--success-foreground));
	background: hsl(var(--success-surface));
	border-color: hsl(var(--success-border));
}

/* Header navigation */

.navbar-nav,
.nav {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: flex-end;
	gap: 0 var(--space-2xs);
	margin: 0;
	padding: 0;
	list-style: none;
}

.nav-item {
	display: flex;
	min-width: 0;
}

.nav-link {
	display: flex;
	align-items: center;
	min-height: var(--tap-min);
	padding: var(--space-xs) var(--space-sm);
	font-size: var(--text-sm);
	color: hsl(var(--header-foreground));
	text-decoration: none;
	border-radius: var(--radius-md);
	transition: background-color var(--transition-fast), color var(--transition-fast);
}

.nav-link:hover,
.nav-link:focus {
	color: hsl(var(--header-foreground));
	background: hsl(var(--header-hover-bg));
	text-decoration: none;
}

.nav-link:focus-visible {
	outline: var(--focus-outline-width) solid hsl(var(--header-focus-ring));
	outline-offset: var(--focus-outline-offset);
}

/* Footer */

.app-footer a {
	display: inline-flex;
	align-items: center;
	min-height: var(--tap-min);
	padding-inline: var(--space-2xs);
	color: hsl(var(--primary-link));
	text-decoration: none;
}

.app-footer a:hover,
.app-footer a:focus {
	color: hsl(var(--primary-link-hover));
	text-decoration: underline;
}

/* Muted rather than hidden: diagnostic text a support call may ask for. */
.footer-version {
	color: hsl(var(--muted-foreground));
}

.footer-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0 var(--space-md);
}
