/* Club branding, per the CVJM Deutschland Gestaltungsrichtlinien (V4.3):
   CVJM-Rot as the primary/accent color, Source Sans Pro (here "Source
   Sans 3", Adobe's current release of the same family) as the web font,
   self-hosted rather than loaded from a CDN - consistent with pico.min.css
   and htmx.min.js already being vendored instead of CDN-loaded. Weights
   are the ones actually used: 400/700 for body/bold text, 600 for
   semibold emphasis, plus an italic cut. The guide only specifies colors
   and typography for print/screen use, not a full UI palette, so this
   intentionally leaves Pico's own neutral grays (tuned for on-screen
   contrast) alone rather than swapping in the guide's print-oriented
   grays. */
@font-face {
	font-family: "Source Sans 3";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("/static/fonts/source-sans-3-regular.woff2") format("woff2");
}
@font-face {
	font-family: "Source Sans 3";
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("/static/fonts/source-sans-3-semibold.woff2") format("woff2");
}
@font-face {
	font-family: "Source Sans 3";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("/static/fonts/source-sans-3-bold.woff2") format("woff2");
}
@font-face {
	font-family: "Source Sans 3";
	font-style: italic;
	font-weight: 400;
	font-display: swap;
	src: url("/static/fonts/source-sans-3-italic.woff2") format("woff2");
}

:root {
	--pico-font-size: 100%;
	--pico-font-family-sans-serif: "Source Sans 3", system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pico-font-family-emoji);
}

/* ---- Cross-document view transitions ----
   This app does real page loads (no SPA router), so the nav is genuinely
   rebuilt on every navigation. CSS can't keep a DOM node alive across
   documents, but it can make the swap look continuous: with cross-document
   view transitions the browser cross-fades the old page into the new one,
   and anything given its own view-transition-name is matched up between
   the two. The nav renders identically on both sides, so it simply sits
   still while the content beneath it changes.

   Progressive enhancement on purpose - Chrome/Edge/Safari honour this,
   Firefox ignores the at-rule entirely and just navigates as before, so
   there is nothing to feature-detect and no fallback to maintain. */
@view-transition {
	navigation: auto;
}

.site-nav {
	view-transition-name: site-nav;
}

/* The cross-fade is motion too, so it goes when motion is unwelcome. */
@media (prefers-reduced-motion: reduce) {
	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation: none !important;
	}
}

/* CVJM-Rot (#e40038) as the primary/accent color, replacing Pico's default
   blue - repeats Pico's own light/dark/explicit-theme selector scaffold
   exactly, since a plain ":root" here would lose the specificity contest
   against Pico's ":root:not([data-theme=dark])" and never apply. Hover/
   focus shades come from the guide's own "Rot-Abstufungen" (page 21)
   rather than an arbitrarily darkened red. Pico itself uses the same
   primary values for light and dark, so there's no need to differentiate
   here either. */
:root:not([data-theme=dark]),
[data-theme=light],
[data-theme=dark] {
	--pico-primary: #e40038;
	--pico-primary-background: #e40038;
	--pico-primary-border: var(--pico-primary-background);
	--pico-primary-hover: #af0b2c;
	--pico-primary-hover-background: #c20831;
	--pico-primary-hover-border: var(--pico-primary-hover-background);
	--pico-primary-hover-underline: var(--pico-primary-hover);
	--pico-primary-focus: rgba(228, 0, 56, 0.5);
	--pico-primary-underline: rgba(228, 0, 56, 0.5);
	--pico-primary-inverse: #fff;
}
@media only screen and (prefers-color-scheme: dark) {
	:root:not([data-theme]) {
		--pico-primary: #e40038;
		--pico-primary-background: #e40038;
		--pico-primary-border: var(--pico-primary-background);
		--pico-primary-hover: #af0b2c;
		--pico-primary-hover-background: #c20831;
		--pico-primary-hover-border: var(--pico-primary-hover-background);
		--pico-primary-hover-underline: var(--pico-primary-hover);
		--pico-primary-focus: rgba(228, 0, 56, 0.5);
		--pico-primary-underline: rgba(228, 0, 56, 0.5);
		--pico-primary-inverse: #fff;
	}
}

