/**
 * Base (mobile-first, no media query = the 320–390px layout — NFR-30/31).
 * All min-width rules below ADD to this; there is no max-width query.
 * Component patterns follow docs/design-system/components.md verbatim
 * where it gives literal markup/CSS — extend there first if a pattern
 * needs to change, not here in isolation.
 */

/*
 * overflow-wrap: a single unbreakable word/phrase (long compound German
 * titles, e.g. "Sommernachtstraum", are common) can be wider than its
 * container at the 76px/58px mobile hero sizes even though normal
 * space-based wrapping is on — found via Phase 8's 320px horizontal-scroll
 * audit (NFR-34): the home hero title alone could force a site-wide
 * horizontal scrollbar. Breaks only as a last resort; normal text is
 * unaffected.
 */
* {
	box-sizing: border-box;
	overflow-wrap: break-word;
}

/*
 * …but break-word does NOT lower an element's min-content width, so inside
 * a grid track (or flex item) that sizes from content, an unbreakable
 * compound still widens the track past a 320px viewport (e2e layout suite:
 * "Generalversammlungseinladungsschreiben" → 429px News list,
 * "Sommernachtstraumverwirrungen" → 383px in a 2-column tile grid). Two layers:
 * every card grid's tracks are minmax(0, 1fr) (not bare 1fr = minmax(auto,
 * 1fr)), and the titles inside cards/rows/tiles get real German
 * hyphenation (<html lang="de"> from language_attributes()) with
 * `overflow-wrap: anywhere` as the safety net where the browser has no
 * hyphenation dictionary. hyphenate-limit-chars keeps hyphenation to long
 * words (≥ 12 chars, ≥ 5 on each side), so ordinary titles wrap exactly as
 * before — only compounds that would otherwise overflow get split.
 */
.tzn-news-row__title,
.tzn-production-card__title,
.tzn-home-card__title,
.tzn-member-card__name,
.tzn-doc-row__title,
.tzn-cast-row__role,
.tzn-cast-row__name,
.tzn-kontakt-venue__title,
.tzn-network__name,
.tzn-network__logo-fallback,
a.tzn-card {
	hyphens: auto;
	hyphenate-limit-chars: 12 5 5;
	overflow-wrap: anywhere;
}

/* The same hyphenation for all running text, inherited: under enlarged
   text a long compound in body copy, a list or a heading
   («Behinderteneinrichtungen», «Generalversammlung») otherwise splits
   between two letters with no hyphen. Long words only (12 5 5, as above). */
body {
	margin: 0;
	line-height: 1.4;
	hyphens: auto;
	hyphenate-limit-chars: 12 5 5;
	text-wrap: pretty;
	min-height: 100vh;
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
}

body.admin-bar {
	min-height: calc(100vh - 32px);
	min-height: calc(100dvh - 32px);
}

/* stylelint-disable-next-line media-feature-name-disallowed-list -- mirrors
   WordPress core's own admin-bar breakpoint (admin-bar.css switches the bar
   to 46px at max-width: 782px); a logged-in-only fix, not page layout. */
@media (max-width: 782px) {
	body.admin-bar {
		min-height: calc(100vh - 46px);
		min-height: calc(100dvh - 46px);
	}
}

img {
	max-width: 100%;
	height: auto;
	display: block;
}

/*
 * Chromium's own UA stylesheet default for `[hidden] { display: none }`
 * doesn't apply to inline <svg> (found via the gallery play/pause icon,
 * home-gallery.js) — SVGElement doesn't reflect the `hidden` IDL property
 * the way HTMLElement does either, so `el.hidden = true` silently no-ops
 * on an <svg>. This explicit author rule covers both the attribute *and*
 * the element-type gap, sitewide, so the same silent bug can't recur
 * wherever else an inline icon gets toggled this way.
 */
[hidden] {
	display: none;
}

/* 1600px — matches .tzn-header__inner/.tzn-footer__inner's own cap, so the
   main content column lines up with the header/footer content above and
   below it instead of reading narrower on a wide desktop. */
.tzn-container {
	padding-inline: 16px;
	margin-inline: auto;
	max-width: 1600px;
}

/*
 * Unlike the header/breadcrumb/footer — each of which layers its own
 * vertical padding on top of .tzn-container's horizontal-only rule — <main>
 * had none, so content sat flush against the breadcrumb above and the
 * footer below on every page. Section-gap values from
 * docs/design-system/tokens.json ("space").
 */
main.tzn-container {
	flex: 1 0 auto;
	width: 100%;
	padding-block: 24px;
}

@media (min-width: 1024px) {
	main.tzn-container {
		padding-block: 56px;
	}
}

/* Skip link — NFR-11 keyboard operability */
.tzn-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100;
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
	padding: 10px 18px;
	border: 2px solid transparent; /* shape in forced colours */
}

.tzn-skip-link:focus {
	left: 16px;
	top: 16px;
}

/*
 * Focus visibility — never remove this without replacing it (NFR-11).
 * WCAG 1.4.11 (non-text contrast ≥ 3:1) applies to the ring itself,
 * measured against whatever it's drawn over (the 2px offset puts it on the
 * *parent's* background, not the element's own). The old 3px accent ring
 * was 2.19:1 on paper, 1.83:1 on card and 1:1 (invisible) for the button
 * inside the accent date card — see components.md "Focus indicator".
 * Ink ring: 15.56:1 on paper, 12.97:1 on card, 16.74:1 on white, 7.09:1 on
 * accent. On the ink surfaces it would vanish, so focusable elements
 * *inside* an ink container switch to the accent ring (7.09:1 on ink).
 * Descendant selectors on purpose: an ink element that is itself focusable
 * (skip link, primary button, dark home card) sits on a light parent and
 * keeps the ink ring. Shadow-free (rule 5) — outline only.
 */
:focus-visible {
	outline: 3px solid var(--tzn-ink);
	outline-offset: 2px;
}

.tzn-nav-overlay :focus-visible,
.tzn-footer :focus-visible,
.tzn-lightbox :focus-visible,
.tzn-card--ink :focus-visible,
.tzn-home-card--dark :focus-visible {
	outline-color: var(--tzn-accent);
}

/* Visually hidden but available to screen readers — NFR-11 */
.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	overflow: hidden;
	/* stylelint-disable-next-line property-no-deprecated -- the standard
	   WordPress .screen-reader-text pattern, same as core's own CSS. */
	clip: rect(1px, 1px, 1px, 1px);
	white-space: nowrap;
}

/* ---------------------------------------------------------------- Header */

/* Values from docs/design-system/mockups/TZN Home.dc.html (2026-09-05,
   superseding the static PNG mockups for header/footer) — the background
   (and footer's) stays full-bleed, edge to edge, rather than
   .tzn-container-capped like the rest of the page. The *content* row gets
   its own explicit 1600px cap — same number .tzn-container itself now
   uses, so the content lines up — rather than reusing .tzn-container
   directly, since that class also carries the horizontal padding/centering
   that already lives on .tzn-header__inner itself. */
.tzn-header {
	background: var(--tzn-surface-paper);
	border-bottom: 1px solid var(--tzn-card-border);
}

.tzn-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;

	/* Mobile: logo and MENÜ always share one row (320px: 181 + 12 + 95 =
	   288px, the row's full width); under enlarged text the logo shrinks
	   (.tzn-logo min-width: 0) instead of MENÜ dropping to a second row.
	   Desktop restores the 32px gap and wrapping below. */
	gap: 12px;
	padding: 16px;
	flex-wrap: nowrap;
	max-width: 1600px;
	margin-inline: auto;
}

/* Sticky on mobile only (client request, 2026-09-05) — keeps the logo +
   MENÜ trigger reachable while scrolling instead of only at the top of the
   page. Desktop's full row already sits inline with page content in every
   mockup, so this doesn't extend there. z-index below .tzn-nav-overlay's 50
   so the expanded overlay still paints over it. */
/* stylelint-disable-next-line media-feature-name-disallowed-list -- mobile-only
   override (see comment above); kept as max-width to avoid restating and
   undoing these declarations in the desktop min-width block. */
@media (max-width: 1023.98px) {
	.tzn-header {
		position: sticky;
		top: 0;
		z-index: 40;
	}
}

/* tokens.json type.logoLockup: "italic 700, Big Shoulders Display,
   letter-spacing .06em, uppercase" — shared with .tzn-nav-overlay__logo
   below, same lockup in both places. */
.tzn-logo {
	display: inline-flex;
	align-items: center;
	min-width: 0;
	min-height: var(--tzn-tap-min);
	font: italic 700 1.25rem/1 var(--tzn-font-display);
	text-decoration: none;
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

/* object-fit keeps the lockup's proportions when the row squeezes it
   narrower than its natural width at the fixed height. */
.tzn-logo__image {
	height: clamp(28px, 3.5vw, 48px);
	width: auto;
	max-width: 100%;
	display: block;
	object-fit: contain;
	object-position: left center;
}

.tzn-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 4px clamp(18px, 2.4vw, 34px);
	width: 100%;
	order: 3;
}

/* Nav items set in the display face, not body text (TZN Home.dc.html) —
   700 21px Big Shoulders Display, not Source Sans 3. */
.tzn-nav a:not(.tzn-btn-accent) {
	display: inline-flex;
	align-items: center;
	min-height: var(--tzn-tap-min);
	text-decoration: none;
	font: 700 1.3125rem/1 var(--tzn-font-display);
	border-bottom: 3px solid transparent;
	padding-bottom: 6px;
	transition:
		border-color 0.18s ease,
		color 0.18s ease;
}

/* Hover (TZN Home.dc.html) shows the accent bar; the current page's bar
   is ink (components.md "Current-section indicator": the underline
   recolours to ink) — accent on paper is 2.19:1, under the 3:1 a state
   indicator needs (WCAG 1.4.11); ink is 15.56:1. Every item reserves the
   same 3px transparent border so neither shifts the row when it colours
   in. */
.tzn-nav a:not(.tzn-btn-accent):hover {
	border-bottom-color: var(--tzn-accent);
}

.tzn-nav a:not(.tzn-btn-accent)[aria-current] {
	border-bottom-color: var(--tzn-ink);
}

.tzn-nav .tzn-btn-accent {
	order: 2;
	margin-left: auto;
	min-height: 44px;
	padding: 0 16px;
	font-size: 1.0625rem;
}

/* Nav-overlay toggle — text-labelled (not an icon hamburger), mobile-only:
   the fixed 5-item row above already covers desktop, components.md's
   expanded overlay is explicitly "mobile only". Solid ink button (matching
   mockups/mobile-mitmachen.png's "MENÜ" button colour — ink, not accent,
   distinguishing it from "Tickets" beside it), not a plain underlined
   link. */
