Design-System: Typo-Skala schließen, h4–h6 auf Abel, Icon-Größen tokenisieren

Zweiter Teil der Konsolidierung. Vorher standen 31 font-size-Werte außerhalb
der Skala — beim Durchgehen stellte sich heraus, dass 20 davon gar keine
Textgrößen sind: der icon()-Helper rendert 1em, also steuerte font-size dort
die Icon-Größe. Dafür gibt es jetzt eine eigene, kleine Skala (--icon-s/m/l/xl
in rem, --icon-inline-sm/--icon-inline/--icon-inline-lg in em für Icons, die im
Textfluss mitwachsen sollen). Elf Ad-hoc-Werte fallen damit auf sieben Stufen.

h4–h6 laufen nicht mehr über Coolvetica. Sie lagen mit 26px und 21px unter der
--fs-650-Grenze aus CLAUDE.md, ab der der Condensed-Schnitt in Uppercase
unleserlich wird — die Regel war also nur dokumentiert, nicht durchsetzbar.
Jetzt tragen h1–h3 Coolvetica (alle über der Grenze) und h4–h6 Abel mit
Sperrung. Bezeichnend: das einzige <h4> im Markup hatte sich genau dieses
Muster bereits von Hand gebaut. Die Basisregel liefert es jetzt, die Komponente
gibt ihre drei Duplikat-Deklarationen ab — sichtbar ändert sich dadurch nichts.

Neue Stufen schließen die Lücken: --fs-550 (fluider Lead), --fs-690 (Karten-
Überschrift) und --fs-1000 (Display-Hero). Vier fast deckungsgleiche clamp()-
Rampen für Karten-Überschriften (bento__heading, bento__stat-value,
group-card__name, team-card__name) laufen jetzt über --fs-690; team-card__name
wächst dabei am Desktop um ~5px, die anderen drei bleiben im 1px-Bereich.

Die Wappen-Initialen im Matchcenter hingen an zwei Mini-Schriftgrößen (10,4 und
11,2px) unterhalb der Skala. Sie sind in Wahrheit ~0,4 × Kreisdurchmesser,
folgen jetzt also --crest-size statt einer eigenen font-size. Nebenbei fallen
drei redundante Doppel-Selektoren weg: .crest--initials trägt immer auch .crest.

Ebenfalls tokenisiert: 27 letter-spacing-Werte mit elf verschiedenen Zahlen auf
vier Stufen (--ls-tight/caps/wide/wider), maximale Abweichung 0,02em.

Danach steht kein font-size- und kein letter-spacing-Wert mehr außerhalb von
tokens.css.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
This commit is contained in:
2026-07-27 10:17:13 +02:00
parent 87055bd14b
commit 3fa521eaf1
10 changed files with 365 additions and 114 deletions

View File