/* Skip link: the nav is ten tab stops before the first bit of content,
   on every single page. Off-screen until focused, then it drops into the
   top-left corner as the first thing Tab reaches. */
.skip-link {
	position: absolute;
	top: -3rem;
	left: 0.5rem;
	z-index: 100;
	padding: 0.5rem 0.75rem;
	background: var(--pico-primary-background);
	color: var(--pico-primary-inverse);
	border-radius: var(--pico-border-radius);
	text-decoration: none;
	transition: top 0.15s ease;
}
.skip-link:focus {
	top: 0.5rem;
}
@media (prefers-reduced-motion: reduce) {
	.skip-link {
		transition: none;
	}
}

/* Visually hidden but still announced by screen readers - same clip-rect
   technique as .nav-toggle below, pulled out as a reusable utility since
   the nav brand now needs it too (icon-only, with a hidden text label). */
.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* ---- Icons ----
   Hand-drawn monoline set (see web/static/icons/sprite.svg), referenced via
   <svg class="icon"><use href="...#icon-x"></svg>. Sized off font-size and
   colored via currentColor (set on the symbols themselves) so an icon
   always matches the text color of whatever it sits next to, in nav links
   as well as inside status badges. */
.icon {
	width: 1.1em;
	height: 1.1em;
	vertical-align: -0.2em;
	flex-shrink: 0;
}
.nav-links a .icon,
.nav-dropdown-toggle .icon,
.nav-dropdown-menu a .icon {
	margin-inline-end: 0.5rem;
}

/* ---- Status badges ----
   Small, quiet indicators for RSVP/vote state (Zusage/Vielleicht/Absage,
   abgestimmt/offen) - the one place besides active-nav that gets color,
   deliberately not CVJM-Rot: red is reserved for the brand and primary
   actions, so these read as their own signal rather than competing with
   it. Tinted background + solid text, not a filled block, so a row of
   badges next to RSVP buttons stays quiet rather than shouting. */
.badge-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	margin-top: 0;
	margin-bottom: 0.5rem;
}
.badge {
	display: inline-flex;
	align-items: center;
	gap: 0.35em;
	padding: 0.15rem 0.6rem;
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	line-height: 1.6;
	white-space: nowrap;
}
.badge .icon {
	width: 0.9em;
	height: 0.9em;
	vertical-align: -0.1em;
}
.badge-positive,
.badge-warning,
.badge-muted {
	color: var(--badge-color);
	background: var(--badge-background);
}
:root:not([data-theme=dark]),
[data-theme=light] {
	--badge-positive-color: #1a7a4c;
	--badge-positive-background: rgba(26, 122, 76, 0.12);
	--badge-warning-color: #a15c00;
	--badge-warning-background: rgba(161, 92, 0, 0.12);
	--badge-muted-color: var(--pico-muted-color);
	--badge-muted-background: rgba(115, 130, 140, 0.12);
}
[data-theme=dark] {
	--badge-positive-color: #4fd897;
	--badge-positive-background: rgba(79, 216, 151, 0.14);
	--badge-warning-color: #f0b132;
	--badge-warning-background: rgba(240, 177, 50, 0.14);
	--badge-muted-color: var(--pico-muted-color);
	--badge-muted-background: rgba(140, 150, 165, 0.14);
}
@media only screen and (prefers-color-scheme: dark) {
	:root:not([data-theme]) {
		--badge-positive-color: #4fd897;
		--badge-positive-background: rgba(79, 216, 151, 0.14);
		--badge-warning-color: #f0b132;
		--badge-warning-background: rgba(240, 177, 50, 0.14);
		--badge-muted-color: var(--pico-muted-color);
		--badge-muted-background: rgba(140, 150, 165, 0.14);
	}
}
.badge-positive { --badge-color: var(--badge-positive-color); --badge-background: var(--badge-positive-background); }
.badge-warning { --badge-color: var(--badge-warning-color); --badge-background: var(--badge-warning-background); }
.badge-muted { --badge-color: var(--badge-muted-color); --badge-background: var(--badge-muted-background); }

