464 lines
16 KiB
CSS
464 lines
16 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 at full strength. Its own token
|
||
(not --color-brand) both because it only ever carries white text (so it needs
|
||
no darkening) and because it must stay a deep blue in dark mode, where
|
||
--color-brand is lightened for text/links. */
|
||
--color-header: #328fc6;
|
||
|
||
/* 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%);
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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 */
|
||
|
||
--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-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);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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. */
|
||
.main-nav a {
|
||
color: rgba(255, 255, 255, 0.85);
|
||
text-decoration: none;
|
||
padding: var(--space-1) 0;
|
||
border-bottom: 2px solid transparent;
|
||
transition: color var(--transition), border-color var(--transition);
|
||
}
|
||
|
||
.main-nav a:hover {
|
||
color: #fff;
|
||
}
|
||
|
||
/* NavLink adds .active to the current route's link; a warm accent underline marks
|
||
where the visitor is on the blue bar. */
|
||
.main-nav a.active {
|
||
color: #fff;
|
||
border-bottom-color: var(--color-accent);
|
||
}
|
||
|
||
/* Mobile: stacked links inside the collapsed menu get row separators. Same global
|
||
placement, same NavLink reason as above. Scoped to the narrow layout via the
|
||
media query so it does not affect the desktop row. */
|
||
@media (max-width: 40rem) {
|
||
.main-nav a {
|
||
padding: 0.6rem 0;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||
}
|
||
|
||
.main-nav a.active {
|
||
border-bottom-color: rgba(255, 255, 255, 0.15);
|
||
}
|
||
}
|
||
|
||
/* --- Shared card styles for content lists --------------------------------------
|
||
Posts, events and FAQs appear both on their own page (PostList/EventList/
|
||
FaqList) AND embedded on the home page (the *Embed components), with the same
|
||
class names. These rules live here (global) so both places share one look
|
||
instead of duplicating the CSS per component. The card look: a white surface
|
||
lifted off the tinted page background with a soft shadow and a gentle hover. */
|
||
|
||
.post-list,
|
||
.event-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
display: grid;
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
.post-list li,
|
||
.event-list li {
|
||
background: var(--color-bg);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
transition: box-shadow var(--transition);
|
||
}
|
||
|
||
.post-list li:hover,
|
||
.event-list li:hover {
|
||
box-shadow: var(--shadow-hover);
|
||
}
|
||
|
||
/* The whole card is one link. The <a> fills the card and carries the padding (so
|
||
the entire surface is clickable, not just the text), and resets link colour and
|
||
underline so the content inside -- title, date, location -- keeps its own look. */
|
||
.card-link {
|
||
display: block;
|
||
padding: var(--space-3) var(--space-4);
|
||
color: inherit;
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* 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);
|
||
}
|
||
|
||
.faq-topic > 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);
|
||
}
|
||
|
||
.faq-topic > summary::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
.faq-topic > summary::before {
|
||
content: "▸";
|
||
color: var(--color-accent-dark);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.faq-topic[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);
|
||
}
|
||
|
||
/* Post and event cards are built the same way, so they read as one style on the
|
||
home page where both appear: the title leads in the brand colour (the whole card
|
||
is the link, so the title itself is plain text), the date sits quietly beneath. */
|
||
.post-title,
|
||
.event-title {
|
||
display: block;
|
||
font-size: 1.15rem;
|
||
font-weight: 600;
|
||
color: var(--color-brand);
|
||
}
|
||
|
||
/* The date's small, softened look comes from PostDate.razor.css / EventDate; here
|
||
only the shared spacing: nudge it down from the title above. */
|
||
.post-list time,
|
||
.event-list time {
|
||
margin-top: var(--space-1);
|
||
}
|
||
|
||
/* The location sits on its own quiet line beneath the date. */
|
||
.event-location {
|
||
display: block;
|
||
margin-top: var(--space-1);
|
||
color: var(--color-text-soft);
|
||
}
|
||
|
||
.event-note {
|
||
margin: var(--space-2) 0 0;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
/* Past events (only on the full events page): same cards, quieter and flat. */
|
||
.event-list-past li {
|
||
box-shadow: none;
|
||
border: 1px solid var(--color-border);
|
||
background: var(--color-surface);
|
||
}
|
||
|
||
.event-list-past li:hover {
|
||
box-shadow: none;
|
||
}
|
||
|
||
.event-list-past time {
|
||
color: var(--color-text-soft);
|
||
font-weight: 400;
|
||
}
|
||
|
||
.event-list-past .event-title {
|
||
font-weight: 400;
|
||
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);
|
||
}
|