.tzn-nav-toggle {
	order: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	flex-shrink: 0;
	min-height: var(--tzn-tap-min);
	padding: 0 12px;
	border: 2px solid transparent; /* shape in forced colours; padding -2px */
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
	font: 700 0.875rem/1 var(--tzn-font-body);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	text-decoration: none;
	cursor: pointer;
}

.tzn-nav-toggle:hover {
	background: var(--tzn-ink-hover);
}

.tzn-nav-toggle__icon {
	flex-shrink: 0;
}

/* All pages, mobile only (mockups/mobile-bilder.png, mobile-mitmachen.png,
   mobile-spielplan.png, mobile-produktion.png, …; extended to the home page
   2026-09-05, superseding the old home-only "always visible" row — client
   asked for one consistent mobile pattern instead of home showing the full
   row and a redundant MENÜ button underneath): logo + MENÜ only, not the
   five direct links/Tickets — those stay in the DOM (desktop still needs
   them) and are just hidden here, not removed. `.tzn-nav > a` catches
   the five top-level links, the conditional Mitgliederbereich link (FR-MEM-11)
   and the Tickets link in one selector, since all three are anchors and
   .tzn-nav-toggle is a <button>. */
/* stylelint-disable-next-line media-feature-name-disallowed-list -- mobile-only
   override (see comment above); kept as max-width to avoid restating and
   undoing these declarations in the desktop min-width block. */
@media (max-width: 1023.98px) {
	/* .tzn-header prefix matches the specificity of `.tzn-nav a:not(.tzn-btn-accent)`
     above (0,2,1 each) so this actually wins — a bare `.tzn-nav > a` (0,1,1)
     loses to it regardless of source order. */
	.tzn-header .tzn-nav > a {
		display: none;
	}

	.tzn-nav {
		width: auto;
		flex-wrap: nowrap;
		margin-left: auto;
		gap: 0;
	}

	.tzn-logo__image {
		max-width: calc(100vw - 150px);
	}
}

@media (min-width: 1024px) {
	.tzn-header {
		flex-wrap: nowrap;
	}

	.tzn-header__inner {
		gap: 12px 32px;
		flex-wrap: wrap;
	}

	.tzn-nav {
		width: auto;
		order: 2;
		flex-wrap: nowrap;
		margin-left: auto;
	}

	.tzn-nav .tzn-btn-accent {
		order: unset;
		margin-left: 8px;
	}

	.tzn-nav-toggle {
		display: none;
	}
}

/* Nav, expanded (mobile full-screen overlay — components.md) */
.tzn-nav-overlay {
	position: fixed;
	inset: 0;
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
	z-index: 50;
	padding: 8px 16px;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
}

.tzn-nav-overlay[hidden] {
	display: none;
}

.tzn-nav-overlay a {
	color: var(--tzn-on-ink);
	text-decoration-color: var(--tzn-accent);
}

.tzn-nav-overlay__bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 24px;
}