/* An "x" to remove/delete a badge inline (e.g. the Instrumente list) -
   plain text weight, not a full Pico button, so it reads as part of the
   badge rather than a separate control competing with it. */
.badge-remove {
	all: unset;
	cursor: pointer;
	margin-inline-start: 0.3rem;
	line-height: 1;
	font-weight: 700;
}
.badge-remove:hover,
.badge-remove:focus-visible {
	text-decoration: underline;
}

/* ---- Main navigation ----
   Mobile-first: collapsed behind a burger toggle by default (CSS-only,
   via a hidden checkbox - no JS needed to open/close), expands to a plain
   horizontal bar above the breakpoint below. Admin-only pages (Mitglieder,
   Rundmails, Mail-Einstellungen, and whatever settings/permissions land
   here later) live under one "Admin" entry instead of each taking a slot
   in the top-level row - regular members never see it anyway, and it
   keeps the row from growing every time an admin feature is added. */
.site-nav {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0 1rem;
	margin-bottom: var(--pico-spacing);
	padding-block: 0.75rem;
	border-bottom: var(--pico-border-width) solid var(--pico-muted-border-color);
}

.site-nav .brand {
	display: flex;
	align-items: center;
	color: var(--pico-color);
	text-decoration: none;
	margin-right: auto;
}
.site-nav .brand .icon {
	width: 1.75rem;
	height: 1.75rem;
}
.site-nav .brand:is(:hover, :focus-visible) {
	color: var(--pico-primary);
}

/* Pico colors every plain <a> with --pico-primary (CVJM-Rot) by default -
   fine for a link inside body text, but with five/six links sitting next
   to each other in the nav bar it read as if the whole bar were shouting.
   Reserve red for the active page and real actions (buttons, RSVP/vote
   choices): nav links get the same neutral ink as body text and only pick
   up red via the existing [aria-current="page"] rule below. */
.site-nav .nav-links a,
.site-nav .nav-links .nav-dropdown-toggle,
.site-nav .nav-dropdown-menu a {
	--pico-color: var(--pico-h1-color);
	color: var(--pico-color);
}

.site-nav .nav-toggle {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* Qualified with .site-nav: Pico's own [type=checkbox]~label rule (which
   this checkbox-hack toggle inevitably matches) outranks a bare
   .nav-burger class selector on specificity, so plain ".nav-burger{...}"
   here would silently lose and the icon would never hide on desktop. */
.site-nav .nav-burger {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0.5rem;
	cursor: pointer;
	border-radius: var(--pico-border-radius);
	margin: 0;
}
.site-nav .nav-burger span {
	display: block;
	height: 2px;
	background: var(--pico-color);
	border-radius: 1px;
}
.site-nav .nav-toggle:focus + .nav-burger {
	outline: 2px solid var(--pico-primary-focus);
	outline-offset: 2px;
}

.site-nav .nav-links {
	display: none;
	flex-direction: column;
	align-items: stretch; /* Pico's own "nav ul{align-items:center}" would otherwise center each link instead of stretching it full-width */
	width: 100%;
	list-style: none;
	margin: 0.5rem 0 0;
	padding: 0;
}
/* Holds the theme toggle and the logout form - grouped together since
   both are controls rather than destinations, unlike .nav-links. */
/* Always visible, even with the burger closed, and ordered into the top
   row beside it: these three are the controls a member reaches for most
   often on a phone, and burying them under the burger put them below six
   menu entries and the admin panel. Order rather than markup changes, so
   the desktop bar keeps its natural source order. */
.site-nav .nav-controls {
	display: flex;
	align-items: center;
	width: auto;
	gap: 0.5rem;
	margin: 0;
	order: 1;
}
/* At every width, not just on the bar: Pico's bottom margin on buttons
   makes the two <button> controls centre their margin box while the
   profile <a> centres its border box, which split the trio across two
   lines again. */
