1229 lines
47 KiB
CSS
1229 lines
47 KiB
CSS
/* Design tokens: one place for colours, spacing, radius, shadow and type. Every
|
||
component pulls from these variables (var(--...)), so the whole site shares one
|
||
look and a change here ripples everywhere. Palette follows the school's own
|
||
colours (IGMH): its blue for the header, links and headings, its green as the
|
||
accent. No external anything -- just CSS custom properties. */
|
||
:root {
|
||
/* Brand blue and two derived shades (darker for hover, lighter for tints). This
|
||
is the IGMH blue (#328fc6) darkened so it stays legible as link/heading text
|
||
on white; the header keeps the brighter original via --color-header below. */
|
||
--color-brand: #2b6f9c;
|
||
--color-brand-dark: #1f5477;
|
||
--color-brand-tint: #eaf3f9;
|
||
|
||
/* The header bar's background: the IGMH blue, eased one step deeper. The
|
||
original #328fc6 carries white menu text at only 3.6:1, below AA for normal
|
||
text; #2a77a6 is the same hue at 4.9:1. Its own token (not --color-brand)
|
||
because it only ever carries white text and must stay a deep blue in dark
|
||
mode, where --color-brand is lightened for text/links. */
|
||
--color-header: #2a77a6;
|
||
|
||
/* Text and menu states on the header. White text, and two translucent dark
|
||
washes for the menu pills: they deepen whatever part of the gradient sits
|
||
behind a link, so one value works from the blue end to the green end and the
|
||
white text gains contrast instead of losing it (a white wash dropped the
|
||
active pill to 2.7:1). The active pill is the stronger one, so "where am I"
|
||
outranks "where is the mouse". */
|
||
--color-on-header: #fff;
|
||
--color-nav-hover: rgba(0, 0, 0, 0.12);
|
||
--color-nav-active: rgba(0, 0, 0, 0.2);
|
||
|
||
/* Footer: a dark brand blue, so the page visibly ends there. Light text in two
|
||
steps (headings and the name brighter, links softer); both stay >= 7:1 on the
|
||
band. The border token is a faint white line for the copyright rule. */
|
||
--color-footer: #1a3d57;
|
||
--color-on-footer: #f1f5f9;
|
||
--color-on-footer-soft: #c5d3df;
|
||
--color-footer-border: rgba(255, 255, 255, 0.15);
|
||
|
||
/* Accent for highlights (active nav, event dates, call-outs): the IGMH green.
|
||
The -dark shade is darkened enough to keep small text and the FAQ marker
|
||
legible on a white card. */
|
||
--color-accent: #6db863;
|
||
--color-accent-dark: #4f9247;
|
||
|
||
/* A whisper of the green for calm background areas (info boxes, highlighted
|
||
rows). Light enough that body text keeps its full contrast on top. */
|
||
--color-accent-tint: #eef7ec;
|
||
|
||
/* Warm yellow for the rare thing that must catch the eye (a "new" marker, a
|
||
deadline). Background only, never text -- yellow text is unreadable on white.
|
||
Used sparingly: if everything is highlighted, nothing is. */
|
||
--color-highlight: #fdf0c2;
|
||
|
||
/* Brand gradient for large surfaces (hero, banner): the header blue flowing into
|
||
the IGMH green. The green end is a deep shade (#3a7a3e, 5.2:1 with white),
|
||
not --color-accent, so white text stays readable across the whole sweep. */
|
||
--gradient-brand: linear-gradient(135deg, var(--color-header) 0%, #3a7a3e 100%);
|
||
|
||
/* The month band of the calendar sheet (CalendarSheet): the deep green of the
|
||
gradient's end, because it carries small white text -- on --color-accent that
|
||
would fall below AA. Same 5.2:1 as in the gradient. */
|
||
--color-calendar-band: #3a7a3e;
|
||
|
||
/* Buttons in editor content (a lone mail link, links in an "aufruf" box): a
|
||
filled blue with white text. Own tokens, not --color-brand, because in dark
|
||
mode --color-brand turns light for link text, and white on that light blue
|
||
would fail WCAG AA. Both shades carry white at >= 5.5:1. */
|
||
--color-button: #2b6f9c;
|
||
--color-button-hover: #1f5477;
|
||
--color-on-button: #fff;
|
||
|
||
/* The light button for dark surfaces (the brand gradient of the home hero),
|
||
where a filled blue button would melt into the blue end. Dark blue text on
|
||
white, 8:1; the hover is a faint blue tint, so the button still reads as one. */
|
||
--color-button-light: #fff;
|
||
--color-button-light-hover: #eaf3f9;
|
||
--color-on-button-light: #1f5477;
|
||
|
||
/* Initials avatars on the person cards (::: team, see TeamMember.cs): only the
|
||
two brand colours, blue and green tint, so the board stays in the IGMH palette.
|
||
The colour is picked from the name, so it means nothing -- it only helps tell
|
||
the cards apart. The tints follow their tokens into dark mode; the green text
|
||
needs its own shade, because --color-accent-dark is too light for small text
|
||
on the tint (3.6:1). Blue 7.2:1, green 5.9:1. */
|
||
--color-avatar-1: var(--color-brand-tint);
|
||
--color-on-avatar-1: var(--color-brand-dark);
|
||
--color-avatar-2: var(--color-accent-tint);
|
||
--color-on-avatar-2: #2f6b33;
|
||
|
||
/* Keyboard focus ring (:focus-visible). Its own token so it can stay strong on
|
||
the light page (dark blue, 8:1 on white) and flip to a light blue on the dark
|
||
one; on the gradient the hero switches it to white. */
|
||
--color-focus: #1f5477;
|
||
|
||
/* The focus ring on the dark brand surfaces (header, hero gradient, footer),
|
||
where the dark ring above would vanish: white in both schemes, >= 4.9:1 on
|
||
every one of them. */
|
||
--color-focus-inverse: #fff;
|
||
|
||
/* Headings. Its own token (not --color-brand) so headings and links can differ:
|
||
in dark mode headings become a soft white while links keep the brand blue. */
|
||
--color-heading: #2b6f9c;
|
||
|
||
/* Text and surfaces. */
|
||
--color-text: #222;
|
||
--color-text-soft: #555;
|
||
--color-bg: #fff; /* cards / content surface */
|
||
--color-page: #f4f6f4; /* near-neutral page background with a faint cool
|
||
tint, so white cards lift off it without the old
|
||
warm beige clashing with the cool blue */
|
||
--color-surface: #fafafa;
|
||
--color-border: #e0e0e0;
|
||
|
||
/* Spacing scale -- use these instead of ad-hoc rem values so gaps stay even. */
|
||
--space-1: 0.25rem;
|
||
--space-2: 0.5rem;
|
||
--space-3: 1rem;
|
||
--space-4: 1.5rem;
|
||
--space-5: 2rem;
|
||
--space-6: 3rem;
|
||
|
||
/* Rounded corners and a soft shadow for cards, matching the friendly tone. */
|
||
--radius: 12px;
|
||
--radius-sm: 6px;
|
||
--radius-lg: 20px; /* large surfaces (hero, banner) look stiff with
|
||
the card radius at their size */
|
||
--shadow: 0 1px 3px rgba(31, 58, 95, 0.08), 0 4px 12px rgba(31, 58, 95, 0.06);
|
||
--shadow-hover: 0 2px 6px rgba(31, 58, 95, 0.12), 0 8px 20px rgba(31, 58, 95, 0.1);
|
||
--shadow-lg: 0 4px 12px rgba(31, 58, 95, 0.1), 0 16px 40px rgba(31, 58, 95, 0.12);
|
||
|
||
/* One transition speed for hovers/focus across the site. */
|
||
--transition: 0.15s ease;
|
||
|
||
/* Width of the page frame (header, content, footer). Wide enough for card grids
|
||
side by side; running text does not follow it -- paragraphs keep their own
|
||
reading width (see p below), so lines never get uncomfortably long. */
|
||
--content-width: 72rem;
|
||
|
||
/* Type. The site font is self-hosted (see @font-face below); the system stack
|
||
stays behind it, so text shows at once and only swaps when Nunito arrives. */
|
||
--font-family: "Nunito", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
|
||
/* Type scale. h1 and h2 grow fluidly with the viewport (clamp: minimum, preferred
|
||
in vw, maximum) instead of jumping at a breakpoint, so a long German compound
|
||
word in a heading still fits a phone screen. Smaller sizes stay fixed: the
|
||
difference would not be visible. Body text sits a touch above the 1rem default
|
||
because Nunito's rounded letters read small. */
|
||
--font-size-h1: clamp(2rem, 1.6rem + 1.6vw, 2.75rem);
|
||
--font-size-h2: clamp(1.5rem, 1.35rem + 0.6vw, 1.75rem);
|
||
--font-size-h3: 1.2rem;
|
||
--font-size-body: 1.0625rem;
|
||
|
||
/* Headings: extra-bold for a friendly, confident lead, pulled slightly tighter
|
||
because heavy letters at large sizes otherwise look spaced out. */
|
||
--font-weight-heading: 800;
|
||
--letter-spacing-heading: -0.015em;
|
||
|
||
/* WCAG AA check of the colour pairs, light / dark (computed, October 2026).
|
||
Text needs 4.5:1, large text and focus rings/graphics 3:1.
|
||
text on card / page ............... 15.9 / 6.2 14.6 / 7.2
|
||
soft text on card / page .......... 7.5 / 6.2 6.9 / 7.2
|
||
soft text on green / blue tint .... 6.8 / 5.6 6.6 / 5.7
|
||
link (brand) on card / page ....... 5.4 / 7.5 5.0 / 8.7
|
||
link on green / blue tint ......... 5.0 / 6.7 4.8 / 6.9
|
||
heading on card / page ............ 5.4 / 9.3 5.0 / 10.9
|
||
white on header / gradient green .. 4.9 / 5.5 5.2 / 6.4
|
||
white on menu pill hover / active . 6.0 / 6.6 6.9 / 7.6
|
||
footer heading / link on footer ... 10.4 / 10.5 7.4 / 6.9
|
||
white on button / button hover .... 5.4 / 5.5 8.1 / 8.1
|
||
light button text ................. 8.1 / 9.7 (hover 7.2 / 8.3)
|
||
white on calendar band ............ 5.2 / 6.4
|
||
avatar blue / green ............... 7.2 / 8.6 5.9 / 6.7
|
||
key figure / caption on card ...... 5.4 / 7.5 5.0 / 6.9
|
||
focus ring on page / card ......... 7.5 / 11.0 8.1 / 9.4
|
||
white ring on header / footer ..... 4.9 / 5.5 11.4 / 16.6
|
||
green markers on card (graphic) ... 3.8 / 7.5
|
||
All pass. Exempt as pure decoration: the light green --color-accent (card
|
||
stripes, heading bars, the active-menu stroke), which never carries text. */
|
||
}
|
||
|
||
/* Dark mode: follows the visitor's OS setting automatically (prefers-color-scheme).
|
||
No toggle and no JavaScript -- the browser applies this when the operating system
|
||
is set to dark. Only the colour tokens change; layout, spacing, radius and type
|
||
stay put, so the whole site adapts from this one block. Choices: a deep blue-grey
|
||
rather than pure black; cards a touch lighter than the page so they still lift off
|
||
it; the accent nudged brighter so it doesn't glare; shadows darkened since soft
|
||
light shadows are invisible on a dark background. The header keeps its deep blue. */
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--color-brand: #7cc0ec; /* the IGMH blue lightened: headings/links need
|
||
to stay bright enough to read on the dark
|
||
page background, not just on the card */
|
||
--color-brand-dark: #a3d5f4; /* hover goes lighter, not darker, on dark */
|
||
--color-brand-tint: #223038;
|
||
|
||
--color-header: #266f9c; /* the IGMH blue eased down, to sit calmly on
|
||
dark; white on it 5.5:1 */
|
||
|
||
--color-footer: #10202e; /* a deep night blue: darker than the page, so
|
||
the band still reads as the page's end */
|
||
--color-on-footer: #c9ced6; /* the heading tone, 10:1 on the band */
|
||
--color-on-footer-soft: #a2a8b2; /* the body text tone, 6.9:1 on the band */
|
||
--color-footer-border: rgba(255, 255, 255, 0.1);
|
||
|
||
--color-accent: #86c97d; /* the IGMH green nudged brighter for dark */
|
||
--color-accent-dark: #86c97d; /* no darkening needed on a dark surface */
|
||
--color-accent-tint: #24332a; /* a dark green-grey: tinted, but the soft
|
||
body text still reads at 5.5:1 */
|
||
|
||
--color-highlight: #3a3220; /* the warm yellow as a deep amber -- a bright
|
||
yellow would glare on the dark page */
|
||
|
||
--gradient-brand: linear-gradient(135deg, var(--color-header) 0%, #2f6b33 100%);
|
||
--color-calendar-band: #2f6b33; /* the gradient's dark-mode green, 6.4:1 with white */
|
||
|
||
--color-button: #266f9c; /* the header blue: deep enough for white
|
||
text, calm on the dark page */
|
||
--color-button-hover: #1f5477;
|
||
|
||
--color-button-light: #e8eef3; /* white eased down, so it does not glare
|
||
on the darker gradient */
|
||
--color-button-light-hover: #d3dee6;
|
||
--color-on-button-light: #1a3d57; /* 9:1 on the eased white */
|
||
|
||
/* Avatar green text: the brightened accent, 6.7:1 on the dark green tint
|
||
(the blue pair follows its tokens, 8.6:1). */
|
||
--color-on-avatar-2: var(--color-accent);
|
||
|
||
--color-focus: #a3d5f4; /* light blue: a dark ring would vanish here */
|
||
|
||
--color-heading: #c9ced6; /* soft white, kept a step brighter than the
|
||
body text so headings still lead the page */
|
||
|
||
--color-text: #a2a8b2; /* eased right down to the soft tone -- full
|
||
near-white reads as glare on the dark page */
|
||
--color-text-soft: #a2a8b2;
|
||
--color-bg: #222834; /* cards / content surface */
|
||
--color-page: #171b22; /* page background, darker than the cards */
|
||
--color-surface: #1c212a;
|
||
--color-border: #333b47;
|
||
|
||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 4px 12px rgba(0, 0, 0, 0.3);
|
||
--shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.5), 0 8px 20px rgba(0, 0, 0, 0.4);
|
||
--shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.5), 0 16px 40px rgba(0, 0, 0, 0.45);
|
||
}
|
||
}
|
||
|
||
/* Nunito, served from our own wwwroot -- never from Google Fonts, which would hand
|
||
every visitor's IP to Google (see docs/recht.md). One variable file covers every
|
||
weight from 400 to 800, so body, bold and headings cost a single ~36 KB request
|
||
instead of three static files. Subset to Latin incl. German and the common
|
||
European names (ä ö ü ß é ç ğ ş ł ...), € and German quotes; anything outside falls
|
||
back to the system font. No italic file: Markdown emphasis is rare, so the browser
|
||
slanting the upright face is worth the saved request. font-display: swap shows the
|
||
system font immediately and swaps once the file is there -- text never waits.
|
||
License: SIL OFL 1.1, see fonts/OFL.txt. */
|
||
@font-face {
|
||
font-family: "Nunito";
|
||
src: url("fonts/nunito-latin-wght.woff2") format("woff2");
|
||
font-weight: 400 800;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
}
|
||
|
||
/* Base styles. */
|
||
html {
|
||
font-family: var(--font-family);
|
||
line-height: 1.65;
|
||
color: var(--color-text);
|
||
/* Tell the browser we support both schemes, so form controls, scrollbars and
|
||
the native <details> marker follow the OS setting instead of staying light. */
|
||
color-scheme: light dark;
|
||
/* Smooth-scroll for in-page anchors, with room so headings clear the sticky
|
||
header instead of hiding under it. */
|
||
scroll-padding-top: 5rem;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
/* A soft tinted page background so white cards/content lift off it. */
|
||
background: var(--color-page);
|
||
color: var(--color-text);
|
||
font-size: var(--font-size-body);
|
||
/* Full-width bands inside the content column (the home hero) reach the window
|
||
edges with 100vw, which includes the vertical scrollbar and would add a few
|
||
pixels of sideways scroll. clip, not hidden: hidden would make body a scroll
|
||
container and stop the sticky header from sticking. */
|
||
overflow-x: clip;
|
||
}
|
||
|
||
a {
|
||
color: var(--color-brand);
|
||
text-underline-offset: 2px;
|
||
transition: color var(--transition);
|
||
}
|
||
|
||
a:hover {
|
||
color: var(--color-brand-dark);
|
||
}
|
||
|
||
/* Headings: clear hierarchy and a touch more air above them than below, so
|
||
sections read as groups. */
|
||
h1, h2, h3, h4 {
|
||
line-height: 1.25;
|
||
color: var(--color-heading);
|
||
font-weight: var(--font-weight-heading);
|
||
letter-spacing: var(--letter-spacing-heading);
|
||
margin: var(--space-5) 0 var(--space-3);
|
||
}
|
||
|
||
h1 {
|
||
font-size: var(--font-size-h1);
|
||
line-height: 1.15; /* big type needs less leading, or wrapped lines drift apart */
|
||
margin-top: 0; /* the first heading on a page needs no gap above */
|
||
}
|
||
|
||
h2 {
|
||
font-size: var(--font-size-h2);
|
||
}
|
||
|
||
h3 {
|
||
font-size: var(--font-size-h3);
|
||
}
|
||
|
||
/* h4 gets its own size so it stays a step below h3 instead of falling back to the
|
||
browser default, which can render as large as (or larger than) a small h3. Body
|
||
size: below that it would read smaller than the text it heads; the extra-bold
|
||
weight alone sets it apart. */
|
||
h4 {
|
||
font-size: var(--font-size-body);
|
||
}
|
||
|
||
/* Comfortable paragraph rhythm and a capped line length for readability. */
|
||
p {
|
||
margin: 0 0 var(--space-3);
|
||
max-width: 42rem;
|
||
}
|
||
|
||
h1:focus {
|
||
outline: none;
|
||
}
|
||
|
||
/* Keyboard focus, one look for the whole site: a thick ring in --color-focus with a
|
||
little air between ring and element, so it never melts into a card's border or a
|
||
button's fill. :focus-visible, not :focus -- the ring shows for keyboard users
|
||
and stays away after a mouse click. Covers every focusable thing a page has:
|
||
links (and with them every card, which is one big link), buttons, the <summary>
|
||
of each disclosure, form fields and anything made focusable with tabindex. Dark
|
||
brand surfaces (header, hero, footer) switch the colour to
|
||
--color-focus-inverse.
|
||
tabindex="-1" is left out: such an element is never reached with Tab, only
|
||
focused by code -- the h1 by FocusOnNavigate on every page load, <main> by the
|
||
skip link. Chromium counts that as :focus-visible, and since this rule outweighs
|
||
h1:focus / main:focus below, the page title would otherwise get a ring. */
|
||
:is(a, button, summary, input, select, textarea, [tabindex]:not([tabindex="-1"])):focus-visible {
|
||
outline: 3px solid var(--color-focus);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* <main> takes focus from the skip link (tabindex="-1") only so the next Tab
|
||
continues inside the content; a ring around the whole page would just be noise. */
|
||
main:focus {
|
||
outline: none;
|
||
}
|
||
|
||
/* Skip link: the first thing on every page, so keyboard and screen-reader users
|
||
can jump past the header menu straight to the content. Off screen until it gets
|
||
focus, then it drops in at the top left over the sticky header -- a link that
|
||
is only hidden visually stays in the tab order, display:none would remove it. */
|
||
.skip-link {
|
||
position: absolute;
|
||
top: var(--space-2);
|
||
left: var(--space-2);
|
||
z-index: 100;
|
||
padding: var(--space-2) var(--space-4);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--color-button-light);
|
||
box-shadow: var(--shadow-lg);
|
||
color: var(--color-on-button-light);
|
||
font-weight: 700;
|
||
text-decoration: none;
|
||
transform: translateY(-200%);
|
||
}
|
||
|
||
/* It lands on the header, so it takes the light button look and the white ring
|
||
of the dark brand surfaces. Shown instantly, no transition: it is a jump aid,
|
||
not an effect. */
|
||
.skip-link:focus {
|
||
transform: none;
|
||
outline-color: var(--color-focus-inverse);
|
||
}
|
||
|
||
.skip-link:hover {
|
||
color: var(--color-on-button-light);
|
||
}
|
||
|
||
/* Text for screen readers only: off screen visually, still read aloud. For places
|
||
where the visible form is decoration (the calendar sheet) and the spoken form
|
||
must carry the full information. Not display:none -- that hides it from screen
|
||
readers too. */
|
||
.visually-hidden {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
margin: -1px;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
clip-path: inset(50%);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
/* Icons (Shared/Icon.razor) sit inline next to text: nudged down to line up with the
|
||
letters instead of the baseline, and never squeezed when they share a flex row
|
||
with a long label. */
|
||
.icon {
|
||
flex-shrink: 0;
|
||
vertical-align: -0.15em;
|
||
}
|
||
|
||
/* Buttons for links that are an action (the hero's "Kommende Termine", "Kontakt
|
||
aufnehmen"). Global, because the look must be the same wherever a component puts
|
||
one. Primary = filled, secondary = outlined; both carry the same 2px border, so a
|
||
primary and a secondary side by side are exactly the same height. Hover changes
|
||
colour and shadow only -- nothing moves (hover movement was removed on purpose). */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
padding: var(--space-2) var(--space-4);
|
||
border: 2px solid transparent;
|
||
border-radius: var(--radius-sm);
|
||
font-weight: 700;
|
||
line-height: 1.4;
|
||
text-decoration: none;
|
||
transition: background-color var(--transition), box-shadow var(--transition),
|
||
color var(--transition), border-color var(--transition);
|
||
}
|
||
|
||
.btn-primary {
|
||
background: var(--color-button);
|
||
border-color: var(--color-button);
|
||
color: var(--color-on-button);
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: var(--color-button-hover);
|
||
border-color: var(--color-button-hover);
|
||
color: var(--color-on-button);
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: transparent;
|
||
border-color: var(--color-button);
|
||
color: var(--color-brand);
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: var(--color-brand-tint);
|
||
color: var(--color-brand);
|
||
}
|
||
|
||
/* Quiet = no frame at all, just the brand-blue label; hover lays the blue tint
|
||
behind it. For a way back ("Alle Beiträge") that should be easy to find but not
|
||
compete with the page's content. The negative inline margin lines the label up
|
||
with the text below while the hover tint still gets its padding. */
|
||
.btn-quiet {
|
||
margin-inline: calc(-1 * var(--space-3));
|
||
padding-inline: var(--space-3);
|
||
color: var(--color-brand);
|
||
}
|
||
|
||
.btn-quiet:hover {
|
||
background: var(--color-brand-tint);
|
||
color: var(--color-brand);
|
||
}
|
||
|
||
/* Header navigation links. These live here (global), not in MainLayout.razor.css:
|
||
NavLink renders plain <a> elements without a scoped-CSS attribute, so a scoped
|
||
".main-nav a" rule would not match them -- the links would fall back to the
|
||
global link colour (dark blue) and become unreadable on the blue header bar.
|
||
Every link is a pill with the same padding whether active or not, so switching
|
||
pages never shifts the row. Full white, not a softened white: the blue end of
|
||
the gradient already leaves little contrast headroom. */
|
||
.main-nav a {
|
||
position: relative;
|
||
padding: var(--space-1) var(--space-3);
|
||
border-radius: 999px;
|
||
color: var(--color-on-header);
|
||
text-decoration: none;
|
||
transition: background-color var(--transition);
|
||
}
|
||
|
||
.main-nav a:hover {
|
||
color: var(--color-on-header);
|
||
background: var(--color-nav-hover);
|
||
}
|
||
|
||
/* The dark default ring would vanish on the blue header (2.3:1); white stands out
|
||
on the whole gradient. */
|
||
.main-nav a:focus-visible {
|
||
outline-color: var(--color-focus-inverse);
|
||
}
|
||
|
||
/* NavLink adds .active to the current route's link: a stronger pill plus a short
|
||
green stroke under the text mark where the visitor is. The stroke is drawn by
|
||
::after, not a border, so it follows the text width inside the rounded pill. */
|
||
.main-nav a.active {
|
||
background: var(--color-nav-active);
|
||
}
|
||
|
||
.main-nav a.active::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: var(--space-3);
|
||
right: var(--space-3);
|
||
bottom: 0.2rem;
|
||
height: 2px;
|
||
border-radius: 1px;
|
||
background: var(--color-accent);
|
||
}
|
||
|
||
/* Mobile: the open menu stacks the links centred, each as wide as its text -- so
|
||
they stay the same pills as on the desktop, with the stroke under the whole word.
|
||
Only taller, for a comfortable tap target; the stroke moves up with the extra
|
||
padding so it still sits right under the text, not at the pill's edge. Same
|
||
global placement, same NavLink reason as above. */
|
||
@media (max-width: 40rem) {
|
||
.main-nav a {
|
||
padding: 0.6rem var(--space-3);
|
||
}
|
||
|
||
.main-nav a.active::after {
|
||
bottom: 0.55rem;
|
||
}
|
||
}
|
||
|
||
/* --- Lists of post and event cards ------------------------------------------------
|
||
Posts and events appear both on their own page (PostList/EventList) AND embedded
|
||
on the home page (the *Embed components). The cards themselves are components
|
||
(PostCard, EventCard) with their own scoped styles; only the list around them is
|
||
shared here: no bullets, one column with even gaps. A page may widen the grid
|
||
(the posts page goes to two columns). */
|
||
|
||
.post-list,
|
||
.event-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
display: grid;
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
/* FAQ topics: each is a collapsible card. Its summary is the topic heading; open it
|
||
to reveal the optional intro and the questions (themselves collapsible .faq-item
|
||
cards). The topic marker is a rotating triangle, distinct from the +/- on the
|
||
questions nested inside, so the two accordion levels read apart. */
|
||
.faq-topic {
|
||
background: var(--color-bg);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
padding: var(--space-3) var(--space-4);
|
||
margin-bottom: var(--space-3);
|
||
transition: box-shadow var(--transition);
|
||
}
|
||
|
||
.faq-topic:hover {
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
/* A <details> with class "disclosure": the browser's own marker is replaced by a
|
||
triangle that turns down when open. Shared by the FAQ topics and the past events,
|
||
so "click to open" looks the same everywhere. */
|
||
.disclosure > summary {
|
||
cursor: pointer;
|
||
font-size: 1.15rem;
|
||
font-weight: 600;
|
||
color: var(--color-brand);
|
||
list-style: none;
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: var(--space-2);
|
||
}
|
||
|
||
.disclosure > summary::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
.disclosure > summary::before {
|
||
content: "▸";
|
||
color: var(--color-accent-dark);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.disclosure[open] > summary::before {
|
||
content: "▾";
|
||
}
|
||
|
||
/* Space the questions off the topic heading once the topic is open. */
|
||
.faq-topic[open] > .faq-item:first-of-type {
|
||
margin-top: var(--space-3);
|
||
}
|
||
|
||
.faq-topic-intro {
|
||
margin-top: var(--space-2);
|
||
color: var(--color-text-soft);
|
||
}
|
||
|
||
/* FAQ: each question is an expandable card with an accent +/– marker. */
|
||
.faq-item {
|
||
background: var(--color-bg);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
padding: var(--space-3) var(--space-4);
|
||
margin-bottom: var(--space-3);
|
||
transition: box-shadow var(--transition);
|
||
}
|
||
|
||
.faq-item:hover {
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
.faq-item summary {
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
color: var(--color-brand);
|
||
list-style: none;
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: var(--space-2);
|
||
}
|
||
|
||
.faq-item summary::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
.faq-item summary::before {
|
||
content: "+";
|
||
color: var(--color-accent-dark);
|
||
font-weight: 700;
|
||
font-size: 1.2rem;
|
||
line-height: 1;
|
||
}
|
||
|
||
.faq-item[open] summary::before {
|
||
content: "–";
|
||
}
|
||
|
||
.faq-answer {
|
||
padding-top: var(--space-2);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
/* --- Editor content (.markdown-body) -------------------------------------------
|
||
Everything the editors write in PocketBase (page bodies, posts, FAQ answers, the
|
||
intros on /posts and /events) is Markdown rendered by Shared/Markdown.cs and
|
||
wrapped in .markdown-body. Editors cannot add classes or HTML, so the look of
|
||
their text comes entirely from these rules. Every selector is scoped to
|
||
.markdown-body so nothing here leaks into the site's own components, and the
|
||
rules must hold for any Markdown an editor might type, including none at all. */
|
||
|
||
/* The line icons the content styles need, as data URIs: no request, no foreign
|
||
host. Same Lucide paths as Shared/IconSet.cs (license notice there); CSS cannot
|
||
reach that C# set, so these are copies. Drawn in black and used as a mask over
|
||
background-color: currentColor, so each icon takes the colour of its text --
|
||
dark mode included -- just like the inline-SVG <Icon> component. */
|
||
.markdown-body {
|
||
--icon-external-link: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E");
|
||
--icon-file-text: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z'/%3E%3Cpath d='M14 2v5a1 1 0 0 0 1 1h5'/%3E%3Cpath d='M10 9H8'/%3E%3Cpath d='M16 13H8'/%3E%3Cpath d='M16 17H8'/%3E%3C/svg%3E");
|
||
--icon-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4'/%3E%3Cpath d='M12 8h.01'/%3E%3C/svg%3E");
|
||
--icon-mail: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7'/%3E%3Crect x='2' y='4' width='20' height='16' rx='2'/%3E%3C/svg%3E");
|
||
}
|
||
|
||
/* A block's last element needs no gap below: the surrounding card or page already
|
||
spaces it, and a trailing margin makes boxes look bottom-heavy. */
|
||
.markdown-body > :last-child,
|
||
.markdown-body div > :last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* Section headings get a short green bar above them. The bodies are mostly a run
|
||
of ## sections, and the bar makes each start easy to spot when scrolling on a
|
||
phone. Decoration only: the heading text carries the meaning. */
|
||
.markdown-body h2::before {
|
||
content: "";
|
||
display: block;
|
||
width: 2.5rem;
|
||
height: 4px;
|
||
margin-bottom: var(--space-2);
|
||
border-radius: 2px;
|
||
background: var(--color-accent);
|
||
}
|
||
|
||
/* Lists: the reading width of paragraphs, a little air between items, and the
|
||
bullets and numbers in the green accent so lists tie into the palette. */
|
||
.markdown-body ul,
|
||
.markdown-body ol {
|
||
margin: 0 0 var(--space-3);
|
||
padding-left: 1.5rem;
|
||
max-width: 42rem;
|
||
}
|
||
|
||
.markdown-body li + li {
|
||
margin-top: var(--space-1);
|
||
}
|
||
|
||
.markdown-body li::marker {
|
||
color: var(--color-accent-dark);
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Quotes (e.g. a passage from the rules of procedure): an accent bar and a faint
|
||
green wash set them apart from the editor's own words. */
|
||
.markdown-body blockquote {
|
||
margin: 0 0 var(--space-3);
|
||
padding: var(--space-3) var(--space-4);
|
||
max-width: 42rem;
|
||
border-left: 4px solid var(--color-accent);
|
||
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
||
background: var(--color-accent-tint);
|
||
}
|
||
|
||
/* Links: a soft underline that firms up on hover. Underlined at all (not colour
|
||
alone) so links stay recognisable for colour-blind visitors. */
|
||
.markdown-body a {
|
||
text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
|
||
text-decoration-thickness: 1px;
|
||
text-underline-offset: 3px;
|
||
transition: color var(--transition), text-decoration-color var(--transition);
|
||
}
|
||
|
||
.markdown-body a:hover {
|
||
text-decoration-color: currentColor;
|
||
}
|
||
|
||
/* Links that leave the site get a small external-link icon, so visitors know
|
||
before clicking. Editors write links to our own pages as relative paths
|
||
(/contact), so "absolute http(s) URL" means "another site". PDFs are left out:
|
||
they carry their own file icon below. */
|
||
.markdown-body a:is([href^="http://"], [href^="https://"]):not([href$=".pdf" i])::after {
|
||
content: "";
|
||
display: inline-block;
|
||
width: 0.8em;
|
||
height: 0.8em;
|
||
margin-left: 0.2em;
|
||
vertical-align: -0.05em;
|
||
background-color: currentColor;
|
||
mask: var(--icon-external-link) center / contain no-repeat;
|
||
}
|
||
|
||
/* PDF links inside running text: a file icon in front and a small "PDF" tag
|
||
behind, so visitors know a download (not a web page) opens. */
|
||
.markdown-body a[href$=".pdf" i]::before {
|
||
content: "";
|
||
display: inline-block;
|
||
width: 1em;
|
||
height: 1em;
|
||
margin-right: 0.25em;
|
||
vertical-align: -0.15em;
|
||
background-color: currentColor;
|
||
mask: var(--icon-file-text) center / contain no-repeat;
|
||
}
|
||
|
||
.markdown-body a[href$=".pdf" i]::after {
|
||
content: "PDF";
|
||
margin-left: 0.35em;
|
||
padding: 0 0.35em;
|
||
border-radius: 4px;
|
||
background: var(--color-brand-tint);
|
||
color: var(--color-text-soft);
|
||
font-size: 0.75em;
|
||
font-weight: 700;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
/* A PDF link standing alone in its paragraph or list item becomes a file card;
|
||
this is what turns a plain list of documents on /downloads into a download
|
||
area. Markdown.cs marks such paragraphs/items .lone-link, because CSS cannot see
|
||
the text around a link. Two child paths: in a tight list the link sits directly
|
||
in the <li>, in a loose list inside a <p>. border-box: the 42rem include the
|
||
padding, so the card ends flush with the running text instead of overhanging
|
||
it (same for the aufruf and hinweis boxes below). */
|
||
.markdown-body .lone-link > a[href$=".pdf" i],
|
||
.markdown-body .lone-link > p > a[href$=".pdf" i] {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-3);
|
||
box-sizing: border-box;
|
||
max-width: 42rem;
|
||
padding: var(--space-3) var(--space-4);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius);
|
||
background: var(--color-bg);
|
||
box-shadow: var(--shadow);
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
transition: box-shadow var(--transition), border-color var(--transition);
|
||
}
|
||
|
||
.markdown-body .lone-link > a[href$=".pdf" i]:hover,
|
||
.markdown-body .lone-link > p > a[href$=".pdf" i]:hover {
|
||
border-color: var(--color-brand);
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
.markdown-body .lone-link > a[href$=".pdf" i]::before,
|
||
.markdown-body .lone-link > p > a[href$=".pdf" i]::before {
|
||
flex-shrink: 0;
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
margin: 0;
|
||
color: var(--color-accent-dark);
|
||
background-color: var(--color-accent-dark);
|
||
}
|
||
|
||
/* The "PDF" tag moves to the card's right edge. */
|
||
.markdown-body .lone-link > a[href$=".pdf" i]::after,
|
||
.markdown-body .lone-link > p > a[href$=".pdf" i]::after {
|
||
margin-left: auto;
|
||
}
|
||
|
||
/* A list of file cards needs no bullets and no indent: the cards are the
|
||
structure. */
|
||
.markdown-body li.lone-link:has(> a[href$=".pdf" i], > p > a[href$=".pdf" i]) {
|
||
list-style: none;
|
||
}
|
||
|
||
.markdown-body ul:has(> li.lone-link > a[href$=".pdf" i], > li.lone-link > p > a[href$=".pdf" i]) {
|
||
padding-left: 0;
|
||
}
|
||
|
||
/* Buttons: a mail link alone in its paragraph (the contact page's main action),
|
||
and every link inside an "aufruf" box. Filled with the button blue (see
|
||
--color-button for why not --color-brand); hover darkens it and adds a shadow,
|
||
nothing moves. */
|
||
.markdown-body .lone-link > a[href^="mailto:"],
|
||
.markdown-body .aufruf a {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
padding: var(--space-2) var(--space-4);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--color-button);
|
||
color: var(--color-on-button);
|
||
font-weight: 700;
|
||
text-decoration: none;
|
||
transition: background-color var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.markdown-body .lone-link > a[href^="mailto:"]:hover,
|
||
.markdown-body .aufruf a:hover {
|
||
background: var(--color-button-hover);
|
||
color: var(--color-on-button);
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
/* The mail button leads with an envelope, so it reads as "write to us" at a
|
||
glance. */
|
||
.markdown-body .lone-link > a[href^="mailto:"]::before {
|
||
content: "";
|
||
width: 1.1em;
|
||
height: 1.1em;
|
||
background-color: currentColor;
|
||
mask: var(--icon-mail) center / contain no-repeat;
|
||
}
|
||
|
||
/* --- Design blocks (::: name ... :::) ------------------------------------------
|
||
Markdown.cs turns "::: name" into <div class="name">. The names are German
|
||
because editors type them (CLAUDE.md: values German). An unknown name has no
|
||
rule here and so simply reads as normal text -- a typo never breaks a page. */
|
||
|
||
/* ::: aufruf -- a highlighted call to action: a tinted box with a brand-blue edge.
|
||
Its links become buttons (rule above). */
|
||
.markdown-body .aufruf {
|
||
box-sizing: border-box; /* padding inside the 42rem, flush with the text */
|
||
margin: 0 0 var(--space-4);
|
||
padding: var(--space-4);
|
||
max-width: 42rem;
|
||
border-left: 4px solid var(--color-brand);
|
||
border-radius: var(--radius);
|
||
background: var(--color-brand-tint);
|
||
}
|
||
|
||
/* ::: hinweis -- a quiet info box with an info icon in front. Green wash, not the
|
||
yellow highlight: it informs, it does not warn. The icon is decoration; the text
|
||
says what matters. */
|
||
.markdown-body .hinweis {
|
||
position: relative;
|
||
box-sizing: border-box; /* padding inside the 42rem, flush with the text */
|
||
margin: 0 0 var(--space-4);
|
||
padding: var(--space-3) var(--space-4) var(--space-3) 3.25rem;
|
||
max-width: 42rem;
|
||
border-radius: var(--radius);
|
||
background: var(--color-accent-tint);
|
||
}
|
||
|
||
.markdown-body .hinweis::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: calc(var(--space-3) + 0.2rem);
|
||
left: var(--space-4);
|
||
width: 1.25rem;
|
||
height: 1.25rem;
|
||
background-color: var(--color-accent-dark);
|
||
mask: var(--icon-info) center / contain no-repeat;
|
||
}
|
||
|
||
/* ::: kacheln and ::: kennzahlen -- a list laid out as a grid of cards. They keep
|
||
the 42rem reading column like the text and the other boxes: one calm left and
|
||
right edge down the page instead of blocks of three different widths. */
|
||
.markdown-body :is(.kacheln, .kennzahlen) > ul {
|
||
display: grid;
|
||
gap: var(--space-3);
|
||
box-sizing: border-box;
|
||
max-width: 42rem;
|
||
margin: 0 0 var(--space-4);
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
/* Kacheln: auto-fit with a 15rem minimum gives exactly two columns in the 42rem
|
||
(three would need 46rem), so four tiles sit as 2 x 2. Below ~31rem it drops to
|
||
one column -- two would squeeze a long word like "Chancengerechtigkeit" out of
|
||
its card. min() keeps a single card from overflowing a very narrow screen. */
|
||
.markdown-body .kacheln > ul {
|
||
grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
|
||
}
|
||
|
||
/* Kennzahlen: one row of equal columns where there is room. auto-fill used to
|
||
leave empty tracks on the right on a wide screen and one figure alone in a second
|
||
row on a phone. A narrow column switches to stacked rows instead (below). The
|
||
block is its own container, so the switch follows the width the figures actually
|
||
get, not the screen. */
|
||
.markdown-body .kennzahlen {
|
||
container-type: inline-size;
|
||
}
|
||
|
||
.markdown-body .kennzahlen > ul {
|
||
grid-auto-flow: column;
|
||
grid-auto-columns: 1fr;
|
||
}
|
||
|
||
/* The grid gap does the spacing; the list's own item spacing would only push the
|
||
later cards of the first row down. */
|
||
.markdown-body :is(.kacheln, .kennzahlen) li + li {
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* Kacheln: each item a white card; a bold start ("- **Mensa** ...") becomes the
|
||
card's title on its own line. */
|
||
.markdown-body .kacheln li {
|
||
padding: var(--space-3) var(--space-4);
|
||
border-radius: var(--radius);
|
||
background: var(--color-bg);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.markdown-body .kacheln li > strong:first-child {
|
||
display: block;
|
||
margin-bottom: var(--space-1);
|
||
color: var(--color-heading);
|
||
}
|
||
|
||
/* On a phone the tiles keep their card but tighten up, so four of them stacked
|
||
do not turn into a long scroll. */
|
||
@media (max-width: 40rem) {
|
||
.markdown-body .kacheln > ul {
|
||
gap: var(--space-2);
|
||
}
|
||
|
||
.markdown-body .kacheln li {
|
||
padding: var(--space-2) var(--space-3);
|
||
}
|
||
|
||
.markdown-body .kacheln li > strong:first-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
/* Kennzahlen: "- **seit 1975** Gründung" -- the bold part becomes a big figure,
|
||
the rest a small caption beneath. A white card with a green stroke on top, not
|
||
a green wash: a ::: hinweis often follows right below, and two green tints in a
|
||
row ran together into one green patch. Brand blue for the figure: 5.4:1 on the
|
||
card (5.0 dark), where the green would not reach 3:1. */
|
||
.markdown-body .kennzahlen li {
|
||
padding: var(--space-4) var(--space-2);
|
||
border-top: 4px solid var(--color-accent);
|
||
border-radius: var(--radius);
|
||
background: var(--color-bg);
|
||
box-shadow: var(--shadow);
|
||
color: var(--color-text-soft);
|
||
text-align: center;
|
||
}
|
||
|
||
/* The figure scales with the block (cqi, not vw): 2rem in the full 42rem, a little
|
||
less just above the switch to rows. nowrap: "seit 1975" split over two lines
|
||
reads as two figures. */
|
||
.markdown-body .kennzahlen li > strong:first-child {
|
||
display: block;
|
||
color: var(--color-brand);
|
||
font-size: clamp(1.5rem, 4.75cqi, 2rem);
|
||
font-weight: var(--font-weight-heading);
|
||
letter-spacing: var(--letter-spacing-heading);
|
||
line-height: 1.15;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Below 30rem three columns get under 10rem each: the figure no longer fits on one
|
||
line and a caption like "im Einsatz für die IGMH" stacks up word by word. So the
|
||
figures become rows of one card -- figure left, caption beside it, a thin line
|
||
between rows. Subgrid gives every row the same figure column, so the captions
|
||
line up whatever the figure's length. */
|
||
@container (max-width: 30rem) {
|
||
.markdown-body .kennzahlen > ul {
|
||
grid-auto-flow: row;
|
||
grid-template-columns: max-content 1fr;
|
||
gap: 0;
|
||
border-top: 4px solid var(--color-accent);
|
||
border-radius: var(--radius);
|
||
background: var(--color-bg);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.markdown-body .kennzahlen li {
|
||
display: grid;
|
||
grid-column: 1 / -1;
|
||
grid-template-columns: subgrid;
|
||
column-gap: var(--space-3);
|
||
align-items: baseline;
|
||
padding: var(--space-2) var(--space-3);
|
||
border-top: none;
|
||
border-radius: 0;
|
||
background: none;
|
||
box-shadow: none;
|
||
text-align: left;
|
||
}
|
||
|
||
.markdown-body .kennzahlen li + li {
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
.markdown-body .kennzahlen li > strong:first-child {
|
||
font-size: 1.35rem;
|
||
}
|
||
}
|
||
|
||
/* ::: team -- one person card per list item (Markdown.cs/TeamMember.cs build the
|
||
avatar, name, role chip, duty tags and note). Each "## Vorsitz" etc. above stays
|
||
a normal heading; every group is a block of its own. One raster for all of them,
|
||
so the cards line up across groups: a group of one person gets the same card
|
||
width as a group of six, not a stretched one. 17rem fits a long double name.
|
||
Like every other block it stays in the 42rem reading column, so the cards share
|
||
the edges of the text above and below them; that leaves room for two columns
|
||
(three would need 52.5rem), one on a phone.
|
||
The cards of a row share one height (the grid's default stretch), so a row reads
|
||
as a calm band; the card itself keeps its content at the top (align-content). */
|
||
.markdown-body .team > ul {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
|
||
gap: var(--space-3);
|
||
box-sizing: border-box;
|
||
max-width: 42rem;
|
||
margin: 0 0 var(--space-4);
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.markdown-body .team li + li {
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* The card: avatar in a column of its own, everything else stacked beside it.
|
||
Hover only deepens the shadow -- nothing moves. */
|
||
.markdown-body .team-card {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr;
|
||
column-gap: var(--space-3);
|
||
align-content: start;
|
||
padding: var(--space-3) var(--space-4);
|
||
border-radius: var(--radius);
|
||
background: var(--color-bg);
|
||
box-shadow: var(--shadow);
|
||
transition: box-shadow var(--transition);
|
||
}
|
||
|
||
.markdown-body .team-card:hover {
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
/* In a loose list (blank lines between items) Markdig wraps the card's text in a
|
||
<p>; display: contents lets its parts join the card's grid all the same. */
|
||
.markdown-body .team-card > p:first-child {
|
||
display: contents;
|
||
}
|
||
|
||
/* Everything but the avatar goes into the text column -- including whatever else
|
||
an editor puts into the item (a second paragraph, a sub-list). */
|
||
.markdown-body .team-card > :not(.team-avatar),
|
||
.markdown-body .team-card > p:first-child > :not(.team-avatar) {
|
||
grid-column: 2;
|
||
}
|
||
|
||
/* A card without the convention (no bold name) is just its text. */
|
||
.markdown-body .team-card-plain {
|
||
display: block;
|
||
}
|
||
|
||
.markdown-body .team-avatar {
|
||
grid-row: 1 / span 4;
|
||
display: grid;
|
||
place-items: center;
|
||
width: 3rem;
|
||
height: 3rem;
|
||
border-radius: 50%;
|
||
font-weight: var(--font-weight-heading);
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
.markdown-body .team-avatar-1 { background: var(--color-avatar-1); color: var(--color-on-avatar-1); }
|
||
.markdown-body .team-avatar-2 { background: var(--color-avatar-2); color: var(--color-on-avatar-2); }
|
||
|
||
.markdown-body .team-name {
|
||
color: var(--color-heading);
|
||
font-size: 1.15rem;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
/* The role as a chip under the name; justify-self keeps it as wide as its text,
|
||
not the column. */
|
||
.markdown-body .team-role {
|
||
justify-self: start;
|
||
margin-top: var(--space-1);
|
||
padding: 0.1rem var(--space-2);
|
||
border-radius: 999px;
|
||
background: var(--color-brand-tint);
|
||
color: var(--color-brand-dark);
|
||
font-size: 0.85rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Duties as small outlined tags: quieter than the role chip, because the role
|
||
says who someone is and the duties only what they look after. A tag never breaks
|
||
inside ("Stellvertretende Schulkonferenz" stays one piece); the row wraps between
|
||
tags instead. */
|
||
.markdown-body .team-duties {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-1);
|
||
margin-top: var(--space-2);
|
||
}
|
||
|
||
.markdown-body .team-duty {
|
||
white-space: nowrap;
|
||
padding: 0.05rem var(--space-2);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-sm);
|
||
color: var(--color-text-soft);
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.markdown-body .team-note {
|
||
margin-top: var(--space-2);
|
||
color: var(--color-text-soft);
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
/* ::: team gross -- the chair. Same raster and card as every other group, so the
|
||
page keeps one rhythm; only the role chip stands out, filled in the button blue
|
||
instead of the pale tint (white on it 5.4:1, in dark mode too). */
|
||
.markdown-body .team.gross .team-role {
|
||
background: var(--color-button);
|
||
color: var(--color-on-button);
|
||
}
|
||
|
||
/* Tables (pipe tables): not in the content yet, styled ahead of time. As a block
|
||
with overflow they scroll sideways on a phone instead of stretching the page. */
|
||
.markdown-body table {
|
||
display: block;
|
||
max-width: 100%;
|
||
overflow-x: auto;
|
||
margin: 0 0 var(--space-4);
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
.markdown-body th,
|
||
.markdown-body td {
|
||
padding: var(--space-2) var(--space-3);
|
||
border-bottom: 1px solid var(--color-border);
|
||
text-align: left;
|
||
vertical-align: top;
|
||
}
|
||
|
||
.markdown-body th {
|
||
background: var(--color-surface);
|
||
color: var(--color-heading);
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Images never overflow the column and get the site's rounded corners. */
|
||
.markdown-body img {
|
||
max-width: 100%;
|
||
height: auto;
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
/* A horizontal rule as a calm thin line with room around it. */
|
||
.markdown-body hr {
|
||
margin: var(--space-5) 0;
|
||
border: none;
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
/* Reduced motion: visitors who asked their system for less movement get none.
|
||
The site has no animations, only short transitions (hover colours, the
|
||
hamburger turning into an X, the mobile menu folding open); these are cut to
|
||
an instant here. Near zero rather than none, so anything that waits for a
|
||
transition to end still gets its event. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*,
|
||
::before,
|
||
::after {
|
||
transition-duration: 0.01ms !important;
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
}
|