.tzn-nav-overlay__logo {
	font: italic 700 1.25rem/1 var(--tzn-font-display);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

/* Compact, uppercase treatment — distinct from the full-size .tzn-btn-accent
   used for "Tickets kaufen" below, matching mockups/mobile-nav-open.png.
   gap, not a relying on the text node's trailing space: .tzn-btn-accent is
   `display: flex`, which collapses trailing whitespace at the end of the
   "Schliessen " text's anonymous flex item, so without an explicit gap the
   × sits flush against the label. */
.tzn-nav-overlay__bar .tzn-btn-accent {
	min-height: var(--tzn-tap-min);
	padding: 0 14px;
	gap: 8px;
	font: 700 0.875rem/1 var(--tzn-font-body);
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

/* sectionGapMobile token: "24-32px" — padding-bottom + margin-bottom + the
   1px border between them lands at 29px, inside that range. Was 41px
   (20+20+1px), which — across 5 sections — overflowed a single mobile
   screen by more than one whole section (confirmed against
   mockups/mobile-nav-open.png at 1x: every section including the sticky
   ticket bar fits in one 390×844 viewport with no scrolling needed). */
.tzn-nav-overlay__section {
	padding-bottom: 10px;
	margin-bottom: 14px;
	border-bottom: 1px solid var(--tzn-ink-hover);
}

.tzn-nav-overlay__heading {
	display: flex;
	align-items: center;
	min-height: var(--tzn-tap-min);
	font: 700 2rem/1.15 var(--tzn-font-display);
	text-decoration: none;
}

/* Current-section indicator (mockups/mobile-nav-open.png) — same 6px accent
   bar already used for .tzn-date-card__accent-bar, reused here rather than
   invented fresh. header.php sets aria-current on the section the visitor
   is in: "page" on its own page, "true" on one of its sub-pages. */
.tzn-nav-overlay__heading[aria-current] {
	position: relative;
	padding-left: 16px;
}

.tzn-nav-overlay__heading[aria-current]::before {
	content: "";
	position: absolute;
	left: 0;
	top: 2px;
	bottom: 2px;
	width: 6px;
	background: var(--tzn-accent);
}

/* min-height on both the heading and each sub-item link is a real 44px tap
   target (NFR-11), not just visual padding, so it's the same floor as the
   fixed 5-item header nav — only the margin between rows was shrunk to hit
   mockups/mobile-nav-open.png's fit (measured against the 1x mockup: heading
   ~44px, each sub-item row ~52px including its own margin). */
.tzn-nav-overlay__sub {
	padding-left: 4px;
	margin-top: 8px;
}

.tzn-nav-overlay__sub a {
	min-height: var(--tzn-tap-min);
	min-width: var(--tzn-tap-min);
	display: inline-flex;
	align-items: center;
	color: var(--tzn-on-ink-muted);
	text-decoration: none;
	font-size: 1.0625rem;
}

/* The sub-page the visitor is on: full-contrast text, underlined in the
   accent colour (not colour alone, WCAG 1.4.1). */
.tzn-nav-overlay__sub a[aria-current="page"] {
	color: var(--tzn-on-ink);
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 6px;
}

.tzn-nav-overlay__ticket {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	min-height: 56px;
	padding: 0 20px;
	margin-top: auto;
	position: sticky;
	bottom: 0;
}

@media (min-width: 1024px) {
	.tzn-nav-overlay {
		display: none;
	}
}

/* ------------------------------------------------------------- Breadcrumb */

/* Full-bleed --tzn-card band (mockups/TZN Bilder.dc.html,
   TZN Kontakt-Lageplan.dc.html, desktop-spielplan.png: measured #EBE1D6
   edge-to-edge with 1px solid #E2D7CC border-bottom) — background lives on the
   nav itself; the .tzn-container moved onto the inner <ol> so the band spans
   the viewport while the crumb list still lines up with the page's normal
   content column. */
.tzn-breadcrumb {
	background: var(--tzn-card);
	border-bottom: 1px solid var(--tzn-card-border);
	font-size: 1.0625rem;
	line-height: 1.2;
	color: var(--tzn-text-muted);
	padding-block: 14px;
}

/* 44×44 hit area (rule 4 / WCAG 2.5.5) without changing the band's visual
   rhythm: the link box grows to --tzn-tap-min, the negative block margin
   gives the extra height back so the line (and the band) stays ~20px +
   14px padding. The 12px overhang lands inside that 14px padding, i.e.
   still inside the breadcrumb band. Was 20px tall. */
.tzn-breadcrumb a {
	font-weight: 600;
	color: var(--tzn-ink-soft-bg);
	text-decoration: none;
	transition: color 0.18s ease;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: var(--tzn-tap-min);
	min-width: var(--tzn-tap-min);
	margin-block: calc((var(--tzn-tap-min) - 1.2em) / -2);
}

.tzn-breadcrumb a:hover {
	color: var(--tzn-ink);
}

/* margin-block, not the margin shorthand — `.tzn-breadcrumb ol` (0,1,1) beats
   `.tzn-container`'s margin-inline: auto (0,1,0), so a bare `margin: 0` here
   was zeroing out the auto side-margins that center the list, pinning it to
   the left edge instead of lining up with the rest of the page's content
   column. */
.tzn-breadcrumb ol {
	list-style: none;
	margin-block: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
}

.tzn-breadcrumb li:not(:last-child)::after {
	content: "›";
	margin-left: 12px;
	font-size: 1rem;
	font-weight: 400;
	color: #9c8f86;
}

/* FR-NAV-05: "the last element is plain text, not a link" — also the
   current page, so it reads as the strongest item, same weight as the
   mobile "‹ …" crumb below (TZN Bilder.dc.html: "Bilder" is 700 17px ink,
   "Home" is 600 17px #3F342E). */
.tzn-breadcrumb li:last-child {
	color: var(--tzn-ink);
	font-weight: 700;
}

/* Mobile: DESIGN-BRIEF.md's breadcrumb note ("needs a mobile treatment — it
   must not wrap to three lines on a phone"). Every mobile mockup
   (mobile-bilder.png, mobile-mitmachen.png, mobile-spielplan.png, …)
   collapses the full trail to a single "‹ <immediate parent>" link, not the
   full Home›L1›L2›Title row — ":nth-last-child(2)" is that immediate
   parent (or "Home" itself when the current page has no Level 1, e.g.
   Bilder — matches mobile-bilder.png's "‹ Home" exactly). */
/* stylelint-disable-next-line media-feature-name-disallowed-list -- mobile-only
   override (see comment above); kept as max-width to avoid restating and
   undoing these declarations in the desktop min-width block. */
@media (max-width: 1023.98px) {
	.tzn-breadcrumb li {
		display: none;
	}

	.tzn-breadcrumb li:nth-last-child(2) {
		display: inline-flex;
		align-items: center;
		gap: 4px;
		color: var(--tzn-ink);
		font-weight: 700;
	}

	.tzn-breadcrumb li:nth-last-child(2)::before {
		content: "‹";
	}

	.tzn-breadcrumb li:nth-last-child(2)::after {
		content: none;
	}
}

/* ------------------------------------------------------------- Date card */
.tzn-date-card {
	position: relative;
	background: var(--tzn-card);
	border: 1px solid transparent; /* card shape in forced colours */
	padding: 20px;
	margin-bottom: 12px;
}

.tzn-date-card__accent-bar {
	position: absolute;
	left: -1px; /* over the card's transparent 1px edge */
	top: -1px;
	bottom: -1px;
	width: 6px;
	background: var(--tzn-accent);
}

.tzn-date-card__head {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: baseline;
	gap: 4px 12px;
}

.tzn-date-card__date {
	font: 700 1.375rem/1.1 var(--tzn-font-display);
}

/* Mobile: the wrapper dissolves, so date and start time share the first
   line (as in mobile-spielplan.png) and the doors line takes its own full
   row beneath them. Wraps instead of overflowing under enlarged text
   (WCAG 1.4.4). Desktop places each span in the row grid (below). */
.tzn-date-card__times {
	display: contents;
}

/* accent-hover on the card's beige background measures 2.37:1 — fails WCAG
   AA (Phase 8 contrast audit, NFR-12). ink passes comfortably; the accent
   variant below already overrides this to ink for its own reasons. */
.tzn-date-card__time {
	color: var(--tzn-ink);
	font: 700 1.375rem/1.1 var(--tzn-font-display); /* same voice as the date: one unit */
}

/* text-muted on card measures 5.89:1 (7.06:1 on paper; token darkened
   from 4.52:1 on card, 2026-11-08); re-measure if either token changes. */
.tzn-date-card__doors {
	flex-basis: 100%;
	color: var(--tzn-text-muted);
}

.tzn-date-card__note {
	color: var(--tzn-ink);
	margin: 8px 0 0;
}

/* A long German word («Nachmittagsvorstellung») is wider than the cell
   under enlarged text: hyphenate it rather than cut it mid-word. */
.tzn-date-card__note,
.tzn-date-card__doors {
	hyphens: auto;
}

.tzn-date-card__action {
	margin-top: 16px;
}

/* «Vorverkauf startet in Kürze.» and «Ausverkauft» take the ticket
   button's place, so they use the button's type (same font, size, weight
   as .tzn-btn-accent's label): the three states read as one column. */
.tzn-date-card__presale {
	margin: 0;
	font: 700 1.1875rem/1.2 var(--tzn-font-body);
}

/* Desktop row, two lines in three fixed columns:
     date        | note       | action
     start time  | Türöffnung | (action spans both lines)
   The head and times wrappers dissolve so their spans become grid cells.
   Fixed tracks (not `auto`) so every card in the list lines up whether
   its last cell is a ticket button or the «Vorverkauf» line, and whether
   it has a note or a doors time. Each line shares one baseline; a button
   is centred in the row. The outer tracks are rem (they grow with the
   reader's text size) but capped at a share of the card, so under enlarged
   text the middle column keeps room for the note instead of collapsing to
   one letter per line (WCAG 1.4.4); at the default size the caps don't
   bite from 1280px up (24rem = 384px < 32 % of the card). */
@media (min-width: 1024px) {
	.tzn-date-card {
		display: grid;
		grid-template-columns: minmax(0, min(24rem, 32%)) minmax(0, 1fr) minmax(0, min(16rem, 30%));
		gap: 4px 24px;
		align-items: baseline;
	}

	.tzn-date-card__head,
	.tzn-date-card__times {
		display: contents;
	}

	.tzn-date-card__date {
		grid-area: 1 / 1;
	}

	.tzn-date-card__time {
		grid-area: 2 / 1;
	}

	.tzn-date-card__note {
		grid-area: 1 / 2;
		margin: 0;
	}

	.tzn-date-card__doors {
		grid-area: 2 / 2;
	}

	/* One body size for every text cell in the row — the scale's mobile
	   body (tokens.json type.scaleMobile.body), not desktop's 21px, so it
	   sits quietly beside the 22px display-font date and time. */
	.tzn-date-card__note,
	.tzn-date-card__doors {
		font-size: 1.0625rem;
		line-height: 1.3;
	}

	/* Button or text, always centred in the row's two lines. */
	.tzn-date-card__action {
		grid-column: 3;
		grid-row: 1 / span 2;
		align-self: center;
		justify-self: end;
		margin-top: 0;
		text-align: right;
	}
}

/* A performance's own date block on its detail page — accent background,
   so the primary (ink) ticket button reads as the dominant action per
   components.md's "Primary… used against an accent card" guidance. */
.tzn-date-card--accent {
	background: var(--tzn-accent);
}

.tzn-date-card--accent .tzn-date-card__note,
.tzn-date-card--accent .tzn-date-card__doors {
	color: var(--tzn-ink);
} /* __time is already ink by default now */

/* --------------------------------------------------- Produktion: layout */

/* Page layout for the Produktion/Vorstellung detail page. Five flat
   children, DOM order = mobile visual order (DESIGN-BRIEF §4: date/ticket
   unmissable, well before the synopsis) — image, Vorstellungen, content,
   Besetzung, gallery — each just stacks below the last on mobile. At
   ≥1024px this becomes a three-row grid via named areas (not reordered via
   DOM or CSS `order`, so mobile reading order stays correct): content next
   to image (row 1, content on the left), Vorstellungen full-width (row 2),
   Besetzung next to the gallery (row 3) — each pair an even two-up split,
   no mockup ratio to match here. */
.tzn-produktion-layout {
	display: flex;
	flex-direction: column;
	gap: clamp(24px, 4vw, 36px);
}

/* A rule before every section but the first — on mobile that's simply
   "between each stacked block". At ≥1024px, __content moves beside
   __image (same row, no line needed there any more); __vorstellungen
   spans the full row so its rule reads as one unbroken line across row
   1→2, and __besetzung + __gallery both starting row 3 read as one line
   (with the grid gap's small break) across row 2→3. */
.tzn-produktion-layout__vorstellungen,
.tzn-produktion-layout__content,
.tzn-produktion-layout__besetzung,
.tzn-produktion-layout__gallery,
.tzn-produktion-layout__ticket-info {
	border-top: 1px solid var(--tzn-card-border);
	padding-top: clamp(24px, 4vw, 36px);
}

/* Spielplan detail: Bilder and Ticket-Info share one slot (one stacked
   block on mobile, with the same gap and rule between them as between
   every other section). */
.tzn-produktion-layout__aside {
	display: flex;
	flex-direction: column;
	gap: clamp(24px, 4vw, 36px);
}

/* Ticket-Info: the editor's free text (tzn-core Ticket_Info) on a card,
   like the old price cards; core blocks inside keep their .tzn-content
   type. */
.tzn-ticket-info {
	background: var(--tzn-card);
	border: 1px solid transparent; /* card shape in forced colours */
	padding: 20px;
}

/* Headings an editor adds stay card-sized, not page-sized. */
.tzn-ticket-info.tzn-content :is(h2, h3, h4) {
	margin: 1.25em 0 0.5em;
	font: 700 1.5rem/1.15 var(--tzn-font-display);
}

.tzn-ticket-info > :first-child {
	margin-top: 0;
}

.tzn-ticket-info > :last-child {
	margin-bottom: 0;
}

@media (min-width: 1024px) {
	.tzn-produktion-layout {
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		grid-template-areas: "content image" "vorst vorst" "besetzung gallery";
		gap: clamp(24px, 3vw, 44px);
		align-items: start;
	}

	.tzn-produktion-layout__image {
		grid-area: image;
	}

	.tzn-produktion-layout__vorstellungen {
		grid-area: vorst;
	}

	.tzn-produktion-layout__content {
		grid-area: content;
		border-top: 0;
		padding-top: 0;
	}

	.tzn-produktion-layout__besetzung {
		grid-area: besetzung;
	}

	.tzn-produktion-layout__gallery {
		grid-area: gallery;
	}

	/* Owner, 2026-11-09: the right column ends level with Besetzung —
	   Ticket-Info at its own height (never cut), Bilder in the rest. The
	   gallery grid adds no height of its own (contain: size below), so the
	   row is as high as Besetzung, or as Ticket-Info plus one row of photos
	   where that is taller. */
	.tzn-produktion-layout__aside {
		grid-area: gallery;
		align-self: stretch;
		gap: clamp(24px, 3vw, 44px);
	}

	.tzn-produktion-layout__aside .tzn-produktion-layout__gallery {
		display: flex;
		flex: 1 1 auto;
		flex-direction: column;
	}

	.tzn-produktion-layout__aside .tzn-produktion-layout__gallery > section {
		display: flex;
		flex: 1 1 auto;
		flex-direction: column;
	}

	/* No overflow clip: the script hides every tile that doesn't fit, and
	   a clip would cut the focus ring of the edge tiles (WCAG 2.4.7). */
	.tzn-gallery-grid--fit.is-fitted {
		flex: 1 1 auto;
		min-height: 8rem;
		align-content: start;
		contain: size;
	}

	/* Every photo fits with room to spare: the grid, and the gallery around
	   it, only as high as its rows; Ticket-Info follows right under. */
	.tzn-gallery-grid--fit.is-natural,
	.tzn-produktion-layout__aside .tzn-produktion-layout__gallery:has(.is-natural),
	.tzn-produktion-layout__aside .tzn-produktion-layout__gallery:has(.is-natural) > section {
		flex: 0 0 auto;
	}

	.tzn-gallery-grid--fit.is-natural {
		min-height: 0;
		contain: none;
	}

	/* gallery-lightbox.js sets the rows' height so the photos that fit fill
	   the space (never taller than square). */
	.tzn-gallery-grid--fit.is-stretched .tzn-gallery-tile img {
		height: 100%;
		aspect-ratio: auto;
	}

	/* Archiv detail (single-tzn_production.php): no Vorstellungen, no
	   Besetzung — the gallery takes the whole row under content/image. */
	.tzn-produktion-layout--archiv {
		grid-template-areas: "content image" "gallery gallery";
	}

	/* Every Vorstellung is past (single-tzn_performance.php shows only
	   today's and future ones): no empty Vorstellungen row. */
	.tzn-produktion-layout--no-dates {
		grid-template-areas: "content image" "besetzung gallery";
	}
}

/* The aside ends level with Besetzung's box (from 1024px): no trailing
   margin under the last cast group to make it look longer. */
.tzn-produktion-layout__besetzung .tzn-cast-group:last-child {
	margin-bottom: 0;
}

/* --------------------------------------------------------- Cast list */

/* "Programmzettel" (components.md → Cast list): sentence case in the body
   font throughout. Mobile: role over name, hairline between rows. From
   768px: role · dotted leader · name on one line, like a printed
   programme; a pair too long for one line wraps, name right-aligned. */
.tzn-cast-group {
	margin-bottom: 32px;
}

.tzn-cast-group__heading {
	font: 700 1.5rem/1.1 var(--tzn-font-display);
	color: var(--tzn-ink);
	margin: 0 0 4px;
	padding-bottom: 8px;
	border-bottom: 2px solid var(--tzn-ink);
}

.tzn-cast-group__list {
	margin: 0;
}

.tzn-cast-row {
	padding: 12px 0;
	border-bottom: 1px solid var(--tzn-card-border);
}

.tzn-cast-row__role {
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.4;
}

.tzn-cast-row__note {
	font-weight: 400;
	color: var(--tzn-text-muted);
}

.tzn-cast-row__name {
	margin: 0;
	font-size: 1.1875rem;
	font-weight: 700;
	line-height: 1.4;
}

@media (min-width: 768px) {
	.tzn-cast-group {
		max-width: 40rem;
	}

	.tzn-cast-row {
		display: flex;
		flex-wrap: wrap;
		align-items: baseline;
		column-gap: 8px;
		padding: 8px 0;
		border-bottom: 0;
	}

	.tzn-cast-row::after {
		content: "";
		order: 1;
		flex: 1 1 24px;
		border-bottom: 2px dotted var(--tzn-hairline);
	}

	.tzn-cast-row__role {
		font-size: 1.125rem;
	}

	.tzn-cast-row__name {
		order: 2;
		margin: 0 0 0 auto;
		font-size: 1.125rem;
		text-align: right;
	}
}

/* --------------------------------------------------------- Sponsor tiles */
.tzn-sponsor-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 8px;
}

.tzn-sponsor-grid--main {
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

.tzn-sponsor-tile {
	height: 76px;
	background: var(--tzn-card);
	border: 1px solid transparent; /* card shape in forced colours */
	display: flex;
	align-items: center;
	justify-content: center;
	text-decoration: none;
}

.tzn-sponsor-tile img {
	max-height: 75%;
	max-width: 80%;
	object-fit: contain;
}

.tzn-sponsor-tile span {
	font-weight: 600;
	font-size: var(--tzn-font-size-small);
	text-align: center;
	padding: 6px 10px;
	color: var(--tzn-ink);
	line-height: 1.2;
}

@media (min-width: 1024px) {
	.tzn-sponsor-grid {
		grid-template-columns: repeat(5, minmax(0, 1fr));
	}

	.tzn-sponsor-tile {
		height: 96px;
	}
}

/* ------------------------------------------------- Netzwerk (page-netzwerk.php)
   Two cards in the light homepage card style (.tzn-home-card--light):
   stacked on mobile, side by side from 768px. Each organisation is a
   white logo tile with its name below, the whole thing one link. */
.tzn-network {
	display: grid;
	gap: clamp(14px, 2vw, 20px);
	margin-block: 24px;
}

.tzn-network__card {
	gap: 20px;
}

.tzn-network__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 16px;
}

.tzn-network__item {
	display: flex;
	flex-direction: column;
	gap: 10px;
	color: inherit;
	text-decoration: none;
}

.tzn-network__logo {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 120px;
	padding: 16px;
	background: var(--tzn-surface);
}

.tzn-network__logo img {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
}

.tzn-network__logo-fallback {
	font: 800 1.375rem/1.1 var(--tzn-font-display);
	color: var(--tzn-ink);
	text-align: center;
}

.tzn-network__name {
	display: flex;
	align-items: center;
	min-height: var(--tzn-tap-min);
	font: 700 1.0625rem/1.3 var(--tzn-font-body);
}

/* The homepage card CTA underline (ink). */
a.tzn-network__item .tzn-network__name {
	text-decoration: underline;
	text-decoration-color: var(--tzn-ink);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

a.tzn-network__item:hover .tzn-network__name {
	text-decoration-thickness: 3px;
}

a.tzn-network__item:hover .tzn-network__logo {
	outline: 2px solid var(--tzn-ink);
}

@media (prefers-reduced-motion: no-preference) {
	.tzn-network__name {
		transition: text-decoration-thickness 0.18s ease;
	}
}

/* One column per card, so a page with only one group fills the row. */
@media (min-width: 768px) {
	.tzn-network {
		grid-auto-flow: column;
		grid-auto-columns: minmax(0, 1fr);
		align-items: stretch;
	}
}

@media (min-width: 1024px) {
	.tzn-network__logo {
		height: 140px;
	}
}

/* --------------------------------------------------- Missing-image fallback */
.tzn-image-fallback {
	background: repeating-linear-gradient(
		135deg,
		#efe7dc 0 12px,
		#f7f1e9 12px 24px
	);
	aspect-ratio: 4 / 3;
}

/* --------------------------------------------------------- Home: columns */

/* Two-column layout for the in-season homepage
   (docs/design-system/mockups/TZN Home.dc.html) — not height-matched to
   the gallery any more (client call: no internal scrolling), so this is
   just a plain two-up grid, natural content height, `align-items: start`
   so neither column stretches to match the other. Stacked on mobile — this
   is a desktop-only layout, the gallery just flows normally above the
   cards below 1024px. */
.tzn-home-columns {
	display: flex;
	flex-direction: column;
	gap: 24px;
}

.tzn-home-columns__cards {
	display: flex;
	flex-direction: column;
	gap: clamp(14px, 2vw, 20px);
}

@media (min-width: 1024px) {
	.tzn-home-columns {
		display: grid;
		grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
		gap: clamp(24px, 3vw, 44px);
		align-items: start;
	}
}

/* --------------------------------------------------------- Home: hero gallery */
.tzn-home-gallery__stage {
	position: relative;
	width: 100%;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	border: 1px solid var(--tzn-card-border);
	/* stylelint-disable-next-line declaration-property-value-allowed-list -- the
	   one documented shadow: tokens.json `shadow.card`, "used only under the
	   gallery plate" (components.md "Homepage gallery"). */
	box-shadow: var(--tzn-shadow-card);
}

.tzn-home-gallery__stage .tzn-image-fallback {
	aspect-ratio: auto;
	height: 100%;
}

.tzn-home-gallery__slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition: opacity 0.9s ease;
}

.tzn-home-gallery__slide.is-active {
	opacity: 1;
}

.tzn-home-gallery__slide img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.tzn-home-gallery__controls {
	display: flex;
	align-items: center;
	gap: 10px;
	padding-top: 14px;
}

.tzn-home-gallery__nav,
.tzn-home-gallery__play {
	border: 2px solid var(--tzn-ink);
	background: var(--tzn-surface);
	color: var(--tzn-ink);
	font: 700 1.25rem/1 var(--tzn-font-body);
	cursor: pointer;
	transition: background 0.18s ease;
}

.tzn-home-gallery__nav:hover,
.tzn-home-gallery__play:hover {
	background: var(--tzn-card);
}

.tzn-home-gallery__nav {
	width: 48px;
	height: 48px;
}

.tzn-home-gallery__play {
	min-width: 48px;
	height: 48px;
	padding: 0 12px;
	border: 2px solid transparent; /* shape in forced colours */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: var(--tzn-accent);
}

.tzn-home-gallery__play:hover {
	background: var(--tzn-accent-hover);
}

.tzn-home-gallery__counter {
	font: 700 1.1875rem/1 var(--tzn-font-display);
	margin-left: auto;
	font-variant-numeric: tabular-nums;
}

/* Identity text under the gallery (mockups/TZN Home.dc.html): the
   Startseite page's own content (Seiten → Startseite, editable there) in
   italic body type over a hairline rule; rule and text span the gallery's
   full width (owner, 2026-11-09). */
.tzn-home-intro {
	margin-top: clamp(1rem, 2.5vw, 1.625rem);
	padding-top: clamp(1rem, 2.5vw, 1.625rem);
	border-top: 1px solid var(--tzn-hairline);
	font: italic 400 clamp(1.0625rem, 1.9vw, 1.3125rem)/1.6 var(--tzn-font-body);
	color: var(--tzn-ink-soft-bg);
}

.tzn-home-intro > * {
	margin: 0;
}

.tzn-home-intro > * + * {
	margin-top: 0.75em;
}

/* --------------------------------------------------------- Home: cards */

/* Three fixed variants per docs/design-system/components.md "Cards" —
   light/ink/bordered — reused as-is rather than inventing a fourth
   look for `ueber_uns` or `news` (TZN_Core\Helpers::render_home_cards()). */
.tzn-home-card {
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: 22px;
	border: 1px solid transparent; /* card shape in forced colours */
	text-decoration: none;
	color: inherit;
	transition: background 0.18s ease;
}

.tzn-home-card--light {
	background: var(--tzn-card);
}

a.tzn-home-card--light:hover {
	background: var(--tzn-accent);
}

/* text-muted on accent is 3.22:1 (fails AA for small text); ink-soft-bg on
   accent is 5.11:1 and is a checked pair in inc/colors.php. Every card that
   turns accent on hover switches its eyebrow (home, Verein, Kontakt venue). */
a.tzn-home-card--light:hover .tzn-home-card__eyebrow,
.tzn-kontakt-venue:hover .tzn-kontakt-eyebrow {
	color: var(--tzn-ink-soft-bg);
}

.tzn-home-card--dark {
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
}

/* ink-soft-bg, not ink-hover (1.25:1 against ink, too close to see), and
   the accent button inside darkens with it, so the hover reads. */
a.tzn-home-card--dark:hover {
	background: var(--tzn-ink-soft-bg);
}

a.tzn-home-card--dark:hover .tzn-home-card__cta--button {
	background: var(--tzn-accent-hover);
}

/* Bordered cards (outline home card, news teaser, Kontakt cards) take the
   hairline: card-border is 1.32:1 on paper and barely draws the box. */
.tzn-home-card--outline {
	border: 1px solid var(--tzn-hairline);
}

a.tzn-home-card--outline:hover {
	background: var(--tzn-card);
}

.tzn-home-card__eyebrow {
	display: block;
	font: 600 var(--tzn-font-size-small)/1 var(--tzn-font-body);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--tzn-text-muted);
	margin: 0;
}