.site-nav .nav-controls button,
.site-nav .nav-controls form {
	margin-bottom: 0;
}
.site-nav .nav-burger {
	order: 2;
}
.site-nav .nav-links {
	order: 3;
}
.nav-toggle:checked ~ .nav-links {
	display: flex;
}

/* Theme toggle and Logout are icon-only everywhere. Logout because it
   isn't important enough to spend a labelled slot on, the theme toggle
   because its state is better shown than spelled out. Both keep an
   .sr-only label and a title, so they stay reachable and hoverable.

   This also retires the min-width that used to reserve room for the
   "Design: Dunkel" text: theme.js no longer writes a label into the
   button, so there is no text to pop in and nothing left to reflow. */
#theme-toggle,
.nav-controls .nav-control-link,
.nav-controls button[type="submit"] {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: auto;
	padding: 0.5rem 0.7rem;
}
#theme-toggle .icon,
.nav-controls .nav-control-link .icon,
.nav-controls button[type="submit"] .icon {
	margin: 0;
}
/* Pico's .outline styles buttons and [role=button], not a plain <a>, so
   the profile link came out with no border at all - 2px shorter than the
   two boxes beside it. Spelled out here rather than adding role="button",
   which would misreport a link as a button to screen readers. */
.nav-controls .nav-control-link {
	line-height: 1.5;
	text-decoration: none;
	border: var(--pico-border-width) solid var(--pico-secondary-border);
	border-radius: var(--pico-border-radius);
	color: var(--pico-secondary);
}
.nav-controls .nav-control-link[aria-current="page"] {
	color: var(--pico-primary);
	border-color: var(--pico-primary);
}

/* Which theme icon shows is driven entirely by the data-theme attribute
   theme.js sets on <html> before first paint - no JS touches these after
   load, so the toggle can never flicker between states. No attribute at
   all means "Auto", which is exactly what theme.js does for that case. */
.theme-icon {
	display: none;
}
:root:not([data-theme]) .theme-icon-auto,
[data-theme="light"] .theme-icon-light,
[data-theme="dark"] .theme-icon-dark {
	display: inline-block;
}

.nav-links li {
	margin: 0;
}
.nav-links :is(a, .nav-dropdown-toggle) {
	display: block;
	padding: 0.6rem 0.25rem;
}
/* Current page: set by web/static/js/nav.js, matched against location.pathname.
   Color only, no font-weight change - nav.js runs (deferred) after the page
   has already painted at the default 400 weight, so flipping to 700 here
   used to reflow the whole row on every navigation (confirmed: ~3.5px
   width change on a single link, enough to visibly shift everything after
   it). Color alone still reads clearly as "active" and repaints instead of
   reflowing, so there's nothing left to visibly jump. */
.nav-links :is(a, .nav-dropdown-toggle)[aria-current="page"] {
	color: var(--pico-primary);
}

/* Mobile: the Admin submenu is just an always-visible indented list under
   its own link - three items don't need a second collapse toggle inside
   an already-collapsed menu. display:block is not cosmetic: Pico's own
   "nav ul{display:flex}" would otherwise apply here too (nothing else in
   this rule sets display, so Pico's rule wins by default), wrapping the
   items into a cramped horizontal run instead of a stacked list. */
.nav-dropdown-menu {
	display: none;
	list-style: none;
	margin: 0.25rem 0 0 1rem;
	padding: 0;
	border-left: var(--pico-border-width) solid var(--pico-muted-border-color);
}
/* Pico makes nav li inline-block, so the six admin entries flowed into
   each other as wrapped rows instead of stacking. */
.nav-dropdown-menu li {
	display: block;
	margin: 0;
}

/* The admin submenu's own toggle. Hidden input + label is the same
   checkbox hack as the burger, for the same reason: no inline JS. The
   input stays focusable (clipped, not display:none) so the panel is still
   reachable by keyboard on desktop via :focus-within. */
