Design round: logo colours, own fonts, blob shapes, broken grid

Prototype for review, not deployed. Newsreader + Schibsted Grotesk
(served locally, italic included), palette from the logo (petrol, sage,
sand, rose) instead of gold, the logo blob as a recurring shape, petrol
bands with a wave edge, margin-note section labels and uneven columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lara 2026-10-06 12:09:28 +02:00
parent 3ae5f815e8
commit f000d64cca
18 changed files with 569 additions and 170 deletions

View file

@ -10,6 +10,7 @@ const FONT_OPTIONS = [
{ name: 'Merriweather', fallback: "Georgia, 'Times New Roman', serif" }, { name: 'Merriweather', fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Libre Baskerville', fallback: "Georgia, 'Times New Roman', serif" }, { name: 'Libre Baskerville', fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'DM Serif Display', fallback: "Georgia, 'Times New Roman', serif" }, { name: 'DM Serif Display', fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Newsreader', fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Georgia (Systemschrift)', fallback: "Georgia, 'Times New Roman', serif" }, { name: 'Georgia (Systemschrift)', fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Inter', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Inter', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Roboto', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Roboto', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
@ -19,6 +20,7 @@ const FONT_OPTIONS = [
{ name: 'Nunito', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Nunito', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Work Sans', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Work Sans', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Source Sans 3', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Source Sans 3', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Schibsted Grotesk', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'System UI (Systemschrift)', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'System UI (Systemschrift)', fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
]; ];

View file

@ -64,7 +64,7 @@
.flatMap(([name, list]) => .flatMap(([name, list]) =>
list.map( list.map(
(face) => (face) =>
`@font-face { font-family: '${name}'; font-weight: ${face.weight}; font-display: swap; ` + `@font-face { font-family: '${name}'; font-style: ${face.style || 'normal'}; font-weight: ${face.weight}; font-display: swap; ` +
`src: url('/admin${face.file}') format('woff2'); unicode-range: ${face.unicodeRange}; }`, `src: url('/admin${face.file}') format('woff2'); unicode-range: ${face.unicodeRange}; }`,
), ),
) )

View file

@ -1,7 +1,7 @@
{ {
"fonts": { "fonts": {
"heading": "Playfair Display", "heading": "Newsreader",
"body": "Inter" "body": "Schibsted Grotesk"
}, },
"fontSizes": { "fontSizes": {
"sm": "0.875rem", "sm": "0.875rem",
@ -20,9 +20,9 @@
"a": 1 "a": 1
}, },
"bgAlt": { "bgAlt": {
"r": 244, "r": 233,
"g": 239, "g": 239,
"b": 228, "b": 234,
"a": 1 "a": 1
}, },
"surface": { "surface": {
@ -32,57 +32,57 @@
"a": 1 "a": 1
}, },
"text": { "text": {
"r": 28, "r": 26,
"g": 24, "g": 38,
"b": 19, "b": 34,
"a": 1 "a": 1
}, },
"textMuted": { "textMuted": {
"r": 107, "r": 78,
"g": 97, "g": 93,
"b": 84, "b": 87,
"a": 1 "a": 1
}, },
"border": { "border": {
"r": 220, "r": 185,
"g": 207, "g": 198,
"b": 182, "b": 190,
"a": 1 "a": 1
}, },
"primary": { "primary": {
"r": 122, "r": 44,
"g": 93, "g": 74,
"b": 31, "b": 66,
"a": 1 "a": 1
}, },
"primaryDark": { "primaryDark": {
"r": 94, "r": 31,
"g": 71, "g": 53,
"b": 22, "b": 48,
"a": 1 "a": 1
}, },
"primaryContrast": { "primaryContrast": {
"r": 255, "r": 252,
"g": 253, "g": 250,
"b": 248, "b": 246,
"a": 1 "a": 1
}, },
"accent": { "accent": {
"r": 122, "r": 142,
"g": 93, "g": 79,
"b": 31, "b": 63,
"a": 1 "a": 1
}, },
"accentDark": { "accentDark": {
"r": 94, "r": 115,
"g": 71, "g": 62,
"b": 22, "b": 49,
"a": 1 "a": 1
}, },
"success": { "success": {
"r": 79, "r": 63,
"g": 107, "g": 107,
"b": 66, "b": 85,
"a": 1 "a": 1
}, },
"danger": { "danger": {
@ -101,7 +101,7 @@
"r": 138, "r": 138,
"g": 59, "g": 59,
"b": 34, "b": 34,
"a": 0.1 "a": 1
}, },
"googleBlue": { "googleBlue": {
"r": 26, "r": 26,

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M6 52 C6 26 24 8 52 6 C76 4 94 24 94 48 C94 74 76 96 48 94 C24 92 6 76 6 52 Z"/></svg>

After

Width:  |  Height:  |  Size: 185 B

1
public/form/fleck.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><defs><linearGradient id="v" gradientUnits="userSpaceOnUse" x1="4" y1="96" x2="96" y2="4"><stop offset="0" stop-color="#B3C4B6"/><stop offset=".48" stop-color="#CFCBAC"/><stop offset="1" stop-color="#D9C0B4"/></linearGradient></defs><path d="M6 52 C6 26 24 8 52 6 C76 4 94 24 94 48 C94 74 76 96 48 94 C24 92 6 76 6 52 Z" fill="url(#v)" stroke="#2C4A42" stroke-width="1.6" vector-effect="non-scaling-stroke"/></svg>

After

Width:  |  Height:  |  Size: 504 B

1
public/form/pille.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 502 240" preserveAspectRatio="none"><defs><linearGradient id="v" gradientUnits="userSpaceOnUse" x1="20" y1="220" x2="490" y2="20"><stop offset="0" stop-color="#B3C4B6"/><stop offset=".48" stop-color="#CFCBAC"/><stop offset="1" stop-color="#D9C0B4"/></linearGradient></defs><path d="M10 120 C10 74 40 40 92 32 C192 10 315 56 408 30 C468 18 492 60 492 120 C492 180 468 222 408 210 C315 184 192 230 92 208 C40 200 10 166 10 120 Z" fill="url(#v)"/></svg>

After

Width:  |  Height:  |  Size: 504 B

View file

@ -1,30 +1,41 @@
import re, json, urllib.request, urllib.error, pathlib # Aufruf: python scripts/fonts-holen.py -> alle Schriften neu holen
# python scripts/fonts-holen.py "Name" ... -> nur diese, Manifest wird ergänzt
import re, json, sys, urllib.request, urllib.error, pathlib
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36" UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36"
FAMS = ['Playfair Display','Lora','Merriweather','Libre Baskerville','DM Serif Display','Inter','Roboto','Open Sans','Poppins','Montserrat','Nunito','Work Sans','Source Sans 3'] FAMS = ['Playfair Display','Lora','Merriweather','Libre Baskerville','DM Serif Display','Inter','Roboto','Open Sans','Poppins','Montserrat','Nunito','Work Sans','Source Sans 3','Newsreader','Schibsted Grotesk']
# Diese Schriften auch kursiv holen (echte Kursive statt schräg gerechneter)
ITAL = {'Newsreader'}
manifest_path = pathlib.Path('src/lib/font-faces.json')
if sys.argv[1:]:
FAMS = sys.argv[1:]
out_dir = pathlib.Path('public/fonts'); out_dir.mkdir(parents=True, exist_ok=True) out_dir = pathlib.Path('public/fonts'); out_dir.mkdir(parents=True, exist_ok=True)
manifest = {} manifest = json.load(open(manifest_path, encoding='utf8')) if sys.argv[1:] else {}
def get(url): def get(url):
return urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': UA})).read() return urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': UA})).read()
for fam in FAMS: for fam in FAMS:
css = None css = None
for w in ['400;500;600;700', '400;700', '400']: for w in ['400;500;600;700', '400;700', '400']:
axis = 'wght'
if fam in ITAL:
axis = 'ital,wght'; w = ';'.join([f'0,{x}' for x in w.split(';')] + [f'1,{x}' for x in w.split(';')])
try: try:
css = get(f"https://fonts.googleapis.com/css2?family={fam.replace(' ','+')}:wght@{w}&display=swap").decode(); break css = get(f"https://fonts.googleapis.com/css2?family={fam.replace(' ','+')}:{axis}@{w}&display=swap").decode(); break
except urllib.error.HTTPError: pass except urllib.error.HTTPError: pass
slug = fam.lower().replace(' ', '-'); d = out_dir / slug; d.mkdir(exist_ok=True) slug = fam.lower().replace(' ', '-'); d = out_dir / slug; d.mkdir(exist_ok=True)
faces = []; seen = {} faces = []; seen = {}
for subset, body in re.findall(r'/\* ([\w-]+) \*/\s*@font-face \{(.*?)\}', css, re.S): for subset, body in re.findall(r'/\* ([\w-]+) \*/\s*@font-face \{(.*?)\}', css, re.S):
if subset not in ('latin', 'latin-ext'): continue if subset not in ('latin', 'latin-ext'): continue
weight = re.search(r'font-weight: (\d+)', body).group(1) weight = re.search(r'font-weight: (\d+)', body).group(1)
style = re.search(r'font-style: (\w+)', body).group(1)
url = re.search(r'url\((.*?)\)', body).group(1) url = re.search(r'url\((.*?)\)', body).group(1)
rng = re.search(r'unicode-range: (.*?);', body).group(1) rng = re.search(r'unicode-range: (.*?);', body).group(1)
if url not in seen: if url not in seen:
fn = f"{slug}-{subset}-{len(seen)}.woff2"; (d / fn).write_bytes(get(url)); seen[url] = fn fn = f"{slug}-{subset}-{len(seen)}.woff2"; (d / fn).write_bytes(get(url)); seen[url] = fn
faces.append({'weight': int(weight), 'subset': subset, 'file': f"/fonts/{slug}/{seen[url]}", 'unicodeRange': rng}) faces.append({'weight': int(weight), 'style': style, 'subset': subset, 'file': f"/fonts/{slug}/{seen[url]}", 'unicodeRange': rng})
# gleiche Datei fuer mehrere Gewichte (variable Schrift) -> eine Regel mit Gewichtsbereich # gleiche Datei fuer mehrere Gewichte (variable Schrift) -> eine Regel mit Gewichtsbereich
merged = {} merged = {}
for f in faces: for f in faces:
k = (f['file'], f['subset']); merged.setdefault(k, dict(f, weights=[]))['weights'].append(f['weight']) k = (f['file'], f['subset'], f['style']); merged.setdefault(k, dict(f, weights=[]))['weights'].append(f['weight'])
manifest[fam] = [{'file': v['file'], 'weight': f"{min(v['weights'])} {max(v['weights'])}" if len(v['weights']) > 1 else str(v['weights'][0]), 'unicodeRange': v['unicodeRange']} for v in merged.values()] manifest[fam] = [dict({'file': v['file'], 'weight': f"{min(v['weights'])} {max(v['weights'])}" if len(v['weights']) > 1 else str(v['weights'][0]), 'unicodeRange': v['unicodeRange']}, **({'style': 'italic'} if v['style'] == 'italic' else {})) for v in merged.values()]
print(fam, len(manifest[fam]), 'Regeln', len(seen), 'Dateien') print(fam, len(manifest[fam]), 'Regeln', len(seen), 'Dateien')
json.dump(manifest, open('src/lib/font-faces.json', 'w', encoding='utf8'), indent=2) json.dump(manifest, open(manifest_path, 'w', encoding='utf8'), indent=2)

View file

@ -0,0 +1,22 @@
---
/**
* Abschnittskennung als Randnotiz: "02 · Was uns unterscheidet" wird zu
* großer Ziffer im Fleck plus kleiner Zeile darunter. Ohne Ziffer am Anfang
* bleibt es eine einfache Zeile.
*/
interface Props {
text: string;
}
const { text } = Astro.props;
const match = text.match(/^(\d+)\s*·\s*(.+)$/);
---
{match ? (
<p class="eyebrow eyebrow-rand">
<span class="eyebrow-num">{match[1]}</span>
<span class="eyebrow-text">{match[2]}</span>
</p>
) : (
<p class="eyebrow">{text}</p>
)}

View file

@ -184,5 +184,41 @@
"weight": "400 700", "weight": "400 700",
"unicodeRange": "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" "unicodeRange": "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
} }
],
"Newsreader": [
{
"file": "/fonts/newsreader/newsreader-latin-ext-0.woff2",
"weight": "400 700",
"unicodeRange": "U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF",
"style": "italic"
},
{
"file": "/fonts/newsreader/newsreader-latin-1.woff2",
"weight": "400 700",
"unicodeRange": "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD",
"style": "italic"
},
{
"file": "/fonts/newsreader/newsreader-latin-ext-2.woff2",
"weight": "400 700",
"unicodeRange": "U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF"
},
{
"file": "/fonts/newsreader/newsreader-latin-3.woff2",
"weight": "400 700",
"unicodeRange": "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
}
],
"Schibsted Grotesk": [
{
"file": "/fonts/schibsted-grotesk/schibsted-grotesk-latin-ext-0.woff2",
"weight": "400 700",
"unicodeRange": "U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF"
},
{
"file": "/fonts/schibsted-grotesk/schibsted-grotesk-latin-1.woff2",
"weight": "400 700",
"unicodeRange": "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
}
] ]
} }