.tzn-home-card--dark .tzn-home-card__eyebrow {
	color: var(--tzn-accent);
}

.tzn-home-card__title {
	font: 800 min(clamp(1.75rem, 3vw, 2.25rem), 11vw)/1.02 var(--tzn-font-display);
	margin: 0;
}

/* The system's body minimum (17px), 19px from 1024px, where body copy is 21px. */
.tzn-home-card__body {
	font: 400 1.0625rem/1.5 var(--tzn-font-body);
	margin: 0;
}

@media (min-width: 1024px) {
	.tzn-home-card__body {
		font-size: 1.1875rem;
	}
}

.tzn-home-card--dark .tzn-home-card__body {
	color: var(--tzn-on-ink-muted);
}

.tzn-home-card__cta--link {
	display: flex;
	align-items: center;
	min-height: 48px;
	font: 700 1.125rem/1 var(--tzn-font-body);
	text-decoration: underline;
	text-decoration-color: var(--tzn-ink);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

.tzn-home-card__cta--button {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 56px;
	padding: 0 16px;
	border: 2px solid transparent; /* shape in forced colours */
	background: var(--tzn-accent);
	color: var(--tzn-ink);
	font: 700 1.1875rem/1 var(--tzn-font-body);
	transition: background 0.18s ease;
}

/* --------------------------------------------------------- Sticky ticket bar */
.tzn-sticky-ticket-bar {
	position: sticky;
	bottom: 0;
	z-index: 10;
	display: flex;
	align-items: center;
	justify-content: space-between;

	/* One row at 320px (date 137 + 8 + button 106 = 251 of 256px); under
	   enlarged text the date takes its own line above the button instead
	   of squeezing into a narrow column. */
	flex-wrap: wrap;
	gap: 8px;
	padding: 12px 16px;
	background: var(--tzn-surface);
	/* stylelint-disable-next-line declaration-property-value-allowed-list --
	   documented in components.md "Sticky ticket bar": "drop shadow facing up
	   (0 -8px 20px rgba(36,28,24,.10))". */
	box-shadow: 0 -8px 20px rgba(36, 28, 24, 0.1);
}

.tzn-sticky-ticket-bar .tzn-btn-accent {
	flex-shrink: 0;
	padding-inline: 12px;
	white-space: nowrap;
}

.tzn-sticky-ticket-bar[hidden] {
	display: none;
}

@media (min-width: 1024px) {
	.tzn-sticky-ticket-bar {
		display: none;
	} /* mobile-only pattern */
}

/* --------------------------------------------------------- Empty states */

/* Starts at the page's own left edge, under its heading, like every other
   block on a list page ("Noch keine News.", the empty Spielplan). The 404
   keeps its centred column (.tzn-404 below). */
.tzn-empty-state {
	padding: 40px 0;
	text-align: start;
}

/* --------------------------------------------------------- Footer */

/* Solid ink band (TZN Home.dc.html) — was unstyled (just inherited the page
   background), so the footer never actually read as a distinct dark
   section the way every mockup shows it. Same full-bleed-background/
   1600px-capped-content split as .tzn-header — see the comment there. */
.tzn-footer {
	margin-top: auto;
	flex-shrink: 0;
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
}

.tzn-footer__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 10px 24px;
	padding: 22px 16px 26px;
	max-width: 1600px;
	margin-inline: auto;
}