.site-nav .nav-subtoggle {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}
.site-nav .nav-dropdown-toggle {
	cursor: pointer;
}
.site-nav .nav-subtoggle:focus ~ .nav-dropdown-toggle {
	outline: 2px solid var(--pico-primary-focus);
	outline-offset: 2px;
}

/* The row has to lay itself out: link, caret, then the panel on its own
   full-width line beneath them. */
.site-nav .nav-dropdown {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
}
.site-nav .nav-dropdown > .nav-dropdown-toggle {
	flex: 1;
}
/* Scoped to the burger range on purpose. Unscoped, this selector outranks
   the desktop ".nav-dropdown-menu{display:none}" on specificity, so a
   submenu left open on a phone would stay stuck open after a resize. */
@media (max-width: 519.98px) {
	.site-nav .nav-subtoggle:checked ~ .nav-dropdown-menu {
		display: block;
		width: 100%;
	}
	/* Reads as the header of a group rather than as another destination:
	   it opens a section instead of going somewhere, and looking like its
	   siblings made that a guess. Tinted panel, smaller muted caps, and
	   the caret at the far end. */
	.site-nav .nav-dropdown > .nav-dropdown-toggle {
		display: flex;
		align-items: center;
		margin-top: 0.35rem;
		padding: 0.5rem;
		background: var(--pico-code-background-color);
		border-radius: var(--pico-border-radius);
		font-size: 0.8em;
		font-weight: 600;
		letter-spacing: 0.08em;
		text-transform: uppercase;
		color: var(--pico-muted-color);
	}
	/* Still has to be able to say "you are in here" - and this needs to
	   outrank the muted colour just above it. */
	.site-nav .nav-dropdown > .nav-dropdown-toggle[aria-current="page"] {
		color: var(--pico-primary);
	}
	.site-nav .nav-dropdown > .nav-dropdown-toggle::after {
		content: "\25BE";
		display: inline-block;
		margin-inline-start: auto;
		font-size: 0.8em;
		transition: transform 0.15s ease;
	}
	.site-nav .nav-subtoggle:checked ~ .nav-dropdown-toggle::after {
		transform: rotate(180deg);
	}
}
@media (prefers-reduced-motion: reduce) {
	.site-nav .nav-dropdown > .nav-dropdown-toggle::after {
		transition: none;
	}
}
.nav-dropdown-menu a {
	display: block;
	padding: 0.4rem 0.25rem;
}
.nav-dropdown-toggle {
	font-weight: 700;
}

/* 520px: re-measured once the top-level items went icon-only below 1200px
   (see that media query further down). Without labels the whole row -
   brand, six items, theme toggle, logout - needs only 419px of content
   (nav.scrollWidth, flex-wrap forced to nowrap, dropdown submenu forced
   closed to match how it actually renders here), so the horizontal bar now
   survives far narrower than the 960px this used to need with labels. The
   burger is therefore only left for genuinely tiny screens. */