@@ -1,5 +1,21 @@
# --- Front Controller ---
RewriteEngine On
# --- Kanonische Adresse: HTTPS erzwingen, nackte Domain auf www ---
# Die Formulare übertragen Name, Adresse und Geburtsdatum — nie über http.
# Der HTTPS-Redirect ist bewusst host-neutral (funktioniert auch auf einer
# Test-/Preview-Domain); nur tsv08kulmbach.de wird hart auf www gezogen.
# Drei unabhängige TLS-Indikatoren, damit kein Redirect-Loop entsteht, falls der
# Hoster die Verschlüsselung anders durchreicht als erwartet.
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
RewriteCond %{SERVER_PORT} !=443
RewriteCond %{HTTP_HOST} !^(localhost|127\.0\.0\.1)(:[0-9]+)?$ [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^tsv08kulmbach\.de$ [NC]
RewriteRule ^ https://www.tsv08kulmbach.de%{REQUEST_URI} [R=301,L]
# --- Front Controller ---
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
@@ -10,6 +26,9 @@ RewriteRule ^ index.php [L]
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
# HSTS nur über HTTPS ausliefern (1 Jahr, ohne includeSubDomains — Subdomains
# wie Webmail liegen nicht in unserer Hand).
Header always set Strict-Transport-Security "max-age=31536000" env=HTTPS
# Streng möglich, weil ausnahmslos alles self-hosted ist (CLAUDE.md Regel 1).
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'"
</IfModule>

View File

@@ -45,19 +45,40 @@ h3,
h4,
h5,
h6 {
font-family: var(--ff-heading);
font-weight: var(--fw-regular);
line-height: var(--lh-heading);
text-transform: uppercase;
text-wrap: balance;
}
/* h1h3 tragen Coolvetica: alle liegen über der 28px-Grenze (--fs-650). */
h1,
h2,
h3 {
font-family: var(--ff-heading);
}
h1 { font-size: var(--fs-900); }
h2 { font-size: var(--fs-800); }
h3 { font-size: var(--fs-700); }
h4 { font-size: 1.625rem; }
/* h4h6 laufen über Abel statt Coolvetica. Coolvetica ist Condensed mit nur
einem Schnitt und wird in Uppercase unter --fs-650 (28px) unleserlich — diese
Ebenen liegen aber naturgemäß darunter. Abel + Sperrung hält sie lesbar und
trotzdem als Auszeichnung erkennbar; die Regel aus CLAUDE.md kann hier damit
gar nicht mehr verletzt werden. Zeilenhöhe zurück auf Fließtext-Maß, weil
--lh-heading (0.9) nur für die extremen Coolvetica-Größen gedacht ist. */
h4,
h5,
h6 { font-size: var(--fs-600); }
h6 {
font-family: var(--ff-body);
line-height: 1.2;
letter-spacing: var(--ls-caps);
}
h4 { font-size: var(--fs-650); }
h5 { font-size: var(--fs-600); }
h6 { font-size: var(--fs-500); }
a {

View File

@@ -90,7 +90,7 @@
display: block;
margin-top: var(--space-xs);
font-size: var(--fs-600);
letter-spacing: 0.14em;
letter-spacing: var(--ls-wide);
}
.hero__text {
@@ -155,14 +155,14 @@
margin-bottom: var(--space-m);
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.18em;
letter-spacing: var(--ls-wider);
/* Sperrung optisch kompensieren (Spacing nach letztem Zeichen). */
text-indent: 0.18em;
color: rgb(255 255 255 / 0.85);
}
.hero--display .hero__title {
font-size: clamp(3.5rem, 2rem + 7.5vw, 7.5rem);
font-size: var(--fs-1000);
}
/* Subline ruhig in Abel, Groß-/Kleinschreibung — konkurriert nicht mehr mit
@@ -170,7 +170,7 @@
.hero--display .hero__tagline {
margin-top: var(--space-xs);
font-family: var(--ff-body);
font-size: clamp(1.25rem, 0.95rem + 1.3vw, 1.75rem);
font-size: var(--fs-550);
font-weight: 400;
letter-spacing: 0;
text-indent: 0;
@@ -189,7 +189,7 @@
width: 44px;
height: 44px;
color: var(--clr-text-muted);
font-size: 1.4rem;
font-size: var(--icon-l);
transition: color var(--transition);
animation: scroll-cue-bounce 2.4s ease-in-out infinite;
}
@@ -277,7 +277,7 @@
height: 2rem;
background: rgb(0 0 0 / 0.6);
border-radius: 50%;
font-size: 0.8rem;
font-size: var(--fs-300);
color: var(--clr-text);
}
@@ -432,7 +432,7 @@
.stats__label {
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
}
@@ -473,7 +473,7 @@
font-family: var(--ff-heading);
/* Coolvetica unter ~28px wird unleserlich (Projekt-Faustregel) */
font-size: var(--fs-650);
letter-spacing: 0.06em;
letter-spacing: var(--ls-caps);
text-transform: uppercase;
text-decoration: none;
color: var(--clr-text-muted);
@@ -680,7 +680,7 @@
border-radius: 8px;
display: grid;
place-items: center;
font-size: 1.1rem;
font-size: var(--icon-s);
color: #fff;
margin-top: var(--space-3xs);
}
@@ -758,7 +758,7 @@
font-family: var(--ff-body);
font-size: var(--fs-300);
font-weight: var(--fw-bold);
letter-spacing: 0.12em;
letter-spacing: var(--ls-wide);
text-transform: uppercase;
color: var(--clr-text-muted);
margin: 0 0 var(--space-s);
@@ -771,7 +771,7 @@
border-radius: 8px;
display: grid;
place-items: center;
font-size: 1.25rem;
font-size: var(--icon-m);
color: #fff;
margin-bottom: var(--space-m);
}
@@ -817,7 +817,7 @@
.sponsor-channels__check {
flex-shrink: 0;
font-size: 1rem;
font-size: var(--icon-s);
color: var(--clr-accent);
}
@@ -942,7 +942,7 @@
.person-card__department {
font-size: var(--fs-650);
letter-spacing: 0.04em;
letter-spacing: var(--ls-tight);
}
.person-card__name {
@@ -1109,7 +1109,7 @@
border-radius: 8px;
display: grid;
place-items: center;
font-size: 1.15rem;
font-size: var(--icon-m);
color: #fff;
}
@@ -1123,7 +1123,7 @@
font-family: var(--ff-body);
font-size: var(--fs-300);
font-weight: var(--fw-bold);
letter-spacing: 0.12em;
letter-spacing: var(--ls-wide);
text-transform: uppercase;
color: var(--clr-text-muted);
margin: 0 0 var(--space-3xs);
@@ -1139,7 +1139,7 @@
.position-card__chevron {
flex: none;
font-size: 0.9rem;
font-size: var(--icon-s);
color: var(--clr-text-muted);
transition: transform var(--transition), color var(--transition);
}
@@ -1190,7 +1190,7 @@
.position-card__check {
flex-shrink: 0;
margin-top: 0.2em;
font-size: 1rem;
font-size: var(--icon-s);
color: var(--clr-accent);
}
@@ -1211,7 +1211,7 @@
gap: var(--space-2xs);
font-size: var(--fs-300);
font-weight: var(--fw-bold);
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
text-transform: uppercase;
color: var(--clr-text-muted);
}
@@ -1457,7 +1457,7 @@
padding: 0 var(--space-2xs);
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: var(--clr-text);
}
@@ -1490,7 +1490,7 @@
.ad-banner__meta-label {
font-size: var(--fs-400);
color: var(--clr-text-muted);
letter-spacing: 0.03em;
letter-spacing: var(--ls-tight);
}
.ad-banner__slide[hidden] {
@@ -1660,7 +1660,7 @@
padding: 0.2em 0.7em;
font-size: var(--fs-300);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
background: rgb(0 0 0 / 0.45);
border: 1px solid rgb(255 255 255 / 0.18);
@@ -1754,7 +1754,7 @@
width: 2.25rem;
height: 2.25rem;
padding: 0;
font-size: 1rem;
font-size: var(--icon-s);
color: var(--clr-text);
background: var(--clr-glass-bg);
border: 1px solid var(--clr-glass-border);
@@ -1782,7 +1782,7 @@
}
.ad-banner__toggle-icon {
font-size: 1rem;
font-size: var(--icon-s);
}
/* Pause-Icon im Abspiel-Zustand, Play-Icon wenn pausiert. */
@@ -1878,7 +1878,7 @@
.key-facts__icon {
display: inline-flex;
font-size: 1.5rem;
font-size: var(--icon-l);
color: var(--clr-accent);
}
@@ -1890,7 +1890,7 @@
.key-facts__label {
font-size: var(--fs-400);
text-transform: uppercase;
letter-spacing: 0.06em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
}
@@ -1960,13 +1960,13 @@
align-items: center;
gap: var(--space-xs);
margin-bottom: var(--space-l);
font-size: clamp(2rem, 1.6rem + 1.2vw, 2.6rem);
font-size: var(--fs-690);
line-height: var(--lh-heading);
}
.bento__hicon {
flex: none;
font-size: 1.6rem;
font-size: var(--icon-l);
color: var(--clr-accent);
}
@@ -2012,13 +2012,13 @@
.bento__stat-label {
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: rgb(255 255 255 / 0.85);
}
.bento__stat-value {
font-family: var(--ff-heading);
font-size: clamp(2rem, 1.5rem + 2vw, 2.8rem);
font-size: var(--fs-690);
line-height: var(--lh-heading);
color: #fff;
}
@@ -2034,7 +2034,7 @@
right: -1rem;
bottom: -1.75rem;
z-index: 0;
font-size: 9rem;
font-size: var(--fs-deco);
line-height: 1;
color: var(--clr-text);
opacity: 0.06;
@@ -2120,7 +2120,7 @@
.turnen-dash__notice-icon {
flex: none;
margin-top: 0.1em;
font-size: 1.5rem;
font-size: var(--icon-l);
}
/* Trainingsort: Adresse links, Route-Button rechts (wie team-bento__venue). */
@@ -2322,7 +2322,7 @@
}
.group-card__name {
font-size: clamp(2.2rem, 1.7rem + 1.5vw, 2.8rem);
font-size: var(--fs-690);
line-height: var(--lh-heading);
}
@@ -2331,7 +2331,7 @@
padding: var(--space-4xs) var(--space-2xs);
font-size: var(--fs-400);
text-transform: uppercase;
letter-spacing: 0.07em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
border: 1px solid var(--clr-glass-border-strong);
border-radius: 999px;
@@ -2347,15 +2347,13 @@
}
/* Detail-Abschnitte: Akzent-Randstrich links als visueller Anker */
/* Font-Familie, Uppercase und Sperrung kommen jetzt aus der h4-Basisregel —
hier bleibt nur, was diese Überschrift von der Basis unterscheidet. */
.group-card__detail-heading {
margin: var(--space-3xl) 0 0;
padding-left: var(--space-xs);
border-left: 2px solid var(--clr-accent);
font-family: var(--ff-body);
font-size: var(--fs-500);
font-weight: 400;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--clr-text);
}
@@ -2398,7 +2396,7 @@
gap: var(--space-2xs);
margin: 0 0 var(--space-s);
font-size: var(--fs-650);
letter-spacing: 0.01em;
letter-spacing: var(--ls-tight);
}
.group-card__schedule-icon {
@@ -2439,7 +2437,7 @@
margin-bottom: var(--space-3xs);
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.07em;
letter-spacing: var(--ls-caps);
color: var(--clr-accent);
font-family: var(--ff-body);
}
@@ -2563,7 +2561,7 @@
.faq__chevron {
flex: none;
font-size: 0.9rem;
font-size: var(--icon-s);
color: var(--clr-text-muted);
transition: transform var(--transition), color var(--transition);
}
@@ -2696,7 +2694,7 @@
padding: var(--space-3xs) var(--space-xs);
font-family: var(--ff-body);
font-size: var(--fs-400);
letter-spacing: 0.12em;
letter-spacing: var(--ls-wide);
text-transform: uppercase;
color: #fff;
background: var(--clr-accent);
@@ -2777,14 +2775,14 @@
.team-card__name {
/* Coolvetica/Heading (siehe CLAUDE.md) — bewusst groß und prägnant. */
font-size: clamp(1.9rem, 1.4rem + 1.4vw, 2.4rem);
font-size: var(--fs-690);
line-height: var(--lh-heading);
}
.team-card__meta {
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.06em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
}
@@ -2898,7 +2896,7 @@
.roster-card__role {
font-size: var(--fs-400);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: var(--clr-accent);
}
@@ -3253,7 +3251,7 @@
margin-bottom: var(--space-m);
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.18em;
letter-spacing: var(--ls-wider);
}
.legal__kicker::before {
@@ -3399,7 +3397,7 @@
.department-choice__subtitle {
font-size: var(--fs-500);
text-transform: uppercase;
letter-spacing: 0.1em;
letter-spacing: var(--ls-wide);
color: var(--clr-accent);
}
@@ -3417,7 +3415,7 @@
}
.department-choice__arrow {
font-size: 0.9em;
font-size: var(--icon-inline);
}
/* === fee-table (Beitragstabellen) === */
@@ -3547,7 +3545,7 @@
.membership-notes__icon {
flex: none;
margin-top: var(--space-4xs);
font-size: 1.5rem;
font-size: var(--icon-l);
line-height: 1;
color: var(--clr-accent);
}
@@ -3578,7 +3576,7 @@
}
.membership-download__icon {
font-size: 0.9em;
font-size: var(--icon-inline);
}
.membership-download__hint,
@@ -3631,11 +3629,15 @@
/* Vereinswappen — gemeinsam für Karten und Tabelle; fehlt das Bild, rendert die
Komponente einen Initialen-Kreis (.crest--initials) statt eines kaputten <img>. */
/* Weißes Chip hält auch helle/weiße Logos auf dunklem Grund sichtbar. */
/* Größe läuft über --crest-size: Kontexte setzen nur diese eine Variable, Breite,
Höhe und die Initialen-Schriftgröße folgen daraus. .crest--initials trägt immer
auch .crest, deshalb genügt überall der Basis-Selektor. */
.crest {
--crest-size: 2.25rem;
box-sizing: border-box;
flex: none;
width: 2.25rem;
height: 2.25rem;
width: var(--crest-size);
height: var(--crest-size);
padding: 3px;
object-fit: contain;
background: #fff;
@@ -3647,7 +3649,10 @@
place-items: center;
padding: 0;
font-family: var(--ff-body);
font-size: var(--fs-300);
/* Initialen als Anteil des Durchmessers statt als eigene Stufe der Typo-Skala:
so bleibt der Kreis in jeder Größe gleich proportioniert, und es entstehen
keine Mini-Schriftgrößen unterhalb von --fs-300. */
font-size: calc(var(--crest-size) * 0.4);
font-weight: var(--fw-bold);
line-height: 1;
color: var(--clr-text-muted);
@@ -3673,7 +3678,7 @@
gap: 0.5em;
font-size: var(--fs-400);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
}
@@ -3694,11 +3699,8 @@
min-width: 0; /* lange Vereinsnamen dürfen umbrechen statt zu überlaufen */
}
.match-feature .crest,
.match-feature .crest--initials {
width: clamp(2.25rem, 1.6rem + 2vw, 3rem);
height: clamp(2.25rem, 1.6rem + 2vw, 3rem);
font-size: var(--fs-400);
.match-feature .crest {
--crest-size: clamp(2.25rem, 1.6rem + 2vw, 3rem);
}
/* Die Vereinsnamen sind das einzige Coolvetica-Element („wer"), alles Beschreibende
@@ -3747,7 +3749,7 @@
font-family: var(--ff-body);
font-size: var(--fs-400);
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-muted);
}
@@ -3894,7 +3896,7 @@ a.match-row__link:hover {
.match-row__meta {
font-size: var(--fs-300);
text-transform: uppercase;
letter-spacing: 0.06em;
letter-spacing: var(--ls-caps);
color: var(--clr-text-faint);
}
@@ -3912,11 +3914,8 @@ a.match-row__link:hover {
min-width: 0;
}
.match-row__team .crest,
.match-row__team .crest--initials {
width: 1.6rem;
height: 1.6rem;
font-size: 0.65rem;
.match-row__team .crest {
--crest-size: 1.6rem;
}
.match-row__name {
@@ -4006,7 +4005,7 @@ a.match-row__link:hover {
font-weight: var(--fw-bold);
font-size: var(--fs-300);
text-transform: uppercase;
letter-spacing: 0.04em;
letter-spacing: var(--ls-tight);
color: var(--clr-text-muted);
white-space: nowrap;
}
@@ -4034,11 +4033,8 @@ a.match-row__link:hover {
font-weight: var(--fw-regular);
}
.league-table tbody .crest,
.league-table tbody .crest--initials {
width: 1.75rem;
height: 1.75rem;
font-size: 0.7rem;
.league-table tbody .crest {
--crest-size: 1.75rem;
}
/* Lange Vereinsnamen sauber kürzen (voller Name im title-Attribut). */
@@ -4143,7 +4139,7 @@ a.match-row__link:hover {
}
.matchcenter-empty__icon {
font-size: 2.5rem;
font-size: var(--icon-xl);
color: var(--clr-accent);
}
@@ -4231,7 +4227,7 @@ a.match-row__link:hover {
.sportheim-bento__addr-label {
font-size: var(--fs-300);
font-weight: var(--fw-bold);
letter-spacing: 0.1em;
letter-spacing: var(--ls-wide);
text-transform: uppercase;
color: var(--clr-text-muted);
margin: 0 0 var(--space-xs);

View File

@@ -142,7 +142,7 @@
}
.site-nav__chevron {
font-size: 0.7em;
font-size: var(--icon-inline-sm);
transition: transform var(--transition);
}
@@ -508,7 +508,7 @@
width: 48px;
height: 48px;
color: var(--clr-text);
font-size: 1.25rem;
font-size: var(--icon-m);
background: rgb(10 10 10 / 0.82);
border: 1px solid var(--clr-glass-border-strong);
border-radius: 50%;
@@ -607,7 +607,7 @@
}
.site-footer__social .icon {
font-size: 1.25em;
font-size: var(--icon-inline-lg);
color: var(--clr-text);
}
@@ -615,7 +615,7 @@
braucht hier mehr Größe + Laufweite für Lesbarkeit */
.site-footer__heading {
font-size: var(--fs-650);
letter-spacing: 0.06em;
letter-spacing: var(--ls-caps);
margin-bottom: var(--space-s);
padding-bottom: var(--space-2xs);
position: relative;

View File

@@ -32,18 +32,41 @@
--fs-300: 0.8125rem; /* 13px Buttons klein */
--fs-400: 0.9375rem; /* 15px Footer, Meta */
--fs-500: 1.125rem; /* 18px Navigation */
--fs-550: clamp(1.25rem, 0.95rem + 1.3vw, 1.75rem); /* 2028px fluider Lead/Tagline */
--fs-600: 1.3125rem; /* 21px Body */
--fs-650: 1.75rem; /* 28px Zwischengröße (z.B. Footer-Headings) */
--fs-675: 2.125rem; /* 34px Header-Wortmarke */
--fs-690: clamp(2rem, 1.5rem + 1.6vw, 2.75rem); /* 3244px Karten-/Kachel-Überschrift */
--fs-700: clamp(2rem, 1.2rem + 3.5vw, 3.125rem); /* 50px h3 */
/* Mobil-Minima bewusst hoch: Coolvetica Condensed wirkt bei gleicher
nomineller Größe deutlich kleiner als normale Schnitte. */
--fs-800: clamp(2.625rem, 1.3rem + 4.5vw, 3.75rem); /* 4260px h2 */
--fs-900: clamp(3.5rem, 1.75rem + 5.5vw, 6rem); /* 5696px h1 */
--fs-1000: clamp(3.5rem, 2rem + 7.5vw, 7.5rem); /* 56120px Display-Hero (nur Startseite) */
--fs-deco: 9rem; /* dekorative Ziffer hinter Bento-Kacheln */
--lh-heading: 0.9;
--lh-body: 1.5;
--fw-regular: 400;
--fw-bold: 700;
/* Sperrung. Coolvetica ist Condensed und hat nur einen Schnitt: unter 28px
wird es in Uppercase unleserlich (siehe CLAUDE.md). Kleinere Auszeichnungen
laufen deshalb über --ff-body + uppercase + --ls-caps statt --ff-heading. */
--ls-tight: 0.03em; /* dezente Straffung in Fließtext-Größen */
--ls-caps: 0.08em; /* Standard für gesperrte Uppercase-Auszeichnungen */
--ls-wide: 0.12em; /* kleine Labels, Meta-Zeilen */
--ls-wider: 0.18em; /* sehr kleine Kicker/Overlines */
/* --- Icon-Größen ---
Der icon()-Helper rendert 1em, die font-size steuert also die Icon-Größe.
rem-Stufen für eigenständige Icons, em-Stufen für Icons, die im Textfluss
mit ihrer Zeile mitwachsen sollen (Chevrons, Pfeile). */
--icon-s: 1rem; /* 16px */
--icon-m: 1.25rem; /* 20px */
--icon-l: 1.5rem; /* 24px */
--icon-xl: 2.5rem; /* 40px */
--icon-inline-sm: 0.75em;
--icon-inline: 0.9em;
--icon-inline-lg: 1.25em;
/* --- Spacing ---
4px-Raster. EINZIGE Quelle für padding/margin/gap — keine rohen rem-Werte