.tzn-footer__links {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 22px;
	list-style: none;
	padding: 0;
	margin: 0;
}

.tzn-footer__links li,
.tzn-footer__links a {
	display: flex;
	align-items: center;
	min-height: var(--tzn-tap-min);
	font: 400 1.0625rem/1.2 var(--tzn-font-body);
}

/* 44px-wide floor for the short one-word links ("Links" was 33px before its rename,
   "Intern" 38px, "E-Mail" 39px — rule 4 / WCAG 2.5.5). The 6px of
   padding is given back by an equal negative margin, so the text stays
   exactly where it was and the 22px gap between items still separates
   the hit areas (10px apart); min-width covers anything shorter still. */
.tzn-footer__links a {
	color: var(--tzn-on-ink);
	text-decoration: none;
	transition: color 0.18s ease;
	justify-content: center;
	min-width: var(--tzn-tap-min);
	padding-inline: 6px;
	margin-inline: -6px;
}

.tzn-footer__links a:hover {
	color: var(--tzn-accent);
}

.tzn-footer__social {
	display: flex;
	gap: 8px;
}

.tzn-footer__social a {
	width: 44px;
	height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;

	/* on-ink-faint on ink is 4.30:1 (UI component edge needs 3:1);
	   text-muted there is 2.20:1. */
	border: 1px solid var(--tzn-on-ink-faint);
	color: var(--tzn-on-ink);
	text-decoration: none;
	transition:
		background 0.18s ease,
		color 0.18s ease;
}

.tzn-footer__social a:hover {
	background: var(--tzn-accent);
	color: var(--tzn-ink);
}

.tzn-social-icon {
	display: block;
	width: 20px;
	height: 20px;
}

@media (prefers-reduced-motion: reduce) {
	* {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}

/* --------------------------------------------------------- Buttons: outline */
.tzn-btn-outline {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 52px;
	padding: 0 20px;
	border: 2px solid var(--tzn-ink);
	background: transparent;
	color: var(--tzn-ink);
	text-decoration: none;
	font-weight: 700;
}

.tzn-btn-outline:hover {
	background: var(--tzn-card);
}

/* --------------------------------------------------------- Production card */
.tzn-production-card {
	text-decoration: none;
	color: inherit;
	display: block;
}

/* Hover underlines the title in the accent, like every other link —
   accent-hover as the text colour measured 2.84:1 on paper (fails even
   large-text AA, 2026-10-11) and editors can now pick the accent. */
.tzn-production-card:hover .tzn-production-card__title {
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 3px;
}

.tzn-production-card__image {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	display: block;
}

.tzn-production-card__title {
	font: 700 clamp(1.5rem, 2.2vw, 1.75rem)/1.1 var(--tzn-font-display);
	margin: 10px 0 3px;
}

.tzn-production-card__meta {
	color: var(--tzn-text-muted);
	font-size: 1rem;
}

/* --------------------------------------------------------- Production grid */
.tzn-production-grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 20px;
}

@media (min-width: 768px) {
	.tzn-production-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 20px;
	}

	.post-type-archive-tzn_production .tzn-production-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: 20px;
	}
}

@media (min-width: 1024px) {
	.tzn-production-grid {
		gap: 24px;
	}

	.post-type-archive-tzn_production .tzn-production-grid {
		gap: 28px;
	}
}

/* --------------------------------------------------------- Bilder overview (docs/design-system/mockups/TZN Bilder.dc.html) */
.tzn-bilder-title:has(+ .tzn-bilder-content),
.tzn-h3:has(+ .tzn-bilder-content) {
	margin-bottom: clamp(12px, 1.5vw, 18px);
}

.tzn-bilder-content {
	max-width: 100%;
	margin-bottom: clamp(24px, 3vw, 36px);
}

.tzn-bilder-content p {
	font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
	line-height: 1.45;
	color: var(--tzn-text-muted);
}

.tzn-gallery-overview-grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: 16px 12px;
	margin-bottom: clamp(28px, 4vw, 40px);
}

@media (min-width: 1024px) {
	.tzn-gallery-overview-grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
		gap: 28px 24px;
	}
}

.tzn-gallery-overview-grid .tzn-production-card {
	text-decoration: none;
	color: inherit;
	display: block;
}

.tzn-gallery-overview-grid
	.tzn-production-card:hover
	.tzn-production-card__title {
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 3px;
}

.tzn-gallery-overview-grid .tzn-production-card__image,
.tzn-gallery-overview-grid .tzn-image-fallback {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	display: block;
	border: 1px solid var(--tzn-card-border);
	margin-bottom: 8px;
}

.tzn-gallery-overview-grid .tzn-production-card__title {
	font: 700 clamp(1.25rem, 2vw, 1.5rem)/0.95 var(--tzn-font-display);
	margin: 0;
	color: var(--tzn-ink);
}

.tzn-gallery-overview-grid .tzn-production-card__meta {
	font: 400 var(--tzn-font-size-small)/1.3 var(--tzn-font-mono);
	color: var(--tzn-text-muted);
	margin: 3px 0 0;
}

/* --------------------------------------------------------- Gallery grid */
.tzn-gallery-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 8px;
}

.tzn-gallery-tile {
	position: relative;
	display: block;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.tzn-gallery-tile img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
}

/* Fewer photos than columns: the tiles share the full width instead of
   leaving empty columns (a production with 1–3 photos). A single photo
   fills a phone; from 768px it keeps half the width, like the hero image
   above, rather than becoming a screen-high block. */
