feat: portfolio-led redesign with Shootings umbrella, original logo and motion layer
This commit is contained in:
178
src/content.py
178
src/content.py
@@ -18,12 +18,13 @@ SITE = {
|
||||
"theme_color": "#17140F",
|
||||
"default_description": (
|
||||
"desfoto ist die Foto- und Videoproduktion von Denny Schulz aus Neumünster: "
|
||||
"Businessfotografie, Portraits, Model- und Familienaufnahmen, Minishootings, "
|
||||
"Videoprojekte und Social-Media-Inhalte."
|
||||
"Shootings für Business, Portrait, Model und Familien, Minishootings, das mobile "
|
||||
"Studio mit autarker Blitzanlage sowie Videoprojekte und Social-Media-Inhalte."
|
||||
),
|
||||
"keywords": (
|
||||
"Fotograf Neumünster, Videograf Neumünster, Businessfotografie, Businessportrait, "
|
||||
"Model-Shooting, Familienfotograf, Minishooting, Musikvideo, Social Media Inhalte, "
|
||||
"Fotograf Neumünster, Videograf Neumünster, Shootings, Businessfotografie, "
|
||||
"Businessportrait, Model-Shooting, Familienfotograf, Minishooting, mobiles Studio, "
|
||||
"Blitzanlage, Musikvideo, Konzertfotografie, Social Media Inhalte, "
|
||||
"Schleswig-Holstein"
|
||||
),
|
||||
}
|
||||
@@ -67,8 +68,8 @@ VIDEO = {
|
||||
|
||||
NAV = [
|
||||
{
|
||||
"label": "Fotografie",
|
||||
"href": "/fotografie/",
|
||||
"label": "Shootings",
|
||||
"href": "/shootings/",
|
||||
"children": [
|
||||
("Businessfotografie", "/businessfotografie/"),
|
||||
("Portrait & Model", "/portrait-und-model/"),
|
||||
@@ -77,11 +78,128 @@ NAV = [
|
||||
],
|
||||
},
|
||||
{"label": "Video", "href": "/video/"},
|
||||
{"label": "Social Media", "href": "/social-media/"},
|
||||
{"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 = [
|
||||
{
|
||||
"slug": "foto",
|
||||
"label": "Foto",
|
||||
"href": "/shootings/",
|
||||
"title": "Shootings",
|
||||
"lead": "Der Überbegriff für alle fotografischen Arbeiten.",
|
||||
"text": (
|
||||
"Businessfotografie, Portrait & Model, Familien & Paare und Minishootings — "
|
||||
"ein Shooting, ein Ablauf, ein Ansprechpartner. Du wählst nur, was du "
|
||||
"wirklich brauchst."
|
||||
),
|
||||
"points": [
|
||||
("Businessfotografie", "/businessfotografie/"),
|
||||
("Portrait & Model", "/portrait-und-model/"),
|
||||
("Familien & Paare", "/familien-und-paare/"),
|
||||
("Minishootings", "/minishootings/"),
|
||||
],
|
||||
"image": "business-portrait-studio",
|
||||
"fit": "50% 30%",
|
||||
},
|
||||
{
|
||||
"slug": "video",
|
||||
"label": "Video",
|
||||
"href": "/video/",
|
||||
"title": "Videoprojekte",
|
||||
"lead": "Bewegtbild von der Idee bis zum fertigen Schnitt.",
|
||||
"text": (
|
||||
"Musikvideo, Imagefilm, Eventfilm und Formate für Social Media — "
|
||||
"gedreht, geschnitten und ausgeliefert in dem Format, in dem du es "
|
||||
"veröffentlichst."
|
||||
),
|
||||
"points": [
|
||||
("Videoprojekte", "/video/"),
|
||||
("Social Media", "/social-media/"),
|
||||
],
|
||||
"image": "studio-lens",
|
||||
"fit": "50% 45%",
|
||||
},
|
||||
]
|
||||
|
||||
# Was in einem Shooting steckt — die gemeinsamen Eckpunkte aller Shootings.
|
||||
SHOOTING_STEPS = [
|
||||
{
|
||||
"title": "Vorab klären",
|
||||
"text": "Wir sprechen kurz über Anlass, Ort und gewünschte Bildsprache. Daraus ergibt sich der Umfang.",
|
||||
},
|
||||
{
|
||||
"title": "Licht planen",
|
||||
"text": "Ob Tageslicht, mobiles Studio oder Bühne: Das Licht entscheidet, wie nah das Ergebnis an deiner Vorstellung liegt.",
|
||||
},
|
||||
{
|
||||
"title": "Fotografieren",
|
||||
"text": "Am Termin führe ich durch den Ablauf, gebe Haltung und Tempo vor und löse im richtigen Moment aus.",
|
||||
},
|
||||
{
|
||||
"title": "Auswählen",
|
||||
"text": "Du bekommst eine geordnete Auswahl und die bearbeiteten Bilddateien in den Größen, die du brauchst.",
|
||||
},
|
||||
]
|
||||
|
||||
# Das mobile Studio: der Betrieb kommt mit Licht und Hintergrund zum Termin.
|
||||
STUDIO = {
|
||||
"title": "Mobiles Studio",
|
||||
"lead": "Das Studio kommt zu dir.",
|
||||
"text": [
|
||||
(
|
||||
"Nicht jedes Team kann für ein Shooting den Betrieb verlassen. Deshalb"
|
||||
" baue ich das Studio dort auf, wo deine Leute sowieso sind: im"
|
||||
" Besprechungsraum, in der Werkstatt, im Vereinsheim oder draußen vor"
|
||||
" der Halle."
|
||||
),
|
||||
(
|
||||
"Dabei ist die komplette Ausrüstung dabei — Hintergründe, Blitze,"
|
||||
" Lichtformer und Ersatztechnik. Die Blitzanlage ist autark, ich bin"
|
||||
" vor Ort also sofort einsatzbereit und brauche nichts aus deinem"
|
||||
" Haushalt."
|
||||
),
|
||||
],
|
||||
"features": [
|
||||
{
|
||||
"title": "Komplette Ausrüstung",
|
||||
"text": "Kamera, Objektive, Hintergründe, Blitze und Lichtformer kommen mit.",
|
||||
},
|
||||
{
|
||||
"title": "Autarke Blitzanlage",
|
||||
"text": "Das Licht ist im Gepäck und in wenigen Minuten aufgebaut.",
|
||||
},
|
||||
{
|
||||
"title": "Dein Ort, deine Zeit",
|
||||
"text": "Geschossen wird zwischen zwei Terminen, in der Mittagspause oder nach Feierabend.",
|
||||
},
|
||||
{
|
||||
"title": "Überall in SH & Hamburg",
|
||||
"text": "Vom Büro in Neumünster bis zum Betrieb in Kiel, Lübeck oder Hamburg.",
|
||||
},
|
||||
],
|
||||
"image": "studio-setup",
|
||||
}
|
||||
|
||||
# Netzwerk: eigene Projekte des Betreibers (ausdrücklicher Wunsch des Betreibers).
|
||||
NETWORK = [
|
||||
{
|
||||
"label": "dennyschulz.de",
|
||||
"href": "https://www.dennyschulz.de/",
|
||||
"title": "dennyschulz.de",
|
||||
"text": "Hochzeiten, Portraits und Business — die fotografische Arbeit unter eigenem Namen.",
|
||||
},
|
||||
{
|
||||
"label": "dennyapp.de",
|
||||
"href": "https://dennyapp.de/",
|
||||
"title": "dennyapp.de",
|
||||
"text": "Websites und Web-Apps aus Neumünster — Design, Entwicklung und eigene Produkte.",
|
||||
},
|
||||
]
|
||||
|
||||
SERVICES = [
|
||||
{
|
||||
"slug": "businessfotografie",
|
||||
@@ -152,21 +270,23 @@ SERVICES = [
|
||||
},
|
||||
]
|
||||
|
||||
# Fotografie-Uebersicht
|
||||
# Fotografie-Übersicht: Shootings als Überbegriff über den einzelnen Leistungen.
|
||||
PHOTO_OVERVIEW = [
|
||||
{
|
||||
"title": "Menschen",
|
||||
"title": "Shootings",
|
||||
"text": (
|
||||
"Portraits, Sedcards, Bewerbungsbilder und Familienaufnahmen. "
|
||||
"Wir arbeiten mit Licht, das zu dir passt — nicht mit dem Standardschema."
|
||||
"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."
|
||||
),
|
||||
"image": "editorial-pose",
|
||||
},
|
||||
{
|
||||
"title": "Marken & Unternehmen",
|
||||
"title": "Mobiles Studio",
|
||||
"text": (
|
||||
"Team-, Standort- und Produktbilder, die auf Website, Kampagne und in "
|
||||
"Stellenanzeigen funktionieren."
|
||||
"Komplette Ausrüstung inklusive autarker Blitzanlage, Hintergründen und "
|
||||
"Lichtformern — aufgebaut an deinem Ort, statt umgekehrt."
|
||||
),
|
||||
"image": "business-portrait-sit",
|
||||
},
|
||||
@@ -176,18 +296,18 @@ PHOTO_OVERVIEW = [
|
||||
"Feiern, Events, Vereinsleben und Premieren. Dokumentarisch begleitet, "
|
||||
"ohne den Ablauf auszubremsen."
|
||||
),
|
||||
"image": "event-dance",
|
||||
"image": "group-feier",
|
||||
},
|
||||
]
|
||||
|
||||
WORK_GALLERY = [
|
||||
("editorial-pose", "Model-Shooting im Studio", "g-w3", "4x5", "50% 28%"),
|
||||
("travel-norway-pano", "Norwegen — Panorama", "g-w3", "4x5", "50% 50%"),
|
||||
("people-forest", "Paarportrait im Wald", "g-w2", "1x1", "50% 26%"),
|
||||
("editorial-back", "Editorial — Detail", "g-w2", "1x1", "50% 40%"),
|
||||
("group-outdoor", "Feier im Freien", "g-w2", "1x1", "50% 36%"),
|
||||
("movement-light", "Lichtspur aus langer Belichtung", "g-w3", "3x2", "50% 50%"),
|
||||
("business-portrait-dark", "Businessportrait im Studiolicht", "g-w3", "3x2", "50% 30%"),
|
||||
("music-guitar-silhouette", "Gitarrist als Silhouette im Bühnennebel", "g-w3", "4x5", "50% 42%"),
|
||||
("free-fjord", "Steile Felsnadeln vor dem Meer", "g-w3", "4x5", "50% 50%"),
|
||||
("music-strings", "Nahaufnahme einer Hand an den Saiten", "g-w2", "1x1", "50% 50%"),
|
||||
("free-cat", "Nahaufnahme einer getigerten Katze", "g-w2", "1x1", "50% 42%"),
|
||||
("business-portrait-dark", "Businessportrait im Studiolicht", "g-w2", "1x1", "50% 32%"),
|
||||
("free-fog-forest", "Nebel über einem Nadelwald", "g-w3", "3x2", "50% 45%"),
|
||||
("editorial-colour", "Model-Shooting im Studio", "g-w3", "3x2", "50% 30%"),
|
||||
]
|
||||
|
||||
PROCESS = [
|
||||
@@ -208,8 +328,9 @@ PROCESS = [
|
||||
{
|
||||
"title": "Dreh",
|
||||
"text": (
|
||||
"Am Termin übernehme ich Technik, Licht und Regie. Du musst nichts "
|
||||
"mitbringen außer Zeit und dich."
|
||||
"Am Termin übernehme ich Technik, Licht und Regie — auf Wunsch mit dem "
|
||||
"mobilen Studio inklusive autarker Blitzanlage an deinem Ort. Du musst "
|
||||
"nichts mitbringen außer Zeit und dich."
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -222,6 +343,10 @@ PROCESS = [
|
||||
]
|
||||
|
||||
FACTS = [
|
||||
{
|
||||
"title": "Mobiles Studio",
|
||||
"text": "Komplette Ausrüstung mit autarker Blitzanlage, Hintergründen und Lichtformern — aufgebaut an deinem Ort.",
|
||||
},
|
||||
{
|
||||
"title": "Aus Neumünster",
|
||||
"text": "Zuhause zwischen Kiel und Hamburg — unterwegs in Schleswig-Holstein und darüber hinaus.",
|
||||
@@ -234,13 +359,10 @@ FACTS = [
|
||||
"title": "Klarer Ablauf",
|
||||
"text": "Umfang, Termin und Preis klären wir vor der Buchung. Keine Überraschungen auf der Rechnung.",
|
||||
},
|
||||
{
|
||||
"title": "Schnelle Lieferung",
|
||||
"text": "Kleine Aufträge bekommst du meist innerhalb weniger Tage, größere Projekte nach Absprache.",
|
||||
},
|
||||
]
|
||||
|
||||
FOCUS_ITEMS = [
|
||||
("Shootings", "/shootings/"),
|
||||
("Businessfotografie", "/businessfotografie/"),
|
||||
("Portrait & Model", "/portrait-und-model/"),
|
||||
("Familien & Paare", "/familien-und-paare/"),
|
||||
|
||||
197
src/images.json
197
src/images.json
@@ -1,13 +1,8 @@
|
||||
{
|
||||
"_comment": "Quellbilder aus dem eigenen Bilderpool (Orgatool-Volume business-suite_app_data/website). 'slug' wird zum Dateinamen der Auslieferungsderivate. 'alt' ist die redaktionelle Bildbeschreibung fuer Alternativtexte. 'raw' ist der Pfad im Pool.",
|
||||
"_comment": "Bildquellen des Betreibers (Denny Schulz). 'source': 'pool' = eigener Bilderpool (Orgatool-Volume, ausgeliefert ueber die eigene API), 'legacy' = eigene Portfolio-Dateien der bestehenden Seite www.dennyschulz.de/legacy/. 'slug' wird zum Dateinamen der Auslieferungsderivate. 'alt' ist die redaktionelle Bildbeschreibung fuer Alternativtexte; wo moeglich, stammt sie aus dem eigenen Portfolio. 'raw' ist der Pfad in der jeweiligen Quelle. Rechte liegen beim Betreiber (eigene Aufnahmen).",
|
||||
"pool_base": "https://orga.dennyschulz.de/api/website/media/files",
|
||||
"legacy_base": "https://www.dennyschulz.de/legacy",
|
||||
"images": [
|
||||
{
|
||||
"slug": "hero-studio",
|
||||
"raw": "Studio/1777027118997_1b1a51a6_dennyschulz_-07282.jpg",
|
||||
"alt": "Hand an einer Kamera im Studio bei violettem Licht",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "studio-lens",
|
||||
"raw": "Studio/1777027118366_91e2a99d_dennyschulz_-07285.jpg",
|
||||
@@ -77,91 +72,73 @@
|
||||
{
|
||||
"slug": "editorial-colour",
|
||||
"raw": "1776343219659_8daa3076_dennyschulz_-02946.jpg",
|
||||
"alt": "Model in dunklem Kleid in farbigem Licht",
|
||||
"focus": "50% 35%"
|
||||
"alt": "Fashion-Portrait in kräftigen Farben",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "portrait-natural",
|
||||
"raw": "1784293240857_deae4788_dennyschulz_-07255.jpg",
|
||||
"alt": "Lachendes Paar im natürlichen Licht",
|
||||
"focus": "50% 35%"
|
||||
"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% 35%"
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "people-joy",
|
||||
"raw": "1776343134779_e893c351_dennyschulz_-03745114107.jpg",
|
||||
"alt": "Frau mit Blumenstrauß lacht in die Kamera",
|
||||
"focus": "50% 35%"
|
||||
"alt": "Paar lacht im Abendlicht",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "people-kiss",
|
||||
"raw": "1776343151721_e3f65ef1_dennyschulz_-04483.jpg",
|
||||
"alt": "Paar in inniger Umarmung",
|
||||
"focus": "50% 40%"
|
||||
},
|
||||
{
|
||||
"slug": "people-forest",
|
||||
"raw": "Portfolio/hochzeit/1788686124131_7ce60a11_280826_dennyschulz_fav-18.jpg",
|
||||
"alt": "Paar im Wald, Portrait",
|
||||
"alt": "Paar küsst sich vor hellem Hintergrund",
|
||||
"focus": "50% 30%"
|
||||
},
|
||||
{
|
||||
"slug": "people-veil",
|
||||
"raw": "Portfolio/hochzeit/1788686144082_7e69d21e_280826_dennyschulz_fav-22.jpg",
|
||||
"alt": "Paar unter einem hellen Schleier, Gegenlicht",
|
||||
"focus": "50% 40%"
|
||||
},
|
||||
{
|
||||
"slug": "people-walk",
|
||||
"raw": "Portfolio/hochzeit/1788347325902_9093b843_280826_feier-3.jpg",
|
||||
"alt": "Paar geht Hand in Hand ueber einen Waldweg",
|
||||
"focus": "50% 40%"
|
||||
},
|
||||
{
|
||||
"slug": "group-feier",
|
||||
"raw": "1777033639756_1f5a9dc4_dennyschulz_-03904.jpg",
|
||||
"alt": "Gruppe feiert gemeinsam vor einem Gebäude",
|
||||
"focus": "50% 45%"
|
||||
"alt": "Feiergesellschaft bei einer Veranstaltung",
|
||||
"focus": "50% 36%"
|
||||
},
|
||||
{
|
||||
"slug": "group-outdoor",
|
||||
"raw": "1784293233172_7a8a3ed9_dennyschulz_-08435.jpg",
|
||||
"alt": "Gruppe bei einer Feier im Freien",
|
||||
"focus": "50% 40%"
|
||||
"alt": "Gruppe posiert im Freien",
|
||||
"focus": "50% 36%"
|
||||
},
|
||||
{
|
||||
"slug": "group-posiert",
|
||||
"raw": "1784293233955_033a5079_dennyschulz_-08456.jpg",
|
||||
"alt": "Gruppe posiert im Grünen",
|
||||
"focus": "50% 45%"
|
||||
"alt": "Gruppe steht zusammen vor der Kamera",
|
||||
"focus": "50% 38%"
|
||||
},
|
||||
{
|
||||
"slug": "event-motion",
|
||||
"raw": "1776670469051_dc2993ae_dennyschulz_-04663.jpg",
|
||||
"alt": "Menschen in Bewegung, bewusst unscharf aufgenommen",
|
||||
"alt": "Bewegung auf einer Veranstaltung",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "event-speaker",
|
||||
"raw": "1784293225986_a37d21f4_dennyschulz_-07139.jpg",
|
||||
"alt": "Frau spricht mit Mikrofon bei einer Veranstaltung",
|
||||
"alt": "Redner auf einer Veranstaltung",
|
||||
"focus": "50% 35%"
|
||||
},
|
||||
{
|
||||
"slug": "event-venue",
|
||||
"raw": "1788932455845_78e0851f_dennyschulz_-00451.jpg",
|
||||
"alt": "Stuhlreihen in einem festlich vorbereiteten Raum",
|
||||
"focus": "50% 40%"
|
||||
"alt": "Veranstaltungsort mit Gästen",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "event-dance",
|
||||
"raw": "1784293226489_fa1b9420_dennyschulz_-09046.jpg",
|
||||
"alt": "Paar tanzt unter einem Kronleuchter",
|
||||
"alt": "Tanzfläche bei einer Feier",
|
||||
"focus": "50% 40%"
|
||||
},
|
||||
{
|
||||
@@ -171,40 +148,130 @@
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "travel-japan",
|
||||
"raw": "1776263908372_dennyschulz_-05756.jpg",
|
||||
"alt": "Torii-Tor in einem moosgrünen Garten in Japan",
|
||||
"focus": "45% 45%"
|
||||
"slug": "music-vocal-blue",
|
||||
"source": "legacy",
|
||||
"raw": "_DES3495.jpg",
|
||||
"alt": "Sänger mit erhobenem Arm im blauen Bühnenlicht",
|
||||
"focus": "50% 28%"
|
||||
},
|
||||
{
|
||||
"slug": "travel-norway-lake",
|
||||
"raw": "1776264398432_dennyschulz_-1044.jpg",
|
||||
"alt": "Spiegelung einer Hütte auf einem See in Norwegen",
|
||||
"slug": "music-guitar-silhouette",
|
||||
"source": "legacy",
|
||||
"raw": "_DES3745.jpg",
|
||||
"alt": "Gitarrist als Silhouette im Bühnennebel",
|
||||
"focus": "50% 42%"
|
||||
},
|
||||
{
|
||||
"slug": "music-strings",
|
||||
"source": "legacy",
|
||||
"raw": "_DES3153.jpg",
|
||||
"alt": "Nahaufnahme einer Hand an den Saiten einer E-Gitarre",
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "travel-norway-coast",
|
||||
"raw": "1776332816743_c0361ec0_dennyschulz.jpg",
|
||||
"alt": "Felsküste an einem Fjord in Norwegen",
|
||||
"slug": "music-drums",
|
||||
"source": "legacy",
|
||||
"raw": "_DES3561.jpg",
|
||||
"alt": "Schlagzeuger am Drumset in Schwarzweiß",
|
||||
"focus": "50% 35%"
|
||||
},
|
||||
{
|
||||
"slug": "music-mic",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz_Jarl_-11.jpg",
|
||||
"alt": "Bärtiger Sänger am Mikrofon mit erhobener Hand",
|
||||
"focus": "50% 26%"
|
||||
},
|
||||
{
|
||||
"slug": "band-extinct",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz_20231211-107.jpg",
|
||||
"alt": "Die Band Extinct steht hintereinander vor schwarzem Hintergrund",
|
||||
"focus": "50% 32%"
|
||||
},
|
||||
{
|
||||
"slug": "band-sagenbringer",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz23-3422.jpg",
|
||||
"alt": "Musiker in Fellkleidung im Wald, von unten fotografiert",
|
||||
"focus": "50% 28%"
|
||||
},
|
||||
{
|
||||
"slug": "free-fjord",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC6468.jpg",
|
||||
"alt": "Steile Felsnadeln vor dem Meer",
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "travel-norway-valley",
|
||||
"raw": "1776333775618_94cc71b8_dennyschulz_--2.jpeg",
|
||||
"alt": "Flusstal zwischen Bergen im Morgennebel",
|
||||
"slug": "free-lake-person",
|
||||
"source": "legacy",
|
||||
"raw": "20230830_dennyschulz-8903.jpg",
|
||||
"alt": "Eine Person am Bergsee spiegelt sich im Wasser",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "free-fog-forest",
|
||||
"source": "legacy",
|
||||
"raw": "20230830_dennyschulz-8377.jpg",
|
||||
"alt": "Nebel zieht durch einen Nadelwald am Berghang",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "free-mountain-view",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC0477-f4c84b16.jpg",
|
||||
"alt": "Eine Person sitzt über einer wolkenverhangenen Berglandschaft",
|
||||
"focus": "50% 38%"
|
||||
},
|
||||
{
|
||||
"slug": "free-fox",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC8399.jpg",
|
||||
"alt": "Nahaufnahme eines Rotfuchses",
|
||||
"focus": "50% 45%"
|
||||
},
|
||||
{
|
||||
"slug": "free-cat",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC7748.jpg",
|
||||
"alt": "Nahaufnahme einer getigerten Katze im Seitenlicht",
|
||||
"focus": "50% 42%"
|
||||
},
|
||||
{
|
||||
"slug": "free-golden-grass",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC0730.jpg",
|
||||
"alt": "Gräser im goldenen Licht des Sonnenuntergangs",
|
||||
"focus": "50% 50%"
|
||||
},
|
||||
{
|
||||
"slug": "travel-norway-pano",
|
||||
"raw": "1776346804976_5ab84574_dennyschulz_pano2.jpg",
|
||||
"alt": "Panorama einer Berglandschaft in Norwegen",
|
||||
"focus": "50% 50%"
|
||||
"slug": "free-woman-coast",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC8274.jpg",
|
||||
"alt": "Eine Frau mit Hut steht an einer felsigen Küste",
|
||||
"focus": "50% 32%"
|
||||
},
|
||||
{
|
||||
"slug": "travel-blossom",
|
||||
"raw": "1776344687313_d34fd8f9_dennyschulz_-05183.jpg",
|
||||
"alt": "Kirschblüte mit einem Eichhörnchen",
|
||||
"focus": "50% 40%"
|
||||
"slug": "free-modern-portrait",
|
||||
"source": "legacy",
|
||||
"raw": "_DSC2415.jpg",
|
||||
"alt": "Portrait einer Frau mit Sonnenbrille",
|
||||
"focus": "50% 28%"
|
||||
},
|
||||
{
|
||||
"slug": "free-portrait-light",
|
||||
"source": "legacy",
|
||||
"raw": "_DS_5501.jpg",
|
||||
"alt": "Frauenportrait im weichen Seitenlicht vor dunklem Hintergrund",
|
||||
"focus": "50% 28%"
|
||||
},
|
||||
{
|
||||
"slug": "free-arch-garden",
|
||||
"source": "legacy",
|
||||
"raw": "dennyschulz_-03704.jpg",
|
||||
"alt": "Blick durch einen steinernen Torbogen in einen Garten",
|
||||
"focus": "50% 45%"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import html
|
||||
import json
|
||||
|
||||
from content import CONTACT, LEGAL, NAV, SITE
|
||||
from content import CONTACT, LEGAL, NAV, NETWORK, SITE
|
||||
|
||||
# Filled by scripts/build-site.py before rendering: slug -> derivatives metadata.
|
||||
IMAGE_TABLE: dict[str, dict] = {}
|
||||
@@ -17,19 +17,27 @@ ICONS = {
|
||||
"external": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14 5h5v5M19 5l-7 7M18 14v4a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h4"/></svg>',
|
||||
}
|
||||
|
||||
MARK_SVG = """<svg class="brand__mark" viewBox="0 0 40 40" role="img" aria-label="desfoto Bildmarke">
|
||||
<circle class="ring" cx="20" cy="20" r="13"/>
|
||||
<circle class="ring" cx="20" cy="20" r="5.2" stroke-width="2"/>
|
||||
<path class="blade" d="M20 7 20 1.6"/>
|
||||
<path class="blade" d="M31.3 26.5 36 29.2"/>
|
||||
<path class="blade" d="M8.7 26.5 4 29.2"/>
|
||||
</svg>"""
|
||||
BRAND_MARK = (
|
||||
'<span class="brand__mark">'
|
||||
'<img src="/assets/img/logo-160.png" width="160" height="160" alt="" decoding="async">'
|
||||
"</span>"
|
||||
)
|
||||
|
||||
|
||||
def e(value: object) -> str:
|
||||
return html.escape(str(value), quote=True)
|
||||
|
||||
|
||||
def words(text: str, *, start: int = 0) -> str:
|
||||
"""Wrap every word in a masked span so hero titles can rise word by word."""
|
||||
parts = []
|
||||
for index, word in enumerate(text.split()):
|
||||
parts.append(
|
||||
f'<span class="w" style="--i:{index + start}"><span>{e(word)}</span></span>'
|
||||
)
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
def picture(
|
||||
slug: str,
|
||||
sizes: str,
|
||||
@@ -154,6 +162,7 @@ def head(
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="stylesheet" href="/assets/fonts.css">
|
||||
<link rel="stylesheet" href="/assets/site.css">
|
||||
<noscript><style>.reveal,.reveal--mask{{opacity:1;transform:none;clip-path:none}}.reveal--mask>*{{clip-path:none}}.hero__title .w>span,.hero__sub,.hero__actions,.hero__facts{{opacity:1;transform:none}}</style></noscript>
|
||||
{preload_html}
|
||||
{ld}
|
||||
</head>
|
||||
@@ -162,7 +171,7 @@ def head(
|
||||
<header class="masthead">
|
||||
<div class="shell masthead__inner">
|
||||
<a class="brand" href="/" aria-label="desfoto Startseite">
|
||||
{MARK_SVG}
|
||||
{BRAND_MARK}
|
||||
<span class="brand__text"><span>desfoto</span><small>{e(SITE['claim'])}</small></span>
|
||||
</a>
|
||||
<button class="nav__toggle" type="button" aria-expanded="false" aria-controls="site-nav" data-nav-toggle>
|
||||
@@ -170,25 +179,32 @@ def head(
|
||||
</button>
|
||||
<nav class="nav" id="site-nav" aria-label="Hauptnavigation">{nav_html(path)}</nav>
|
||||
</div>
|
||||
<span class="progress" aria-hidden="true"><span class="progress__bar" data-progress></span></span>
|
||||
</header>
|
||||
<main id="main">"""
|
||||
|
||||
|
||||
def footer_html() -> str:
|
||||
network_items = "".join(
|
||||
f'<a class="footer__ext" href="{e(item["href"])}" target="_blank" rel="noopener">'
|
||||
f'{e(item["title"])} {ICONS["external"]}</a><small>{e(item["text"])}</small>'
|
||||
for item in NETWORK
|
||||
)
|
||||
return f"""</main>
|
||||
<footer class="footer">
|
||||
<div class="shell">
|
||||
<div class="footer__grid">
|
||||
<div class="footer__brand">
|
||||
<span class="brand">
|
||||
{MARK_SVG}
|
||||
{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>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Fotografie</h2>
|
||||
<h2>Shootings</h2>
|
||||
<div class="footer__links">
|
||||
<a href="/shootings/">Überblick Shootings</a>
|
||||
<a href="/businessfotografie/">Businessfotografie</a>
|
||||
<a href="/portrait-und-model/">Portrait & Model</a>
|
||||
<a href="/familien-und-paare/">Familien & Paare</a>
|
||||
@@ -196,7 +212,7 @@ def footer_html() -> str:
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Weitere Leistungen</h2>
|
||||
<h2>Video & mehr</h2>
|
||||
<div class="footer__links">
|
||||
<a href="/video/">Videoprojekte</a>
|
||||
<a href="/social-media/">Social Media</a>
|
||||
@@ -213,6 +229,10 @@ def footer_html() -> str:
|
||||
<span class="muted">{e(CONTACT['region'])}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Netzwerk</h2>
|
||||
<div class="footer__links">{network_items}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer__bottom">
|
||||
<nav aria-label="Rechtliches">
|
||||
|
||||
525
src/pages.py
525
src/pages.py
@@ -10,14 +10,18 @@ from content import (
|
||||
CONTACT,
|
||||
FACTS,
|
||||
LEGAL,
|
||||
NETWORK,
|
||||
PHOTO_OVERVIEW,
|
||||
PILLARS,
|
||||
PROCESS,
|
||||
SERVICES,
|
||||
SHOOTING_STEPS,
|
||||
SITE,
|
||||
STUDIO,
|
||||
VIDEO,
|
||||
WORK_GALLERY,
|
||||
)
|
||||
from layout import ICONS, cta_band, e, page, page_head, picture, preload, section
|
||||
from layout import ICONS, cta_band, e, page, page_head, picture, preload, section, words
|
||||
|
||||
# ---------------------------------------------------------------- helper bits
|
||||
|
||||
@@ -34,14 +38,14 @@ def service_cards(slugs: list[str] | None = None) -> str:
|
||||
f'<div class="card__foot"><a class="link-arrow" href="{e(service["href"])}">Mehr erfahren {ICONS["arrow"]}</a></div>'
|
||||
"</article>"
|
||||
)
|
||||
return '<div class="grid grid--3">' + "".join(cards) + "</div>"
|
||||
return '<div class="grid grid--3" data-stagger="0.08">' + "".join(cards) + "</div>"
|
||||
|
||||
|
||||
def work_gallery() -> str:
|
||||
figures = []
|
||||
for slug, caption, width_class, ratio, fit in WORK_GALLERY:
|
||||
figures.append(
|
||||
f'<figure class="{width_class}">'
|
||||
f'<figure class="{width_class} reveal reveal--mask">'
|
||||
+ picture(
|
||||
slug,
|
||||
"(min-width: 62rem) 45vw, 92vw",
|
||||
@@ -51,7 +55,7 @@ def work_gallery() -> str:
|
||||
)
|
||||
+ f"<figcaption>{e(caption)}</figcaption></figure>"
|
||||
)
|
||||
return '<div class="gallery">' + "".join(figures) + "</div>"
|
||||
return '<div class="gallery" data-stagger="0.09">' + "".join(figures) + "</div>"
|
||||
|
||||
|
||||
def process_steps() -> str:
|
||||
@@ -99,6 +103,129 @@ def two_col(*, left: str, right: str, ratio: str = "minmax(0,1fr) minmax(0,1fr)"
|
||||
return f'<div class="grid" style="grid-template-columns:{ratio}">{left}{right}</div>'
|
||||
|
||||
|
||||
def media_grid(
|
||||
items: list[tuple[str, str, str, str]],
|
||||
*,
|
||||
columns: str = "grid--3",
|
||||
stagger: str = "0.09",
|
||||
) -> str:
|
||||
"""Responsive image grid; items are (slug, alt, ratio, object-position)."""
|
||||
cells = "".join(
|
||||
'<div class="reveal reveal--mask">'
|
||||
+ picture(
|
||||
slug,
|
||||
"(min-width: 62rem) 30vw, (min-width: 40rem) 45vw, 92vw",
|
||||
css_class=f"frame frame--{ratio}",
|
||||
fit=fit,
|
||||
alt=alt,
|
||||
)
|
||||
+ "</div>"
|
||||
for slug, alt, ratio, fit in items
|
||||
)
|
||||
return f'<div class="grid {columns}" data-stagger="{stagger}">{cells}</div>'
|
||||
|
||||
|
||||
def caption_gallery(
|
||||
items: list[tuple[str, str, str, str, str]],
|
||||
*,
|
||||
stagger: str = "0.08",
|
||||
) -> str:
|
||||
"""Editorial gallery with captions; items match the WORK_GALLERY tuples."""
|
||||
figures = "".join(
|
||||
f'<figure class="{width_class}">'
|
||||
+ picture(
|
||||
slug,
|
||||
"(min-width: 62rem) 45vw, 92vw",
|
||||
css_class=f"frame frame--{ratio}",
|
||||
fit=fit,
|
||||
alt=caption,
|
||||
)
|
||||
+ f"<figcaption>{e(caption)}</figcaption></figure>"
|
||||
for slug, caption, width_class, ratio, fit in items
|
||||
)
|
||||
return f'<div class="gallery" data-stagger="{stagger}">{figures}</div>'
|
||||
|
||||
|
||||
def pillar_cards() -> str:
|
||||
"""The two big entry cards: Shootings (photo) and Videoprojekte (video).
|
||||
|
||||
The body carries its own links, so the card 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"]
|
||||
)
|
||||
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>"
|
||||
)
|
||||
return '<div class="pillars" data-stagger="0.12">' + "".join(cards) + "</div>"
|
||||
|
||||
|
||||
def marquee(items: list[str]) -> str:
|
||||
"""CSS-only running band; the list is duplicated for a seamless loop."""
|
||||
spans = "".join(f"<span>{e(item)}</span>" for item in items)
|
||||
return (
|
||||
'<div class="marquee" aria-hidden="true">'
|
||||
f'<div class="marquee__track">{spans}{spans}</div>'
|
||||
"</div>"
|
||||
)
|
||||
|
||||
|
||||
def studio_section(*, variant: str = "paper2", tight: bool = False) -> str:
|
||||
"""The mobile studio: complete kit including a self-sufficient flash system."""
|
||||
features = "".join(
|
||||
f'<div><h3>{e(feature["title"])}</h3><p>{e(feature["text"])}</p></div>'
|
||||
for feature in STUDIO["features"]
|
||||
)
|
||||
paragraphs = "".join(f"<p>{e(paragraph)}</p>" for paragraph in STUDIO["text"])
|
||||
content = (
|
||||
'<div class="studio">'
|
||||
'<div class="studio__media reveal reveal--mask">'
|
||||
+ picture(
|
||||
STUDIO["image"],
|
||||
"(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>"
|
||||
"</div>"
|
||||
'<div class="stack reveal reveal--right">'
|
||||
+ paragraphs
|
||||
+ f'<div class="studio__features" data-stagger="0.07">{features}</div>'
|
||||
"</div></div>"
|
||||
)
|
||||
return section(
|
||||
kicker="Mobiles Studio",
|
||||
title="Das Studio kommt zu dir",
|
||||
lead="Komplette Ausrüstung inklusive autarker Blitzanlage — aufgebaut dort, wo deine Leute sind.",
|
||||
content=content,
|
||||
variant=variant,
|
||||
tight=tight,
|
||||
)
|
||||
|
||||
|
||||
def contact_form() -> str:
|
||||
return f"""<form class="form" data-contact-form novalidate action="mailto:{e(CONTACT['email'])}" method="post" enctype="text/plain">
|
||||
<div class="field-row">
|
||||
@@ -192,33 +319,34 @@ def service_ld(name: str, path: str, description: str) -> dict:
|
||||
def home() -> str:
|
||||
hero = f"""<section class="hero">
|
||||
<div class="hero__media">
|
||||
{picture('hero-studio', '100vw', css_class='', priority=True, loading='eager', fit='50% 45%', alt='Hand an einer Kamera im Studio bei violettem Licht')}
|
||||
{picture('music-vocal-blue', '100vw', css_class='', priority=True, loading='eager', fit='50% 32%', alt='Sänger mit erhobenem Arm im blauen Bühnenlicht')}
|
||||
</div>
|
||||
<div class="shell hero__inner">
|
||||
<span class="kicker hero__kicker">{e(SITE['tagline'])}</span>
|
||||
<h1 class="hero__title">Bilder und Filme, die nicht beliebig aussehen.</h1>
|
||||
<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 — für Unternehmen,
|
||||
Agenturen, Models, Vereine und Menschen, die sich selbst zeigen wollen.
|
||||
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.
|
||||
</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>Mobiles Studio</strong>Komplette Ausrüstung inklusive autarker Blitzanlage vor Ort.</div>
|
||||
<div class="hero__fact"><strong>Foto & Video</strong>Beides aus einer Hand, damit Optik und Ton zusammenpassen.</div>
|
||||
<div class="hero__fact"><strong>Studio & on location</strong>In Neumünster und überall dort, wo dein Projekt spielt.</div>
|
||||
<div class="hero__fact"><strong>B2B & privat</strong>Von der Agenturserie bis zum Familienshooting.</div>
|
||||
<div class="hero__fact"><strong>Kurze Wege</strong>Direkter Kontakt, klare Absprachen, schnelle Lieferung.</div>
|
||||
<div class="hero__fact"><strong>Musik & Bühne</strong>Konzerte, Bands und Musikvideos aus eigener Erfahrung.</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="scroll-cue" aria-hidden="true">Scroll<i></i></span>
|
||||
</section>"""
|
||||
|
||||
statement = section(
|
||||
tight=True,
|
||||
kicker="Haltung",
|
||||
content="""<div class="statement">
|
||||
<div><p class="eyebrow">Kurz gesagt</p></div>
|
||||
<div><p class="eyebrow">Haltung</p></div>
|
||||
<div class="statement__body">
|
||||
<p>Ich mache Bilder, die etwas erzählen und trotzdem <em>funktionieren</em>: auf der Website,
|
||||
im Feed, im Bewerbungsportfolio, im Plakat. Kein Katalog von der Stange — wir klären kurz,
|
||||
@@ -227,20 +355,22 @@ def home() -> str:
|
||||
</div>""",
|
||||
)
|
||||
|
||||
services = section(
|
||||
kicker="Leistungen",
|
||||
title="Was ich für dich mache",
|
||||
lead="Sechs Bereiche, ein Ansprechpartner. Klick dich rein oder schreib direkt, wenn du unsicher bist, was zu deinem Vorhaben passt.",
|
||||
content=service_cards(),
|
||||
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(),
|
||||
)
|
||||
|
||||
works = section(
|
||||
variant="paper2",
|
||||
kicker="Aus der Arbeit",
|
||||
title="Auszug aus Aufträgen und eigenen Projekten",
|
||||
lead="Studioportraits, Businessbilder, Paare, Veranstaltungen und Reisen — die Auswahl zeigt die Bandbreite, nicht nur den schönsten Moment.",
|
||||
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.",
|
||||
content=work_gallery()
|
||||
+ '<div class="btn-row" style="margin-top:2rem"><a class="btn btn--ink" href="/projekte/">Alle Projekte</a></div>',
|
||||
+ '<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>',
|
||||
)
|
||||
|
||||
video_sec = f"""<section class="section section--ink">
|
||||
@@ -260,6 +390,8 @@ def home() -> str:
|
||||
</div>
|
||||
</section>"""
|
||||
|
||||
studio = studio_section()
|
||||
|
||||
process = section(
|
||||
kicker="Ablauf",
|
||||
title="Vier Schritte, kein Rätselraten",
|
||||
@@ -278,7 +410,7 @@ def home() -> str:
|
||||
title="desfoto · Fotografie & Video aus Neumünster",
|
||||
description=SITE["default_description"],
|
||||
path="/",
|
||||
preload_html=preload("hero-studio", "100vw"),
|
||||
preload_html=preload("music-vocal-blue", "100vw"),
|
||||
json_ld=[
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
@@ -302,42 +434,97 @@ def home() -> str:
|
||||
"knowsAbout": [s["title"] for s in SERVICES],
|
||||
}
|
||||
],
|
||||
body=hero + statement + services + works + video_sec + process + facts + cta_band(),
|
||||
body=hero
|
||||
+ statement
|
||||
+ pillars
|
||||
+ marquee(
|
||||
[
|
||||
"Musik & Bühne",
|
||||
"Businessfotografie",
|
||||
"Portrait & Model",
|
||||
"Familien & Paare",
|
||||
"Minishootings",
|
||||
"Mobiles Studio",
|
||||
"Videoprojekte",
|
||||
"Social Media",
|
||||
]
|
||||
)
|
||||
+ works
|
||||
+ video_sec
|
||||
+ studio
|
||||
+ process
|
||||
+ facts
|
||||
+ cta_band(),
|
||||
)
|
||||
|
||||
|
||||
def fotografie() -> str:
|
||||
path = "/fotografie/"
|
||||
def shootings() -> str:
|
||||
path = "/shootings/"
|
||||
overview = "".join(
|
||||
f'<article class="card reveal"><div>'
|
||||
+ picture(item["image"], "(min-width: 62rem) 30vw, 92vw", css_class="frame frame--4x5", fit="50% 30%")
|
||||
'<article class="card reveal"><div>'
|
||||
+ picture(
|
||||
item["image"],
|
||||
"(min-width: 62rem) 30vw, 92vw",
|
||||
css_class="frame frame--4x5",
|
||||
fit="50% 30%",
|
||||
alt=item["title"],
|
||||
)
|
||||
+ f'</div><h3>{e(item["title"])}</h3><p class="card__text">{e(item["text"])}</p></article>'
|
||||
for item in PHOTO_OVERVIEW
|
||||
)
|
||||
body = (
|
||||
page_head(
|
||||
kicker="Fotografie",
|
||||
title="Menschen, Marken und Momente",
|
||||
lead="Vier Schwerpunkte, ein Handwerk. Entscheide dich für den Bereich, der passt — oder schreib mir, wenn du zwischen zwei Bereichen stehst.",
|
||||
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.",
|
||||
image="editorial-earring",
|
||||
breadcrumb=("Fotografie", path),
|
||||
breadcrumb=("Shootings", path),
|
||||
fit="45% 30%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Projekt anfragen</a>'
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Shooting anfragen</a>'
|
||||
'<a class="btn btn--outline-light" href="/projekte/">Arbeiten ansehen</a>',
|
||||
)
|
||||
+ section(
|
||||
kicker="Überblick",
|
||||
title="Womit ich arbeite",
|
||||
lead="Studiolicht in Neumünster, available light draußen, Reportage dort, wo es lebendig ist.",
|
||||
content=f'<div class="grid grid--3">{overview}</div>',
|
||||
title="Ein Überbegriff, vier Wege",
|
||||
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 Bereiche im Detail",
|
||||
content=service_cards(["businessfotografie", "portrait-und-model", "familien-und-paare", "minishootings"]),
|
||||
title="Die vier Shootings im Detail",
|
||||
content=service_cards(
|
||||
["businessfotografie", "portrait-und-model", "familien-und-paare", "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.",
|
||||
content='<div class="steps">'
|
||||
+ "".join(
|
||||
f'<div class="step reveal"><h3>{e(step["title"])}</h3><p>{e(step["text"])}</p></div>'
|
||||
for step in SHOOTING_STEPS
|
||||
)
|
||||
+ "</div>",
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Beispiele",
|
||||
title="Aus Shootings und Aufträgen",
|
||||
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%"),
|
||||
("business-portrait-stool", "Sitzendes Portrait im weichen Studiolicht", "3x2", "50% 35%"),
|
||||
("group-feier", "Feiergesellschaft bei einer Veranstaltung", "3x2", "50% 36%"),
|
||||
("portrait-natural", "Natürliches Portrait im Freien", "3x2", "50% 32%"),
|
||||
]
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
kicker="Technik & Ausgabe",
|
||||
title="Was am Ende bei dir landet",
|
||||
@@ -353,21 +540,32 @@ def fotografie() -> str:
|
||||
),
|
||||
right=ticks(
|
||||
[
|
||||
"Studiolicht und mobile Blitzanlage im Einsatz",
|
||||
"Available-light-Fotografie on location",
|
||||
"Tageslicht, Studiolicht und mobile Blitzanlage",
|
||||
"Mobiles Studio direkt an deinem Ort",
|
||||
"Reportagestil bei Veranstaltungen",
|
||||
"Lieferung per Downloadlink",
|
||||
]
|
||||
),
|
||||
),
|
||||
)
|
||||
+ cta_band()
|
||||
+ cta_band(
|
||||
title="Welches Shooting 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="Fotografie · desfoto",
|
||||
description="Fotografie aus Neumünster: Business, Portrait & Model, Familien & Paare, Minishootings. Studiolicht und available light, ein Ansprechpartner.",
|
||||
title="Shootings in Neumünster · desfoto",
|
||||
description="Shootings aus Neumünster: Businessfotografie, Portrait & Model, Familien & Paare und Minishootings — mit mobilem Studio inklusive autarker Blitzanlage.",
|
||||
path=path,
|
||||
json_ld=[service_ld("Fotografie", path, "Fotografie für Unternehmen, Models, Familien und Paare."), breadcrumb_ld("Fotografie", path)],
|
||||
json_ld=[
|
||||
service_ld(
|
||||
"Shootings",
|
||||
path,
|
||||
"Fotografische Shootings für Business, Portrait & Model, Familien und Paare.",
|
||||
),
|
||||
breadcrumb_ld("Shootings", path),
|
||||
],
|
||||
body=body,
|
||||
)
|
||||
|
||||
@@ -387,7 +585,7 @@ def businessfotografie() -> str:
|
||||
+ section(
|
||||
kicker="Typische Aufträge",
|
||||
title="Wofür Unternehmen mich buchen",
|
||||
content='<div class="grid grid--3">'
|
||||
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 [
|
||||
@@ -419,8 +617,8 @@ def businessfotografie() -> str:
|
||||
)
|
||||
+ "</div>",
|
||||
)
|
||||
+ studio_section(variant="paper2")
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Zusammenarbeit",
|
||||
title="Was ich von euch brauche",
|
||||
lead="Ein kurzer Call oder eine E-Mail mit drei Angaben reicht für ein verbindliches Angebot.",
|
||||
@@ -438,14 +636,18 @@ def businessfotografie() -> str:
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Beispiele",
|
||||
title="Aus der Business- und Studiopraxis",
|
||||
content='<div class="grid grid--2">'
|
||||
+ picture("business-portrait-dark", "(min-width: 62rem) 45vw, 92vw", css_class="frame frame--4x5", fit="50% 28%")
|
||||
+ picture("business-portrait-stool", "(min-width: 62rem) 45vw, 92vw", css_class="frame frame--4x5", fit="50% 35%")
|
||||
+ picture("gear-cases", "(min-width: 62rem) 45vw, 92vw", css_class="frame frame--3x2")
|
||||
+ picture("studio-setup", "(min-width: 62rem) 45vw, 92vw", css_class="frame frame--3x2")
|
||||
+ "</div>",
|
||||
content=media_grid(
|
||||
[
|
||||
("business-portrait-dark", "Portrait eines Mannes vor dunklem Hintergrund", "4x5", "50% 28%"),
|
||||
("business-portrait-stool", "Sitzendes Portrait im weichen Studiolicht", "4x5", "50% 35%"),
|
||||
("gear-cases", "Kamerakoffer und Objektive vor einem Studiorack", "3x2", "50% 50%"),
|
||||
("studio-setup", "Studioequipment mit Logo von Denny Schulz Fotografie", "3x2", "50% 45%"),
|
||||
],
|
||||
columns="grid--2",
|
||||
),
|
||||
)
|
||||
+ cta_band(
|
||||
title="Bildpaket für euer Unternehmen?",
|
||||
@@ -632,11 +834,11 @@ def familien_und_paare() -> str:
|
||||
+ "".join(
|
||||
picture(slug, "(min-width: 62rem) 30vw, 92vw", css_class="frame frame--4x5", fit=fit)
|
||||
for slug, fit in [
|
||||
("people-forest", "50% 24%"),
|
||||
("people-veil", "50% 32%"),
|
||||
("people-walk", "50% 32%"),
|
||||
("portrait-natural", "50% 30%"),
|
||||
("people-kiss", "50% 32%"),
|
||||
("portrait-couple", "50% 30%"),
|
||||
("group-feier", "50% 36%"),
|
||||
("group-outdoor", "50% 36%"),
|
||||
("group-posiert", "50% 38%"),
|
||||
]
|
||||
)
|
||||
@@ -739,6 +941,7 @@ def minishootings() -> str:
|
||||
("portrait-natural", "50% 28%"),
|
||||
("business-portrait-studio", "50% 28%"),
|
||||
("editorial-black", "50% 28%"),
|
||||
("free-modern-portrait", "50% 30%"),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
@@ -768,9 +971,9 @@ def video() -> str:
|
||||
kicker="Videoprojekte",
|
||||
title="Vom Treatment bis zur finalen Datei",
|
||||
lead="Musikvideo, Imagefilm, Recruiting oder Eventnachbericht — ich übernehme Konzept, Dreh, Schnitt, Ton und Ausgabe in den Formaten, die dein Kanal braucht.",
|
||||
image="studio-lens",
|
||||
image="music-guitar-silhouette",
|
||||
breadcrumb=("Video", path),
|
||||
fit="50% 45%",
|
||||
fit="50% 42%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Videoprojekt anfragen</a>',
|
||||
)
|
||||
+ f"""<section class="section section--ink">
|
||||
@@ -788,7 +991,7 @@ def video() -> str:
|
||||
+ section(
|
||||
kicker="Formate",
|
||||
title="Welche Videos ich produziere",
|
||||
content='<div class="grid grid--3">'
|
||||
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 [
|
||||
@@ -822,6 +1025,21 @@ def video() -> str:
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Musik & Bühne",
|
||||
title="Konzerte, Bands und Musiker",
|
||||
lead="Als Musiker kenne ich beide Seiten der Bühne. Live fotografiert wird mittendrin statt am Rand — oft im selben Termin wie ein Videoprojekt.",
|
||||
content=media_grid(
|
||||
[
|
||||
("music-vocal-blue", "Sänger mit erhobenem Arm im blauen Bühnenlicht", "4x5", "50% 30%"),
|
||||
("music-guitar-silhouette", "Gitarrist als Silhouette im Bühnennebel", "4x5", "50% 42%"),
|
||||
("music-strings", "Nahaufnahme einer Hand an den Saiten einer E-Gitarre", "1x1", "50% 50%"),
|
||||
("music-drums", "Schlagzeuger am Drumset in Schwarzweiß", "1x1", "50% 45%"),
|
||||
("music-mic", "Bärtiger Sänger am Mikrofon mit erhobener Hand", "3x2", "50% 35%"),
|
||||
("band-sagenbringer", "Musiker in Fellkleidung im Wald, von unten fotografiert", "3x2", "50% 30%"),
|
||||
]
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
kicker="Produktionsweg",
|
||||
title="So entsteht ein Video",
|
||||
content='<div class="steps">'
|
||||
@@ -837,6 +1055,7 @@ def video() -> str:
|
||||
+ "</div>",
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Zusammenarbeit",
|
||||
title="Was ich von dir brauche",
|
||||
content=two_col(
|
||||
@@ -853,23 +1072,17 @@ def video() -> str:
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Einblicke",
|
||||
title="Hinter der Kamera",
|
||||
content='<div class="gallery">'
|
||||
+ "".join(
|
||||
f'<figure class="{w}">'
|
||||
+ picture(slug, "(min-width: 62rem) 45vw, 92vw", css_class=f"frame frame--{ratio}", fit=fit)
|
||||
+ f"<figcaption>{e(cap)}</figcaption></figure>"
|
||||
for slug, cap, w, ratio, fit in [
|
||||
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%"),
|
||||
("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%"),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
),
|
||||
)
|
||||
+ cta_band(
|
||||
title="Videoidee im Kopf?",
|
||||
@@ -1001,66 +1214,68 @@ def social_media() -> str:
|
||||
|
||||
def projekte() -> str:
|
||||
path = "/projekte/"
|
||||
gallery = (
|
||||
'<div class="gallery">'
|
||||
+ "".join(
|
||||
f'<figure class="{w}">'
|
||||
+ picture(slug, "(min-width: 62rem) 45vw, 92vw", css_class=f"frame frame--{ratio}", fit=fit)
|
||||
+ f"<figcaption>{e(cap)}</figcaption></figure>"
|
||||
for slug, cap, w, ratio, fit in [
|
||||
("travel-japan", "Japan — eigene Reiseserie", "g-w3", "4x5", "45% 42%"),
|
||||
("travel-norway-lake", "Norwegen — Seen", "g-w3", "4x5", "50% 48%"),
|
||||
("travel-norway-coast", "Norwegen — Küste", "g-w2", "1x1", "50% 48%"),
|
||||
("travel-norway-valley", "Morgennebel im Tal", "g-w2", "1x1", "50% 50%"),
|
||||
("travel-norway-pano", "Panorama", "g-w2", "1x1", "50% 50%"),
|
||||
("travel-blossom", "Kirschblüte", "g-w3", "3x2", "50% 38%"),
|
||||
("movement-light", "Lichtmalerei", "g-w3", "3x2", "50% 50%"),
|
||||
]
|
||||
)
|
||||
+ "</div>"
|
||||
)
|
||||
body = (
|
||||
page_head(
|
||||
kicker="Projekte",
|
||||
title="Eigene Arbeiten und pro bono",
|
||||
lead="Neben Auftragsarbeiten entstehen eigene Projekte. Manches davon startet als Idee im Kopf, manches als Anfrage von Menschen, die etwas Unterstützung brauchen.",
|
||||
image="movement-light",
|
||||
title="Eigene Arbeiten, Musik und freie Strecken",
|
||||
lead="Neben Auftragsarbeiten entstehen eigene Projekte: Bands und Konzerte, Landschaft und Tierwelt, Menschen und Momente ohne Termin.",
|
||||
image="free-mountain-view",
|
||||
breadcrumb=("Projekte", path),
|
||||
fit="50% 50%",
|
||||
fit="50% 45%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Projekt anfragen</a>'
|
||||
'<a class="btn btn--outline-light" href="/video/">Musikvideo ansehen</a>',
|
||||
)
|
||||
+ section(
|
||||
kicker="Eigene Projekte",
|
||||
title="Arbeiten, die ich selbst angestoßen habe",
|
||||
lead="Reisefotografie, Lichtmalerei und Musikvideo. Diese Projekte entstehen ohne Auftraggeber und zeigen, wie ich mir Bilder vorstelle.",
|
||||
content='<div class="grid grid--2">'
|
||||
+ "".join(
|
||||
f'<article class="card reveal"><h3>{e(t)}</h3><p class="card__text">{e(d)}</p></article>'
|
||||
for t, d in [
|
||||
(
|
||||
"Skogamor — Musikvideo",
|
||||
"Eigenes Musikvideo für Thjódrörir mit Konzept, Dreh, Schnitt und Farbkorrektur. Zu sehen auf der Videoseite.",
|
||||
),
|
||||
(
|
||||
"Norwegen",
|
||||
"Reihe über Küsten, Seen und Berglandschaften. Landschaft als Gegenpol zur Portrait- und Auftragsarbeit.",
|
||||
),
|
||||
(
|
||||
"Japan",
|
||||
"Ruhige Bilder von Parks, Gärten und Architektur, entstanden auf Reisen statt auf Termin.",
|
||||
),
|
||||
(
|
||||
"Lichtmalerei",
|
||||
"Experimente mit langer Belichtung und bewegtem Licht — technische Spielwiese mit Ausstellungsqualität.",
|
||||
),
|
||||
kicker="Musik & Bühne",
|
||||
title="Bands, Konzerte und Musikvideos",
|
||||
lead="Musik ist mein zweites Standbein. Für Extinct und Sagenbringer entstehen Bilder und Videoarbeiten, dazu das eigene Musikvideo für Thjódrörir.",
|
||||
content=media_grid(
|
||||
[
|
||||
("music-vocal-blue", "Sänger mit erhobenem Arm im blauen Bühnenlicht", "4x5", "50% 30%"),
|
||||
("band-sagenbringer", "Musiker in Fellkleidung im Wald, von unten fotografiert", "4x5", "50% 30%"),
|
||||
("band-extinct", "Die Band Extinct steht hintereinander vor schwarzem Hintergrund", "3x2", "50% 45%"),
|
||||
("music-mic", "Bärtiger Sänger am Mikrofon mit erhobener Hand", "1x1", "50% 35%"),
|
||||
("music-drums", "Schlagzeuger am Drumset in Schwarzweiß", "1x1", "50% 45%"),
|
||||
("music-strings", "Nahaufnahme einer Hand an den Saiten einer E-Gitarre", "3x2", "50% 50%"),
|
||||
]
|
||||
)
|
||||
+ "</div>"
|
||||
+ '<div style="margin-top:2rem"><a class="link-arrow" href="/video/">Das Musikvideo ansehen '
|
||||
+ '<div class="btn-row" style="margin-top:2rem">'
|
||||
'<a class="link-arrow" href="/video/">Zum Musikvideo '
|
||||
+ ICONS["arrow"]
|
||||
+ "</a></div>",
|
||||
)
|
||||
+ section(variant="paper2", kicker="Bildstrecke", title="Aus eigenen Serien und Aufträgen", content=gallery)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Freie Arbeiten",
|
||||
title="Strecken ohne Auftrag",
|
||||
lead="Landschaft, Tierwelt und Menschen, entstanden nebenbei und ohne Auftraggeber. Drei Themen, ein Blick — das, was ich ohnehin fotografiere.",
|
||||
content=media_grid(
|
||||
[
|
||||
("free-mountain-view", "Eine Person sitzt über einer wolkenverhangenen Berglandschaft", "4x5", "50% 45%"),
|
||||
("free-fjord", "Steile Felsnadeln vor dem Meer", "4x5", "50% 50%"),
|
||||
("free-woman-coast", "Eine Frau mit Hut steht an einer felsigen Küste", "4x5", "50% 40%"),
|
||||
("free-fog-forest", "Nebel zieht durch einen Nadelwald am Berghang", "3x2", "50% 45%"),
|
||||
("free-lake-person", "Eine Person am Bergsee spiegelt sich im Wasser", "3x2", "50% 50%"),
|
||||
("free-golden-grass", "Gräser im goldenen Licht des Sonnenuntergangs", "3x2", "50% 50%"),
|
||||
("free-fox", "Nahaufnahme eines Rotfuchses", "1x1", "50% 45%"),
|
||||
("free-cat", "Nahaufnahme einer getigerten Katze im Seitenlicht", "1x1", "50% 42%"),
|
||||
("free-arch-garden", "Blick durch einen steinernen Torbogen in einen Garten", "1x1", "50% 45%"),
|
||||
]
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
kicker="Menschen",
|
||||
title="Portraits ohne Anlass",
|
||||
content=media_grid(
|
||||
[
|
||||
("free-portrait-light", "Frauenportrait im weichen Seitenlicht vor dunklem Hintergrund", "4x5", "50% 28%"),
|
||||
("free-modern-portrait", "Portrait einer Frau mit Sonnenbrille", "4x5", "50% 30%"),
|
||||
("movement-light", "Lichtspur aus einer langen Belichtung", "4x5", "50% 50%"),
|
||||
]
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Pro bono",
|
||||
title="Arbeiten, die ich unterstütze",
|
||||
lead="Manche Projekte sind wichtig, haben aber kein Budget. Wenn es passt, mache ich sie trotzdem.",
|
||||
@@ -1079,7 +1294,6 @@ def projekte() -> str:
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Auftragsarbeiten",
|
||||
title="Und sonst?",
|
||||
lead="Business, Portrait, Familie, Events und Video — alles, was in den Leistungen steht. Der beste Weg ist ein kurzer Blick in die Bereiche.",
|
||||
@@ -1087,9 +1301,9 @@ 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/"),
|
||||
("Businessfotografie", "Portraits, Teams und Produkte für Unternehmen.", "/businessfotografie/"),
|
||||
("Portrait & Model", "Portraits, Sedcards und Editorial.", "/portrait-und-model/"),
|
||||
("Video", "Musikvideo, Imagefilm und Eventnachbericht.", "/video/"),
|
||||
("Videoprojekte", "Musikvideo, Imagefilm und Eventnachbericht.", "/video/"),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
@@ -1098,7 +1312,7 @@ def projekte() -> str:
|
||||
)
|
||||
return page(
|
||||
title="Projekte · desfoto",
|
||||
description="Eigene Projekte und pro-bono-Arbeiten von desfoto: Reisefotografie, Lichtmalerei, Musikvideo und Unterstützung für Initiativen ohne Budget.",
|
||||
description="Eigene Projekte von desfoto: Bands und Konzerte, Landschaft und Tierwelt, Portraits ohne Anlass und pro-bono-Arbeiten für Initiativen ohne Budget.",
|
||||
path=path,
|
||||
json_ld=[breadcrumb_ld("Projekte", path)],
|
||||
body=body,
|
||||
@@ -1111,26 +1325,32 @@ def ueber() -> str:
|
||||
page_head(
|
||||
kicker="Über",
|
||||
title="Fotograf und Videograf aus Neumünster",
|
||||
lead="Ich bin Denny Schulz. Seit vielen Jahren fotografiere ich Menschen, Marken und Anlässe — und seit einiger Zeit genauso gern in Bewegung.",
|
||||
image="business-portrait-dark",
|
||||
lead="Ich bin Denny Schulz. Ich fotografiere Menschen, Marken und Musik — und produziere seit einigen Jahren genauso gern Bewegtbild.",
|
||||
image="studio-setup",
|
||||
breadcrumb=("Über", path),
|
||||
fit="50% 25%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Kennst du noch nicht? Schreib mir</a>',
|
||||
fit="50% 45%",
|
||||
actions='<a class="btn btn--solid" href="/kontakt/">Schreib mir</a>',
|
||||
)
|
||||
+ section(
|
||||
content="""<div class="statement">
|
||||
<div><p class="eyebrow">Arbeitsweise</p></div>
|
||||
<div class="statement__body">
|
||||
<p>Ich arbeite schnell, direkt und unaufgeregt. Du sagst mir, was du brauchst, ich sage dir,
|
||||
was dafür nötig ist — und dann machen wir es. <em>Ohne Umwege und ohne Fachchinesisch.</em></p>
|
||||
</div>
|
||||
</div>""",
|
||||
kicker="Wer ich bin",
|
||||
title="Vom Reisen zur Kamera",
|
||||
content='<div class="statement">'
|
||||
'<div><p class="eyebrow">Kurz</p></div>'
|
||||
'<div class="statement__body">'
|
||||
'<p>Ich bin gelernter Kaufmann, Ausbilder und Musiker. Zur Fotografie gekommen bin ich '
|
||||
'über Reisen, Freunde und die ersten Hochzeiten — erst als Begleitung, dann immer öfter '
|
||||
'mit der Kamera in der Hand.</p>'
|
||||
'<p>Was mich fotografisch am meisten zieht, sind die nordischen Länder mit ihrem Licht '
|
||||
'und ihren Landschaften. Und Menschen, die wissen, dass sie nicht perfekt sein müssen, '
|
||||
'damit ein Bild stimmt.</p>'
|
||||
'<p class="quote">„Kameras, Ersatztechnik und mobile Blitze bringe ich passend zum Termin mit."</p>'
|
||||
"</div></div>",
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Was ich mache",
|
||||
title="Drei Dinge, richtig",
|
||||
content='<div class="grid grid--3">'
|
||||
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 [
|
||||
@@ -1139,18 +1359,20 @@ def ueber() -> str:
|
||||
"Portrait, Business, Model, Familie und Paar. Die Arbeit an Gesichtern, Licht und Ausdruck ist mein Kern.",
|
||||
),
|
||||
(
|
||||
"Bewegtbild produzieren",
|
||||
"Von der Idee über den Dreh bis zu Schnitt, Farbkorrektur und Ton. Auch als Ergänzung zu einem Fototermin.",
|
||||
"Musik & Bühne begleiten",
|
||||
"Konzerte, Bands und Musikvideos — als Musiker kenne ich beide Seiten der Bühne.",
|
||||
),
|
||||
(
|
||||
"Inhalte für Kanäle liefern",
|
||||
"Material, das ohne Umweg in Website, Feed oder Stellenanzeige passt — im richtigen Format und Look.",
|
||||
"Bewegtbild produzieren",
|
||||
"Von der Idee über den Dreh bis zu Schnitt, Farbkorrektur und Ton. Auch als Ergänzung zu einem Fototermin.",
|
||||
),
|
||||
]
|
||||
)
|
||||
+ "</div>",
|
||||
)
|
||||
+ studio_section(variant="")
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Werte",
|
||||
title="Wie ich arbeite",
|
||||
content=two_col(
|
||||
@@ -1173,13 +1395,33 @@ def ueber() -> str:
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Zuhause & unterwegs",
|
||||
title="Neumünster und darüber hinaus",
|
||||
lead="Ich fotografiere im Studio in Neumünster und unterwegs in Schleswig-Holstein, Hamburg und Kiel. Für passende Projekte fahre ich auch weiter.",
|
||||
lead="Ich fotografiere im Studio in Neumünster, mit dem mobilen Studio bei euch im Betrieb und unterwegs in Schleswig-Holstein, Hamburg und Kiel.",
|
||||
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%"),
|
||||
("free-fox", "Nahaufnahme eines Rotfuchses", "1x1", "50% 45%"),
|
||||
],
|
||||
columns="grid--2",
|
||||
),
|
||||
)
|
||||
+ section(
|
||||
variant="paper2",
|
||||
kicker="Netzwerk",
|
||||
title="Wo ich sonst zu finden bin",
|
||||
lead="Zwei eigene Projekte, die zu desfoto gehören.",
|
||||
content='<div class="grid grid--2">'
|
||||
+ picture("business-portrait-studio", "(min-width: 62rem) 45vw, 92vw", css_class="frame frame--4x5", fit="50% 28%")
|
||||
+ picture("studio-setup", "(min-width: 62rem) 45vw, 92vw", css_class="frame frame--4x5", fit="50% 45%")
|
||||
+ "".join(
|
||||
'<article class="card reveal">'
|
||||
f'<h3>{e(item["title"])}</h3>'
|
||||
f'<p class="card__text">{e(item["text"])}</p>'
|
||||
f'<div class="card__foot"><a class="link-arrow" href="{e(item["href"])}" '
|
||||
f'target="_blank" rel="noopener">Ansehen {ICONS["arrow_up_right"]}</a></div>'
|
||||
"</article>"
|
||||
for item in NETWORK
|
||||
)
|
||||
+ "</div>",
|
||||
)
|
||||
+ section(
|
||||
@@ -1198,7 +1440,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, Werte und Kontaktmöglichkeiten.",
|
||||
description="desfoto ist die Foto- und Videoproduktion von Denny Schulz aus Neumünster. Arbeitsweise, mobiles Studio, Netzwerk und Kontaktmöglichkeiten.",
|
||||
path=path,
|
||||
json_ld=[
|
||||
breadcrumb_ld("Über", path),
|
||||
@@ -1209,6 +1451,7 @@ def ueber() -> str:
|
||||
"jobTitle": "Fotograf und Videograf",
|
||||
"url": SITE["url"] + path,
|
||||
"worksFor": {"@type": "Organization", "name": LEGAL["business"], "url": SITE["url"]},
|
||||
"sameAs": [item["href"] for item in NETWORK],
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": LEGAL["street"],
|
||||
@@ -1544,7 +1787,7 @@ def not_found() -> str:
|
||||
|
||||
ROUTES = [
|
||||
("/", "index.html", home, None, "1.0"),
|
||||
("/fotografie/", "fotografie/index.html", fotografie, None, "0.9"),
|
||||
("/shootings/", "shootings/index.html", shootings, 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"),
|
||||
|
||||
166
src/theme.py
166
src/theme.py
@@ -98,9 +98,8 @@ hr{border:0;border-top:1px solid var(--line);margin:0}
|
||||
}
|
||||
.masthead__inner{display:flex;align-items:center;gap:1rem;min-height:4.4rem;padding-block:.7rem}
|
||||
.brand{display:inline-flex;align-items:center;gap:.62rem;font-family:var(--font-display);font-size:1.42rem;font-weight:600;letter-spacing:-.015em}
|
||||
.brand__mark{width:1.72rem;height:1.72rem;flex:none}
|
||||
.brand__mark .ring{stroke:var(--ink);fill:none;stroke-width:2.4}
|
||||
.brand__mark .blade{stroke:var(--accent);stroke-width:2.6;stroke-linecap:round}
|
||||
.brand__mark{width:2.05rem;height:2.05rem;flex:none;display:block;border-radius:6px;overflow:hidden;box-shadow:0 0 0 1px var(--line);background:var(--paper)}
|
||||
.brand__mark img{width:100%;height:100%;object-fit:cover;display:block}
|
||||
.brand small{display:block;font-family:var(--font-sans);font-size:.62rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-50);line-height:1}
|
||||
.brand__text{display:flex;flex-direction:column;gap:.22rem}
|
||||
|
||||
@@ -303,7 +302,7 @@ img.frame{width:100%;height:auto;object-fit:cover;transition:transform .8s var(-
|
||||
.footer a:hover{color:#fff}
|
||||
.footer__grid{display:grid;gap:2rem;grid-template-columns:minmax(0,1.5fr) repeat(auto-fit,minmax(9rem,1fr))}
|
||||
.footer__brand .brand{color:var(--paper)}
|
||||
.footer__brand .brand__mark .ring{stroke:var(--paper)}
|
||||
.footer__brand .brand__mark{box-shadow:0 0 0 1px rgba(246,242,234,.34)}
|
||||
.footer__claim{margin-top:1rem;max-width:34ch;color:rgba(246,242,234,.66);font-size:.9rem}
|
||||
.footer h2{font-family:var(--font-sans);font-size:.74rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:rgba(246,242,234,.5);margin-bottom:.9rem}
|
||||
.footer__links{display:grid;gap:.5rem}
|
||||
@@ -316,7 +315,7 @@ img.frame{width:100%;height:auto;object-fit:cover;transition:transform .8s var(-
|
||||
/* ---------- page hero (sub pages) ---------- */
|
||||
.page-head{position:relative;isolation:isolate;overflow:hidden;background:var(--ink);color:var(--paper)}
|
||||
.page-head__bg{position:absolute;inset:0;z-index:-2}
|
||||
.page-head__bg img{width:100%;height:100%;object-fit:cover}
|
||||
.page-head__bg img{width:100%;height:100%;object-fit:cover;animation:hero-zoom 44s var(--ease) both}
|
||||
.page-head::before{
|
||||
content:"";position:absolute;inset:0;z-index:-1;
|
||||
background:linear-gradient(115deg,rgba(12,11,9,.9) 0%,rgba(12,11,9,.65) 55%,rgba(12,11,9,.38) 100%);
|
||||
@@ -352,21 +351,154 @@ img.frame{width:100%;height:auto;object-fit:cover;transition:transform .8s var(-
|
||||
/* ---------- motion ---------- */
|
||||
.reveal{opacity:0;transform:translateY(1.4rem);transition:opacity .8s var(--ease),transform .8s var(--ease)}
|
||||
.reveal.is-in{opacity:1;transform:none}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.reveal{opacity:1;transform:none;transition:none}
|
||||
html:focus-within{scroll-behavior:auto}
|
||||
.btn:hover{transform:none}
|
||||
.nav__link::after{transition:none}
|
||||
|
||||
/* ---------- motion: entrance, reveal, parallax ---------- */
|
||||
@keyframes hero-zoom{from{transform:scale(1.05)}to{transform:scale(1.18)}}
|
||||
@keyframes rise{from{opacity:0;transform:translateY(1.15em)}to{opacity:1;transform:none}}
|
||||
@keyframes cue{0%{transform:scaleY(.12);transform-origin:top}45%{transform:scaleY(1);transform-origin:top}55%{transform:scaleY(1);transform-origin:bottom}100%{transform:scaleY(.12);transform-origin:bottom}}
|
||||
@keyframes marquee{to{transform:translateX(-50%)}}
|
||||
@keyframes fade-in{from{opacity:0}to{opacity:1}}
|
||||
|
||||
.progress{position:absolute;left:0;right:0;bottom:-1px;height:2px;overflow:hidden;pointer-events:none}
|
||||
.progress__bar{display:block;height:100%;background:linear-gradient(90deg,var(--accent),#E6A184,var(--teal));transform:scaleX(var(--scroll,0));transform-origin:left;transition:transform .12s linear}
|
||||
|
||||
.hero__media::after{
|
||||
content:"";position:absolute;inset:0;pointer-events:none;
|
||||
background:radial-gradient(105% 80% at 82% 8%,rgba(192,67,28,.32),transparent 56%);
|
||||
}
|
||||
.hero__media img{animation:hero-zoom 36s var(--ease) both}
|
||||
.hero__title .w{display:inline-block;overflow:hidden;vertical-align:bottom;padding-bottom:.06em}
|
||||
.hero__title .w>span{display:inline-block;animation:rise 1.05s var(--ease) both;animation-delay:calc(var(--i,0) * .07s + .16s)}
|
||||
.hero__sub,.hero__actions,.hero__facts{animation:fade-in 1.2s var(--ease) both;animation-delay:.62s}
|
||||
.hero__actions{animation-delay:.74s}
|
||||
.hero__facts{animation-delay:.9s}
|
||||
.scroll-cue{
|
||||
position:absolute;right:max(4vw,1rem);bottom:2.4rem;z-index:1;
|
||||
display:grid;justify-items:center;gap:.6rem;
|
||||
font-size:.62rem;letter-spacing:.26em;text-transform:uppercase;color:rgba(246,242,234,.62);
|
||||
}
|
||||
.scroll-cue i{display:block;width:1px;height:3.2rem;background:linear-gradient(180deg,rgba(246,242,234,.85),transparent);animation:cue 2.6s var(--ease) infinite}
|
||||
|
||||
.reveal{opacity:0;transform:translate3d(0,1.7rem,0);transition:opacity .95s var(--ease),transform .95s var(--ease),clip-path 1.1s var(--ease);transition-delay:var(--d,0s)}
|
||||
.reveal--scale{transform:scale(.962)}
|
||||
.reveal--left{transform:translate3d(-2.4rem,0,0)}
|
||||
.reveal--right{transform:translate3d(2.4rem,0,0)}
|
||||
/* The mask lives on the children: Chromium folds an element's own clip-path into
|
||||
the IntersectionObserver geometry, so a clipped target never reports as visible. */
|
||||
.reveal--mask{opacity:1;transition:transform 1.15s var(--ease);transition-delay:var(--d,0s)}
|
||||
.reveal--mask>*{clip-path:inset(0 0 100% 0);transition:clip-path 1.15s var(--ease);transition-delay:var(--d,0s)}
|
||||
.reveal--mask.is-in>*{clip-path:inset(0 0 0 0)}
|
||||
.reveal.is-in{opacity:1;transform:none}
|
||||
.parallax{transform:translate3d(0,var(--py,0),0);will-change:transform}
|
||||
|
||||
/* ---------- pillars: the two halves of the work ---------- */
|
||||
.pillars{display:grid;gap:clamp(1.1rem,2.2vw,2rem);grid-template-columns:repeat(auto-fit,minmax(min(100%,26rem),1fr))}
|
||||
.pillar{
|
||||
position:relative;isolation:isolate;display:flex;flex-direction:column;
|
||||
min-height:clamp(25rem,54vh,33rem);border-radius:var(--radius-lg);overflow:hidden;
|
||||
color:var(--paper);background:#14110D;cursor:pointer;
|
||||
transition:transform .6s var(--ease),box-shadow .6s var(--ease);
|
||||
}
|
||||
.pillar:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
|
||||
.pillar__media{position:absolute;inset:0;z-index:-2}
|
||||
.pillar__media img{width:100%;height:100%;object-fit:cover;transition:transform 1.3s var(--ease)}
|
||||
.pillar:hover .pillar__media img{transform:scale(1.055)}
|
||||
.pillar::before{
|
||||
content:"";position:absolute;inset:0;z-index:-1;
|
||||
background:linear-gradient(180deg,rgba(12,11,9,.1) 12%,rgba(12,11,9,.62) 55%,rgba(12,11,9,.94) 100%);
|
||||
}
|
||||
.pillar__body{margin-top:auto;padding:clamp(1.3rem,2.4vw,2.1rem);display:grid;gap:.85rem}
|
||||
.pillar__label{
|
||||
justify-self:start;font-size:.68rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;
|
||||
color:#F0B79B;border:1px solid rgba(246,242,234,.34);border-radius:var(--radius-pill);padding:.3rem .8rem;
|
||||
}
|
||||
.pillar__title{font-size:clamp(1.7rem,2.6vw,2.3rem)}
|
||||
.pillar__text{color:rgba(246,242,234,.84);font-size:.95rem;max-width:46ch}
|
||||
.pillar__points{display:flex;flex-wrap:wrap;gap:.45rem .95rem;font-size:.82rem;color:rgba(246,242,234,.72)}
|
||||
.pillar__points a{position:relative;z-index:2;border-bottom:1px solid rgba(246,242,234,.3);padding-bottom:.1rem}
|
||||
.pillar__points a:hover{color:#fff;border-color:#fff}
|
||||
.pillar__go{margin-top:.35rem;display:inline-flex;align-items:center;gap:.5rem;font-weight:600;font-size:.92rem;color:inherit}
|
||||
/* Stretched link: the whole card is clickable without nesting anchors. */
|
||||
.pillar__go::after{content:"";position:absolute;inset:0;z-index:1;border-radius:inherit}
|
||||
.pillar__go svg{width:.9em;height:.9em;fill:none;stroke:currentColor;stroke-width:2;transition:transform .3s var(--ease)}
|
||||
.pillar:hover .pillar__go svg{transform:translateX(.3rem)}
|
||||
|
||||
.spot::after{
|
||||
content:"";position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .45s var(--ease);z-index:1;
|
||||
background:radial-gradient(22rem 22rem at var(--mx,50%) var(--my,50%),rgba(246,242,234,.16),transparent 62%);
|
||||
}
|
||||
.spot:hover::after{opacity:1}
|
||||
|
||||
/* ---------- media cards ---------- */
|
||||
.scard{
|
||||
position:relative;display:flex;flex-direction:column;overflow:hidden;
|
||||
border:1px solid var(--line);border-radius:var(--radius-lg);background:rgba(255,255,255,.42);
|
||||
transition:transform .5s var(--ease),box-shadow .5s var(--ease),border-color .5s var(--ease);
|
||||
}
|
||||
.scard:hover{transform:translateY(-4px);box-shadow:var(--shadow);border-color:var(--line-soft)}
|
||||
.scard__media{position:relative;aspect-ratio:4/3;overflow:hidden;background:var(--paper-3)}
|
||||
.scard__media img{width:100%;height:100%;object-fit:cover;transition:transform 1.1s var(--ease)}
|
||||
.scard:hover .scard__media img{transform:scale(1.06)}
|
||||
.scard__body{padding:1.1rem 1.2rem 1.35rem;display:grid;gap:.45rem;align-content:start}
|
||||
.scard__body h3{font-size:1.14rem}
|
||||
.scard__body p{font-size:.92rem;color:var(--ink-70)}
|
||||
.scard__body .link-arrow{margin-top:.35rem}
|
||||
|
||||
/* ---------- split + statement upgrades ---------- */
|
||||
.split{display:grid;gap:clamp(1.4rem,3vw,3.2rem);grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:center}
|
||||
.split--wide{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr)}
|
||||
.split--reverse .split__media{order:2}
|
||||
.words{font-family:var(--font-display);font-size:clamp(1.6rem,3.6vw,2.9rem);line-height:1.14;letter-spacing:-.02em;font-weight:400;text-wrap:pretty}
|
||||
.words em{font-style:italic;color:var(--accent)}
|
||||
.words strong{font-weight:600}
|
||||
|
||||
/* ---------- marquee ---------- */
|
||||
.marquee{overflow:hidden;border-block:1px solid var(--line);padding-block:1.1rem;background:var(--paper-2)}
|
||||
.marquee__track{display:flex;align-items:center;gap:clamp(1.6rem,3vw,3.2rem);width:max-content;animation:marquee 42s linear infinite}
|
||||
.marquee__track span{
|
||||
display:inline-flex;align-items:center;gap:clamp(1.6rem,3vw,3.2rem);
|
||||
font-family:var(--font-display);font-size:clamp(1.05rem,1.9vw,1.6rem);color:var(--ink-70);white-space:nowrap;
|
||||
}
|
||||
.marquee__track span::after{content:"";width:.4rem;height:.4rem;border-radius:50%;background:var(--accent);flex:none}
|
||||
.marquee:hover .marquee__track{animation-play-state:paused}
|
||||
|
||||
/* ---------- mobile studio ---------- */
|
||||
.studio{display:grid;gap:clamp(1.5rem,3vw,3.2rem);grid-template-columns:minmax(0,1.02fr) minmax(0,1fr);align-items:center}
|
||||
.studio__media{position:relative}
|
||||
.studio__media img{border-radius:var(--radius-lg)}
|
||||
.studio__badge{
|
||||
position:absolute;left:-.6rem;bottom:1.4rem;background:var(--paper);border:1px solid var(--line);
|
||||
border-radius:var(--radius);padding:.7rem 1rem;box-shadow:var(--shadow);
|
||||
}
|
||||
.studio__badge strong{display:block;font-family:var(--font-display);font-size:1.05rem}
|
||||
.studio__badge span{font-size:.78rem;color:var(--ink-50)}
|
||||
.studio__features{display:grid;gap:1.1rem 1.4rem;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));margin-top:1.6rem}
|
||||
.studio__features h3{font-size:1rem;margin-bottom:.3rem}
|
||||
.studio__features p{font-size:.88rem;color:var(--ink-70)}
|
||||
.section--ink .studio__features p{color:rgba(246,242,234,.72)}
|
||||
|
||||
/* ---------- band / project strip ---------- */
|
||||
.band-strip{display:grid;gap:clamp(.8rem,1.5vw,1.2rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr))}
|
||||
.band-strip figure{margin:0}
|
||||
.band-strip figcaption{margin-top:.55rem;font-size:.8rem;color:var(--ink-50)}
|
||||
|
||||
/* ---------- footer network ---------- */
|
||||
.footer__links small{display:block;color:rgba(246,242,234,.52);font-size:.78rem;line-height:1.5;max-width:30ch;margin-top:.1rem}
|
||||
.footer__ext{display:inline-flex;align-items:center;gap:.4rem}
|
||||
.footer__ext svg{width:.8em;height:.8em;fill:none;stroke:currentColor;stroke-width:2}
|
||||
|
||||
/* ---------- responsive ---------- */
|
||||
@media (max-width:62rem){
|
||||
.statement{grid-template-columns:1fr}
|
||||
.contact-grid{grid-template-columns:1fr}
|
||||
.split,.split--wide{grid-template-columns:1fr}
|
||||
.split--reverse .split__media{order:0}
|
||||
.studio{grid-template-columns:1fr}
|
||||
.gallery{grid-template-columns:repeat(4,1fr)}
|
||||
.gallery .g-w3{grid-column:span 2}
|
||||
.gallery .g-w4{grid-column:span 4}
|
||||
.gallery .g-w2{grid-column:span 2}
|
||||
.scroll-cue{display:none}
|
||||
}
|
||||
@media (max-width:52rem){
|
||||
.nav{
|
||||
@@ -393,4 +525,18 @@ img.frame{width:100%;height:auto;object-fit:cover;transition:transform .8s var(-
|
||||
.hero__facts{grid-template-columns:1fr 1fr}
|
||||
.page-head__inner{max-width:none}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.reveal,.reveal--mask{opacity:1;transform:none;clip-path:none;transition:none}
|
||||
.reveal--mask>*{clip-path:none;transition:none}
|
||||
html:focus-within{scroll-behavior:auto}
|
||||
.btn:hover,.pillar:hover,.scard:hover{transform:none}
|
||||
.nav__link::after{transition:none}
|
||||
.hero__media img,.page-head__bg img{animation:none}
|
||||
.hero__title .w>span,.hero__sub,.hero__actions,.hero__facts{animation:none;opacity:1;transform:none}
|
||||
.marquee__track{animation:none;flex-wrap:wrap;width:auto}
|
||||
.scroll-cue{display:none}
|
||||
.parallax{transform:none}
|
||||
.pillar:hover .pillar__media img,.scard:hover .scard__media img{transform:none}
|
||||
}
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user