@media (min-width: 520px) {
	.site-nav .nav-burger {
		display: none;
	}
	/* Its checkbox has to go too, not just the icon: clipped is still
	   focusable, so Tab landed on an invisible control that does nothing
	   at this width. */
	.site-nav .nav-toggle {
		display: none;
	}
	.site-nav .nav-links {
		display: flex;
		flex-direction: row;
		align-items: center;
		width: auto;
		margin: 0;
		gap: 0.125rem;
	}
	/* The two clusters used to sit on different centre lines - visibly, the
	   right-hand icons rode about 5px higher than the left-hand ones. Their
	   boxes were always the same height; what differed were the wrappers.
	   Pico pads nav li vertically, making the ul 57.6px tall, and gives
	   buttons a bottom margin, making .nav-controls 51.6px. Both are
	   centred in .site-nav, so two different wrapper heights meant two
	   different centres. Zeroing both makes every control a plain equal box
	   in one row. */
	/* display:flex so the li actually takes its link's height. As a block
	   with an inline-flex child, the li measured the line box (27.6px) and
	   let the 35.6px link overflow it, which put the link's centre 4px
	   above the controls' centre no matter what the wrappers did. */
	.site-nav .nav-links > li {
		display: flex;
		align-items: center;
		/* Vertical only: Pico's horizontal padding on nav li is what spaces
		   the boxes apart, and zeroing all four sides ran them together. */
		padding-block: 0;
	}
	/* Pico hangs a negative margin on both vertical sides of a nav link, so
	   the link overflowed its li and left the ul measuring 27.6px around
	   35.6px of content - half that overflow was the offset. Both sides
	   have to go: zeroing only the bottom leaves the top pulling the link
	   up by the same 8px. */
	.site-nav .nav-links > li > :is(a, .nav-dropdown-toggle) {
		margin-block: 0;
	}
	.site-nav .nav-dropdown > .nav-dropdown-toggle {
		flex: none;
	}
	.site-nav .nav-controls {
		display: flex;
		align-items: center;
		width: auto;
		margin: 0;
		order: 0;
	}
	.site-nav .nav-links {
		order: 0;
	}
	/* Divides the destinations from the controls that concern the person
	   using the app (profile, theme, logout). */
	.site-nav .nav-controls {
		margin-inline-start: 0.75rem;
		padding-top: 0;
		padding-inline-start: 0.75rem;
		border-top: none;
		border-inline-start: var(--pico-border-width) solid var(--pico-muted-border-color);
	}
	.nav-links :is(a, .nav-dropdown-toggle) {
		padding: 0.5rem 0.75rem;
	}

	/* On mobile the submenu is always expanded (see above); on desktop it
	   becomes a proper dropdown, shown on hover or keyboard focus so Tab
	   still reaches it without any JS. */
	.nav-dropdown {
		position: relative;
	}
	.nav-dropdown-toggle::after {
		content: " \25BE"; /* ▾ */
		font-size: 0.7em;
	}
	.nav-dropdown-menu {
		display: none;
		position: absolute;
		top: 100%;
		left: 0;
		margin: 0;
		padding: 0.4rem 0;
		min-width: 12rem;
		background: var(--pico-card-background-color);
		border: var(--pico-border-width) solid var(--pico-muted-border-color);
		border-radius: var(--pico-border-radius);
		box-shadow: var(--pico-card-box-shadow);
		z-index: 10;
	}
	.nav-dropdown:hover .nav-dropdown-menu,
	.nav-dropdown:focus-within .nav-dropdown-menu {
		display: block;
	}
	.nav-dropdown-menu a {
		padding: 0.5rem 1rem;
		white-space: nowrap;
	}
}

/* Between the burger breakpoint and 1200px the top-level nav is icons
   only - there's room for the row but not for six labels, and an icon
   with a title beats a cramped or wrapped word. Deliberately scoped to
   ".nav-links > li > a": the Admin dropdown's own entries keep their
   labels, since a panel of bare icons would be unreadable, and so does
   the burger menu below this range, where the items stack vertically and
   icons alone would be useless. Hidden the .sr-only way rather than with
   display:none so the label still reaches screen readers. */
@media (min-width: 520px) and (max-width: 1199.98px) {
	.site-nav .nav-links > li > :is(a, .nav-dropdown-toggle) > .nav-label {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
	}

	/* Every destination gets the same outlined box the theme toggle and
	   Logout already wear, so the whole bar reads as one row of equal
	   controls rather than plain links sitting next to two buttons.
	   Matching their padding is what makes the boxes line up; secondary
	   (not primary) keeps them quiet, with the current page as the only
	   thing carrying colour. */
	.site-nav .nav-links > li > :is(a, .nav-dropdown-toggle) {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		padding: 0.5rem 0.7rem;
		border: var(--pico-border-width) solid var(--pico-secondary-border);
		border-radius: var(--pico-border-radius);
		color: var(--pico-secondary);
	}
	.site-nav .nav-links > li > :is(a, .nav-dropdown-toggle) > .icon {
		margin-inline-end: 0;
	}
	.site-nav .nav-links {
		gap: 0.5rem; /* same as .nav-controls, so both clusters space alike */
	}
	.site-nav .nav-links > li > :is(a, .nav-dropdown-toggle)[aria-current="page"] {
		color: var(--pico-primary);
		border-color: var(--pico-primary);
	}
	/* The dropdown caret would push the Admin box out of step with the
	   others, and the panel opens on hover/focus anyway. */
	.nav-dropdown-toggle::after {
		content: none;
	}
}

