
diff --git a/src/config/locales/de/audit.ts b/src/config/locales/de/audit.ts
index d338034..ed2f040 100644
--- a/src/config/locales/de/audit.ts
+++ b/src/config/locales/de/audit.ts
@@ -138,8 +138,12 @@ export const audit = {
},
teaser: {
- title: 'Erst mal sehen, wo du stehst?',
- text: 'Ich prüfe eine Seite deiner Website nach WCAG und schicke dir einen kostenfreien Kurzreport – plus 15 Minuten Auswertung. Für Unternehmen.',
- ctaText: 'Zum kostenfreien Audit',
+ title: 'Kostenlose Angebote',
+ auditTitle: 'Erst mal sehen, wo du stehst?',
+ auditText: 'Im kostenlosen One-Page Audit schauen wir uns eine Seite deiner Wahl gemeinsam an und besprechen die wichtigsten nächsten Schritte.',
+ auditCtaText: 'Zum kostenlosen Audit',
+ quickcheckTitle: 'Barrierefreiheits-Schnellcheck',
+ quickcheckText: 'Lade eine Checkliste mit 10 Punkten und eine Vorlage für deine Erklärung zur Barrierefreiheit herunter.',
+ quickcheckCtaText: 'Schnellcheck herunterladen',
},
};
diff --git a/src/config/locales/de/footer.ts b/src/config/locales/de/footer.ts
index f399c1d..073ec56 100644
--- a/src/config/locales/de/footer.ts
+++ b/src/config/locales/de/footer.ts
@@ -1,9 +1,11 @@
export const footer = {
- imprintText: 'Impressum und Datenschutz',
+ imprintText: 'Impressum',
+ privacyPolicyText: 'Datenschutz',
copyrightText: 'Alle Rechte vorbehalten.',
technicalLinkText: 'Technisches Portfolio',
landingLinkText: 'Landing Page',
auditLinkText: 'Kostenfreies Audit',
+ quickcheckLinkText: 'Kostenloser Schnellcheck',
gitAriaLabel: 'Git-Profil',
linkedinAriaLabel: 'LinkedIn-Profil',
emailAriaLabel: 'E-Mail senden',
diff --git a/src/config/locales/de/imprint.ts b/src/config/locales/de/imprint.ts
index 900304e..4233e34 100644
--- a/src/config/locales/de/imprint.ts
+++ b/src/config/locales/de/imprint.ts
@@ -1,6 +1,6 @@
export const imprint = {
- title: 'Impressum und Datenschutz',
- subtitle: 'Rechtliche Informationen',
+ title: 'Impressum',
+ subtitle: 'Rechtliche Informationen zum Anbieter',
companyInfoTitle: 'Angaben gemäß § 5 DDG',
umsatzsteuerID: 'Umsatzsteuer-ID: DE316934637',
vatID: 'VAT ID: DE316934637',
@@ -91,6 +91,25 @@ export const imprint = {
],
retentionText:
'Sofern wir (bestimmte Teile) Ihre(r) Daten jedoch noch für andere Zwecke vorhalten müssen, weil dies etwa steuerliche Aufbewahrungsfristen (in der Regel 6 Jahre für Geschäftskorrespondenz bzw. 10 Jahre für Buchungsbelege) oder die Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen aus vertraglichen Beziehungen (bis zu vier Jahren) erforderlich machen oder die Daten zum Schutz der Rechte einer anderen natürlichen oder juristischen Person gebraucht werden, löschen wir (den Teil) Ihre(r) Daten erst nach Ablauf dieser Fristen. Bis zum Ablauf dieser Fristen beschränken wir die Verarbeitung dieser Daten jedoch auf diese Zwecke (Erfüllung der Aufbewahrungspflichten).',
+ quickcheckFormTitle: 'Formular „Barrierefreiheits-Schnellcheck“',
+ quickcheckFormText:
+ 'Wenn Sie das Schnellcheck-Formular absenden, werden die von Ihnen eingegebenen Daten an unseren eigenen Server übermittelt und von dort per E-Mail an uns weitergeleitet. Es sind keine externen Formulardienstleister beteiligt. Der Schnellcheck ist ein Selbsttest und kein vollständiges Audit oder Rechtsgutachten.',
+ quickcheckFormDataTitle: 'Verarbeitete Daten:',
+ quickcheckFormDataList: [
+ 'Name (optional)',
+ 'E-Mail-Adresse',
+ 'Bestätigung, dass Sie als Unternehmer, Gewerbetreibender oder Freiberufler anfragen (§ 14 BGB)',
+ 'Ihre Einwilligung zur Verarbeitung der Angaben',
+ ],
+ quickcheckFormPurposeLabel: 'Zweck:',
+ quickcheckFormPurpose:
+ 'Bearbeitung der Schnellcheck-Anfrage und Kontaktaufnahme zur Bereitstellung des Downloads.',
+ quickcheckFormLegalBasisLabel: 'Rechtsgrundlage:',
+ quickcheckFormLegalBasis:
+ 'Ihre Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO, die Sie beim Absenden des Formulars erteilen. Sie können Ihre Einwilligung jederzeit formlos widerrufen.',
+ quickcheckFormRetentionLabel: 'Speicherdauer:',
+ quickcheckFormRetention:
+ 'Wir löschen Ihre Angaben, sobald die Anfrage abschließend bearbeitet ist und keine gesetzlichen Aufbewahrungspflichten entgegenstehen – spätestens zwölf Monate nach dem letzten Kontakt.',
webhostingTitle: 'Webhosting',
webhostingText:
'Wir bedienen uns zum Vorhalten unserer Internetseiten eines Anbieters, auf dessen Server unsere Internetseiten gespeichert und für den Abruf im Internet verfügbar gemacht werden (Hosting). Hierbei können von dem Anbieter all diejenigen über den von Ihnen genutzten Browser übertragenen Daten verarbeitet werden, die bei der Nutzung unserer Internetseiten anfallen. Hierzu gehören insbesondere Ihre IP-Adresse, die der Anbieter benötigt, um unser Online-Angebot an den von Ihnen genutzten Browser ausliefern zu können sowie sämtliche von Ihnen über unsere Internetseite getätigten Eingaben. Daneben kann der von uns genutzte Anbieter',
diff --git a/src/config/locales/de/navigation.ts b/src/config/locales/de/navigation.ts
index 6fdb667..ed6c3d4 100644
--- a/src/config/locales/de/navigation.ts
+++ b/src/config/locales/de/navigation.ts
@@ -5,6 +5,7 @@ export const navigation = {
{ section: 'Skills', label: 'Fähigkeiten' },
{ section: 'Certifications', label: 'Zertifikate' },
{ section: 'Projects', label: 'Projekte' },
+ { section: 'Audit-Teaser', label: 'Kostenlose Angebote' },
{ section: 'Contact', label: 'Kontakt' },
],
landingMenuItems: [
@@ -14,6 +15,7 @@ export const navigation = {
{ section: 'Processes', label: 'Wie ich arbeite' },
{ section: 'Projects', label: 'Projekte' },
{ section: 'References', label: 'Referenzen' },
+ { section: 'Audit-Teaser', label: 'Kostenlose Angebote' },
{ section: 'Contact', label: 'Kontakt' },
],
mobileMenuAriaLabel: 'Menü öffnen',
diff --git a/src/config/locales/de/privacy-policy.ts b/src/config/locales/de/privacy-policy.ts
index dad8234..87b6054 100644
--- a/src/config/locales/de/privacy-policy.ts
+++ b/src/config/locales/de/privacy-policy.ts
@@ -3,7 +3,7 @@ export const privacyPolicy = {
lastUpdated: 'Zuletzt aktualisiert: November 2025',
introTitle: 'Überblick',
introText:
- 'Diese Website ist eine statische Portfolio-Website. Es werden keine Cookies, Tracking- oder Analyse-Tools eingesetzt, und es sind keine Dienste Dritter eingebunden – auch die Schriftarten werden von unserem eigenen Server ausgeliefert. Personenbezogene Daten werden nur dann verarbeitet, wenn Sie das Formular auf der Seite „Kostenfreies Barrierefreiheits-Audit nach WCAG“ absenden.',
+ 'Diese Website ist eine statische Portfolio-Website. Es werden keine Cookies, Tracking- oder Analyse-Tools eingesetzt, und es sind keine Dienste Dritter eingebunden – auch die Schriftarten werden von unserem eigenen Server ausgeliefert. Personenbezogene Daten werden verarbeitet, wenn Sie das Formular auf der Seite „Kostenfreies Barrierefreiheits-Audit nach WCAG“ oder den kostenlosen Barrierefreiheits-Schnellcheck absenden.',
dataCollectionTitle: 'Datenerhebung',
dataCollectionText:
'Ohne Ihr Zutun erhebt diese Website keine personenbezogenen Daten. Wenn Sie auf Kontaktlinks klicken (E-Mail, LinkedIn, Git), werden Sie zu externen Diensten weitergeleitet, die eigene Datenschutzrichtlinien haben:',
@@ -28,6 +28,25 @@ export const privacyPolicy = {
auditFormPurposeLabel: 'Zweck:',
auditFormPurpose:
'Bearbeitung Ihrer Anfrage, Erstellung des Audit-Reports und Kontaktaufnahme zur Terminvereinbarung.',
+ quickcheckFormTitle: 'Formular „Barrierefreiheits-Schnellcheck“',
+ quickcheckFormText:
+ 'Wenn Sie das Schnellcheck-Formular absenden, werden die von Ihnen eingegebenen Daten an unseren eigenen Server übermittelt und von dort per E-Mail an uns weitergeleitet. Es sind keine externen Formulardienstleister beteiligt. Der Schnellcheck ist ein Selbsttest und kein vollständiges Audit oder Rechtsgutachten.',
+ quickcheckFormDataTitle: 'Verarbeitete Daten:',
+ quickcheckFormDataList: [
+ 'Name (optional)',
+ 'E-Mail-Adresse',
+ 'Bestätigung, dass Sie als Unternehmer, Gewerbetreibender oder Freiberufler anfragen (§ 14 BGB)',
+ 'Ihre Einwilligung zur Verarbeitung der Angaben',
+ ],
+ quickcheckFormPurposeLabel: 'Zweck:',
+ quickcheckFormPurpose:
+ 'Bearbeitung der Schnellcheck-Anfrage und Kontaktaufnahme zur Bereitstellung des Downloads.',
+ quickcheckFormLegalBasisLabel: 'Rechtsgrundlage:',
+ quickcheckFormLegalBasis:
+ 'Ihre Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO, die Sie beim Absenden des Formulars erteilen. Sie können Ihre Einwilligung jederzeit formlos widerrufen.',
+ quickcheckFormRetentionLabel: 'Speicherdauer:',
+ quickcheckFormRetention:
+ 'Wir löschen Ihre Angaben, sobald die Anfrage abschließend bearbeitet ist und keine gesetzlichen Aufbewahrungspflichten entgegenstehen – spätestens zwölf Monate nach dem letzten Kontakt.',
auditFormLegalBasisLabel: 'Rechtsgrundlage:',
auditFormLegalBasis:
'Art. 6 Abs. 1 lit. b DSGVO (Durchführung vorvertraglicher Maßnahmen auf Ihre Anfrage hin) sowie Ihre Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO, die Sie beim Absenden erteilen. Ihre Einwilligung können Sie jederzeit formlos widerrufen.',
diff --git a/src/config/locales/de/projects.ts b/src/config/locales/de/projects.ts
index 1b9b8cd..52b853c 100644
--- a/src/config/locales/de/projects.ts
+++ b/src/config/locales/de/projects.ts
@@ -2,13 +2,16 @@ import portfolioImage from '@/assets/portfolio.PNG';
import dancaAlegriaImage from '@/assets/Danca-Alegria.png';
import lukasImage from '@/assets/lukas.png';
import a11yhubImage from '@/assets/a11yhub.png';
+import arcumNovaImage from '@/assets/arcum-nova.png';
+import gxplexImage from '@/assets/gxplex.png';
+import aovmImage from '@/assets/aovm.png';
import placeholderImage from '@/assets/og-image.png';
export const projects = {
title: 'Ausgewählte Projekte',
subtitle: 'Eine Übersicht meiner Arbeiten und persönlichen Projekte',
codeButtonText: 'Code',
- liveButtonText: 'Live',
+ liveButtonText: 'Website öffnen',
projectItems: [
{
id: 1,
@@ -53,21 +56,33 @@ export const projects = {
},
{
id: 5,
- title: 'Barrierefreiheits-Audit für einen Unternehmenskunden',
+ title: 'arcum-nova.de',
description:
- 'Vollständige BITV 2.0 / EN 301 549 Prüfung (115 Kriterien) nach WCAG 2.2 Level AA über 21 Seiten, mit priorisiertem Befundbericht (Critical/High/Medium/Low), konkreten Lösungsvorschlägen und Re-Audit-Fahrplan.',
- image: placeholderImage,
- technologies: ['WCAG 2.2', 'BITV 2.0', 'EN 301 549', 'axe DevTools', 'WAVE'],
+ 'Umsetzung von WCAG 2.2 auf einer bestehenden WordPress-Seite mit Avada / Fusion Builder für eine mobile Fahrradwerkstatt im Havelland. Dazu gehörten unter anderem das Entfernen eines unerwünschten seitenweiten Bildrahmens und die konforme Anpassung der Kontraste aktiver Menüzustände.',
+ image: arcumNovaImage,
+ technologies: ['WCAG 2.2', 'WordPress', 'Avada', 'Barrierefreiheit'],
year: '2026',
+ live: 'https://arcum-nova.de',
+ },
+ {
+ id: 8,
+ title: 'aovm.de',
+ description:
+ 'Laufende technische Wartung und inhaltliche Pflege einer WordPress-Seite mit eigenem Theme nach Kundenvorgaben. Der Leistungsumfang umfasste Updates, Backups und Inhaltsänderungen. Die Seite basiert auf WordPress; im Projektkontext ist außerdem Flatsome dokumentiert. Das Projekt wurde im August 2026 beendet.',
+ image: aovmImage,
+ technologies: ['WordPress', 'Wartung', 'Seitenpflege'],
+ year: '2026',
+ live: 'https://aovm.de',
},
{
id: 6,
- title: 'Individuelle MediaWiki-Plattform (GxPlex)',
+ title: 'gxplex.org',
description:
- 'Aufbau einer maßgeschneiderten MediaWiki-Instanz inkl. Installation, MySQL-Datenbank, SSL und automatischer Backups; Einrichtung rollenbasierter Rechte (Admin, Mod, Verifiziert, User), FlaggedRevisions-Workflow für die redaktionelle Prüfung sowie Kommentar-/Bewertungs-Extensions.',
- image: placeholderImage,
- technologies: ['MediaWiki', 'MySQL', 'PHP', 'SSL'],
+ 'Aufbau einer englischsprachigen MediaWiki-Plattform mit Semantic MediaWiki, Skin Cavendish und Bewertungsfunktion. Die kollaborative Datenbank bündelt Gesetze, Verordnungen und Leitlinien der klinischen Forschung. Sie umfasst einen Dokumentindex, eine Modulübersicht, ein Glossar, Suche sowie eine Mitgliedschaft mit Beitragsfunktion. Über 1.100 Referenzen sind nach Land, Anwendungsbereich, Dokumenttyp und Thema getaggt.',
+ image: gxplexImage,
+ technologies: ['MediaWiki 1.43.8', 'Semantic MediaWiki', 'Cavendish', 'Bewertungsfunktion'],
year: '2026',
+ live: 'https://gxplex.org',
},
{
id: 7,
diff --git a/src/config/locales/en/TextConfig.ts b/src/config/locales/en/TextConfig.ts
index 92752f9..9f014a5 100644
--- a/src/config/locales/en/TextConfig.ts
+++ b/src/config/locales/en/TextConfig.ts
@@ -20,10 +20,12 @@ export interface TextConfig {
// Footer
footer: {
imprintText: string;
+ privacyPolicyText: string;
copyrightText: string;
technicalLinkText: string;
landingLinkText: string;
auditLinkText: string;
+ quickcheckLinkText: string;
gitAriaLabel: string;
linkedinAriaLabel: string;
emailAriaLabel: string;
@@ -236,6 +238,16 @@ export interface TextConfig {
dataDeletionIntro: string;
dataDeletionReasons: string[];
retentionText: string;
+ quickcheckFormTitle: string;
+ quickcheckFormText: string;
+ quickcheckFormDataTitle: string;
+ quickcheckFormDataList: string[];
+ quickcheckFormPurposeLabel: string;
+ quickcheckFormPurpose: string;
+ quickcheckFormLegalBasisLabel: string;
+ quickcheckFormLegalBasis: string;
+ quickcheckFormRetentionLabel: string;
+ quickcheckFormRetention: string;
webhostingTitle: string;
webhostingText: string;
webhostingDataTypes: string[];
@@ -309,6 +321,16 @@ export interface TextConfig {
auditFormDataList: string[];
auditFormPurposeLabel: string;
auditFormPurpose: string;
+ quickcheckFormTitle: string;
+ quickcheckFormText: string;
+ quickcheckFormDataTitle: string;
+ quickcheckFormDataList: string[];
+ quickcheckFormPurposeLabel: string;
+ quickcheckFormPurpose: string;
+ quickcheckFormLegalBasisLabel: string;
+ quickcheckFormLegalBasis: string;
+ quickcheckFormRetentionLabel: string;
+ quickcheckFormRetention: string;
auditFormLegalBasisLabel: string;
auditFormLegalBasis: string;
auditFormRetentionLabel: string;
@@ -402,8 +424,12 @@ export interface TextConfig {
};
teaser: {
title: string;
- text: string;
- ctaText: string;
+ auditTitle: string;
+ auditText: string;
+ auditCtaText: string;
+ quickcheckTitle: string;
+ quickcheckText: string;
+ quickcheckCtaText: string;
};
};
diff --git a/src/config/locales/en/audit.ts b/src/config/locales/en/audit.ts
index 6b0a225..14e567c 100644
--- a/src/config/locales/en/audit.ts
+++ b/src/config/locales/en/audit.ts
@@ -138,8 +138,12 @@ export const audit = {
},
teaser: {
- title: 'Want to see where you stand first?',
- text: 'I review one page of your website against WCAG and send you a free short report – plus a 15 minute walkthrough. For businesses.',
- ctaText: 'Go to the free audit',
+ title: 'Free resources',
+ auditTitle: 'Want to see where you stand first?',
+ auditText: 'In the free one-page audit, we look at one page of your choice together and discuss the most important next steps.',
+ auditCtaText: 'Go to the free audit',
+ quickcheckTitle: 'Accessibility quick check',
+ quickcheckText: 'Download a 10-point checklist and a template for your accessibility statement.',
+ quickcheckCtaText: 'Download the quick check',
},
};
diff --git a/src/config/locales/en/footer.ts b/src/config/locales/en/footer.ts
index 4ede331..b0a7b1b 100644
--- a/src/config/locales/en/footer.ts
+++ b/src/config/locales/en/footer.ts
@@ -1,9 +1,11 @@
export const footer = {
- imprintText: 'Impressum und Datenschutz',
+ imprintText: 'Imprint',
+ privacyPolicyText: 'Privacy Policy',
copyrightText: 'All rights reserved.',
technicalLinkText: 'Technical Portfolio',
landingLinkText: 'Landing Page',
auditLinkText: 'Free audit',
+ quickcheckLinkText: 'Free quick check',
gitAriaLabel: 'Git Profile',
linkedinAriaLabel: 'LinkedIn Profile',
emailAriaLabel: 'Send Email',
diff --git a/src/config/locales/en/imprint.ts b/src/config/locales/en/imprint.ts
index 5b4d396..dfd56b8 100644
--- a/src/config/locales/en/imprint.ts
+++ b/src/config/locales/en/imprint.ts
@@ -1,5 +1,5 @@
export const imprint = {
- title: 'Imprint and Privacy Policy',
+ title: 'Imprint',
subtitle: 'Legal Information',
companyInfoTitle: 'Information pursuant to § 5 DDG',
umsatzsteuerID: 'Umsatzsteuer-ID: DE316934637',
@@ -91,6 +91,25 @@ export const imprint = {
],
retentionText:
'However, if we (certain parts of) your data still need to be retained for other purposes, such as statutory retention periods (generally 6 years for business correspondence or 10 years for accounting records), the assertion, exercise, or defense of legal claims arising from contractual relationships (up to four years), or if the data is needed to protect the rights of another natural or legal person, we will only delete (the respective part of) your data after the expiry of these periods. Until these periods expire, we will restrict the processing of this data to these purposes (fulfillment of retention obligations).',
+ quickcheckFormTitle: 'The "Accessibility quick check" form',
+ quickcheckFormText:
+ 'When you submit the quick check form, the data you enter is transmitted to our own server and forwarded from there to us by email. No external form provider is involved. The quick check is a self-test, not a complete audit or legal opinion.',
+ quickcheckFormDataTitle: 'Data processed:',
+ quickcheckFormDataList: [
+ 'Name (optional)',
+ 'Email address',
+ 'Confirmation that you are asking as an entrepreneur, trader or freelancer (§ 14 BGB)',
+ 'Your consent to processing the information',
+ ],
+ quickcheckFormPurposeLabel: 'Purpose:',
+ quickcheckFormPurpose:
+ 'Handling the quick check request and contacting you to provide the download.',
+ quickcheckFormLegalBasisLabel: 'Legal basis:',
+ quickcheckFormLegalBasis:
+ 'Your consent under Art. 6(1)(a) GDPR, given when submitting the form. You may withdraw your consent at any time, informally.',
+ quickcheckFormRetentionLabel: 'Retention period:',
+ quickcheckFormRetention:
+ 'We delete your details once the request has been dealt with conclusively and no statutory retention obligation applies - at the latest twelve months after the last contact.',
webhostingTitle: 'Web Hosting',
webhostingText:
'We use a provider to host our websites, on whose servers our websites are stored and made available for retrieval on the internet (hosting). In doing so, the provider may process all data transmitted by you via the browser during use of our websites. This includes in particular your IP address, which the provider needs to deliver our online offering to the browser you are using, as well as all inputs made by you on our website. In addition, the provider we use may collect',
diff --git a/src/config/locales/en/navigation.ts b/src/config/locales/en/navigation.ts
index cc50b97..7102c78 100644
--- a/src/config/locales/en/navigation.ts
+++ b/src/config/locales/en/navigation.ts
@@ -5,6 +5,7 @@ export const navigation = {
{ section: 'Skills', label: 'Skills' },
{ section: 'Certifications', label: 'Certifications' },
{ section: 'Projects', label: 'Projects' },
+ { section: 'Audit-Teaser', label: 'Free resources' },
{ section: 'Contact', label: 'Contact' },
],
landingMenuItems: [
@@ -14,6 +15,7 @@ export const navigation = {
{ section: 'Processes', label: 'How I Work' },
{ section: 'Projects', label: 'Projects' },
{ section: 'References', label: 'References' },
+ { section: 'Audit-Teaser', label: 'Free resources' },
{ section: 'Contact', label: 'Contact' },
],
mobileMenuAriaLabel: 'Open menu',
diff --git a/src/config/locales/en/privacy-policy.ts b/src/config/locales/en/privacy-policy.ts
index 35c114d..8bdbc93 100644
--- a/src/config/locales/en/privacy-policy.ts
+++ b/src/config/locales/en/privacy-policy.ts
@@ -3,7 +3,7 @@ export const privacyPolicy = {
lastUpdated: 'Last updated: November 2025',
introTitle: 'Overview',
introText:
- 'This website is a static portfolio website. No cookies, tracking or analytics tools are used, and no third-party services are embedded - the fonts are served from our own server as well. Personal data is only processed if you submit the form on the "Free accessibility audit to WCAG" page.',
+ 'This website is a static portfolio website. No cookies, tracking or analytics tools are used, and no third-party services are embedded - the fonts are served from our own server as well. Personal data is processed if you submit the form on the "Free accessibility audit to WCAG" page or the free accessibility quick check.',
dataCollectionTitle: 'Data Collection',
dataCollectionText:
'Unless you act, this website collects no personal data. When you click on contact links (email, LinkedIn, Git), you will be redirected to external services that have their own privacy policies:',
@@ -28,6 +28,25 @@ export const privacyPolicy = {
auditFormPurposeLabel: 'Purpose:',
auditFormPurpose:
'Handling your request, producing the audit report and contacting you to arrange the call.',
+ quickcheckFormTitle: 'The "Accessibility quick check" form',
+ quickcheckFormText:
+ 'When you submit the quick check form, the data you enter is transmitted to our own server and forwarded from there to us by email. No external form provider is involved. The quick check is a self-test, not a complete audit or legal opinion.',
+ quickcheckFormDataTitle: 'Data processed:',
+ quickcheckFormDataList: [
+ 'Name (optional)',
+ 'Email address',
+ 'Confirmation that you are asking as an entrepreneur, trader or freelancer (§ 14 BGB)',
+ 'Your consent to processing the information',
+ ],
+ quickcheckFormPurposeLabel: 'Purpose:',
+ quickcheckFormPurpose:
+ 'Handling the quick check request and contacting you to provide the download.',
+ quickcheckFormLegalBasisLabel: 'Legal basis:',
+ quickcheckFormLegalBasis:
+ 'Your consent under Art. 6(1)(a) GDPR, given when submitting the form. You may withdraw your consent at any time, informally.',
+ quickcheckFormRetentionLabel: 'Retention period:',
+ quickcheckFormRetention:
+ 'We delete your details once the request has been dealt with conclusively and no statutory retention obligation applies - at the latest twelve months after the last contact.',
auditFormLegalBasisLabel: 'Legal basis:',
auditFormLegalBasis:
'Art. 6(1)(b) GDPR (steps taken at your request prior to entering into a contract) together with your consent under Art. 6(1)(a) GDPR, which you give on submitting the form. You may withdraw that consent at any time, informally.',
diff --git a/src/config/locales/en/projects.ts b/src/config/locales/en/projects.ts
index 05f8b8b..98418b7 100644
--- a/src/config/locales/en/projects.ts
+++ b/src/config/locales/en/projects.ts
@@ -2,13 +2,16 @@ import portfolioImage from '@/assets/portfolio.PNG';
import dancaAlegriaImage from '@/assets/Danca-Alegria.png';
import lukasImage from '@/assets/lukas.png';
import a11yhubImage from '@/assets/a11yhub.png';
+import arcumNovaImage from '@/assets/arcum-nova.png';
+import gxplexImage from '@/assets/gxplex.png';
+import aovmImage from '@/assets/aovm.png';
import placeholderImage from '@/assets/og-image.png';
export const projects = {
title: 'Featured Projects',
subtitle: 'A showcase of my work and personal projects',
codeButtonText: 'Code',
- liveButtonText: 'Live',
+ liveButtonText: 'Open website',
projectItems: [
{
id: 1,
@@ -53,21 +56,33 @@ export const projects = {
},
{
id: 5,
- title: 'Accessibility Audit for a Corporate Client',
+ title: 'arcum-nova.de',
description:
- 'Full BITV 2.0 / EN 301 549 conformance audit (115 criteria) against WCAG 2.2 Level AA across 21 pages, delivering a prioritized findings report (Critical/High/Medium/Low) with concrete remediation guidance and a re-audit roadmap.',
- image: placeholderImage,
- technologies: ['WCAG 2.2', 'BITV 2.0', 'EN 301 549', 'axe DevTools', 'WAVE'],
+ 'Implementation of WCAG 2.2 on an existing WordPress website using Avada / Fusion Builder for a mobile bicycle workshop in Havelland. The work included removing an unwanted site-wide image frame and making the contrast of active menu states conformant.',
+ image: arcumNovaImage,
+ technologies: ['WCAG 2.2', 'WordPress', 'Avada', 'Accessibility'],
year: '2026',
+ live: 'https://arcum-nova.de',
+ },
+ {
+ id: 8,
+ title: 'aovm.de',
+ description:
+ 'Ongoing technical maintenance and content updates for a WordPress website with a custom theme, following client requirements. The work included updates, backups, and content changes. The site uses WordPress; Flatsome is also documented in the project context. The project ended in August 2026.',
+ image: aovmImage,
+ technologies: ['WordPress', 'Maintenance', 'Content updates'],
+ year: '2026',
+ live: 'https://aovm.de',
},
{
id: 6,
- title: 'Custom MediaWiki Platform (GxPlex)',
+ title: 'gxplex.org',
description:
- 'Set up a tailored MediaWiki instance including installation, MySQL database, SSL, and automated backups; configured role-based permissions (Admin, Mod, Verified, User), a FlaggedRevisions editorial review workflow, and comment/rating extensions.',
- image: placeholderImage,
- technologies: ['MediaWiki', 'MySQL', 'PHP', 'SSL'],
+ 'Built an English-language MediaWiki platform with Semantic MediaWiki, the Cavendish skin, and a rating function. The collaborative database brings together laws, regulations, and guidelines relevant to clinical research. It includes a document index, module overview, glossary, search, and membership with contribution functionality. More than 1,100 references are tagged by country, application area, document type, and topic.',
+ image: gxplexImage,
+ technologies: ['MediaWiki 1.43.8', 'Semantic MediaWiki', 'Cavendish', 'Rating function'],
year: '2026',
+ live: 'https://gxplex.org',
},
{
id: 7,
diff --git a/src/config/personal.ts b/src/config/personal.ts
index 2974541..7349ee5 100644
--- a/src/config/personal.ts
+++ b/src/config/personal.ts
@@ -7,7 +7,7 @@ export const personalConfig = {
email: {
user: 'freelancer',
domain: 'sascha-bach.de',
- full: 'freelancer [at] sascha-bach.de',
+ full: 'freelancer@sascha-bach.de',
},
// Booking
diff --git a/src/contexts/LanguageContext.tsx b/src/contexts/LanguageContext.tsx
index 5571c95..1506abb 100644
--- a/src/contexts/LanguageContext.tsx
+++ b/src/contexts/LanguageContext.tsx
@@ -3,6 +3,8 @@ import { en, type TextConfig } from '../config/locales/en';
import { de } from '../config/locales/de';
import type { Language } from '../data/types';
+const LANGUAGE_STORAGE_KEY = 'preferred-language';
+
interface LanguageContextType {
language: Language;
setLanguage: (lang: Language) => void;
@@ -11,25 +13,44 @@ interface LanguageContextType {
const LanguageContext = createContext
(undefined);
-/** Derive the language from the URL pathname prefix (/en/… or /de/…). */
-export function getLanguageFromPath(pathname: string): Language | null {
- if (pathname.startsWith('/en')) return 'en';
- if (pathname.startsWith('/de')) return 'de';
+function normalizeLanguage(value: string | null): Language | null {
+ if (value === 'de' || value === 'en') return value;
return null;
}
-export function LanguageProvider({ initialLanguage = 'de', children }: Readonly<{ initialLanguage?: Language; children: ReactNode; }>) {
- const [language, setLanguage] = useState(initialLanguage);
+function getStoredLanguage(): Language | null {
+ try {
+ return normalizeLanguage(globalThis.localStorage?.getItem(LANGUAGE_STORAGE_KEY) ?? null);
+ } catch {
+ return null;
+ }
+}
+
+function detectBrowserLanguage(): Language {
+ const browserLanguage = globalThis.navigator?.language?.toLowerCase() ?? '';
+ return browserLanguage.startsWith('en') ? 'en' : 'de';
+}
+
+export function detectInitialLanguage(): Language {
+ return getStoredLanguage() ?? detectBrowserLanguage();
+}
+
+export function LanguageProvider({ initialLanguage, children }: Readonly<{ initialLanguage?: Language; children: ReactNode; }>) {
+ const [language, setLanguage] = useState(initialLanguage ?? detectInitialLanguage());
const texts = language === 'de' ? de : en;
const handleSetLanguage = (lang: Language) => {
setLanguage(lang);
- document.documentElement.lang = lang;
};
useEffect(() => {
document.documentElement.lang = language;
+ try {
+ globalThis.localStorage?.setItem(LANGUAGE_STORAGE_KEY, language);
+ } catch {
+ // Ignore storage errors (private mode, denied storage, etc.).
+ }
}, [language]);
return (
diff --git a/src/pages/AuditPage.tsx b/src/pages/AuditPage.tsx
index b77ec65..59e6696 100644
--- a/src/pages/AuditPage.tsx
+++ b/src/pages/AuditPage.tsx
@@ -8,7 +8,7 @@ import { PageSeo } from '../components/PageSeo';
import { useLanguage } from '../contexts/LanguageContext';
export default function AuditPage() {
- const { language, texts } = useLanguage();
+ const { texts } = useLanguage();
const t = texts.audit;
return (
@@ -16,7 +16,7 @@ export default function AuditPage() {
diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx
index a866a78..b523e49 100644
--- a/src/pages/HomePage.tsx
+++ b/src/pages/HomePage.tsx
@@ -4,6 +4,7 @@ import ServicesSection from '../components/sections/ServicesSection';
import SkillsSection from '../components/sections/SkillsSection';
import CertificationsSection from '../components/sections/CertificationsSection';
import ProjectsSection from '../components/sections/ProjectsSection';
+import AuditTeaserSection from '../components/landing/AuditTeaserSection';
import ContactSection from '../components/sections/ContactSection';
import { PageSeo } from '../components/PageSeo';
import { useLanguage } from '../contexts/LanguageContext';
@@ -20,7 +21,7 @@ export default function HomePage() {
description={language === 'de'
? 'Das technische Portfolio von Sascha Bach: Services, Fähigkeiten, Zertifizierungen und Projekte in React, TypeScript und barrierefreier Webentwicklung.'
: 'Explore the technical portfolio of Sascha Bach: services, skills, certifications, and projects in React, TypeScript, and accessible web development.'}
- canonical={`/${language}/technical`}
+ canonical="/technical"
/>
@@ -28,6 +29,7 @@ export default function HomePage() {
+
>
);
diff --git a/src/pages/ImprintPage.tsx b/src/pages/ImprintPage.tsx
index dd4a612..3e7c180 100644
--- a/src/pages/ImprintPage.tsx
+++ b/src/pages/ImprintPage.tsx
@@ -82,170 +82,6 @@ export default function ImprintPage() {