.tzn-gallery-grid:has(> :only-child) {
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
	.tzn-gallery-grid:has(> :only-child) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1024px) {
	.tzn-gallery-grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}

	.tzn-gallery-grid:has(> :nth-child(2):last-child) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.tzn-gallery-grid:has(> :nth-child(3):last-child) {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	/* Spielplan detail: the gallery already sits in the half column beside
	   Besetzung, so a single photo fills that column. */
	.tzn-produktion-layout:not(.tzn-produktion-layout--archiv) .tzn-gallery-grid:has(> :only-child) {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* Photos past the «+N» tile: only in the lightbox (`display: block`
   above would otherwise beat the UA's [hidden]). */
.tzn-gallery-tile[hidden] {
	display: none;
}

/* «+N» on the last photo that fits (gallery-lightbox.js, Spielplan
   detail): a solid ink scrim over the photo (AGENTS.md rule 5: no text
   on unscrimmed photography), opens the lightbox there. */
.tzn-gallery-tile__more {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: color-mix(in sRGB, var(--tzn-ink) 80%, transparent);
	color: var(--tzn-on-ink);
	font: 700 2rem/1 var(--tzn-font-display);
	font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------- Lightbox */
.tzn-lightbox {
	position: fixed;
	inset: 0;
	z-index: 60;
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
	display: flex;
	flex-direction: column;
	padding: 16px;
}

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

.tzn-lightbox__bar {
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.tzn-lightbox__counter {
	font: 700 1rem/1 var(--tzn-font-display);
}

.tzn-lightbox__close {
	min-width: var(--tzn-tap-min);
	min-height: var(--tzn-tap-min);
	background: none;
	border: 0;
	color: var(--tzn-on-ink);
	font-size: 1.5rem;
	cursor: pointer;
}

.tzn-lightbox__stage {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 0;
}

.tzn-lightbox__stage img {
	max-height: 100%;
	max-width: 100%;
	object-fit: contain;
}

.tzn-lightbox__prev,
.tzn-lightbox__next {
	min-width: 48px;
	min-height: 48px;
	background: none;
	border: 0;
	color: var(--tzn-on-ink);
	font-size: 1.75rem;
	cursor: pointer;
	flex-shrink: 0;
}

.tzn-lightbox__caption {
	text-align: center;

	/* Was --tzn-on-ink-faint: 4.30:1 on ink, fails 4.5:1 for 17px text.
	   --tzn-on-ink-muted = 10.81:1. on-ink-faint is not a text colour on
	   ink (tokens.json "contrastMeasured2026"). */
	color: var(--tzn-on-ink-muted);
	padding-top: 12px;
}

/* --------------------------------------------------------- Lead / Intro paragraph & Director */

/* inline-block: .tzn-lead (subtitle) and .tzn-director (Regie: …) sit on
   the same row when space permits, or wrap naturally on mobile. */
.tzn-lead,
.tzn-director {
	display: inline-block;
	font-size: clamp(1.125rem, 2vw, 1.375rem);
	line-height: 1.55;
	color: var(--tzn-ink-soft-bg);
	max-width: min(100%, 65ch);
}

.tzn-lead {
	margin: 0 20px clamp(16px, 2vw, 24px) 0;
}

.tzn-lead:has(+ .tzn-director) {
	margin-bottom: 6px;
}

.tzn-director {
	margin: 0 0 clamp(16px, 2vw, 24px);
}

/* --------------------------------------------------------- Member sections, grid & card (one section per Rolle) */
.tzn-member-grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 16px;
	list-style: none;
	padding: 0;
	margin: 0 0 36px;
}

@media (min-width: 600px) {
	.tzn-member-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 16px;
	}
}

@media (min-width: 800px) {
	.tzn-member-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: 20px;
	}
}

@media (min-width: 1024px) {
	.tzn-member-grid {
		grid-template-columns: repeat(5, minmax(0, 1fr));
		gap: 16px;
	}
}

.tzn-member-card {
	background: var(--tzn-card);
	border: 1px solid transparent; /* card shape in forced colours */
	padding: 16px;
	display: flex;
	flex-direction: column;
}

.tzn-member-card__photo {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	margin-bottom: 12px;
}

.tzn-member-card__name {
	font: 700 clamp(1.25rem, 1.5vw, 1.5rem)/1.15 var(--tzn-font-display);
	margin: 0 0 4px;
}

.tzn-member-card__function {
	color: var(--tzn-text-muted);
	font-size: var(--tzn-font-size-small);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	font-weight: 600;
	margin: 0 0 10px;
}

.tzn-member-card__quote {
	font-style: italic;
	color: var(--tzn-ink-soft-bg);
	margin: 0 0 14px;
	flex: 1;
	line-height: 1.5;
}

.tzn-member-card__email {
	margin-top: auto;
	padding-top: 12px;
	margin-bottom: 0;
}

.tzn-member-card a {
	font-weight: 600;
	color: var(--tzn-ink);
	font-size: var(--tzn-font-size-small);
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
}

.tzn-member-card a:hover {
	text-decoration-color: var(--tzn-ink);
}

/* --------------------------------------------------------- Verein cards
   page-verein.php: the four sub-pages as homepage cards (.tzn-home-card),
   2 × 2 from 782px, stacked below. Titles carry the same short accent rule
   as the Spielformen cards; the call to action sits at the card's foot. */
.tzn-verein-cards {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 20px;
	margin-top: clamp(32px, 4.5vw, 56px);
}

@media (min-width: 782px) {
	.tzn-verein-cards {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1024px) {
	.tzn-verein-cards {
		gap: clamp(24px, 3vw, 40px);
	}
}

.tzn-verein-cards .tzn-home-card__title::after {
	content: "";
	display: block;
	width: 56px;
	height: 6px;
	margin-top: 12px;
	background: var(--tzn-accent);
}

/* On the light card's accent hover the rule would vanish: ink instead. */
.tzn-verein-cards a.tzn-home-card--light:hover .tzn-home-card__title::after {
	background: var(--tzn-ink);
}

.tzn-verein-cards .tzn-home-card__cta {
	margin-top: auto;
}

/* --------------------------------------------------------- «Karten» columns (Spielformen)
   Core Columns block in the theme's «Karten» style (functions.php): each
   column is a light card, the homepage card look (.tzn-home-card--light;
   components.md "Cards"): flat, no radius or shadow, 22px padding, display
   title, 16px body, underlined call to action at the bottom. Core stacks
   the columns below 782px; the cards keep equal height side by side. */
.wp-block-columns.is-style-tzn-cards {
	gap: 20px;
	margin-block: clamp(24px, 3vw, 40px) 0;
}

@media (min-width: 1024px) {
	.wp-block-columns.is-style-tzn-cards {
		gap: clamp(24px, 3vw, 40px);
	}
}

.is-style-tzn-cards > .wp-block-column {
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: 22px;
	background: var(--tzn-card);
	border: 1px solid transparent; /* card shape in forced colours */
}

.is-style-tzn-cards > .wp-block-column > * {
	margin: 0;
}

/* An empty paragraph left in the editor would add a gap. */
.is-style-tzn-cards > .wp-block-column > p:empty {
	display: none;
}

.tzn-content .is-style-tzn-cards h2,
.tzn-content .is-style-tzn-cards h3 {
	font: 800 clamp(1.75rem, 3vw, 2.25rem)/1.02 var(--tzn-font-display);
	margin: 0;
	hyphens: auto;
	hyphenate-limit-chars: 12 5 5;
	overflow-wrap: anywhere;
}

/* Colour accent (owner pick, variant E, 2026-11-08): a short terracotta
   rule under each card title, decoration only. */
.is-style-tzn-cards > .wp-block-column > :is(h2, h3)::after {
	content: "";
	display: block;
	width: 56px;
	height: 6px;
	margin-top: 12px;
	background: var(--tzn-accent);
}

.tzn-content .is-style-tzn-cards p {
	font: 400 1rem/1.5 var(--tzn-font-body);
}

/* tzn-core/spielform-links: the card's call to action. Always at the foot
   of its card (order + margin-top: auto), wherever an editor puts the block
   in the column, so both cards' links line up. It is the column's only
   focusable content, so focus order is unaffected. */
.tzn-spielform-links {
	order: 1;
	margin-top: auto;
	padding-top: 8px;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}

.is-style-tzn-cards > .wp-block-column > .tzn-spielform-links {
	margin-top: auto;
}

.tzn-spielform-links__link {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-height: 48px;
	font: 700 1.125rem/1.2 var(--tzn-font-body);
	color: var(--tzn-ink);
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
	hyphens: auto;
	overflow-wrap: anywhere;
}

/* The arrow is aria-hidden decoration; the text stays ink (12.97:1). */
.tzn-spielform-links__link > span {
	color: var(--tzn-accent-hover);
}

.tzn-spielform-links__link:hover {
	text-decoration-color: var(--tzn-ink);
}

.tzn-spielform-links__link:hover > span {
	color: var(--tzn-ink);
}

.tzn-content .tzn-spielform-links__note {
	font: 400 1rem/1.5 var(--tzn-font-body);
	color: var(--tzn-text-muted);
	margin: 0;
}

/* --------------------------------------------------------- News teaser */
.tzn-news-teaser {
	display: block;
	border: 1px solid var(--tzn-hairline); /* bordered card: see .tzn-home-card--outline */
	padding: 24px;
	margin-bottom: 20px;
	text-decoration: none;
	color: inherit;
	transition:
		border-color 0.18s ease,
		background 0.18s ease;
}

.tzn-news-teaser:hover {
	border-color: var(--tzn-ink);
	background: var(--tzn-surface);
}

.tzn-news-teaser h2 {
	font: 800 clamp(1.375rem, 2.5vw, 1.875rem)/1.1 var(--tzn-font-display);
	margin: 6px 0 10px;
	color: var(--tzn-ink);
}

.tzn-news-teaser p:not(.tzn-eyebrow) {
	margin: 0 0 12px;
	color: var(--tzn-ink-soft-bg);
	line-height: 1.5;
}

.tzn-news-teaser__link {
	font-weight: 700;
	color: var(--tzn-ink);
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-underline-offset: 3px;
	display: inline-flex;
	align-items: center;
	gap: 4px;
}

.tzn-news-teaser:hover .tzn-news-teaser__link {
	text-decoration-color: var(--tzn-ink);
}

.tzn-news-teaser__image {
	margin-bottom: 14px;
}

/* --------------------------------------------------------- News (list & detail) */

/* Layout & styling from docs/design-system/mockups/TZN News.dc.html */
.tzn-news-wrap {
	width: 100%;
}

.tzn-news-title:has(+ .tzn-news-content),
.tzn-h3:has(+ .tzn-news-content) {
	margin-bottom: clamp(12px, 1.5vw, 18px);
}

.tzn-news-content {
	max-width: 100%;
	margin-bottom: clamp(24px, 3vw, 36px);
}

.tzn-news-content p {
	font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
	line-height: 1.45;
	color: var(--tzn-text-muted);
}

.tzn-news-list {
	display: flex;
	flex-direction: column;
}

.tzn-news-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 14px;
	padding: 24px 0;
	border-bottom: 1px solid var(--tzn-card-border);
	text-decoration: none;
	color: var(--tzn-ink);
}

.tzn-news-row__thumb {
	aspect-ratio: 4 / 3;
	background: repeating-linear-gradient(
		135deg,
		#efe7dc 0 12px,
		#f7f1e9 12px 24px
	);
	border: 1px solid var(--tzn-card-border);
	overflow: hidden;
	display: block;
}

.tzn-news-row__thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.tzn-news-row__date {
	font: 400 var(--tzn-font-size-small)/1 var(--tzn-font-mono);
	color: var(--tzn-text-muted);
	margin-bottom: 6px;
}

.tzn-news-row__title {
	font: 800 clamp(1.625rem, 3vw, 2rem)/1.1 var(--tzn-font-display);
	margin: 0 0 8px;
	color: var(--tzn-ink);
	transition: color 0.18s ease;
}

.tzn-news-row:hover .tzn-news-row__title {
	color: var(--tzn-ink-hover);
}

.tzn-news-row__excerpt {
	font: 400 1.125rem/1.55 var(--tzn-font-body);
	color: var(--tzn-ink-soft-bg);
	margin: 0 0 6px;
	text-wrap: pretty;
}

.tzn-news-row__more {
	font: 700 1.0625rem/1 var(--tzn-font-body);
	color: var(--tzn-ink);
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 3px;
	display: inline-block;
	transition: text-decoration-color 0.18s ease;
}

.tzn-news-row:hover .tzn-news-row__more {
	text-decoration-color: var(--tzn-ink);
}

@media (min-width: 1024px) {
	.tzn-news-row {
		grid-template-columns: 200px minmax(0, 1fr);
		gap: 32px;
		align-items: start;
		padding: 32px 0;
	}

	.tzn-news-row--no-thumb {
		grid-template-columns: minmax(0, 1fr);
	}
}

.tzn-news-load-more {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 56px;
	margin-top: 32px;
	border: 2px solid var(--tzn-ink);
	font: 700 1.125rem/1 var(--tzn-font-body);
	text-decoration: none;
	color: var(--tzn-ink);
	width: 100%;
	transition: background 0.18s ease;
}

.tzn-news-load-more:hover {
	background: var(--tzn-card);
}

.tzn-news-load-more[aria-busy="true"] {
	opacity: 0.6;
	cursor: wait;
}

/* --------------------------------------------------------- Filter bar */
.tzn-filter-bar {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: clamp(24px, 3vw, 36px);
}

.tzn-filter-bar__chip {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 0 16px;
	border: 1px solid var(--tzn-ink);
	background: transparent;
	color: var(--tzn-ink);
	text-decoration: none;
	font: 600 1rem/1 var(--tzn-font-body);
	cursor: pointer;
	transition:
		background 0.15s ease,
		color 0.15s ease;
}

.tzn-filter-bar__chip:hover {
	background: var(--tzn-card);
}

/* The selected type: solid ink, the same "selected" fill as the current
   page in .tzn-pagination below. */
.tzn-filter-bar__chip[aria-current="true"] {
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
}

.tzn-filter-bar select {
	min-height: 44px;
	border: 1px solid var(--tzn-ink);
	background-color: transparent;
	color: var(--tzn-ink);
	font: 600 1rem/1 var(--tzn-font-body);
	padding: 0 50px 0 16px;
	appearance: none;
	-webkit-appearance: none;
	border-radius: 0;
	cursor: pointer;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23241C18' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 22px center;
	transition:
		background-color 0.15s ease,
		color 0.15s ease;
}

.tzn-filter-bar select:hover {
	background-color: var(--tzn-card);
}

.tzn-filter-bar select::-ms-expand {
	display: none;
}

/* --------------------------------------------------------- Pagination */

/* Styles core WordPress's own paginate_links()/the_posts_pagination()
   markup by its default classes (.page-numbers/.current/.dots/.prev/.next)
   rather than a bespoke wrapper — so any future paginated list gets this
   for free just by wrapping the call in <nav class="tzn-pagination">,
   the same "boring, core-idiomatic" choice as everywhere else in this
   theme. Tile look/44px touch target matches .tzn-filter-bar__chip right
   above, on the same Archiv page; current page fills solid ink, the same
   "selected" treatment .tzn-btn-primary already uses elsewhere. */
.tzn-pagination {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	margin-top: clamp(24px, 4vw, 40px);
}

.tzn-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px;
	min-height: 44px;
	padding: 0 12px;
	border: 1px solid var(--tzn-ink);
	color: var(--tzn-ink);
	font: 600 1rem/1 var(--tzn-font-body);
	text-decoration: none;
	transition:
		background 0.15s ease,
		color 0.15s ease;
}

a.page-numbers:hover {
	background: var(--tzn-card);
}

.tzn-pagination .page-numbers.current {
	background: var(--tzn-ink);
	border-color: var(--tzn-ink);
	color: var(--tzn-on-ink);
}

.tzn-pagination .page-numbers.dots {
	border: 0;
	min-width: auto;
	padding: 0 4px;
	color: var(--tzn-text-muted);
}

.tzn-pagination .page-numbers.prev,
.tzn-pagination .page-numbers.next {
	padding: 0 16px;
}

/* --------------------------------------------------------- Kontakt/Lageplan */

/* Structure/copy from docs/design-system/mockups/TZN Kontakt-Lageplan.dc.html,
   but not its own independent max-width/centering — that mockup's <main> has
   no width cap of its own, so re-centering a narrower 1040px box inside it
   looks fine in isolation. On the real site every page shares the same
   1600px .tzn-container (main/header/footer/breadcrumb all line up on that
   one column now), so doing the same here just re-centers this page's
   content in the middle of the viewport, independently of and misaligned
   with the breadcrumb/header above it. Dropped — this page now just fills
   .tzn-container like every other page. */
.tzn-kontakt-title {
	font: 800 2.75rem/1.05 var(--tzn-font-display);
	margin: 0 0 clamp(24px, 3.5vw, 40px);
}

@media (min-width: 1024px) {
	.tzn-kontakt-title {
		font-size: 3.625rem;
	}
}

.tzn-kontakt-grid {
	display: flex;
	flex-direction: column;
	gap: 20px;
	margin-bottom: clamp(24px, 3vw, 32px);
}

@media (min-width: 600px) {
	.tzn-kontakt-grid {
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	}

	.tzn-kontakt-grid > .tzn-kontakt-card--full {
		grid-column: 1 / -1;
	}
}

.tzn-kontakt-eyebrow {
	display: block;
	font: 600 var(--tzn-font-size-small)/1 var(--tzn-font-body);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--tzn-text-muted);
	margin-bottom: 8px;
}