/* ---- Tables: scroll horizontally instead of blowing out the page ----
   The min-width forces the browser to actually overflow (and scroll)
   instead of crushing flexible cell content - e.g. the Mitglieder table's
   inline name input/role select/save button would otherwise shrink to a
   few unusable pixels rather than the table ever scrolling. */
.table-scroll {
	overflow-x: auto;
}
.table-scroll table {
	min-width: 48rem;
}
/* Pico's inputs/selects are width:100%, which gives the table's auto
   layout nothing to size the column by - it can shrink to almost nothing
   before the outer min-width above ever kicks in. A floor on the controls
   themselves (e.g. the Mitglieder table's inline name/role editor) keeps
   them usable and lets the table-scroll wrapper do the rest. */
.table-scroll input[type="text"],
.table-scroll select {
	min-width: 8rem;
}

/* ---- Choice/action button groups: wrap instead of squeezing on very
   narrow phones (e.g. the Zusagen/Vielleicht/Absagen RSVP buttons) ---- */
@media (max-width: 480px) {
	[role="group"] {
		flex-wrap: wrap;
	}
}

/* ---- Dashboard ----
   Not Pico's .grid: that lays every child out in a single row, which works
   for the three cards this page used to have and collapses to unreadable
   slivers now that every permitted module gets one. auto-fill + minmax
   wraps instead, so the card count can grow with the viewer's permissions
   without the layout caring. */
.dashboard-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
	gap: 1rem;
}
.dashboard-card {
	margin: 0; /* the grid owns the spacing, not the article */
}
.dashboard-card h2 {
	font-size: 1.15rem;
	margin-bottom: 0.5rem;
}
.dashboard-note {
	color: var(--pico-muted-color);
	margin-bottom: 0.5rem;
}
.dashboard-items {
	margin: 0;
	padding: 0;
	list-style: none;
}
/* list-style has to sit on the li: Pico sets a square marker on the item
   itself, which a rule on the ul does not override. */
.dashboard-items li {
	list-style: none;
	padding: 0.15rem 0;
}
/* Meta on its own line: a date after a long event title would otherwise
   wrap mid-phrase and read as part of the title. */
.dashboard-items li small {
	display: block;
	color: var(--pico-muted-color);
}

/* The address under the name in the member table: present for
   identification, but the name is what the row is read by. */
.member-email {
	margin: 0 0 0.5rem;
	color: var(--pico-muted-color);
}

/* Confirmation dialogs (reminders, deletions). Pico already styles
   <dialog>; this only sizes the body and spaces the footer buttons. */
.confirm-dialog article {
	max-width: 32rem;
	margin: 0;
}
.confirm-dialog footer {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	margin: 0;
	padding-top: 0.5rem;
}
.confirm-dialog footer button {
	margin: 0;
	width: auto;
}
.remind-status {
	margin: 0.5rem 0 0;
	font-weight: 600;
}

/* Form and page-level error messages - see the "form-error" partial. */
.form-error {
	color: var(--pico-del-color);
}

/* ---- Page-level actions ----
   A row of equally weighted ways in and out of a list (new, import,
   export). Wraps rather than overflowing on a phone. */
.page-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: var(--pico-spacing);
}
.page-actions a[role="button"] {
	width: auto;
	margin: 0;
}

/* Controls inside a table row: side by side, not stacked, and never
   stretched to the column width the way Pico sizes buttons by default. */
.row-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem;
}
.row-actions a[role="button"],
.row-actions button {
	width: auto;
	margin: 0;
	padding: 0.3rem 0.6rem;
	font-size: 0.85rem;
}
/* The pencil is icon-only, so it gets a square-ish box rather than the
   padding meant for a word. */
.row-actions a[role="button"] .icon {
	margin: 0;
}
