feat: reposition desfoto as an independent brand with four Arbeitsbereiche
This commit is contained in:
149
src/content.py
149
src/content.py
@@ -13,19 +13,22 @@ SITE = {
|
||||
"url": "https://desfoto.de",
|
||||
"claim": "Fotografie & Video",
|
||||
"tagline": "Fotografie und Video aus Neumünster",
|
||||
# The only sentence that ties the brand back to its owner; it belongs in the
|
||||
# footer and nowhere else (the legal entity itself lives in the Impressum).
|
||||
"ownership": "desfoto ist ein Angebot von Denny Schulz, Neumünster.",
|
||||
"locale": "de_DE",
|
||||
"language": "de",
|
||||
"theme_color": "#17140F",
|
||||
"default_description": (
|
||||
"desfoto ist die Foto- und Videoproduktion von Denny Schulz aus Neumünster: "
|
||||
"Shootings für Business, Portrait, Model und Familien, Minishootings, das mobile "
|
||||
"Studio mit autarker Blitzanlage sowie Videoprojekte und Social-Media-Inhalte."
|
||||
"desfoto steht für Fotografie, Video und mediale Produktionen aus Neumünster: "
|
||||
"Fotografie für Menschen und Unternehmen, Video für Unternehmen, Musik und Events, "
|
||||
"Musik- und Bühnenarbeiten sowie eigene Projekte."
|
||||
),
|
||||
"keywords": (
|
||||
"Fotograf Neumünster, Videograf Neumünster, Shootings, Businessfotografie, "
|
||||
"Fotograf Neumünster, Videograf Neumünster, Fotografie, Businessfotografie, "
|
||||
"Businessportrait, Model-Shooting, Familienfotograf, Minishooting, mobiles Studio, "
|
||||
"Blitzanlage, Musikvideo, Konzertfotografie, Social Media Inhalte, "
|
||||
"Schleswig-Holstein"
|
||||
"Blitzanlage, Musikvideo, Konzertfotografie, Bandfotografie, Musik & Bühne, "
|
||||
"Social Media Inhalte, Schleswig-Holstein"
|
||||
),
|
||||
}
|
||||
|
||||
@@ -68,61 +71,72 @@ VIDEO = {
|
||||
|
||||
NAV = [
|
||||
{
|
||||
"label": "Shootings",
|
||||
"href": "/shootings/",
|
||||
"label": "Fotografie",
|
||||
"href": "/fotografie/",
|
||||
"children": [
|
||||
("Businessfotografie", "/businessfotografie/"),
|
||||
("Portrait & Model", "/portrait-und-model/"),
|
||||
("Familien & Paare", "/familien-und-paare/"),
|
||||
("Familie", "/familie/"),
|
||||
("Minishootings", "/minishootings/"),
|
||||
],
|
||||
},
|
||||
{"label": "Video", "href": "/video/"},
|
||||
{"label": "Musik & Bühne", "href": "/musik-und-buehne/"},
|
||||
{"label": "Projekte", "href": "/projekte/"},
|
||||
{"label": "Über", "href": "/ueber/"},
|
||||
]
|
||||
|
||||
# Die zwei Säulen der Arbeit: Foto (Shootings) und Video bleiben getrennt,
|
||||
# damit beide Bereiche für sich verständlich bleiben.
|
||||
PILLARS = [
|
||||
# Die vier Arbeitsbereiche der Marke. Die Startseite erklärt desfoto darüber —
|
||||
# erst der Bereich, dann die einzelnen buchbaren Leistungen darunter.
|
||||
AREAS = [
|
||||
{
|
||||
"slug": "foto",
|
||||
"label": "Foto",
|
||||
"href": "/shootings/",
|
||||
"title": "Shootings",
|
||||
"lead": "Der Überbegriff für alle fotografischen Arbeiten.",
|
||||
"slug": "fotografie",
|
||||
"title": "Fotografie",
|
||||
"scope": "Menschen, Unternehmen, Produkte, Veranstaltungen.",
|
||||
"text": (
|
||||
"Businessfotografie, Portrait & Model, Familien & Paare und Minishootings — "
|
||||
"ein Shooting, ein Ablauf, ein Ansprechpartner. Du wählst nur, was du "
|
||||
"wirklich brauchst."
|
||||
"Portrait, Business, Familie und Minishootings — im Studio in Neumünster "
|
||||
"oder mit dem mobilen Studio an deinem Ort."
|
||||
),
|
||||
"points": [
|
||||
("Businessfotografie", "/businessfotografie/"),
|
||||
("Portrait & Model", "/portrait-und-model/"),
|
||||
("Familien & Paare", "/familien-und-paare/"),
|
||||
("Minishootings", "/minishootings/"),
|
||||
],
|
||||
"image": "business-portrait-studio",
|
||||
"fit": "50% 30%",
|
||||
"href": "/fotografie/",
|
||||
"image": "business-portrait-sit",
|
||||
"fit": "50% 35%",
|
||||
},
|
||||
{
|
||||
"slug": "video",
|
||||
"label": "Video",
|
||||
"href": "/video/",
|
||||
"title": "Videoprojekte",
|
||||
"lead": "Bewegtbild von der Idee bis zum fertigen Schnitt.",
|
||||
"title": "Video",
|
||||
"scope": "Unternehmen, Musik, Events, Social Content.",
|
||||
"text": (
|
||||
"Musikvideo, Imagefilm, Eventfilm und Formate für Social Media — "
|
||||
"gedreht, geschnitten und ausgeliefert in dem Format, in dem du es "
|
||||
"veröffentlichst."
|
||||
"Musikvideo, Imagefilm, Recruiting und Eventnachbericht — von der Idee "
|
||||
"über den Dreh bis zum fertigen Export."
|
||||
),
|
||||
"points": [
|
||||
("Videoprojekte", "/video/"),
|
||||
("Social Media", "/social-media/"),
|
||||
],
|
||||
"image": "studio-lens",
|
||||
"href": "/video/",
|
||||
"image": "movement-light",
|
||||
"fit": "50% 45%",
|
||||
},
|
||||
{
|
||||
"slug": "musik-und-buehne",
|
||||
"title": "Musik & Bühne",
|
||||
"scope": "Bands, Künstler, Konzerte, Musikvideo.",
|
||||
"text": (
|
||||
"Live, Bandportraits und Musikvideo — als Musiker kenne ich beide Seiten "
|
||||
"der Bühne."
|
||||
),
|
||||
"href": "/musik-und-buehne/",
|
||||
"image": "music-drums",
|
||||
"fit": "50% 45%",
|
||||
},
|
||||
{
|
||||
"slug": "projekte",
|
||||
"title": "Projekte",
|
||||
"scope": "Freie Arbeiten, Kooperationen, besondere Produktionen.",
|
||||
"text": (
|
||||
"Landschaft, Tierwelt, Menschen und pro bono — die Arbeiten, die neben "
|
||||
"den Aufträgen entstehen."
|
||||
),
|
||||
"href": "/projekte/",
|
||||
"image": "free-mountain-view",
|
||||
"fit": "50% 50%",
|
||||
},
|
||||
]
|
||||
|
||||
# Was in einem Shooting steckt — die gemeinsamen Eckpunkte aller Shootings.
|
||||
@@ -158,7 +172,7 @@ STUDIO = {
|
||||
{
|
||||
"title": "Studio Neumünster",
|
||||
"text": (
|
||||
"Ein eigenes Studio in Neumünster steht für Shootings bereit:"
|
||||
"Ein eigenes Studio in Neumünster steht für Fototermine bereit:"
|
||||
" kontrolliertes Licht, ruhiger Hintergrund und Platz für"
|
||||
" Konzeptarbeit. Gut für Portraits, Businessbilder und alles, was"
|
||||
" eine wiederholbare Optik braucht — unabhängig vom Wetter."
|
||||
@@ -200,7 +214,7 @@ STUDIO = {
|
||||
"text": "Vom Büro in Neumünster bis zum Betrieb in Kiel, Lübeck oder Hamburg.",
|
||||
},
|
||||
],
|
||||
"image": "studio-setup",
|
||||
"image": "studio-lens",
|
||||
}
|
||||
|
||||
# Netzwerk: eigene Projekte des Betreibers (ausdrücklicher Wunsch des Betreibers).
|
||||
@@ -209,7 +223,7 @@ NETWORK = [
|
||||
"label": "dennyschulz.de",
|
||||
"href": "https://www.dennyschulz.de/",
|
||||
"title": "dennyschulz.de",
|
||||
"text": "Hochzeiten, Portraits und Business — die fotografische Arbeit unter eigenem Namen.",
|
||||
"text": "Hochzeiten und Paare — die Arbeit unter eigenem Namen, mit eigenem Schwerpunkt.",
|
||||
},
|
||||
{
|
||||
"label": "dennyapp.de",
|
||||
@@ -244,14 +258,14 @@ SERVICES = [
|
||||
"fit": "50% 30%",
|
||||
},
|
||||
{
|
||||
"slug": "familien-und-paare",
|
||||
"href": "/familien-und-paare/",
|
||||
"title": "Familien & Paare",
|
||||
"slug": "familie",
|
||||
"href": "/familie/",
|
||||
"title": "Familie",
|
||||
"short": (
|
||||
"Familienshootings und Paarportraits, die nicht gestellt wirken. "
|
||||
"Zeit lassen, mitgehen, im richtigen Moment auslösen."
|
||||
"Familienshootings, die nicht gestellt wirken. Zeit lassen, mitgehen, "
|
||||
"im richtigen Moment auslösen."
|
||||
),
|
||||
"image": "people-joy",
|
||||
"image": "free-portrait-light",
|
||||
"fit": "50% 30%",
|
||||
},
|
||||
{
|
||||
@@ -262,7 +276,7 @@ SERVICES = [
|
||||
"Die kurze Session für alle, die schnell ein gutes Bild brauchen: "
|
||||
"klarer Ablauf, kleine Auswahl, kurze Lieferzeit."
|
||||
),
|
||||
"image": "portrait-natural",
|
||||
"image": "free-modern-portrait",
|
||||
"fit": "50% 30%",
|
||||
},
|
||||
{
|
||||
@@ -284,39 +298,39 @@ SERVICES = [
|
||||
"Foto- und Videocontent im Format deiner Kanäle — wiederkehrend, planbar "
|
||||
"und über die Monate hinweg einheitlich."
|
||||
),
|
||||
"image": "event-motion",
|
||||
"image": "event-speaker",
|
||||
"fit": "50% 45%",
|
||||
},
|
||||
]
|
||||
|
||||
# Fotografie-Übersicht: Shootings als Überbegriff über den einzelnen Leistungen.
|
||||
# Fotografie-Übersicht: die drei Themen, unter denen die einzelnen Angebote liegen.
|
||||
PHOTO_OVERVIEW = [
|
||||
{
|
||||
"title": "Shootings",
|
||||
"title": "Menschen",
|
||||
"text": (
|
||||
"Ein Überbegriff, vier Wege: Businessfotografie für Teams und Marken, "
|
||||
"Portrait & Model für die persönliche Bildsprache, Familien & Paare für "
|
||||
"die Menschen, die zu dir gehören, und Minishootings für alle, die schnell "
|
||||
"ein gutes Bild brauchen."
|
||||
"Portrait, Bewerbungsbilder, Familie und Model — im Studio in Neumünster, "
|
||||
"an einem Ort deiner Wahl oder dort, wo dein Alltag stattfindet."
|
||||
),
|
||||
"image": "editorial-pose",
|
||||
"fit": "50% 30%",
|
||||
},
|
||||
{
|
||||
"title": "Studio & mobil",
|
||||
"title": "Unternehmen & Marken",
|
||||
"text": (
|
||||
"Shootings im eigenen Studio in Neumünster oder mit dem mobilen Studio "
|
||||
"inklusive autarker Blitzanlage, Hintergründen und Lichtformern — "
|
||||
"aufgebaut an deinem Ort, statt umgekehrt."
|
||||
"Team- und Standortbilder, Produkte und Kampagnenmotive — abgestimmt auf "
|
||||
"die Flächen, auf denen sie später stehen."
|
||||
),
|
||||
"image": "business-portrait-sit",
|
||||
"fit": "50% 30%",
|
||||
},
|
||||
{
|
||||
"title": "Anlässe",
|
||||
"title": "Veranstaltungen",
|
||||
"text": (
|
||||
"Feiern, Events, Vereinsleben und Premieren. Dokumentarisch begleitet, "
|
||||
"ohne den Ablauf auszubremsen."
|
||||
),
|
||||
"image": "event-speaker",
|
||||
"fit": "50% 40%",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -329,10 +343,10 @@ WORK_GALLERY = [
|
||||
("free-dog-husky", "Ein Hund auf einem sonnigen Waldweg", "g-w2", "1x1", "50% 45%"),
|
||||
("editorial-colour", "Model-Shooting im Studio", "g-w3", "3x2", "50% 30%"),
|
||||
("music-dreadlocks", "Musiker mit Dreadlocks vor einem Mikrofon", "g-w3", "3x2", "50% 35%"),
|
||||
("couple-backlight-kiss", "Paar küsst sich im Gegenlicht", "g-w3", "4x5", "50% 45%"),
|
||||
("free-woman-coast", "Eine Frau mit Hut steht an einer felsigen Küste", "g-w3", "4x5", "50% 40%"),
|
||||
("free-fog-forest", "Nebel über einem Nadelwald", "g-w3", "3x2", "50% 45%"),
|
||||
("free-cat", "Nahaufnahme einer getigerten Katze", "g-w2", "1x1", "50% 42%"),
|
||||
("couple-sun-silhouette", "Silhouetten eines Paares vor der Sonne", "g-w3", "3x2", "50% 50%"),
|
||||
("portrait-marina-sunset", "Silhouette einer Frau vor orangefarbenem Abendhimmel", "g-w3", "3x2", "50% 50%"),
|
||||
]
|
||||
|
||||
PROCESS = [
|
||||
@@ -387,11 +401,12 @@ FACTS = [
|
||||
]
|
||||
|
||||
FOCUS_ITEMS = [
|
||||
("Shootings", "/shootings/"),
|
||||
("Fotografie", "/fotografie/"),
|
||||
("Businessfotografie", "/businessfotografie/"),
|
||||
("Portrait & Model", "/portrait-und-model/"),
|
||||
("Familien & Paare", "/familien-und-paare/"),
|
||||
("Familie", "/familie/"),
|
||||
("Minishootings", "/minishootings/"),
|
||||
("Videoprojekte", "/video/"),
|
||||
("Video", "/video/"),
|
||||
("Musik & Bühne", "/musik-und-buehne/"),
|
||||
("Social Media", "/social-media/"),
|
||||
]
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
"alt": "Kameraobjektiv im Studiolicht",
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "studio-setup",
|
||||
"raw": "Studio/1777027120196_c501de64_dennyschulz_-05467.jpg",
|
||||
"alt": "Studioequipment mit Logo von Denny Schulz Fotografie",
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "gear-cases",
|
||||
"raw": "1778949186414_b1648d20_dennyschulz_-05400-2.jpg",
|
||||
@@ -75,36 +69,6 @@
|
||||
"alt": "Fashion-Portrait in kräftigen Farben",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "portrait-natural",
|
||||
"raw": "1784293240857_deae4788_dennyschulz_-07255.jpg",
|
||||
"alt": "Natürliches Portrait im Freien",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "portrait-couple",
|
||||
"raw": "1784293231735_a8ccbd3e_dennyschulz_-08620.jpg",
|
||||
"alt": "Paarportrait im Grünen",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "people-joy",
|
||||
"raw": "1776343134779_e893c351_dennyschulz_-03745114107.jpg",
|
||||
"alt": "Paar lacht im Abendlicht",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "people-kiss",
|
||||
"raw": "1776343151721_e3f65ef1_dennyschulz_-04483.jpg",
|
||||
"alt": "Paar küsst sich vor hellem Hintergrund",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "event-motion",
|
||||
"raw": "1776670469051_dc2993ae_dennyschulz_-04663.jpg",
|
||||
"alt": "Bewegung auf einer Veranstaltung",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "event-speaker",
|
||||
"raw": "1784293225986_a37d21f4_dennyschulz_-07139.jpg",
|
||||
@@ -117,12 +81,6 @@
|
||||
"alt": "Veranstaltungsort mit Gästen",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "event-dance",
|
||||
"raw": "1784293226489_fa1b9420_dennyschulz_-09046.jpg",
|
||||
"alt": "Tanzfläche bei einer Feier",
|
||||
"focus": "50% 40%"
|
||||
},
|
||||
{
|
||||
"slug": "movement-light",
|
||||
"raw": "1788850842323_a5ae410e_280826_shooting-69.jpg",
|
||||
@@ -353,41 +311,6 @@
|
||||
"alt": "Marta springt mit angewinkelten Beinen",
|
||||
"focus": "50% 35%"
|
||||
},
|
||||
{
|
||||
"slug": "couple-park",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz-5142.jpg",
|
||||
"alt": "Sara und Carlos stehen Arm in Arm im Grünen",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "couple-backlight-kiss",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz-5191.jpg",
|
||||
"alt": "Sara und Carlos küssen sich im Gegenlicht",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "couple-forest-walk",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz-4415.jpg",
|
||||
"alt": "Sara und Carlos gehen Hand in Hand durch den Wald",
|
||||
"focus": "50% 35%"
|
||||
},
|
||||
{
|
||||
"slug": "couple-sun-silhouette",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz-2076.jpg",
|
||||
"alt": "Die Silhouetten des Paars rahmen die tief stehende Sonne",
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "couple-laugh-walk",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz-0368.jpg",
|
||||
"alt": "Tamara und Tobi lachen auf einem Waldweg",
|
||||
"focus": "50% 35%"
|
||||
},
|
||||
{
|
||||
"slug": "portrait-marina-snow",
|
||||
"source": "legacy",
|
||||
|
||||
@@ -199,23 +199,29 @@ def footer_html() -> str:
|
||||
{BRAND_MARK}
|
||||
<span class="brand__text"><span>desfoto</span><small>{e(SITE['claim'])}</small></span>
|
||||
</span>
|
||||
<p class="footer__claim">{e(SITE['tagline'])}. Ein Angebot von {e(LEGAL['business'])}.</p>
|
||||
<p class="footer__claim">{e(SITE['ownership'])}</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Shootings</h2>
|
||||
<h2>Fotografie</h2>
|
||||
<div class="footer__links">
|
||||
<a href="/shootings/">Überblick Shootings</a>
|
||||
<a href="/fotografie/">Überblick Fotografie</a>
|
||||
<a href="/businessfotografie/">Businessfotografie</a>
|
||||
<a href="/portrait-und-model/">Portrait & Model</a>
|
||||
<a href="/familien-und-paare/">Familien & Paare</a>
|
||||
<a href="/familie/">Familie</a>
|
||||
<a href="/minishootings/">Minishootings</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Video & mehr</h2>
|
||||
<h2>Video & Bühne</h2>
|
||||
<div class="footer__links">
|
||||
<a href="/video/">Videoprojekte</a>
|
||||
<a href="/musik-und-buehne/">Musik & Bühne</a>
|
||||
<a href="/social-media/">Social Media</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Mehr</h2>
|
||||
<div class="footer__links">
|
||||
<a href="/projekte/">Projekte</a>
|
||||
<a href="/ueber/">Über desfoto</a>
|
||||
</div>
|
||||
@@ -240,7 +246,7 @@ def footer_html() -> str:
|
||||
<a href="/datenschutz/">Datenschutz</a>
|
||||
<a href="/kontakt/">Kontakt</a>
|
||||
</nav>
|
||||
<span>© {e(LEGAL['owner'])} · {e(LEGAL['business'])} · Alle Rechte an den gezeigten Bildern und Filmen vorbehalten.</span>
|
||||
<span>© {e(LEGAL['owner'])} · Alle Rechte an den gezeigten Bildern und Filmen vorbehalten.</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -296,17 +302,20 @@ def page_head(
|
||||
f'<div class="breadcrumb"><a href="/">Start</a><span>/</span><span>{e(breadcrumb[0])}</span></div>'
|
||||
)
|
||||
actions_html = f'<div class="page-head__actions">{actions}</div>' if actions else ""
|
||||
# Bedingte Zeilen tragen ihren Zeilenumbruch selbst. Sonst bleibt bei leerem
|
||||
# Wert eine Zeile aus Einrückung stehen — trailing whitespace, das das
|
||||
# Release-Gateway (`git diff --check`) ablehnt.
|
||||
crumb_line = f" {crumb}\n" if crumb else ""
|
||||
actions_line = f" {actions_html}\n" if actions_html else ""
|
||||
return f"""<section class="page-head">
|
||||
<div class="page-head__bg">
|
||||
{picture(image, "100vw", css_class="", priority=True, loading="eager", alt="", fit=fit)}
|
||||
</div>
|
||||
<div class="shell page-head__inner">
|
||||
{crumb}
|
||||
<span class="kicker">{e(kicker)}</span>
|
||||
{crumb_line} <span class="kicker">{e(kicker)}</span>
|
||||
<h1 class="balance">{e(title)}</h1>
|
||||
<p class="page-head__lead">{lead}</p>
|
||||
{actions_html}
|
||||
</div>
|
||||
{actions_line} </div>
|
||||
</section>"""
|
||||
|
||||
|
||||
|
||||
378
src/pages.py
378
src/pages.py
@@ -7,12 +7,12 @@ canonical paths are returned by the ROUTES table at the bottom of the module.
|
||||
from __future__ import annotations
|
||||
|
||||
from content import (
|
||||
AREAS,
|
||||
CONTACT,
|
||||
FACTS,
|
||||
LEGAL,
|
||||
NETWORK,
|
||||
PHOTO_OVERVIEW,
|
||||
PILLARS,
|
||||
PROCESS,
|
||||
SERVICES,
|
||||
SHOOTING_STEPS,
|
||||
@@ -81,6 +81,9 @@ def video_block(*, dark: bool = True, with_meta: bool = True) -> str:
|
||||
if with_meta
|
||||
else ""
|
||||
)
|
||||
# Ohne Meta-Zeile darf die Einrückung nicht stehenbleiben: eine Zeile aus zwei
|
||||
# Leerzeichen ist trailing whitespace und `git diff --check` lehnt sie ab.
|
||||
meta_line = f" {meta}\n" if meta else ""
|
||||
return f"""<div class="video-shell">
|
||||
<button class="video-facade" type="button" data-video-id="{e(VIDEO['id'])}"
|
||||
aria-label="Video starten: {e(VIDEO['title'])} (lädt YouTube nach Klick)">
|
||||
@@ -90,8 +93,7 @@ def video_block(*, dark: bool = True, with_meta: bool = True) -> str:
|
||||
<span class="video-facade__label">Video starten</span>
|
||||
</span>
|
||||
</button>
|
||||
{meta}
|
||||
</div>
|
||||
{meta_line}</div>
|
||||
<p class="form__hint" style="margin-top:.8rem">Das Video wird erst nach deinem Klick von YouTube geladen (youtube-nocookie.com). Mehr dazu in der <a href="/datenschutz/">Datenschutzerklärung</a>.</p>"""
|
||||
|
||||
|
||||
@@ -146,39 +148,27 @@ def caption_gallery(
|
||||
return f'<div class="gallery" data-stagger="{stagger}">{figures}</div>'
|
||||
|
||||
|
||||
def pillar_cards() -> str:
|
||||
"""The two big entry cards: Shootings (photo) and Videoprojekte (video).
|
||||
def area_cards() -> str:
|
||||
"""The four Arbeitsbereiche of the brand.
|
||||
|
||||
The body carries its own links, so the card must not be one great ``<a>``:
|
||||
Fotografie, Video, Musik & Bühne and Projekte replace the old flat service
|
||||
list: the area comes first, the bookable services sit underneath it. Each
|
||||
card carries its own link, so the card itself must not be one great ``<a>``:
|
||||
nested anchors are invalid HTML and the parser would break the grid apart.
|
||||
``.pillar__go`` stretches over the card and keeps the whole surface clickable.
|
||||
"""
|
||||
cards = []
|
||||
for pillar in PILLARS:
|
||||
points = "".join(
|
||||
f'<a href="{e(href)}">{e(label)}</a>' for label, href in pillar["points"]
|
||||
)
|
||||
for area in AREAS:
|
||||
cards.append(
|
||||
'<article class="pillar spot reveal">'
|
||||
'<span class="pillar__media">'
|
||||
+ picture(
|
||||
pillar["image"],
|
||||
"(min-width: 62rem) 46vw, 92vw",
|
||||
css_class="",
|
||||
fit=pillar.get("fit"),
|
||||
alt="",
|
||||
)
|
||||
+ "</span>"
|
||||
'<div class="pillar__body">'
|
||||
f'<span class="pillar__label">{e(pillar["label"])}</span>'
|
||||
f'<h3 class="pillar__title">{e(pillar["title"])}</h3>'
|
||||
f'<p class="pillar__text">{e(pillar["lead"])} {e(pillar["text"])}</p>'
|
||||
f'<div class="pillar__points">{points}</div>'
|
||||
f'<a class="pillar__go" href="{e(pillar["href"])}">'
|
||||
f'{e(pillar["title"])} ansehen {ICONS["arrow"]}</a>'
|
||||
"</div></article>"
|
||||
'<article class="card reveal">'
|
||||
f'<div>{picture(area["image"], "(min-width: 72rem) 22vw, (min-width: 40rem) 44vw, 92vw", css_class="frame frame--16x10", fit=area.get("fit"))}</div>'
|
||||
f'<h3 class="card__title">{e(area["title"])}</h3>'
|
||||
f'<p class="eyebrow">{e(area["scope"])}</p>'
|
||||
f'<p class="card__text">{e(area["text"])}</p>'
|
||||
f'<div class="card__foot"><a class="link-arrow" href="{e(area["href"])}">'
|
||||
f'{e(area["title"])} ansehen {ICONS["arrow"]}</a></div>'
|
||||
"</article>"
|
||||
)
|
||||
return '<div class="pillars" data-stagger="0.12">' + "".join(cards) + "</div>"
|
||||
return '<div class="grid grid--areas" data-stagger="0.08">' + "".join(cards) + "</div>"
|
||||
|
||||
|
||||
def marquee(items: list[str]) -> str:
|
||||
@@ -211,7 +201,6 @@ def studio_section(*, variant: str = "paper2", tight: bool = False) -> str:
|
||||
"(min-width: 62rem) 46vw, 92vw",
|
||||
css_class="frame frame--4x5",
|
||||
fit="50% 45%",
|
||||
alt="Studioequipment mit Logo von Denny Schulz Fotografie",
|
||||
)
|
||||
+ '<span class="studio__badge"><strong>Autarke Blitzanlage</strong>'
|
||||
"<span>Hintergründe, Blitze, Lichtformer</span></span>"
|
||||
@@ -250,9 +239,10 @@ def contact_form() -> str:
|
||||
<select id="cf-topic" name="topic">
|
||||
<option>Businessfotografie</option>
|
||||
<option>Portrait & Model</option>
|
||||
<option>Familien & Paare</option>
|
||||
<option>Familie</option>
|
||||
<option>Minishooting</option>
|
||||
<option>Videoprojekt</option>
|
||||
<option>Video</option>
|
||||
<option>Musik & Bühne</option>
|
||||
<option>Social-Media-Inhalte</option>
|
||||
<option>Pro-bono-Anfrage</option>
|
||||
<option>Etwas anderes</option>
|
||||
@@ -331,19 +321,19 @@ def home() -> str:
|
||||
<span class="kicker hero__kicker">{e(SITE['tagline'])}</span>
|
||||
<h1 class="hero__title">{words('Bilder von Menschen, Marken und Musik.')}</h1>
|
||||
<p class="hero__sub">
|
||||
desfoto ist die fotografische und filmische Arbeit von Denny Schulz aus Neumünster:
|
||||
Shootings für Business, Portrait und Familie, dazu Konzerte, eigene Projekte,
|
||||
Video und Inhalte für Social Media.
|
||||
desfoto ist Fotografie, Video und mediale Produktion aus Neumünster:
|
||||
Bilder für Menschen und Unternehmen, Bewegtbild für Marken, Musik und Events,
|
||||
Bühnenarbeiten und eigene Projekte.
|
||||
</p>
|
||||
<div class="btn-row hero__actions">
|
||||
<a class="btn btn--solid" href="/kontakt/">Projekt anfragen</a>
|
||||
<a class="btn btn--outline-light" href="/projekte/">Arbeiten ansehen</a>
|
||||
</div>
|
||||
<div class="hero__facts">
|
||||
<div class="hero__fact"><strong>Shootings</strong>Business, Portrait, Familie und Minishootings unter einem Dach.</div>
|
||||
<div class="hero__fact"><strong>Studio & vor Ort</strong>Eigenes Studio in Neumünster oder mobiles Studio inklusive autarker Blitzanlage bei dir.</div>
|
||||
<div class="hero__fact"><strong>Foto & Video</strong>Beides aus einer Hand, damit Optik und Ton zusammenpassen.</div>
|
||||
<div class="hero__fact"><strong>Musik & Bühne</strong>Konzerte, Bands und Musikvideos aus eigener Erfahrung.</div>
|
||||
<div class="hero__fact"><strong>Klarer Ablauf</strong>Umfang, Termin und Preis sind vor der Buchung geklärt.</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="scroll-cue" aria-hidden="true">Scroll<i></i></span>
|
||||
@@ -361,22 +351,29 @@ def home() -> str:
|
||||
</div>""",
|
||||
)
|
||||
|
||||
pillars = section(
|
||||
kicker="Zwei Bereiche",
|
||||
title="Shootings und Videoprojekte",
|
||||
lead="Foto und Video bleiben getrennt, damit beide Bereiche für sich verständlich sind — und kommen trotzdem von derselben Person.",
|
||||
content=pillar_cards(),
|
||||
areas = section(
|
||||
kicker="Arbeitsbereiche",
|
||||
title="Fotografie, Video, Musik & Bühne und Projekte",
|
||||
lead="Vier Bereiche, in denen ich arbeite. Was du davon buchen kannst, steht direkt darunter — die freien Arbeiten laufen daneben weiter.",
|
||||
content=area_cards(),
|
||||
)
|
||||
|
||||
bookable = section(
|
||||
variant="paper2",
|
||||
kicker="Buchen",
|
||||
title="Was du bei mir buchen kannst",
|
||||
lead="Auftragsarbeiten mit klarem Umfang: Businessfotografie, Portrait & Model, Familie, Minishootings, Video und Social-Media-Inhalte.",
|
||||
content=service_cards(),
|
||||
)
|
||||
|
||||
works = section(
|
||||
variant="paper2",
|
||||
kicker="Portfolio",
|
||||
title="Musik, Menschen und freie Arbeiten",
|
||||
lead="Konzerte und Bands, Portraits und Business, dazu eigene Strecken aus Landschaft und Tierwelt. Das ist die Bandbreite meiner Arbeit.",
|
||||
title="Was ich außerdem mache",
|
||||
lead="Freie Arbeiten, Musik & Bühne und pro bono — Projekte, die nicht jede Woche auf einer Rechnung stehen. Hier muss kein Bild ein Angebot verkaufen.",
|
||||
content=work_gallery()
|
||||
+ '<div class="btn-row" style="margin-top:2rem">'
|
||||
'<a class="btn btn--ink" href="/projekte/">Alle Projekte</a>'
|
||||
'<a class="btn btn--ghost" href="/shootings/">Shootings ansehen</a></div>',
|
||||
'<a class="btn btn--ghost" href="/musik-und-buehne/">Musik & Bühne</a></div>',
|
||||
)
|
||||
|
||||
video_sec = f"""<section class="section section--ink">
|
||||
@@ -422,7 +419,6 @@ def home() -> str:
|
||||
"@context": "https://schema.org",
|
||||
"@type": "ProfessionalService",
|
||||
"name": SITE["brand"],
|
||||
"alternateName": LEGAL["business"],
|
||||
"description": SITE["default_description"],
|
||||
"url": SITE["url"] + "/",
|
||||
"image": SITE["url"] + "/assets/img/og-desfoto.jpg",
|
||||
@@ -442,19 +438,23 @@ def home() -> str:
|
||||
],
|
||||
body=hero
|
||||
+ statement
|
||||
+ pillars
|
||||
+ areas
|
||||
+ marquee(
|
||||
[
|
||||
"Fotografie",
|
||||
"Video",
|
||||
"Musik & Bühne",
|
||||
"Projekte",
|
||||
"Businessfotografie",
|
||||
"Portrait & Model",
|
||||
"Familien & Paare",
|
||||
"Familie",
|
||||
"Minishootings",
|
||||
"Mobiles Studio",
|
||||
"Videoprojekte",
|
||||
"Musikvideo",
|
||||
"Social Media",
|
||||
]
|
||||
)
|
||||
+ bookable
|
||||
+ works
|
||||
+ video_sec
|
||||
+ studio
|
||||
@@ -464,15 +464,15 @@ def home() -> str:
|
||||
)
|
||||
|
||||
|
||||
def shootings() -> str:
|
||||
path = "/shootings/"
|
||||
def fotografie() -> str:
|
||||
path = "/fotografie/"
|
||||
overview = "".join(
|
||||
'<article class="card reveal"><div>'
|
||||
+ picture(
|
||||
item["image"],
|
||||
"(min-width: 62rem) 30vw, 92vw",
|
||||
css_class="frame frame--4x5",
|
||||
fit="50% 30%",
|
||||
fit=item.get("fit"),
|
||||
alt=item["title"],
|
||||
)
|
||||
+ f'</div><h3>{e(item["title"])}</h3><p class="card__text">{e(item["text"])}</p></article>'
|
||||
@@ -480,35 +480,35 @@ def shootings() -> str:
|
||||
)
|
||||
body = (
|
||||
page_head(
|
||||
kicker="Shootings",
|
||||
title="Shootings — von Business bis Familie",
|
||||
lead="Alles, was ich fotografisch mache, läuft unter dem Begriff Shooting: vier Schwerpunkte, ein Ablauf, ein Ansprechpartner.",
|
||||
kicker="Fotografie",
|
||||
title="Fotografie — Menschen, Unternehmen, Produkte, Veranstaltungen",
|
||||
lead="Alles, was ich fotografisch mache: drei Themen, vier Schwerpunkte, ein Ablauf und ein Ansprechpartner — im Studio in Neumünster oder mit dem mobilen Studio bei dir.",
|
||||
image="editorial-earring",
|
||||
breadcrumb=("Shootings", path),
|
||||
breadcrumb=("Fotografie", path),
|
||||
fit="45% 30%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Shooting anfragen</a>'
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Fototermin anfragen</a>'
|
||||
'<a class="btn btn--outline-light" href="/projekte/">Arbeiten ansehen</a>',
|
||||
)
|
||||
+ section(
|
||||
kicker="Überblick",
|
||||
title="Ein Überbegriff, vier Wege",
|
||||
title="Drei Themen, ein Blick",
|
||||
lead="Du musst dich nicht für eine Schublade entscheiden — erzähl einfach, worum es geht.",
|
||||
content=f'<div class="grid grid--3" data-stagger="0.09">{overview}</div>',
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Schwerpunkte",
|
||||
title="Die vier Shootings im Detail",
|
||||
title="Die vier buchbaren Schwerpunkte",
|
||||
content=service_cards(
|
||||
["businessfotografie", "portrait-und-model", "familien-und-paare", "minishootings"]
|
||||
["businessfotografie", "portrait-und-model", "familie", "minishootings"]
|
||||
),
|
||||
tight=True,
|
||||
)
|
||||
+ studio_section(variant="")
|
||||
+ section(
|
||||
kicker="Ablauf",
|
||||
title="So läuft ein Shooting",
|
||||
lead="Bei jedem Shooting derselbe Weg — vom ersten Satz bis zur fertigen Datei.",
|
||||
title="So läuft ein Termin",
|
||||
lead="Bei jedem Auftrag derselbe Weg — vom ersten Satz bis zur fertigen Datei.",
|
||||
content='<div class="steps">'
|
||||
+ "".join(
|
||||
f'<div class="step reveal"><h3>{e(step["title"])}</h3><p>{e(step["text"])}</p></div>'
|
||||
@@ -519,16 +519,16 @@ def shootings() -> str:
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Beispiele",
|
||||
title="Aus Shootings und Aufträgen",
|
||||
title="Aus Aufträgen und eigener Arbeit",
|
||||
content=media_grid(
|
||||
[
|
||||
("business-portrait-dark", "Portrait eines Mannes vor dunklem Hintergrund", "4x5", "50% 28%"),
|
||||
("editorial-colour", "Fashion-Portrait in kräftigen Farben", "4x5", "50% 30%"),
|
||||
("portrait-couple", "Paarportrait im Grünen", "4x5", "50% 30%"),
|
||||
("editorial-balance", "Editorial-Pose mit ausbalancierter Haltung", "4x5", "50% 40%"),
|
||||
("portrait-marina-sunset", "Silhouette einer Frau vor orangefarbenem Abendhimmel", "4x5", "50% 40%"),
|
||||
("business-portrait-stool", "Sitzendes Portrait im weichen Studiolicht", "3x2", "50% 35%"),
|
||||
("business-hotel-window", "Drei Frauen in Bademänteln und Handtuchturbanen am Fenster", "3x2", "50% 40%"),
|
||||
("portrait-natural", "Natürliches Portrait im Freien", "3x2", "50% 32%"),
|
||||
("couple-backlight-kiss", "Sara und Carlos küssen sich im Gegenlicht", "1x1", "50% 45%"),
|
||||
("portrait-marina-snow", "Marina mit roten Zöpfen auf einem verschneiten Waldweg", "3x2", "50% 30%"),
|
||||
("free-woman-coast", "Eine Frau mit Hut steht an einer felsigen Küste", "1x1", "50% 40%"),
|
||||
("editorial-leap", "Marta springt mit angewinkelten Beinen", "1x1", "50% 35%"),
|
||||
("free-dog-husky", "Ein Hund steht auf einem sonnigen Waldweg", "1x1", "50% 45%"),
|
||||
]
|
||||
@@ -558,22 +558,22 @@ def shootings() -> str:
|
||||
),
|
||||
)
|
||||
+ cta_band(
|
||||
title="Welches Shooting passt zu dir?",
|
||||
title="Welcher Bereich passt zu dir?",
|
||||
text="Schreib kurz, worum es geht und wann es stattfinden soll. Du bekommst Ablauf, Umfang und Preis.",
|
||||
secondary=("Minishootings ansehen", "/minishootings/"),
|
||||
)
|
||||
)
|
||||
return page(
|
||||
title="Shootings in Neumünster · desfoto",
|
||||
description="Shootings aus Neumünster: Businessfotografie, Portrait & Model, Familien & Paare und Minishootings — mit mobilem Studio inklusive autarker Blitzanlage.",
|
||||
title="Fotografie in Neumünster · desfoto",
|
||||
description="Fotografie aus Neumünster: Businessfotografie, Portrait & Model, Familie und Minishootings — mit mobilem Studio inklusive autarker Blitzanlage.",
|
||||
path=path,
|
||||
json_ld=[
|
||||
service_ld(
|
||||
"Shootings",
|
||||
"Fotografie",
|
||||
path,
|
||||
"Fotografische Shootings für Business, Portrait & Model, Familien und Paare.",
|
||||
"Fotografie für Menschen, Unternehmen, Produkte und Veranstaltungen.",
|
||||
),
|
||||
breadcrumb_ld("Shootings", path),
|
||||
breadcrumb_ld("Fotografie", path),
|
||||
],
|
||||
body=body,
|
||||
)
|
||||
@@ -655,7 +655,7 @@ def businessfotografie() -> str:
|
||||
("business-hotel-window", "Drei Frauen in Bademänteln und Handtuchturbanen am Fenster", "4x5", "50% 40%"),
|
||||
("business-hotel-tea", "Drei Frauen mit Teetassen und Handtuchturbanen", "4x5", "50% 45%"),
|
||||
("gear-cases", "Kamerakoffer und Objektive vor einem Studiorack", "3x2", "50% 50%"),
|
||||
("studio-setup", "Studioequipment mit Logo von Denny Schulz Fotografie", "3x2", "50% 45%"),
|
||||
("business-portrait-sit", "Sitzendes Portrait mit Kameratasche im Studio", "3x2", "50% 35%"),
|
||||
("business-hotel-orange", "Frauen im Bademantel halten Orangenscheiben vor ihre Gesichter", "3x2", "50% 50%"),
|
||||
("business-hotel-laugh", "Drei Frauen lachen mit Orangenscheiben in den Händen", "3x2", "50% 45%"),
|
||||
],
|
||||
@@ -765,8 +765,8 @@ def portrait_und_model() -> str:
|
||||
("editorial-studio-side", "50% 30%"),
|
||||
("editorial-balance", "50% 40%"),
|
||||
("editorial-leap", "50% 35%"),
|
||||
("portrait-natural", "50% 30%"),
|
||||
("people-kiss", "50% 32%"),
|
||||
("free-portrait-light", "50% 30%"),
|
||||
("free-modern-portrait", "50% 30%"),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
@@ -789,21 +789,21 @@ def portrait_und_model() -> str:
|
||||
)
|
||||
|
||||
|
||||
def familien_und_paare() -> str:
|
||||
path = "/familien-und-paare/"
|
||||
def familie() -> str:
|
||||
path = "/familie/"
|
||||
body = (
|
||||
page_head(
|
||||
kicker="Familien & Paare",
|
||||
kicker="Familie",
|
||||
title="Wachsendes festhalten, ohne Pose",
|
||||
lead="Familienshootings und Paarportraits, die nicht gestellt wirken. Zeit lassen, mitgehen und im richtigen Moment auslösen.",
|
||||
image="people-joy",
|
||||
breadcrumb=("Familien & Paare", path),
|
||||
fit="50% 30%",
|
||||
lead="Familienshootings, die nicht gestellt wirken. Zeit lassen, mitgehen und im richtigen Moment auslösen.",
|
||||
image="free-portrait-light",
|
||||
breadcrumb=("Familie", path),
|
||||
fit="50% 28%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Termin anfragen</a>',
|
||||
)
|
||||
+ section(
|
||||
kicker="Anlässe",
|
||||
title="Für welche Momente mich Familien und Paare buchen",
|
||||
title="Für welche Momente mich Familien buchen",
|
||||
content='<div class="grid grid--3">'
|
||||
+ "".join(
|
||||
f'<article class="card reveal"><h3>{e(t)}</h3><p class="card__text">{e(d)}</p></article>'
|
||||
@@ -812,18 +812,6 @@ def familien_und_paare() -> str:
|
||||
"Familienshooting",
|
||||
"Alle zusammen, ohne dass es nach Fototermin aussieht. Auch mit mehreren Generationen.",
|
||||
),
|
||||
(
|
||||
"Paarshooting",
|
||||
"Zwei Menschen, die sich nah sind. Vor der Hochzeit, zum Jahrestag oder einfach so.",
|
||||
),
|
||||
(
|
||||
"Verlobung & Ja-Wort",
|
||||
"Kleine Zeremonien, Standesamt, freie Trauung — begleitend oder als Portraitstrecke.",
|
||||
),
|
||||
(
|
||||
"Jubiläum & Familientreffen",
|
||||
"Wenn die ganze Verwandtschaft zusammenkommt, lohnt sich ein gemeinsames Bild.",
|
||||
),
|
||||
(
|
||||
"Generationenbilder",
|
||||
"Großeltern, Eltern, Enkel: Bilder, die in Jahren mehr wert sind als heute.",
|
||||
@@ -832,6 +820,18 @@ def familien_und_paare() -> str:
|
||||
"Kinderportraits",
|
||||
"Kurz, spielerisch und geduldig — lieber zehn ehrliche Bilder als eine Stunde Stillhalten.",
|
||||
),
|
||||
(
|
||||
"Geschwister & Patchwork",
|
||||
"Nicht jede Familie sieht gleich aus. Gezeigt wird, wer zu euch gehört.",
|
||||
),
|
||||
(
|
||||
"Jubiläum & Familientreffen",
|
||||
"Wenn die ganze Verwandtschaft zusammenkommt, lohnt sich ein gemeinsames Bild.",
|
||||
),
|
||||
(
|
||||
"Bei euch oder im Studio",
|
||||
"Zuhause, im Park oder im Studio in Neumünster: dort, wo ihr euch wohlfühlt.",
|
||||
),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
@@ -845,20 +845,16 @@ def familien_und_paare() -> str:
|
||||
)
|
||||
+ section(
|
||||
kicker="Beispiele",
|
||||
title="Paare und Menschen",
|
||||
title="Menschen aus meiner Arbeit",
|
||||
lead="Familienshootings laufen ruhig und ohne Regieanweisungen ab. Genau so entstehen die Portraits, die ich hier zeige.",
|
||||
content='<div class="grid grid--3">'
|
||||
+ "".join(
|
||||
picture(slug, "(min-width: 62rem) 30vw, 92vw", css_class="frame frame--4x5", fit=fit)
|
||||
for slug, fit in [
|
||||
("portrait-natural", "50% 30%"),
|
||||
("people-kiss", "50% 32%"),
|
||||
("portrait-couple", "50% 30%"),
|
||||
("couple-park", "50% 30%"),
|
||||
("couple-backlight-kiss", "50% 45%"),
|
||||
("couple-forest-walk", "50% 35%"),
|
||||
("couple-laugh-walk", "50% 35%"),
|
||||
("couple-sun-silhouette", "50% 50%"),
|
||||
("free-lake-person", "50% 45%"),
|
||||
("portrait-marina-snow", "50% 30%"),
|
||||
("free-modern-portrait", "50% 30%"),
|
||||
("free-woman-coast", "50% 40%"),
|
||||
("portrait-marina-sunset", "50% 45%"),
|
||||
]
|
||||
)
|
||||
@@ -871,12 +867,16 @@ def familien_und_paare() -> str:
|
||||
)
|
||||
)
|
||||
return page(
|
||||
title="Familien- & Paarfotografie in Neumünster · desfoto",
|
||||
description="Familien- und Paarshootings aus Neumünster: natürliche Bilder ohne Pose, im Studio, in der Natur oder bei euch zuhause.",
|
||||
title="Familienfotografie in Neumünster · desfoto",
|
||||
description="Familienshootings aus Neumünster: natürliche Bilder ohne Pose, im Studio, in der Natur oder bei euch zuhause.",
|
||||
path=path,
|
||||
json_ld=[
|
||||
service_ld("Familien- und Paarfotografie", path, "Familienshootings und Paarportraits ohne gestellte Posen."),
|
||||
breadcrumb_ld("Familien & Paare", path),
|
||||
service_ld(
|
||||
"Familienfotografie",
|
||||
path,
|
||||
"Familienshootings ohne gestellte Posen, im Studio, in der Natur oder zuhause.",
|
||||
),
|
||||
breadcrumb_ld("Familie", path),
|
||||
],
|
||||
body=body,
|
||||
)
|
||||
@@ -889,7 +889,7 @@ def minishootings() -> str:
|
||||
kicker="Minishootings",
|
||||
title="Kurze Session, sofort brauchbares Bild",
|
||||
lead="Der Termin für alle, die schnell ein gutes Bild brauchen: klarer Ablauf, kleine Auswahl, kurze Lieferzeit. Meist in rund 20 bis 30 Minuten erledigt.",
|
||||
image="portrait-natural",
|
||||
image="free-modern-portrait",
|
||||
breadcrumb=("Minishootings", path),
|
||||
fit="50% 30%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Minishooting buchen</a>',
|
||||
@@ -902,11 +902,11 @@ def minishootings() -> str:
|
||||
f'<article class="card reveal"><h3>{e(t)}</h3><p class="card__text">{e(d)}</p></article>'
|
||||
for t, d in [
|
||||
("Profilbild", "Für LinkedIn, Bewerbung, Vereinsseite oder die Website."),
|
||||
("Paar & Familie", "Ein, zwei schöne Bilder zusammen — ohne großen Termin."),
|
||||
("Familie", "Eltern und Kinder, ohne großen Termin — ein, zwei schöne Bilder zusammen."),
|
||||
("Social Media", "Frisches Material für deine Kanäle, passend zum Format."),
|
||||
("Verein & Mannschaft", "Schnell alle Gesichter für Mannschaftsfotos und Anmeldungen."),
|
||||
("Businesskurzportrait", "Du brauchst nur ein Bild für die neue Stelle oder Präsentation."),
|
||||
("Geschenkidee", "Gutschein für ein gemeinsames Shooting — auf Anfrage."),
|
||||
("Geschenkidee", "Gutschein für ein Shooting — auf Anfrage."),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
@@ -958,11 +958,11 @@ def minishootings() -> str:
|
||||
+ "".join(
|
||||
picture(slug, "(min-width: 62rem) 30vw, 92vw", css_class="frame frame--4x5", fit=fit)
|
||||
for slug, fit in [
|
||||
("portrait-natural", "50% 28%"),
|
||||
("free-woman-coast", "50% 30%"),
|
||||
("business-portrait-studio", "50% 28%"),
|
||||
("editorial-black", "50% 28%"),
|
||||
("editorial-studio-side", "50% 30%"),
|
||||
("free-modern-portrait", "50% 30%"),
|
||||
("free-portrait-light", "50% 30%"),
|
||||
("portrait-marina-snow", "50% 30%"),
|
||||
]
|
||||
)
|
||||
@@ -1006,6 +1006,7 @@ def video() -> str:
|
||||
<h2 class="balance">{e(VIDEO['title'])}</h2>
|
||||
<p class="lead measure" style="color:rgba(246,242,234,.82)">{e(VIDEO['description'])}</p>
|
||||
</div>
|
||||
<a class="link-arrow" style="color:#F6F2EA;border-color:rgba(246,242,234,.4)" href="/musik-und-buehne/">Musik & Bühne {ICONS['arrow']}</a>
|
||||
</div>
|
||||
<div style="margin-top:clamp(1.6rem,3vw,2.6rem)">{video_block()}</div>
|
||||
</div>
|
||||
@@ -1102,7 +1103,7 @@ def video() -> str:
|
||||
content=caption_gallery(
|
||||
[
|
||||
("studio-lens", "Brennweite und Licht im Studio", "g-w3", "3x2", "50% 50%"),
|
||||
("event-motion", "Bewegung im Bild", "g-w3", "3x2", "50% 45%"),
|
||||
("band-extinct-group", "Band vor der Kamera", "g-w3", "3x2", "50% 40%"),
|
||||
("movement-light", "Lange Belichtung", "g-w2", "1x1", "50% 50%"),
|
||||
("event-speaker", "Statement vor der Kamera", "g-w2", "1x1", "50% 30%"),
|
||||
("event-venue", "Location vor dem Dreh", "g-w2", "1x1", "50% 45%"),
|
||||
@@ -1129,6 +1130,116 @@ def video() -> str:
|
||||
)
|
||||
|
||||
|
||||
def musik_und_buehne() -> str:
|
||||
path = "/musik-und-buehne/"
|
||||
body = (
|
||||
page_head(
|
||||
kicker="Musik & Bühne",
|
||||
title="Bands, Künstler und Konzerte",
|
||||
lead="Live, Bandportraits und Musikvideo — als Musiker kenne ich beide Seiten der Bühne.",
|
||||
image="music-vocal-blue",
|
||||
breadcrumb=("Musik & Bühne", path),
|
||||
fit="50% 30%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Musikprojekt anfragen</a>'
|
||||
'<a class="btn btn--outline-light" href="/projekte/">Arbeiten ansehen</a>',
|
||||
)
|
||||
+ section(
|
||||
kicker="Für Bands und Veranstalter",
|
||||
title="Was ich für Musik und Bühne mache",
|
||||
content='<div class="grid grid--3" data-stagger="0.08">'
|
||||
+ "".join(
|
||||
f'<article class="card reveal"><h3>{e(t)}</h3><p class="card__text">{e(d)}</p></article>'
|
||||
for t, d in [
|
||||
(
|
||||
"Live & Konzert",
|
||||
"Bühnenbilder aus Clubs, Festivals und Vereinsheimen — mitten im Geschehen statt am Rand, auch bei wenig Licht.",
|
||||
),
|
||||
(
|
||||
"Band- & Künstlerportraits",
|
||||
"Pressebilder, Bandportraits und Artwork-Motive, die zur Musik passen.",
|
||||
),
|
||||
(
|
||||
"Musikvideo",
|
||||
"Konzept, Dreh, Schnitt und Farbkorrektur — für Songs, die ein Bild brauchen.",
|
||||
),
|
||||
(
|
||||
"Promotion & Content",
|
||||
"Standbilder und Kurzclips für Ankündigungen, Kanäle und Presse.",
|
||||
),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
)
|
||||
+ f"""<section class="section section--ink">
|
||||
<div class="shell">
|
||||
<div class="section-head">
|
||||
<div class="section-head__text stack">
|
||||
<span class="kicker">Musikvideo</span>
|
||||
<h2 class="balance">{e(VIDEO['title'])}</h2>
|
||||
<p class="lead measure" style="color:rgba(246,242,234,.82)">{e(VIDEO['description'])}</p>
|
||||
</div>
|
||||
<a class="link-arrow" style="color:#F6F2EA;border-color:rgba(246,242,234,.4)" href="/video/">Mehr zu Video {ICONS['arrow']}</a>
|
||||
</div>
|
||||
<div style="margin-top:clamp(1.6rem,3vw,2.6rem)">{video_block(with_meta=False)}</div>
|
||||
</div>
|
||||
</section>"""
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Referenz",
|
||||
title="Musikvideo Thjódrörir: Konzept, Dreh, Schnitt, Farbkorrektur",
|
||||
lead="Der komplette Weg vom Song zum fertigen Video — ohne zweite Crew und ohne Zwischenabstimmungen zwischen Dienstleistern.",
|
||||
content='<div class="grid grid--4" data-stagger="0.08">'
|
||||
+ "".join(
|
||||
f'<article class="card reveal"><h3>{e(t)}</h3><p class="card__text">{e(d)}</p></article>'
|
||||
for t, d in [
|
||||
("Konzept", "Idee, Bildsprache und Drehorte, abgestimmt auf den Song."),
|
||||
("Dreh", "Kamera, Licht und Regie am Drehtag — aus einer Hand."),
|
||||
("Schnitt", "Schnitt und Aufbau des Videos in der Länge, die der Song vorgibt."),
|
||||
("Farbkorrektur", "Farbkorrektur und Ausgabe in den Formaten, die YouTube und die Kanäle brauchen."),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
)
|
||||
+ section(
|
||||
kicker="Aus der Arbeit",
|
||||
title="Konzerte, Bands, Backstage",
|
||||
lead="Für Extinct und Sagenbringer entstehen Bilder und Videoarbeiten — live, auf der Probe und für Ankündigungen.",
|
||||
content=media_grid(
|
||||
[
|
||||
("music-guitar-silhouette", "Gitarrist als Silhouette im Bühnennebel", "4x5", "50% 42%"),
|
||||
("band-extinct-guitar", "Zwei Bandmitglieder stehen Rücken an Rücken mit Gitarre", "4x5", "50% 35%"),
|
||||
("music-neck", "Hand am Gitarrenhals im dunklen Bühnenlicht", "4x5", "50% 50%"),
|
||||
("music-mic", "Bärtiger Sänger am Mikrofon mit erhobener Hand", "3x2", "50% 35%"),
|
||||
("music-banner", "Gitarrist vor einem beleuchteten Bandbanner", "3x2", "50% 40%"),
|
||||
("band-sagenbringer", "Musiker in Fellkleidung im Wald, von unten fotografiert", "3x2", "50% 30%"),
|
||||
("music-drums", "Schlagzeuger am Drumset in Schwarzweiß", "1x1", "50% 45%"),
|
||||
("band-extinct-group", "Vier Bandmitglieder hintereinander in Schwarzweiß", "1x1", "50% 40%"),
|
||||
("music-horns", "Zwei Musiker trinken aus Trinkhörnern", "1x1", "50% 35%"),
|
||||
]
|
||||
),
|
||||
)
|
||||
+ cta_band(
|
||||
title="Konzert, Band oder Musikvideo?",
|
||||
text="Schreib mir, was ansteht: Termin, Ort und was du mit den Bildern oder dem Film vorhast.",
|
||||
secondary=("Videoprojekte ansehen", "/video/"),
|
||||
)
|
||||
)
|
||||
return page(
|
||||
title="Musik & Bühne · Konzerte, Bands, Musikvideos · desfoto",
|
||||
description="Musik- und Bühnenfotografie aus Neumünster: Konzerte, Bandportraits und Musikvideos — plus Video für Künstler und Veranstalter.",
|
||||
path=path,
|
||||
json_ld=[
|
||||
service_ld(
|
||||
"Musik- und Bühnenfotografie",
|
||||
path,
|
||||
"Konzertfotografie, Bandportraits und Musikvideos für Bands, Künstler und Veranstalter.",
|
||||
),
|
||||
breadcrumb_ld("Musik & Bühne", path),
|
||||
],
|
||||
body=body,
|
||||
)
|
||||
|
||||
|
||||
def social_media() -> str:
|
||||
path = "/social-media/"
|
||||
body = (
|
||||
@@ -1136,7 +1247,7 @@ def social_media() -> str:
|
||||
kicker="Social Media",
|
||||
title="Content, der regelmäßig kommt",
|
||||
lead="Foto- und Videomaterial im Format deiner Kanäle — wiederkehrend, planbar und über die Monate hinweg einheitlich.",
|
||||
image="event-motion",
|
||||
image="event-speaker",
|
||||
breadcrumb=("Social Media", path),
|
||||
fit="50% 42%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Content anfragen</a>',
|
||||
@@ -1214,9 +1325,9 @@ def social_media() -> str:
|
||||
+ "".join(
|
||||
picture(slug, "(min-width: 62rem) 30vw, 92vw", css_class="frame frame--4x5", fit=fit)
|
||||
for slug, fit in [
|
||||
("event-motion", "50% 45%"),
|
||||
("event-dance", "50% 36%"),
|
||||
("event-speaker", "50% 30%"),
|
||||
("event-venue", "50% 45%"),
|
||||
("free-modern-portrait", "50% 40%"),
|
||||
("music-dreadlocks", "50% 35%"),
|
||||
("business-hotel-tea", "50% 45%"),
|
||||
("business-hotel-laugh", "50% 45%"),
|
||||
("music-drummer", "50% 45%"),
|
||||
@@ -1274,7 +1385,7 @@ def projekte() -> str:
|
||||
]
|
||||
)
|
||||
+ '<div class="btn-row" style="margin-top:2rem">'
|
||||
'<a class="link-arrow" href="/video/">Zum Musikvideo '
|
||||
'<a class="link-arrow" href="/musik-und-buehne/">Musik & Bühne '
|
||||
+ ICONS["arrow"]
|
||||
+ "</a></div>",
|
||||
)
|
||||
@@ -1343,7 +1454,7 @@ def projekte() -> str:
|
||||
+ "".join(
|
||||
f'<article class="card reveal"><h3>{e(t)}</h3><p class="card__text">{e(d)}</p><div class="card__foot"><a class="link-arrow" href="{h}">Ansehen {ICONS["arrow"]}</a></div></article>'
|
||||
for t, d, h in [
|
||||
("Shootings", "Business, Portrait, Familie und Minishootings.", "/shootings/"),
|
||||
("Fotografie", "Menschen, Unternehmen, Produkte und Veranstaltungen.", "/fotografie/"),
|
||||
("Businessfotografie", "Portraits, Teams und Produkte für Unternehmen.", "/businessfotografie/"),
|
||||
("Videoprojekte", "Musikvideo, Imagefilm und Eventnachbericht.", "/video/"),
|
||||
]
|
||||
@@ -1367,8 +1478,8 @@ def ueber() -> str:
|
||||
page_head(
|
||||
kicker="Über",
|
||||
title="Fotograf und Videograf aus Neumünster",
|
||||
lead="Ich bin Denny Schulz. Ich fotografiere Menschen, Marken und Musik — und produziere seit einigen Jahren genauso gern Bewegtbild.",
|
||||
image="studio-setup",
|
||||
lead="Hinter desfoto stehe ich, Denny Schulz. Fotograf, Videograf und Musiker aus Neumünster.",
|
||||
image="gear-cases",
|
||||
breadcrumb=("Über", path),
|
||||
fit="50% 45%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Schreib mir</a>',
|
||||
@@ -1398,7 +1509,7 @@ def ueber() -> str:
|
||||
for t, d in [
|
||||
(
|
||||
"Menschen fotografieren",
|
||||
"Portrait, Business, Model, Familie und Paar. Die Arbeit an Gesichtern, Licht und Ausdruck ist mein Kern.",
|
||||
"Portrait, Business, Model und Familie. Die Arbeit an Gesichtern, Licht und Ausdruck ist mein Kern.",
|
||||
),
|
||||
(
|
||||
"Musik & Bühne begleiten",
|
||||
@@ -1443,9 +1554,9 @@ def ueber() -> str:
|
||||
content=media_grid(
|
||||
[
|
||||
("business-portrait-studio", "Mann im weißen Hemd vor dunklem Hintergrund, Softbox im Bild", "4x5", "50% 28%"),
|
||||
("studio-setup", "Studioequipment mit Logo von Denny Schulz Fotografie", "4x5", "50% 45%"),
|
||||
("studio-lens", "Kameraobjektiv im Studiolicht", "4x5", "50% 45%"),
|
||||
("business-hotel-window", "Drei Frauen in Bademänteln und Handtuchturbanen am Fenster", "4x5", "50% 40%"),
|
||||
("couple-park", "Sara und Carlos stehen Arm in Arm im Grünen", "4x5", "50% 30%"),
|
||||
("portrait-marina-snow", "Marina mit roten Zöpfen auf einem verschneiten Waldweg", "4x5", "50% 30%"),
|
||||
("free-fox", "Nahaufnahme eines Rotfuchses", "1x1", "50% 45%"),
|
||||
],
|
||||
columns="grid--2",
|
||||
@@ -1455,7 +1566,7 @@ def ueber() -> str:
|
||||
variant="paper2",
|
||||
kicker="Netzwerk",
|
||||
title="Wo ich sonst zu finden bin",
|
||||
lead="Zwei eigene Projekte, die zu desfoto gehören.",
|
||||
lead="Zwei eigene Projekte des Betreibers.",
|
||||
content='<div class="grid grid--2">'
|
||||
+ "".join(
|
||||
'<article class="card reveal">'
|
||||
@@ -1484,7 +1595,7 @@ def ueber() -> str:
|
||||
)
|
||||
return page(
|
||||
title="Über desfoto · Denny Schulz, Neumünster",
|
||||
description="desfoto ist die Foto- und Videoproduktion von Denny Schulz aus Neumünster. Arbeitsweise, mobiles Studio, Netzwerk und Kontaktmöglichkeiten.",
|
||||
description="desfoto steht für Fotografie, Video und mediale Produktionen von Denny Schulz aus Neumünster: Arbeitsweise, mobiles Studio, Musik & Bühne, Netzwerk und Kontakt.",
|
||||
path=path,
|
||||
json_ld=[
|
||||
breadcrumb_ld("Über", path),
|
||||
@@ -1494,7 +1605,7 @@ def ueber() -> str:
|
||||
"name": LEGAL["owner"],
|
||||
"jobTitle": "Fotograf und Videograf",
|
||||
"url": SITE["url"] + path,
|
||||
"worksFor": {"@type": "Organization", "name": LEGAL["business"], "url": SITE["url"]},
|
||||
"worksFor": {"@type": "Organization", "name": SITE["brand"], "url": SITE["url"] + "/"},
|
||||
"sameAs": [item["href"] for item in NETWORK],
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
@@ -1576,7 +1687,7 @@ def kontakt() -> str:
|
||||
title="Lieber direkt telefonieren?",
|
||||
text="Kein Problem. Ruf einfach an — falls ich gerade im Termin bin, melde ich mich zurück.",
|
||||
primary=("Anrufen", "tel:+491754083133"),
|
||||
secondary=("E-Mail schreiben", "mailto:info@dennyschulz.de"),
|
||||
secondary=("E-Mail schreiben", f"mailto:{CONTACT['email']}"),
|
||||
)
|
||||
)
|
||||
return page(
|
||||
@@ -1831,12 +1942,13 @@ def not_found() -> str:
|
||||
|
||||
ROUTES = [
|
||||
("/", "index.html", home, None, "1.0"),
|
||||
("/shootings/", "shootings/index.html", shootings, None, "0.9"),
|
||||
("/fotografie/", "fotografie/index.html", fotografie, None, "0.9"),
|
||||
("/businessfotografie/", "businessfotografie/index.html", businessfotografie, None, "0.9"),
|
||||
("/portrait-und-model/", "portrait-und-model/index.html", portrait_und_model, None, "0.9"),
|
||||
("/familien-und-paare/", "familien-und-paare/index.html", familien_und_paare, None, "0.9"),
|
||||
("/familie/", "familie/index.html", familie, None, "0.9"),
|
||||
("/minishootings/", "minishootings/index.html", minishootings, None, "0.9"),
|
||||
("/video/", "video/index.html", video, None, "0.9"),
|
||||
("/musik-und-buehne/", "musik-und-buehne/index.html", musik_und_buehne, None, "0.9"),
|
||||
("/social-media/", "social-media/index.html", social_media, None, "0.9"),
|
||||
("/projekte/", "projekte/index.html", projekte, None, "0.8"),
|
||||
("/ueber/", "ueber/index.html", ueber, None, "0.8"),
|
||||
|
||||
@@ -204,6 +204,11 @@ img.frame{width:100%;height:auto;object-fit:cover;transition:transform .8s var(-
|
||||
.grid--2{grid-template-columns:repeat(auto-fit,minmax(19rem,1fr))}
|
||||
.grid--3{grid-template-columns:repeat(auto-fit,minmax(16.5rem,1fr))}
|
||||
.grid--4{grid-template-columns:repeat(auto-fit,minmax(13.5rem,1fr))}
|
||||
/* Vier Arbeitsbereiche: vier Spalten auf großen, zwei auf mittleren und eine
|
||||
auf kleinen Schirmen. auto-fit würde bei mittlerer Breite 3+1 Karten setzen. */
|
||||
.grid--areas{grid-template-columns:1fr}
|
||||
@media (min-width:40rem){.grid--areas{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
@media (min-width:72rem){.grid--areas{grid-template-columns:repeat(4,minmax(0,1fr))}}
|
||||
.card{display:flex;flex-direction:column;gap:1rem;height:100%}
|
||||
.card__title{display:flex;align-items:baseline;gap:.6rem}
|
||||
.card__num{font-size:.72rem;font-weight:600;letter-spacing:.16em;color:var(--accent)}
|
||||
|
||||
Reference in New Issue
Block a user