.tzn-kontakt-venue {
	background: var(--tzn-card);
	border: 1px solid transparent; /* card shape in forced colours */
	padding: 24px;
	text-decoration: none;
	display: flex;
	flex-direction: column;
	gap: 16px;
	color: var(--tzn-ink);
	transition: background 0.18s ease;
}

.tzn-kontakt-venue:hover {
	background: var(--tzn-accent);
}

.tzn-kontakt-venue__title {
	display: block;
	font: 800 min(clamp(1.625rem, 3vw, 2.125rem), 10vw)/1.1 var(--tzn-font-display);
	color: var(--tzn-ink);
}

.tzn-kontakt-venue__address {
	display: block;
	font: 400 1.125rem/1.5 var(--tzn-font-body);
	color: var(--tzn-ink-soft-bg);
	margin-top: 6px;
}

.tzn-kontakt-venue__btn {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	min-height: 52px;
	padding: 0 16px;
	border: 2px solid transparent; /* shape in forced colours */
	background: var(--tzn-ink);
	color: var(--tzn-on-ink);
	font: 700 1.125rem/1 var(--tzn-font-body);
}

.tzn-kontakt-card {
	border: 1px solid var(--tzn-hairline); /* bordered card: see .tzn-home-card--outline */
	padding: 22px;
	background: transparent;
}

.tzn-kontakt-card .tzn-kontakt-eyebrow {
	margin-bottom: 10px;
}

/* A 44px target (rule 4): the line box alone is 36px, so the link is a
   flex box with a tap-size minimum, centred text, and only as wide as its
   text; long addresses still wrap under enlarged text. */
.tzn-kontakt-card__value {
	display: flex;
	align-items: center;
	width: fit-content;
	max-width: 100%;
	min-height: var(--tzn-tap-min);
	overflow-wrap: anywhere;
	font: 700 min(1.875rem, 12vw)/1.2 var(--tzn-font-display);
	margin-bottom: 0;
	color: var(--tzn-ink);
	text-decoration: underline;
	text-decoration-color: var(--tzn-accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 3px;
	transition: text-decoration-color 0.18s ease;
}

.tzn-kontakt-card__value:hover {
	text-decoration-color: var(--tzn-ink);
}

.tzn-kontakt-card__body {
	font: 400 1.1875rem/1.6 var(--tzn-font-body);
	max-width: 68ch;
}

/* --------------------------------------------------------- Impressum photo credits */
.tzn-photo-credits dt {
	font-weight: 700;
	margin-top: 12px;
}

.tzn-photo-credits dd {
	margin: 4px 0 0;
	color: var(--tzn-text-muted);
}

/* --------------------------------------------------------- Load more */
.tzn-load-more {
	display: flex;
	width: 100%;
	align-items: center;
	justify-content: center;
	min-height: 56px;
	padding: 0 28px;
	border: 2px solid var(--tzn-ink);
	font: 700 1.125rem/1 var(--tzn-font-body);
	text-align: center;
	margin-top: 0;
}

@media (min-width: 600px) {
	.tzn-load-more {
		display: inline-flex;
		width: auto;
	}
}

/* --------------------------------------------------------- Search */
.tzn-search-form {
	display: flex;
	gap: 8px;
	margin-bottom: 24px;
}

.tzn-search-form__input {
	flex: 1;
	min-height: 44px;
	border: 2px solid var(--tzn-ink);
	background: var(--tzn-surface);
	padding: 0 12px;
}

/* --------------------------------------------------------- Mitwirken form */

/* Breathing room between the Page's own editable content (intro/benefits)
   and the form below it — without this the last paragraph/list and the
   form's first fieldset sat right on top of each other. */
.tzn-mitwirken-intro {
	margin-bottom: clamp(28px, 4vw, 44px);
}

/* A heading editors put in the page's own content takes the in-page
   subsection role (.tzn-h4 in tokens.css) instead of the browser default. */
.tzn-mitwirken-intro :is(h2, h3) {
	font: 800 min(2rem, 16vw)/1.1 var(--tzn-font-display);
	color: var(--tzn-ink);
	margin: clamp(28px, 4vw, 40px) 0 16px;
}

.tzn-mitwirken-intro > :is(h2, h3):first-child {
	margin-top: 0;
}

@media (min-width: 1024px) {
	.tzn-mitwirken-intro :is(h2, h3) {
		font-size: min(2.5rem, 5vw);
	}
}

.tzn-field-note {
	color: var(--tzn-text-muted);
	font-size: var(--tzn-font-size-small);
	margin: 16px 0 0;
}

.tzn-required {
	color: var(--tzn-danger);
}

.tzn-form fieldset {
	border: 0;
	padding: 0;
	margin: 0 0 28px;
}

.tzn-form legend {
	font: 700 1.25rem/1.2 var(--tzn-font-display);
	padding: 0 0 4px;
	width: 100%;
}

/* "Ich möchte"/"Ich wünsche" (page-mitwirken.php): same weight, family and
   size as a Vorname/Nachname-style input label, not the heavier display
   font the other legends use — client request, 2026-09-05, so these two
   read as a peer of the field labels around them rather than a sub-heading.
   `.tzn-form legend.…` (not just `.tzn-legend--label`) so this outweighs
   the generic `.tzn-form legend` rule above on specificity, not source
   order. */
.tzn-form legend.tzn-legend--label {
	font: 700 1.0625rem var(--tzn-font-body);
	padding: 0 0 4px;
	width: 100%;
}

@media (min-width: 1024px) {
	.tzn-form legend.tzn-legend--label {
		font-size: 1.3125rem;
	}
}

.tzn-field {
	margin-bottom: 16px;
}

/* "Ich interessiere mich für" (page-mitwirken.php) only shows while
   "Aktivmitglied" is checked. Pure CSS, so it's right on first paint (a
   footer-script toggle flashed the fieldset on load) and works without JS
   (FR-FRM-06); a browser without :has() just keeps it visible. */
.tzn-form:has([data-tzn-mitwirken-aktiv]:not(:checked)) [data-tzn-mitwirken-interests] {
	display: none;
}

/* :not(.tzn-h4) — Bemerkung's label (page-mitwirken.php) opts into the
   tzn-h4 heading look instead; without this exclusion this rule's higher
   specificity (a class + a :not() + the label element) would win over
   .tzn-h4's own font/margin despite loading after it. */
.tzn-field > label:not(.tzn-checkbox):not(.tzn-h4) {
	display: block;
	font-weight: 700;
	margin-bottom: 6px;
}

/* .tzn-h4 itself never sets `display` — every other user of it is already
   a block-level <h1>-<h6>. A <label> defaults to inline, where a
   margin-bottom is a no-op (found via Bemerkung rendering with no gap to
   its textarea at all, unlike Kontaktdaten's real <h2>): without this,
   Bemerkung's label doesn't actually push the field below it down. */
.tzn-field > label.tzn-h4 {
	display: block;
}

/* Required and optional fields share the same border weight/colour —
   client request, 2026-09-05: the darker `:required` border read as
   "bold". The `*` next to the label is the only required indicator now.
   Border: hairline measured 1.33:1 against the field (WCAG 1.4.11 needs
   3:1 for a field's outline, 2026-10-11); text-muted gives 5.89:1 on card
   and more on paper, at 1px so the fields don't read as bold again. */
.tzn-field input[type="text"],
.tzn-field input[type="email"],
.tzn-field input[type="tel"],
.tzn-field input[type="password"],
.tzn-field textarea {
	width: 100%;
	min-height: 52px;
	border: 1px solid var(--tzn-text-muted);
	background: var(--tzn-card);
	padding: 0 14px;
	font: 400 1.0625rem/1.4 var(--tzn-font-body);
	color: var(--tzn-ink);
}

.tzn-field textarea {
	min-height: 120px;
	padding: 12px 14px;
	resize: vertical;
}

/* `.tzn-field.tzn-field--error` + the same type list in :is() (0,3,1)
   outweighs the base field rule above (0,2,1) on specificity, not source
   order — the bare `.tzn-field--error input` (0,1,1) lost to it, so the
   red border never showed. Same text controls only: the consent checkbox
   keeps its own .tzn-checkbox look. */
.tzn-field.tzn-field--error :is(input[type="text"], input[type="email"], input[type="tel"], input[type="password"], textarea) {
	border: 2px solid var(--tzn-danger);
	background: var(--tzn-surface);
}

.tzn-field__error {
	color: var(--tzn-danger);
	font-weight: 700;
	margin: 6px 0 0;
}

.tzn-field-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: 12px;
	margin-bottom: 16px; /* the gap a .tzn-field leaves below itself — the row zeroes its fields' own */
}