View file

@ -18,6 +18,7 @@ export const FONT_OPTIONS: FontOption[] = [
{ name: 'Merriweather', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" }, { name: 'Merriweather', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Libre Baskerville', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" }, { name: 'Libre Baskerville', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'DM Serif Display', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" }, { name: 'DM Serif Display', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Newsreader', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Georgia (Systemschrift)', category: 'serif', google: false, fallback: "Georgia, 'Times New Roman', serif" }, { name: 'Georgia (Systemschrift)', category: 'serif', google: false, fallback: "Georgia, 'Times New Roman', serif" },
{ name: 'Inter', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Inter', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Roboto', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Roboto', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
@ -27,6 +28,7 @@ export const FONT_OPTIONS: FontOption[] = [
{ name: 'Nunito', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Nunito', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Work Sans', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Work Sans', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Source Sans 3', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'Source Sans 3', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'Schibsted Grotesk', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
{ name: 'System UI (Systemschrift)', category: 'sans', google: false, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" }, { name: 'System UI (Systemschrift)', category: 'sans', google: false, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
]; ];
@ -43,7 +45,7 @@ export function fontFamilyCss(name: string): string {
* public/fonts (see font-faces.json). The fonts are served from our own * public/fonts (see font-faces.json). The fonts are served from our own
* server so a page view never contacts Google. System fonts are skipped. */ * server so a page view never contacts Google. System fonts are skipped. */
export function localFontFacesCss(fontNames: string[]): string { export function localFontFacesCss(fontNames: string[]): string {
const faces = fontFaces as Record<string, { file: string; weight: string; unicodeRange: string }[]>; const faces = fontFaces as Record<string, { file: string; weight: string; unicodeRange: string; style?: string }[]>;
return fontNames return fontNames
.map(findFont) .map(findFont)
.filter((f) => f.google) .filter((f) => f.google)
@ -51,7 +53,7 @@ export function localFontFacesCss(fontNames: string[]): string {
.flatMap((f) => .flatMap((f) =>
(faces[f.name] || []).map( (faces[f.name] || []).map(
(face) => (face) =>
`@font-face { font-family: '${f.name}'; font-style: normal; font-weight: ${face.weight}; ` + `@font-face { font-family: '${f.name}'; font-style: ${face.style || 'normal'}; font-weight: ${face.weight}; ` +
`font-display: swap; src: url('${face.file}') format('woff2'); unicode-range: ${face.unicodeRange}; }`, `font-display: swap; src: url('${face.file}') format('woff2'); unicode-range: ${face.unicodeRange}; }`,
), ),
) )

View file

@ -2,6 +2,7 @@
import Layout from '../layouts/Layout.astro'; import Layout from '../layouts/Layout.astro';
import Devices from '../components/Devices.astro'; import Devices from '../components/Devices.astro';
import Zoom from '../components/Zoom.astro'; import Zoom from '../components/Zoom.astro';
import Kicker from '../components/Kicker.astro';
import { site, rich } from '../lib/content'; import { site, rich } from '../lib/content';
const { hero, warum, preis, highlights, leistungen, mappe, ablauf, about, faq, kontakt } = site; const { hero, warum, preis, highlights, leistungen, mappe, ablauf, about, faq, kontakt } = site;
@ -21,7 +22,7 @@ const findItem = (src: string) =>
<section class="section hero"> <section class="section hero">
<div class="container hero-grid"> <div class="container hero-grid">
<div class="hero-copy"> <div class="hero-copy">
<p class="eyebrow">{hero.eyebrow}</p> <Kicker text={hero.eyebrow} />
<h1>{hero.titleBefore}<em>{hero.titleEmphasis}</em>{hero.titleAfter}</h1> <h1>{hero.titleBefore}<em>{hero.titleEmphasis}</em>{hero.titleAfter}</h1>
<p class="text-lead" set:html={rich(hero.lead)} /> <p class="text-lead" set:html={rich(hero.lead)} />
<div class="hero-actions"> <div class="hero-actions">
@ -33,6 +34,7 @@ const findItem = (src: string) =>
</div> </div>
<div class="hero-visual"> <div class="hero-visual">
<span class="hero-fleck" aria-hidden="true"></span>
{hero.visual.customImage ? ( {hero.visual.customImage ? (
<img class="hero-visual-image" src={hero.visual.customImage} alt="" loading="lazy" /> <img class="hero-visual-image" src={hero.visual.customImage} alt="" loading="lazy" />
) : ( ) : (
@ -52,7 +54,7 @@ const findItem = (src: string) =>
<section id="preis" class="section-tight section-dark"> <section id="preis" class="section-tight section-dark">
<div class="container price-grid"> <div class="container price-grid">
<div class="price-main"> <div class="price-main">
<p class="eyebrow">{preis.eyebrow}</p> <Kicker text={preis.eyebrow} />
<h2>{preis.title}</h2> <h2>{preis.title}</h2>
<p class="price-tag"><span class="price-amount">{preis.amount}</span> {preis.amountNote}</p> <p class="price-tag"><span class="price-amount">{preis.amount}</span> {preis.amountNote}</p>
<p class="text-lead" set:html={rich(preis.lead)} /> <p class="text-lead" set:html={rich(preis.lead)} />
@ -78,9 +80,9 @@ const findItem = (src: string) =>
</section> </section>
<!-- Warum uns unterscheidet --> <!-- Warum uns unterscheidet -->
<section id="warum" class="section"> <section id="warum" class="section section-warum">
<div class="container"> <div class="container split">
<p class="eyebrow">{warum.eyebrow}</p> <Kicker text={warum.eyebrow} />
<h2>{warum.title}</h2> <h2>{warum.title}</h2>
<p class="text-lead" set:html={rich(warum.lead)} /> <p class="text-lead" set:html={rich(warum.lead)} />
@ -117,9 +119,9 @@ const findItem = (src: string) =>
</section> </section>
<!-- Leistungen --> <!-- Leistungen -->
<section id="leistungen" class="section"> <section id="leistungen" class="section section-leistungen tint-sand">
<div class="container"> <div class="container split">
<p class="eyebrow">{leistungen.eyebrow}</p> <Kicker text={leistungen.eyebrow} />
<h2>{leistungen.title}</h2> <h2>{leistungen.title}</h2>
<p class="text-lead" set:html={rich(leistungen.lead)} /> <p class="text-lead" set:html={rich(leistungen.lead)} />
@ -139,7 +141,7 @@ const findItem = (src: string) =>
<h3>{leistungen.nichtMachenTitle}</h3> <h3>{leistungen.nichtMachenTitle}</h3>
<div class="grid grid-2"> <div class="grid grid-2">
{leistungen.nichtMachen.map((text) => ( {leistungen.nichtMachen.map((text) => (
<p class="nicht-machen-item">— {text}</p> <p class="nicht-machen-item">{text}</p>
))} ))}
</div> </div>
</div> </div>
@ -147,9 +149,9 @@ const findItem = (src: string) =>
</section> </section>
<!-- Mappe / Arbeiten --> <!-- Mappe / Arbeiten -->
<section id="arbeiten" class="section"> <section id="arbeiten" class="section section-arbeiten">
<div class="container"> <div class="container split">
<p class="eyebrow">{mappe.eyebrow}</p> <Kicker text={mappe.eyebrow} />
<h2>{mappe.title}</h2> <h2>{mappe.title}</h2>
<div class="mappe-grid"> <div class="mappe-grid">
@ -190,9 +192,9 @@ const findItem = (src: string) =>
</section> </section>
<!-- Ablauf --> <!-- Ablauf -->
<section id="ablauf" class="section"> <section id="ablauf" class="section section-ablauf tint-salbei">
<div class="container"> <div class="container split">
<p class="eyebrow">{ablauf.eyebrow}</p> <Kicker text={ablauf.eyebrow} />
<h2>{ablauf.title}</h2> <h2>{ablauf.title}</h2>
<div class="ablauf-list"> <div class="ablauf-list">
@ -211,7 +213,7 @@ const findItem = (src: string) =>
</section> </section>
<!-- Über uns --> <!-- Über uns -->
<section class="section about"> <section class="section about section-about tint-rose">
<div class="container about-grid"> <div class="container about-grid">
{about.photo ? ( {about.photo ? (
<div class="about-photo about-photo-image"> <div class="about-photo about-photo-image">
@ -227,9 +229,9 @@ const findItem = (src: string) =>
)} )}
<div class="about-copy"> <div class="about-copy">
<p class="eyebrow">{about.eyebrow}</p> <Kicker text={about.eyebrow} />
<h2>{about.title}</h2> <h2>{about.title}</h2>
<p class="about-quote">{about.quote}</p> <blockquote class="about-quote"><p>{about.quote}</p></blockquote>
{about.paragraphs.map((paragraph) => ( {about.paragraphs.map((paragraph) => (
<p set:html={rich(paragraph)} /> <p set:html={rich(paragraph)} />
@ -240,8 +242,8 @@ const findItem = (src: string) =>
<!-- FAQ --> <!-- FAQ -->
<section class="section faq"> <section class="section faq">
<div class="container"> <div class="container split">
<p class="eyebrow">{faq.eyebrow}</p> <Kicker text={faq.eyebrow} />
<h2>{faq.title}</h2> <h2>{faq.title}</h2>
<div class="faq-list"> <div class="faq-list">
@ -264,7 +266,7 @@ const findItem = (src: string) =>
<section id="kontakt" class="section-tight section-dark contact"> <section id="kontakt" class="section-tight section-dark contact">
<div class="container contact-grid"> <div class="container contact-grid">
<div class="contact-intro"> <div class="contact-intro">
<p class="eyebrow">{kontakt.eyebrow}</p> <Kicker text={kontakt.eyebrow} />
<h2>{kontakt.title}</h2> <h2>{kontakt.title}</h2>
<p class="text-lead" set:html={rich(kontakt.lead)} /> <p class="text-lead" set:html={rich(kontakt.lead)} />
@ -363,19 +365,43 @@ const findItem = (src: string) =>
<style> <style>
/* Hero */ /* Hero */
.hero {
overflow: hidden;
}
.hero-grid { .hero-grid {
position: relative;
isolation: isolate;
display: grid; display: grid;
grid-template-columns: 1.1fr 0.9fr; grid-template-columns: 1.15fr 0.85fr;
gap: var(--space-8); gap: var(--space-8);
align-items: center; align-items: center;
} }
.hero-copy { .hero-copy {
max-width: 34rem; max-width: 38rem;
} }
.hero h1 { .hero h1 {
font-size: clamp(2.5rem, 5vw, 3.5rem); font-size: clamp(2.8rem, 6.2vw, 5.4rem);
line-height: 1.02;
letter-spacing: -0.035em;
font-weight: 400;
max-width: none;
}
.hero-visual {
position: relative;
}
/* Die Pille aus dem Logo, groß und schräg hinter den Geräten */
.hero-fleck {
position: absolute;
z-index: -1;
inset: -14% -30% -8% -12%;
background: url('/form/pille.svg') center / 100% 100% no-repeat;
transform: rotate(-8deg);
opacity: 0.85;
} }
.hero h1 em { .hero h1 em {
@ -408,11 +434,21 @@ const findItem = (src: string) =>
margin: var(--space-4) 0; margin: var(--space-4) 0;
} }
.price-tag {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 0 var(--space-4);
}
.price-amount { .price-amount {
font-family: var(--font-serif); font-family: var(--font-serif);
font-size: clamp(2.5rem, 6vw, 4rem); font-size: clamp(5rem, 13vw, 10rem);
line-height: 0.9;
letter-spacing: -0.05em;
color: var(--color-accent); color: var(--color-accent);
font-weight: 600; font-weight: 400;
font-style: italic;
} }
.price-progress { .price-progress {
@ -422,10 +458,10 @@ const findItem = (src: string) =>
} }
.price-progress span { .price-progress span {
height: 4px; height: 5px;
width: 3rem; width: 3rem;
background: var(--color-accent); background: var(--color-accent);
border-radius: 2px; border-radius: 999px;
} }
.price-note { .price-note {
@ -452,10 +488,12 @@ const findItem = (src: string) =>
content: ''; content: '';
position: absolute; position: absolute;
left: 0; left: 0;
top: 0.5em; top: 0.4em;
width: 8px; width: 11px;
height: 8px; height: 10px;
background: var(--color-accent); background: var(--color-accent);
-webkit-mask: url('/form/fleck-maske.svg') center / 100% 100% no-repeat;
mask: url('/form/fleck-maske.svg') center / 100% 100% no-repeat;
} }
.price-list-excluded { .price-list-excluded {
@ -463,8 +501,8 @@ const findItem = (src: string) =>
} }
.price-list-excluded::before { .price-list-excluded::before {
background: transparent; background: var(--color-text-muted);
border: 1px solid var(--color-text-muted); opacity: 0.45;
} }
.price-aside { .price-aside {
@ -477,31 +515,75 @@ const findItem = (src: string) =>
} }
/* Warum / Differentiators */ /* Warum / Differentiators */
.differentiators { .section-warum .differentiators,
margin-top: var(--space-7); .section-warum .readability {
grid-column: 1 / -1;
} }
.differentiator { .differentiators {
border-top: 1px solid var(--color-border); margin-top: var(--space-8);
padding-top: var(--space-4); grid-template-columns: 1.35fr 1fr 0.9fr;
gap: var(--space-7);
align-items: start;
}
.differentiator:nth-child(2) {
margin-top: var(--space-8);
}
.differentiator:nth-child(3) {
margin-top: calc(var(--space-8) * 2);
}
.differentiator:first-child h3 {
font-size: clamp(1.6rem, 2.4vw, 2.1rem);
} }
.roman { .roman {
display: block; position: relative;
display: grid;
place-items: center;
width: 3.6rem;
height: 3.2rem;
font-family: var(--font-serif); font-family: var(--font-serif);
font-size: var(--font-size-2xl); font-style: italic;
color: var(--color-text-muted); font-size: var(--font-size-xl);
margin-bottom: var(--space-3); color: #2c4a42;
margin-bottom: var(--space-4);
isolation: isolate;
} }
.roman::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
background: url('/form/fleck.svg') center / 100% 100% no-repeat;
}
.differentiator:nth-child(2) .roman::before { transform: rotate(70deg) scaleX(-1); }
.differentiator:nth-child(3) .roman::before { transform: rotate(160deg); }
.readability { .readability {
margin-top: var(--space-8); margin-top: var(--space-9);
padding-top: var(--space-8); display: grid;
border-top: 1px solid var(--color-border); grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
gap: 0 var(--space-8);
align-items: center;
}
.readability h3 {
font-size: clamp(1.6rem, 2.6vw, 2.2rem);
grid-column: 1;
}
.readability .text-lead {
grid-column: 1;
} }
.readability-hours { .readability-hours {
margin-top: var(--space-6); grid-column: 2;
grid-row: 1 / span 2;
max-width: 26rem; max-width: 26rem;
} }
@ -514,9 +596,10 @@ const findItem = (src: string) =>
.hours-card { .hours-card {
background: var(--color-surface); background: var(--color-surface);
border: 1px solid var(--color-border); border: 1.5px solid #2c4a42;
border-radius: var(--radius-md); border-radius: 28px 40px 30px 44px / 36px 28px 42px 30px;
padding: var(--space-5); padding: var(--space-6);
transform: rotate(-1.5deg);
} }
.hours-card h4 { .hours-card h4 {
@ -542,21 +625,30 @@ const findItem = (src: string) =>
} }
.leistungen-list li { .leistungen-list li {
display: flex; display: grid;
gap: var(--space-6); grid-template-columns: 3rem minmax(0, 1fr);
padding-block: var(--space-5); gap: var(--space-5);
padding-block: var(--space-6);
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
} }
.leistungen-list li > div {
display: grid;
grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
gap: var(--space-6);
align-items: baseline;
}
.leistungen-num { .leistungen-num {
font-family: var(--font-serif); font-family: var(--font-serif);
font-style: italic;
font-size: var(--font-size-lg);
color: var(--color-accent); color: var(--color-accent);
flex-shrink: 0;
width: 2.5rem;
} }
.leistungen-list h3 { .leistungen-list h3 {
margin-bottom: var(--space-2); margin: 0;
font-size: clamp(1.3rem, 2vw, 1.6rem);
} }
.leistungen-list p { .leistungen-list p {
@ -570,19 +662,50 @@ const findItem = (src: string) =>
} }
.nicht-machen-item { .nicht-machen-item {
position: relative;
padding-left: 1.7rem;
color: var(--color-text); color: var(--color-text);
} }
.nicht-machen-item::before {
content: '';
position: absolute;
left: 0;
top: 0.2em;
width: 1.05rem;
height: 1rem;
background: url('/form/fleck.svg') center / 100% 100% no-repeat;
}
/* Mappe */ /* Mappe */
.mappe-grid { .mappe-grid {
grid-column: 1 / -1;
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
gap: var(--space-7); gap: var(--space-8);
margin-top: var(--space-7); margin-top: var(--space-8);
align-items: start;
} }
.mappe-item:first-child { .mappe-item:first-child {
grid-column: 1 / -1; grid-column: 1 / -1;
display: grid;
grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.7fr);
gap: 0 var(--space-7);
align-items: end;
}
.mappe-item:first-child .mappe-devices {
grid-row: 1 / span 4;
margin: 0;
}
.mappe-item:nth-child(3) {
margin-top: calc(var(--space-9) * 1.5);
}
.mappe-item h3 {
font-size: clamp(1.5rem, 2.4vw, 2rem);
} }
.mappe-shot { .mappe-shot {
@ -609,12 +732,11 @@ const findItem = (src: string) =>
} }
.mappe-fall dt { .mappe-fall dt {
font-size: 0.75rem; font-family: var(--font-serif);
font-weight: 600; font-style: italic;
letter-spacing: 0.06em; font-size: var(--font-size-md);
text-transform: uppercase;
color: var(--color-accent); color: var(--color-accent);
margin-top: var(--space-3); margin-top: var(--space-4);
} }
.mappe-fall dd { .mappe-fall dd {
@ -632,24 +754,61 @@ const findItem = (src: string) =>
/* Ablauf */ /* Ablauf */
.ablauf-list { .ablauf-list {
margin-top: var(--space-7); grid-column: 1 / -1;
position: relative;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--space-6);
margin-top: var(--space-8);
}
/* geschwungener Faden durch die vier Ziffern */
.ablauf-list::before {
content: '';
position: absolute;
left: 2rem;
right: 14%;
top: 1rem;
height: 3.5rem;
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 40' preserveAspectRatio='none'%3E%3Cpath d='M0 20 C 120 2 220 38 340 20 S 560 4 670 20 S 880 36 1000 18' fill='none' stroke='%232C4A42' stroke-width='1.5' stroke-dasharray='5 7' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
} }
.ablauf-step { .ablauf-step {
display: grid; position: relative;
grid-template-columns: 3rem 1fr auto; display: flex;
gap: var(--space-5); flex-direction: column;
align-items: baseline; gap: var(--space-4);
padding-block: var(--space-6); }
border-bottom: 1px solid var(--color-border);
.ablauf-step:nth-child(even) {
margin-top: var(--space-6);
} }
.ablauf-num { .ablauf-num {
position: relative;
display: grid;
place-items: center;
width: 4.6rem;
height: 4rem;
font-family: var(--font-serif); font-family: var(--font-serif);
font-size: var(--font-size-2xl); font-style: italic;
color: var(--color-accent); font-size: var(--font-size-xl);
color: #2c4a42;
isolation: isolate;
} }
.ablauf-num::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
background: url('/form/fleck.svg') center / 100% 100% no-repeat;
}
.ablauf-step:nth-child(2) .ablauf-num::before { transform: rotate(80deg); }
.ablauf-step:nth-child(3) .ablauf-num::before { transform: scaleX(-1) rotate(20deg); }
.ablauf-step:nth-child(4) .ablauf-num::before { transform: rotate(200deg); }
.ablauf-body h3 { .ablauf-body h3 {
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -659,9 +818,13 @@ const findItem = (src: string) =>
} }
.ablauf-dauer { .ablauf-dauer {
align-self: flex-start;
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
color: var(--color-text-muted); color: var(--color-text);
white-space: nowrap; white-space: nowrap;
padding: 0.15rem 0.7rem;
border: 1px solid #2c4a42;
border-radius: 999px;
} }
/* About */ /* About */
@ -673,10 +836,10 @@ const findItem = (src: string) =>
} }
.about-photo { .about-photo {
aspect-ratio: 3 / 4; aspect-ratio: 4 / 5;
background: var(--color-surface); background: url('/form/fleck.svg') center / 100% 100% no-repeat;
border: 1px dashed var(--color-border); border: 0;
border-radius: var(--radius-md); color: #2c4a42;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -688,8 +851,9 @@ const findItem = (src: string) =>
} }
.about-photo-image { .about-photo-image {
border: none;
padding: 0; padding: 0;
-webkit-mask: url('/form/fleck-maske.svg') center / 100% 100% no-repeat;
mask: url('/form/fleck-maske.svg') center / 100% 100% no-repeat;
} }
.about-photo-image img { .about-photo-image img {
@ -699,13 +863,17 @@ const findItem = (src: string) =>
} }
.about-quote { .about-quote {
margin: 0 0 var(--space-6);
}
.about-quote p {
font-family: var(--font-serif); font-family: var(--font-serif);
font-style: italic; font-style: italic;
color: var(--color-accent); color: var(--color-accent);
font-size: var(--font-size-lg); font-size: clamp(1.6rem, 3vw, 2.4rem);
border-left: 2px solid var(--color-accent); line-height: 1.15;
padding-left: var(--space-4); letter-spacing: -0.02em;
margin-bottom: var(--space-5); margin: 0;
} }
/* FAQ */ /* FAQ */
@ -724,8 +892,10 @@ const findItem = (src: string) =>
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
gap: var(--space-5);
list-style: none; list-style: none;
font-weight: var(--font-weight-medium); font-family: var(--font-serif);
font-size: var(--font-size-lg);
color: var(--color-text); color: var(--color-text);
} }
@ -734,8 +904,16 @@ const findItem = (src: string) =>
} }
.faq-icon { .faq-icon {
color: var(--color-accent); display: grid;
place-items: center;
flex: none;
width: 2.2rem;
height: 2rem;
font-family: var(--font-sans);
color: #2c4a42;
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
background: url('/form/fleck.svg') center / 100% 100% no-repeat;
transition: transform var(--transition-base);
} }
.faq-item[open] .faq-icon { .faq-item[open] .faq-icon {
@ -768,12 +946,12 @@ const findItem = (src: string) =>
.contact-phone { .contact-phone {
font-family: var(--font-serif); font-family: var(--font-serif);
font-size: var(--font-size-lg); font-size: var(--font-size-xl);
} }
.contact-email { .contact-email {
font-family: var(--font-serif); font-family: var(--font-serif);
font-size: var(--font-size-lg); font-size: var(--font-size-xl);
margin-bottom: var(--space-1); margin-bottom: var(--space-1);
} }
@ -793,7 +971,7 @@ const findItem = (src: string) =>
width: 100%; width: 100%;
padding: var(--space-3) var(--space-4); padding: var(--space-3) var(--space-4);
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-radius: var(--radius-sm); border-radius: var(--radius-md);
background: var(--color-bg); background: var(--color-bg);
color: var(--color-text); color: var(--color-text);
} }
@ -829,20 +1007,62 @@ const findItem = (src: string) =>
position: static; position: static;
} }
.mappe-grid { .mappe-grid,
grid-template-columns: 1fr; .mappe-item:first-child {
grid-template-columns: minmax(0, 1fr);
} }
.mappe-item:first-child { .mappe-item:first-child {
grid-column: auto; grid-column: auto;
} }
.ablauf-step { .mappe-item:first-child .mappe-devices {
grid-template-columns: 2.5rem 1fr; grid-row: auto;
margin-bottom: var(--space-5);
} }
.ablauf-dauer { .mappe-item:nth-child(3) {
grid-column: 2; margin-top: 0;
}
.differentiators {
grid-template-columns: minmax(0, 1fr);
}
.differentiator:nth-child(n) {
margin-top: 0;
}
.readability {
grid-template-columns: minmax(0, 1fr);
}
.readability-hours {
grid-column: 1;
grid-row: auto;
margin-top: var(--space-5);
}
.ablauf-list {
grid-template-columns: minmax(0, 1fr);
gap: var(--space-7);
}
.ablauf-list::before {
display: none;
}
.ablauf-step:nth-child(even) {
margin-top: 0;
}
.leistungen-list li > div {
grid-template-columns: minmax(0, 1fr);
gap: var(--space-2);
}
.hero-fleck {
inset: -6% -20% -4% -10%;
} }
} }
</style> </style>

View file

@ -27,8 +27,8 @@
--color-google-blue: #8fb2e8; --color-google-blue: #8fb2e8;
/* Typografie */ /* Typografie */
--font-serif: 'Playfair Display', Georgia, 'Times New Roman', serif; --font-serif: 'Newsreader', Georgia, 'Times New Roman', serif;
--font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; --font-sans: 'Schibsted Grotesk', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
--font-size-sm: 0.875rem; --font-size-sm: 0.875rem;
--font-size-base: 1rem; --font-size-base: 1rem;
@ -106,13 +106,20 @@ svg {
h1, h2, h3, h4 { h1, h2, h3, h4 {
margin: 0 0 var(--space-4) 0; margin: 0 0 var(--space-4) 0;
line-height: var(--line-height-tight); line-height: var(--line-height-tight);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-medium);
letter-spacing: -0.01em; letter-spacing: -0.015em;
font-family: var(--font-serif); font-family: var(--font-serif);
color: var(--color-text);
} }
h1 { font-size: var(--font-size-3xl); } h1 { font-size: var(--font-size-3xl); }
h2 { font-size: var(--font-size-2xl); } /* Skala mit Loch: Fließtext bleibt klein, Abschnittstitel werden deutlich größer */
h2 {
font-size: clamp(2.1rem, 4.2vw, 3.5rem);
line-height: 1.08;
letter-spacing: -0.025em;
max-width: 20ch;
}
h3 { font-size: var(--font-size-lg); } h3 { font-size: var(--font-size-lg); }
h4 { font-size: var(--font-size-md); } h4 { font-size: var(--font-size-md); }
@ -196,49 +203,145 @@ textarea {
padding-block: var(--space-7); padding-block: var(--space-7);
} }
/* Dunkles Band (aus dem Prototyp "Nacht"): Angebot und Kontakt. /* Dunkles Band in Petrol (Logofarbe): Angebot und Kontakt.
Die Farben werden hier als Variablen neu gesetzt, damit Text, Karten, Die Farben werden hier als Variablen neu gesetzt, damit Text, Karten,
Buttons und Formularfelder darin automatisch mitdrehen - keine Buttons und Formularfelder darin automatisch mitdrehen - keine
Einzelregeln, die gegen Bauteilklassen verlieren koennten. Einzelregeln, die gegen Bauteilklassen verlieren koennten.
Kontraste auf #1E1A14: Text 15,0 · leise 9,9 · Gold 9,1 · Rand 3,8. */ Kontraste auf #1F3530: Text 11,5 · leise 8,2 · Rosé 7,6 · Rand 3,4.
Oberkante als Welle aus der Logoform: per Maske ausgeschnitten, darüber
scheint der Papiergrund durch. */
.section-dark { .section-dark {
--color-bg: #1e1a14; --color-bg: #1f3530;
--color-bg-alt: #2a241b; --color-bg-alt: #2a443d;
--color-surface: #2a241b; --color-surface: #2a443d;
--color-text: #f5eee2; --color-text: #f3f0e8;
--color-text-muted: #cfc2ac; --color-text-muted: #c5d0c9;
--color-border: #82755f; --color-border: #6c877e;
--color-primary: #d8b87e; --color-primary: #d9c0b4;
--color-primary-dark: #e8d2a6; --color-primary-dark: #ead8cf;
--color-primary-contrast: #241c0b; --color-primary-contrast: #1f3530;
--color-accent: #d8b87e; --color-accent: #d9c0b4;
--color-accent-dark: #e8d2a6; --color-accent-dark: #ead8cf;
--color-flag: #e0836a; --color-flag: #f0a08a;
--color-flag-bg: rgba(224, 131, 106, 0.14); --color-flag-bg: rgba(240, 160, 138, 0.14);
--welle: 56px;
background: var(--color-bg); background: var(--color-bg);
color: var(--color-text); color: var(--color-text);
padding-block: var(--space-9); padding-block: calc(var(--space-9) + var(--welle) * 0.5) var(--space-9);
-webkit-mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 56' preserveAspectRatio='none'%3E%3Cpath d='M0 34 C 240 2 520 52 830 24 C 1070 4 1270 8 1440 30 V56 H0 Z'/%3E%3C/svg%3E") top / 100% var(--welle) no-repeat,
linear-gradient(#000 0 0) 0 calc(var(--welle) - 1px) / 100% 100% no-repeat;
mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 56' preserveAspectRatio='none'%3E%3Cpath d='M0 34 C 240 2 520 52 830 24 C 1070 4 1270 8 1440 30 V56 H0 Z'/%3E%3C/svg%3E") top / 100% var(--welle) no-repeat,
linear-gradient(#000 0 0) 0 calc(var(--welle) - 1px) / 100% 100% no-repeat;
} }
.section-dark h1, .section-dark h1,
.section-dark h2, .section-dark h2,
.section-dark h3 { .section-dark h3 {
color: #fffaf2; color: #fbf8f1;
} }
.section-dark ::placeholder { .section-dark ::placeholder {
color: #9a8f7b; color: #9fb1a8;
opacity: 1; opacity: 1;
} }
/* Getönte Abschnitte für Rhythmus: Salbei, Sand, Rosé aus dem Logoverlauf */
.tint-salbei { background: #e9efea; }
.tint-sand { background: #f1efe3; }
.tint-rose { background: #f5eae4; }
.eyebrow { .eyebrow {
display: inline-block; display: inline-block;
color: var(--color-primary); color: var(--color-text-muted);
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-medium);
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
letter-spacing: 0.06em; letter-spacing: 0.01em;
text-transform: uppercase; margin-bottom: var(--space-4);
margin-bottom: var(--space-3); }
/* Abschnittskennung: Ziffer im Fleck aus dem Logo + Zeile daneben */
.eyebrow-rand {
display: flex;
align-items: center;
gap: var(--space-3);
}
.eyebrow-num {
position: relative;
display: grid;
place-items: center;
flex: none;
width: 2.6em;
height: 2.3em;
font-family: var(--font-serif);
font-style: italic;
font-size: 1.05rem;
font-weight: 500;
color: #2c4a42;
isolation: isolate;
}
.eyebrow-num::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
background: url('/form/fleck.svg') center / 100% 100% no-repeat;
}
/* Randspalten-Raster: Kennung links in der Marginalie, Inhalt rechts.
Kinder landen standardmäßig in Spalte 2; wer volle Breite braucht,
setzt grid-column: 1 / -1. */
.split {
display: grid;
grid-template-columns: minmax(8rem, 13rem) minmax(0, 1fr);
column-gap: clamp(2rem, 5vw, 5rem);
}
.split > * {
grid-column: 2;
}
.split > .eyebrow-rand {
grid-column: 1;
grid-row: 1 / span 3;
align-self: start;
flex-direction: column;
align-items: flex-start;
gap: var(--space-3);
max-width: 12rem;
}
.split > .eyebrow-rand .eyebrow-num {
font-size: 2.6rem;
}
@media (max-width: 900px) {
.split {
grid-template-columns: minmax(0, 1fr);
}
.split > *,
.split > .eyebrow-rand {
grid-column: 1;
grid-row: auto;
}
.split > .eyebrow-rand {
flex-direction: row;
align-items: center;
max-width: none;
}
.split > .eyebrow-rand .eyebrow-num {
font-size: 1.4rem;
}
.section-dark {
--welle: 32px;
}
} }
.text-lead { .text-lead {
@ -268,7 +371,7 @@ textarea {
.card { .card {
background: var(--color-surface); background: var(--color-surface);
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-radius: var(--radius-md); border-radius: var(--radius-lg);
padding: var(--space-6); padding: var(--space-6);
box-shadow: var(--shadow-sm); box-shadow: var(--shadow-sm);
} }
@ -277,8 +380,8 @@ textarea {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: var(--space-2); gap: var(--space-2);
border-radius: var(--radius-sm); border-radius: 999px;
padding: var(--space-3) var(--space-5); padding: 0.8rem 1.6rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
font-size: var(--font-size-base); font-size: var(--font-size-base);
border: 1px solid transparent; border: 1px solid transparent;
@ -299,13 +402,13 @@ textarea {
.btn-secondary { .btn-secondary {
background: transparent; background: transparent;
color: var(--color-text); color: var(--color-primary);
border-color: var(--color-border); border: 1.5px solid var(--color-primary);
} }
.btn-secondary:hover { .btn-secondary:hover {
border-color: var(--color-primary); background: var(--color-primary);
color: var(--color-primary); color: var(--color-primary-contrast);
text-decoration: none; text-decoration: none;
} }