.tzn-field-row .tzn-field {
	margin-bottom: 0;
}

.tzn-checkbox {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	min-height: 44px;
	padding: 12px 0;
	border-bottom: 1px solid var(--tzn-card-border);
	cursor: pointer;
}

/* Consent (page-mitwirken.php) is always the last checkbox before Senden —
   the line right above the button read as a stray rule, not a separator
   between two items, so it's dropped just there. */
.tzn-checkbox--no-line {
	border-bottom: 0;
}

.tzn-checkbox input[type="checkbox"],
.tzn-checkbox input[type="radio"] {
	appearance: none;
	-webkit-appearance: none;
	flex-shrink: 0;
	width: 26px;
	height: 26px;
	margin: 2px 0 0;
	border: 2px solid var(--tzn-ink);
	background: var(--tzn-surface) center/14px no-repeat;
}

.tzn-checkbox input[type="radio"] {
	/* stylelint-disable-next-line declaration-property-value-allowed-list -- a
	   radio must stay recognisably round to read as "pick one" next to the
	   square checkboxes (NFR-09); not a rounded corner on a box. */
	border-radius: 50%;
}

.tzn-checkbox input:checked {
	background-color: var(--tzn-ink);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23fff' d='M6.2 11.4 2.8 8l1.1-1.1 2.3 2.3 5.6-5.6L13 4.7z'/%3E%3C/svg%3E");
}

.tzn-checkbox input[type="radio"]:checked {
	background-image: none;
	/* stylelint-disable-next-line declaration-property-value-allowed-list -- not a
	   drop shadow: an inset ring that draws the checked radio's centre dot. */
	box-shadow: inset 0 0 0 5px var(--tzn-surface);
}

.tzn-honeypot {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.tzn-form .tzn-btn-accent {
	width: 100%;
	margin-top: 8px;
}

.tzn-form-success {
	background: var(--tzn-card);
	padding: 24px 20px;
	margin-bottom: 24px;
}

/*
 * Column refactor (2026-09-05, #6 — now three: Deine Wahl, Kontaktdaten,
 * the rest) — see the rationale comment in page-mitwirken.php. No
 * border/background on any column (client request) — they're plain layout
 * columns, not bordered panels; the grid gap plus each column's own
 * headings/fieldsets already separate them. Mobile-first: stacked into one
 * column below 1024px, in DOM order.
 */
.tzn-form-columns {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.tzn-form-col > .tzn-h4:first-child {
	margin-top: 0;
}

@media (min-width: 1024px) {
	.tzn-form {
		max-width: 1080px;
		margin-inline: auto;
	}

	.tzn-form-columns {
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		gap: clamp(32px, 4vw, 56px);
		align-items: start;
	}

	/* Three narrower columns need more width than two wider ones to stay
     comfortable, not the same 1080px cap. */
	.tzn-form--three-col {
		max-width: 1400px;
	}

	.tzn-form-columns--three {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

/* --------------------------------------------------------- Mitgliederbereich */
.tzn-member-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 24px;
}

.tzn-member-bar .tzn-h3 {
	margin-bottom: 0;
}

.tzn-doc-group {
	margin-bottom: 32px;
}

.tzn-doc-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

/* The whole row is the download link: title + details + icon "button". */
.tzn-doc-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	min-height: 44px;
	padding: 12px 0;
	border-bottom: 1px solid var(--tzn-card-border);
	color: var(--tzn-ink);
	text-decoration: none;
}

.tzn-doc-row__text {
	min-width: 0;
}

.tzn-doc-row__title {
	display: block;
	font: 700 1.125rem/1.2 var(--tzn-font-display);
	text-decoration: underline;
	text-decoration-color: transparent;
	text-underline-offset: 3px;
}

.tzn-doc-row__meta {
	display: block;
	color: var(--tzn-text-muted);
	font-size: var(--tzn-font-size-small);
	margin-top: 2px;
}

/* Same look as .tzn-btn-outline, square: a 48px target even on its own. */
.tzn-doc-row__icon {
	display: inline-flex;
	flex-shrink: 0;
	align-items: center;
	justify-content: center;
	width: 48px;
	height: 48px;
	border: 2px solid var(--tzn-ink);
}

.tzn-doc-row:hover .tzn-doc-row__title {
	text-decoration-color: var(--tzn-accent);
}

.tzn-doc-row:hover .tzn-doc-row__icon {
	background: var(--tzn-card);
}

.tzn-doc-group__empty {
	color: var(--tzn-text-muted);
	padding: 16px 0;
	border-bottom: 1px solid var(--tzn-card-border);
}

/* From 1024px: Jahresprogramm | Vereinsstatuten side by side, the GV list
   on its own full-width row below, in two newspaper columns (newest first
   reads downwards). */
@media (min-width: 1024px) {
	.tzn-doc-columns {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: clamp(32px, 4vw, 56px);
		align-items: start;
	}

	.tzn-doc-group--wide {
		grid-column: 1 / -1;
	}

	.tzn-doc-group--wide .tzn-doc-list {
		column-count: 2;
		column-gap: clamp(32px, 4vw, 56px);
	}

	.tzn-doc-group--wide .tzn-doc-list li {
		break-inside: avoid;
	}
}

/* -------------------------------------------------- Editorial & Content */
.tzn-content {
	max-width: 68ch;
	line-height: 1.6;
}

.tzn-content--full,
.single-tzn_performance .tzn-content,
.single-tzn_production .tzn-content,
.single-post .tzn-content,
.tzn-news-detail .tzn-content,
.page-verein .tzn-content,
.parent-verein .tzn-content,
.page-id-8 .tzn-content,
.parent-pageid-8 .tzn-content,
.page-bilder .tzn-content,
.page-id-25 .tzn-content,
.tzn-bilder-content,
.blog .tzn-content,
.page-id-4 .tzn-content,
.tzn-news-content,
.page-datenschutz .tzn-content,
.page-id-3 .tzn-content,
.privacy-policy .tzn-content,
.page-impressum .tzn-content,
.page-id-14 .tzn-content {
	max-width: 100%;
}

/* Verein: the intro (.tzn-lead, in the page text or the template's
   fallback) runs as wide as the cards below it, not 65ch (owner request
   2026-11-09). */
.page-verein .tzn-lead {
	max-width: 100%;
	margin-right: 0;
}

/* On these full-width pages running text (paragraphs, lists, quotes) runs
   as wide as the content column, in line with the cards, forms and
   galleries beside it; no 68ch cap (owner request 2026-11-09). */

.tzn-content > * + * {
	margin-top: 1.25em;
}

.tzn-content h1,
.tzn-content h2,
.tzn-content h3,
.tzn-content h4,
.tzn-content h5,
.tzn-content h6 {
	font-family: var(--tzn-font-display);
	font-weight: 800;
	color: var(--tzn-ink);
	margin-top: 1.5em;
	margin-bottom: 0.5em;
	letter-spacing: normal;
}

.tzn-content > *:first-child,
.tzn-content h1:first-child,
.tzn-content h2:first-child,
.tzn-content h3:first-child,
.tzn-content h4:first-child,
.tzn-content h5:first-child,
.tzn-content h6:first-child {
	margin-top: 0;
}

.tzn-content h1 {
	font-size: min(4.75rem, 24vw);
	line-height: 0.9;
}

.tzn-content h2 {
	font-size: min(3.625rem, 22vw);
	line-height: 0.95;
}

.tzn-content h3 {
	font-size: min(2.75rem, 14vw);
	line-height: 1.05;
}

.tzn-content h4 {
	font-size: min(2rem, 16vw);
	line-height: 1.1;
}

.tzn-content h5 {
	font-size: 1.625rem;
	line-height: 1.15;
}

.tzn-content h6 {
	font-size: 1.375rem;
	line-height: 1.2;
}

@media (min-width: 1024px) {
	.tzn-content h1 {
		font-size: 8rem;
	}

	.tzn-content h2 {
		font-size: 5.5rem;
	}

	.tzn-content h3 {
		font-size: 3.625rem;
	}

	.tzn-content h4 {
		font-size: min(2.5rem, 5vw);
	}
}

.tzn-content ul,
.tzn-content ol {
	padding-left: 1.5em;
}

.tzn-content li + li {
	margin-top: 0.5em;
}

/* News detail image and layout */
.tzn-news-detail__title {
	margin-bottom: clamp(16px, 2.5vw, 24px);
}

.tzn-news-detail__figure {
	margin: 0 0 clamp(20px, 3vw, 28px);
	max-width: fit-content;
}

.tzn-news-detail__img {
	display: block;
	width: auto;
	height: auto;
	max-width: min(100%, 380px);
	max-height: 260px;
	border: 1px solid var(--tzn-card-border);
}

/* 404 page layout */
.tzn-404 {
	max-width: 520px;
	margin-inline: auto;
	text-align: center;
}

.tzn-404 .tzn-search-form {
	margin-top: 32px;
	justify-content: center;
}
