migrate to nextjs
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -12,6 +12,11 @@ dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
build/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
|
||||
311
app/agb/page.tsx
Normal file
311
app/agb/page.tsx
Normal file
@@ -0,0 +1,311 @@
|
||||
export default function AGB() {
|
||||
return (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1 className="no-underline">Liefer- und Zahlungsbedingungen</h1>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: '2rem', flexWrap: 'wrap', gap: '1rem' }}>
|
||||
<p className="subtitle" style={{ margin: 0 }}>Stand Januar 2026</p>
|
||||
<a
|
||||
href="/assets/AGB MB Grid 1-2026.pdf"
|
||||
download
|
||||
className="button"
|
||||
style={{ fontSize: '0.9rem', padding: '0.5rem 1rem' }}
|
||||
>
|
||||
Als PDF herunterladen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="legal-content">
|
||||
<div className="legal-section">
|
||||
<h2>1. Allgemeines</h2>
|
||||
<p>
|
||||
Diese Liefer- und Zahlungsbedingungen (L&Z) der MB Grid Solutions & Services gelten ausschließlich;
|
||||
entgegenstehende oder von unseren Bedingungen abweichende Bedingungen des Kunden erkennen wir nicht an,
|
||||
es sei denn, wir hätten ausdrücklich schriftlich ihrer Geltung zugestimmt. Unsere L&Z gelten auch dann,
|
||||
wenn wir in Kenntnis entgegenstehender oder von unseren L&Z abweichender Bedingungen des Bestellers die
|
||||
Lieferung an diesen vorbehaltlos ausführen. Unsere L&Z gelten nur gegenüber Unternehmern im Sinn von
|
||||
§ 310 Abs. 1 BGB sowie juristischen Personen des öffentlichen Rechts oder öffentlich-rechtliches Sondervermögen.
|
||||
</p>
|
||||
<p>
|
||||
Nebenabreden, Vorbehalte, Änderungen, Ergänzungen usw. bedürfen zu ihrer Wirksamkeit unserer schriftlichen Bestätigung.
|
||||
</p>
|
||||
<p>
|
||||
Hinweise auf die Geltung gesetzlicher Vorschriften haben nur klarstellende Bedeutung. Auch ohne eine
|
||||
derartige Klarstellung gelten daher die gesetzlichen Vorschriften, soweit sie in diesen L&Z nicht
|
||||
unmittelbar abgeändert oder ausdrücklich ausgeschlossen werden. Bezüglich Beratungsleistungen weisen
|
||||
wir ausdrücklich auf Punkt 17 hin.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>2. Angebote</h2>
|
||||
<p>
|
||||
Sofern nicht ausdrücklich als bindend bezeichnet, sind unsere Angebote freibleibend; die Bestellung
|
||||
des Kunden ist als Angebot gemäß § 145 BGB zu qualifizieren.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>3. Preise</h2>
|
||||
<p>
|
||||
Die Preise gelten für den in unseren Angeboten und Auftragsbestätigungen aufgeführten Leistungs- und
|
||||
Lieferumfang. Mehrleistungen werden gesondert berechnet. Die Hohlpreise verstehen sich in Euro zuzüglich
|
||||
Metallzuschlag, gegebenenfalls Verpackung, auftragsspezifischer Schnittkosten und der gesetzlichen Mehrwertsteuer.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>4. Metallnotierung</h2>
|
||||
<p>
|
||||
Basis zur Kupferabrechnung ist die Notierung „LME Copper official price cash offer“, Durchschnitt des
|
||||
Liefervormonats zuzüglich der dann aktuellen von uns benannten Kupfer-Prämie.
|
||||
</p>
|
||||
<p>
|
||||
Basis zur Aluminiumabrechnung ist die Notierung „LME Aluminium official price cash offer“, Durchschnitt
|
||||
des Liefervormonats zuzüglich der dann von uns benannten Aluminium-Prämie. USD werden auf Basis des
|
||||
EUR/USD LME-FX-Rate (MTLE) in EUR umgerechnet. Die entsprechenden Notierungen können Sie der Web-Seite
|
||||
<a href="https://www.westmetall.com" target="_blank" rel="noopener noreferrer"> www.westmetall.com</a> entnehmen.
|
||||
Die Prämienzuschläge können stark variieren und MB Grid Solutions & Services behält sich das Recht vor,
|
||||
diese fristgerecht anzupassen, ungeachtet der Angebotslegung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>5. Metallzahl</h2>
|
||||
<p>
|
||||
Die von uns ausgewiesene Metallzahl ist eine rein kaufmännische Berechnungsgröße für den Metallinhalt,
|
||||
die in die Berechnung des Gesamtpreises eines Kabels eingeht. Damit entsprechen wir Ihrem Wunsch eine
|
||||
Vergleichbarkeit in ihrem System auf Hohlpreisbasis zu ermöglichen. Die Metallzahl gibt damit nicht das
|
||||
Gewicht des tatsächlich im Kabel enthaltenen Leitermetalls an. Sie ist ein rein kalkulatorischer
|
||||
Berechnungsfaktor, der jedoch keine unmittelbaren Rückschlüsse auf die im Kabel verwendeten Kupfer- bzw.
|
||||
Aluminiummengen zulässt. Wir weisen ausdrücklich darauf hin, final nur den Vollpreis für Vergleichszwecke
|
||||
heranzuziehen. Soweit Sie es wünschen andere Metallzahlen zu Grunde zu legen, sind wir gerne dazu bereit,
|
||||
das Angebot in den Bestandteilen umzurechnen. Bei jeglicher Änderung bleibt aber der Vollpreis der gleiche Betrag.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>6. Auftragsänderung / Auftragsstorno</h2>
|
||||
<p>
|
||||
Nach Auftragsbestätigung werden Änderungen an bestätigten Aufträgen nur nach Prüfung und gesonderter
|
||||
ausdrücklicher Zustimmung durch uns akzeptiert. Wir behalten uns bei allen Auftragsänderungen das Recht vor,
|
||||
einen durch die Änderung entstandenen Mehraufwand, wie z.B. Bearbeitungskosten oder Entsorgungskosten in
|
||||
Rechnung zu stellen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>7. Eigentumsvorbehalt</h2>
|
||||
<p>
|
||||
Wir behalten uns an den von uns gelieferten Waren – nachfolgend: Vorbehaltsware – bis zur vollständigen
|
||||
Begleichung aller unserer Forderungen aus den Geschäftsbeziehungen mit dem Besteller, das Eigentum vor.
|
||||
Der Eigentumsvorbehalt bleibt auch dann bestehen, wenn einzelne Forderungen in eine laufende Rechnung
|
||||
aufgenommen werden (Kontokorrentvorbehalt).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>8. Zahlungsbedingungen | Aufrechnung | Zurückbehaltungsrechte</h2>
|
||||
<p>
|
||||
Unsere Rechnungen sind 10 Tage nach Rechnungsdatum ohne jeden Abzug zahlbar. Bei Nichteinhaltung der
|
||||
vereinbarten Zahlungsbedingungen sind wir berechtigt, Zinsen in Höhe von 7 %-Punkten über dem Basiszinssatz
|
||||
zu berechnen; das Recht zur Geltendmachung weitergehender Schäden, insbesondere nachgewiesener höherer
|
||||
Zinsen, bleibt hiervon unberührt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>9. Liefervorbehalt | Teillieferungen</h2>
|
||||
<p>
|
||||
Sämtliche Lieferzusagen unsererseits stehen, sofern nichts anderes ausdrücklich schriftlich vereinbart ist,
|
||||
unter dem Vorbehalt der richtigen und rechtzeitigen Belieferung durch unsere Produzenten. Wir behalten uns
|
||||
jederzeit Teillieferungen vor. Darüber hinaus behalten wir uns branchenübliche Über- oder Unterlieferungen
|
||||
bis zu 10 % der bestellten Menge vor.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>10. Lieferfristen und Liefertermine</h2>
|
||||
<p>
|
||||
Die Lieferfrist wird individuell vereinbart bzw. von uns bei Annahme der Bestellung angegeben. Sofern wir
|
||||
verbindliche Lieferfristen aus Gründen, die wir nicht zu vertreten haben, nicht einhalten können
|
||||
(Nichtverfügbarkeit der Leistung), werden wir den Besteller hierüber unverzüglich informieren und
|
||||
gleichzeitig die voraussichtliche, neue Lieferfrist mitteilen. Ist die Leistung auch innerhalb der neuen
|
||||
Lieferfrist nicht verfügbar, sind wir berechtigt, ganz oder teilweise vom Vertrag zurückzutreten. Eine
|
||||
bereits erbrachte Gegenleistung des Bestellers werden wir unverzüglich erstatten. Nichtverfügbarkeit der
|
||||
Leistung liegt beispielsweise vor bei nicht rechtzeitiger Selbstbelieferung durch unseren Zulieferer, wenn
|
||||
wir ein kongruentes Deckungsgeschäft abgeschlossen haben, bei sonstigen Störungen in der Lieferkette etwa
|
||||
aufgrund höherer Gewalt oder wenn wir im Einzelfall zur Beschaffung nicht verpflichtet sind.
|
||||
</p>
|
||||
<p>
|
||||
Der Eintritt unseres Lieferverzugs bestimmt sich nach den gesetzlichen Vorschriften. In jedem Fall ist
|
||||
aber eine Mahnung durch den Käufer erforderlich.
|
||||
</p>
|
||||
<p>
|
||||
Die gesetzlichen Rechte bleiben im Übrigen unberührt.
|
||||
</p>
|
||||
<p>
|
||||
Fixgeschäfte setzen die ausdrückliche schriftliche Bezeichnung als solche voraus. Ansonsten ist der
|
||||
Besteller stets verpflichtet, uns schriftlich eine angemessene Nachfrist zu setzen, wenn von uns
|
||||
zugesagte Termine und/ oder Fristen nicht eingehalten werden. Wird auch die Nachfrist nicht eingehalten,
|
||||
ist der Besteller berechtigt, vom Vertrag zurückzutreten. Im Fall höherer Gewalt und/oder sonstiger von
|
||||
uns nicht vorhersehbarer außergewöhnlicher und/oder unverschuldeter Umstände, auch wenn sie bei unserem
|
||||
Vorlieferanten eintreten, verlängert sich eine von uns zugesagte Lieferfrist bis zur Behebung des
|
||||
vorerwähnten Ereignisses. Ist dieser Zeitpunkt nicht überblickbar, sind sowohl der Besteller als auch
|
||||
wir berechtigt, von dem abgeschlossenen Vertrag zurückzutreten. In diesem Fall sind beiderseits
|
||||
Schadensersatzansprüche ausgeschlossen. Wir verpflichten uns, bei Bekanntwerden vorerwähnter Umstände
|
||||
den Besteller hiervon unverzüglich zu benachrichtigen.
|
||||
</p>
|
||||
<p>
|
||||
Ist die Einhaltung eines Termins davon abhängig, dass uns seitens des Bestellers bestimmte Angaben
|
||||
und/oder Pläne, Freigabeerklärungen oder ähnliches erteilt werden, beginnt die Lieferfrist erst von dem
|
||||
Zeitpunkt an zu laufen, zu dem uns die vollständigen Angaben des Bestellers schriftlich vorliegen.
|
||||
Wird die Anlieferung auf Wunsch des Bestellers über den vertraglich vorgesehenen Zeitpunkt hinausgeschoben,
|
||||
kann von uns beginnend mit einer Frist von frühestens 10 Werktagen nach Anzeige der Versandbereitschaft
|
||||
dem Besteller ein Lagergeld in Höhe von 2 % des Rechnungsbetrages für jeden angefangenen Monat, maximal
|
||||
jedoch 10 % insgesamt berechnet werden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>11. Abrufaufträge</h2>
|
||||
<p>
|
||||
Wird uns ein Abrufauftrag erteilt und werden über die Abruftermine keine gesonderten schriftlichen
|
||||
Vereinbarungen getroffen, ist der Besteller verpflichtet, uns die einzelnen Abruftermine so mitzuteilen,
|
||||
dass zwischen Eingang der Abrufmitteilung bei uns und Auslieferung mindestens 14 Werktage und die letzte
|
||||
Auslieferung spätestens 90 Tage nach unserer Auftragsbestätigung liegt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>12. Maß- und Gewichtsangaben</h2>
|
||||
<p>
|
||||
Alle Angaben über Durchmesser, Gewicht, technische Gestaltung, Herstellung und Umfang der von uns zu
|
||||
liefernden Ware stehen unter dem Vorbehalt der Abweichung innerhalb der handelsüblichen zulässigen
|
||||
Toleranzen. Darüber hinaus behalten wir uns Änderungen, die einer technischen Verbesserung dienen,
|
||||
jederzeit vor. Farbabweichungen und/oder Abweichungen in der äußeren Beschaffenheit der von uns zu
|
||||
liefernden Ware, die jedoch deren Qualität und technische Wirksamkeit unbeeinflusst lässt, begründen
|
||||
keine Mängelhaftungsansprüche des Bestellers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>13. Gefahrübergang und -tragung</h2>
|
||||
<p>
|
||||
Die Lieferung erfolgt DAP frei Bestimmungsort Deutschland, wo auch der Erfüllungsort für die Lieferung
|
||||
und eine etwaige Nacherfüllung ist.
|
||||
</p>
|
||||
<p>
|
||||
Wird die bestellte Ware von uns versandbereit gestellt und/oder verzögert sich die Versendung und/oder
|
||||
der Abruf aus Gründen, die vom Besteller zu vertreten sind, sind wir berechtigt, Ersatz des hieraus
|
||||
entstehenden Schadens einschließlich Mehraufwendungen zu verlangen. Hierfür berechnen wir eine pauschale
|
||||
Entschädigung i.H. von 2% des Rechnungsbetrages für jeden angefangenen Monat, maximal jedoch 10 %
|
||||
insgesamt beginnend mit der Lieferfrist bzw. – mangels einer Lieferfrist – mit der Mitteilung der
|
||||
Versandbereitschaft der Ware. Der Nachweis eines höheren Schadens und unsere gesetzlichen Ansprüche
|
||||
(insbesondere Ersatz von Mehraufwendungen, angemessene Entschädigung, Kündigung) bleiben unberührt;
|
||||
die Pauschale ist aber auf weitergehende Geldansprüche anzurechnen. Dem Besteller bleibt der Nachweis
|
||||
gestattet, dass uns überhaupt kein oder nur ein wesentlich geringerer Schaden als vorstehende Pauschale
|
||||
entstanden ist. Rücksendungen an uns, die nicht vorher von uns schriftlich bestätigt worden sind,
|
||||
erfolgen auf alleinige Gefahr des Bestellers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>14. Mängelhaftung</h2>
|
||||
<p>
|
||||
Wir haften nur dann für die Einhaltung objektiver Anforderungen an der Ware, wenn und soweit zwischen
|
||||
dem Besteller und uns keine Beschaffenheitsvereinbarung getroffen wurde. Die einzuhaltenden subjektiven
|
||||
Anforderungen gehen den einzuhaltenden objektiven Anforderungen vor. Im Zweifel ergeben sich die
|
||||
vereinbarten Anforderungen an die Ware aus dem von uns bereitgestellten Datenblatt. Einzelne, nicht
|
||||
immer auszuschließende marginale Abweichungen, dürfen durch Reparaturen, wie zum Beispiel
|
||||
Mantelmanschetten nachgebessert werden.
|
||||
</p>
|
||||
<p>
|
||||
Jedwede Mängelhaftungsansprüche des Bestellers setzen voraus, dass dieser die ihm übersandte Ware
|
||||
unverzüglich, d. h. in der Regel sofort bei Anlieferung (noch in Anwesenheit des Transporteurs) auf
|
||||
ihre ordnungsgemäße Beschaffenheit hin überprüft und uns zu verzeichnende sichtbare Mängel unmittelbar
|
||||
nach Erhalt der Ware und verdeckte Mängel unmittelbar nach deren Feststellung schriftlich mitteilt.
|
||||
Soweit ein rechtzeitig gerügter, nicht nur unerheblicher Mangel der Kaufsache vorliegt, sind wir nach
|
||||
unserer Wahl zur Mangelbeseitigung oder zur Ersatzlieferung (Nacherfüllung) berechtigt.
|
||||
</p>
|
||||
<p>
|
||||
Wir übernehmen im Rahmen der Nacherfüllung in keinem Fall Ein- oder Ausbaukosten, wenn und soweit die
|
||||
Mangelhaftigkeit der Ware zum Zeitpunkt des Einbaus dem Besteller bekannt oder grob fahrlässig unbekannt
|
||||
geblieben ist. Sind wir zur Mangelbeseitigung/Ersatzlieferung nicht bereit oder nicht in der Lage oder
|
||||
verzögert sich diese über angemessene Fristen hinaus aus Gründen, die wir zu vertreten haben, oder
|
||||
schlägt sie in sonstiger Weise fehl, so ist der Besteller nach seiner Wahl berechtigt, vom Vertrag
|
||||
zurückzutreten oder eine entsprechende Minderung des Kaufpreises zu verlangen.
|
||||
</p>
|
||||
<p>
|
||||
Weitergehende Ansprüche des Bestellers, gleich aus welchem Rechtsgrund, sind nach näherer Maßgabe der
|
||||
Regelungen in nachstehender Ziffer 15 ausgeschlossen bzw. beschränkt. Die Verjährungsfristen für
|
||||
Mängelhaftungsansprüche beträgt 24 Monate ab Übergabe der Ware.
|
||||
</p>
|
||||
<p>
|
||||
Sollte es bei einer Mängelrüge zu unterschiedlichen Meinungen bezüglich des Kabelschaden kommen, gilt
|
||||
hier im Zweifelsfall nur die Expertise des VDE-Instituts selbst. Andere, auch akkreditierte Testlabore,
|
||||
akzeptieren wir nicht. Wir weisen ausdrücklich daraufhin, dass beim Verlegen des Kabels in den Graben
|
||||
oder in Rohren, bzw. in Bauwerke eine ständige Sichtkontrolle durch den Kabelverleger vorzunehmen ist,
|
||||
ob Auffälligkeiten zu vermerken sind. Eine spätere Reklamation, die fahrlässiges Verhalten vermuten lässt,
|
||||
schränkt sich damit ein. Dies gilt auch bei der Annahme der Ware, wo offensichtliche Beschädigungen
|
||||
direkt zu kommunizieren sind. Spätere Ansprüche nach Akzeptanz einer einwandfreien Belieferung sind
|
||||
detailliert zu beweisen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>15. Schadenersatz | Gesamthaftung</h2>
|
||||
<p>
|
||||
Wir haften unbeschränkt nur für Vorsatz und grobe Fahrlässigkeit sowie für Schäden aus einer Verletzung
|
||||
von Leben, Körper oder Gesundheit, die auf mindestens fahrlässiger Pflichtverletzung unsererseits oder
|
||||
unserer gesetzlichen Vertreter oder Erfüllungsgehilfen beruhen; ebenso haften wir unbeschränkt im Fall
|
||||
von uns übernommenen bzw. abgegebenen Garantien und Zusicherungen, sofern ein davon umfasster Mangel
|
||||
unsere Haftung auslöst sowie im Fall einer Haftung nach dem Produkthaftungsgesetz oder sonstigen
|
||||
Gefährdungshaftungstatbeständen. Im Fall sonstiger schuldhafter Verletzung wesentlicher Vertragspflichten
|
||||
(„Kardinalpflichten“) ist unsere verbleibende Haftung auf den vertragstypischen vorhersehbaren Schaden
|
||||
beschränkt. Mangelfolgeschäden sowie entgangener Gewinn schließen wir grundsätzlich aus.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>16. Kabeltrommeln</h2>
|
||||
<p>
|
||||
Unsere Kabel werden auf stabilen Vollholztrommeln geliefert. Auf Wunsch vermitteln wir Ihnen Partner,
|
||||
die diese Trommeln gegen eine Gebühr abholen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>17. Technische Beratungsdienstleistungen</h2>
|
||||
<p>
|
||||
Die technische Unterstützung ersetzt weder die Fachplanung noch die Ausführungs- oder Prüfverantwortung
|
||||
des beauftragten Ingenieurbüros, Planers oder der ausführenden Fachfirma bzw. verantwortlichen Abteilung.
|
||||
</p>
|
||||
<p>
|
||||
Alle Hinweise, Einschätzungen und Empfehlungen der MB Grid Solutions and Services erfolgen ohne Gewähr
|
||||
und entbinden den jeweiligen Auftragnehmer nicht von seiner eigenen fachlichen Prüfung, Planung und Verantwortung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>18. Sonstiges</h2>
|
||||
<p>
|
||||
Es gilt ausschließlich das Recht der Bundesrepublik Deutschland unter Ausschluss des UN-Kaufrechts (CISG).
|
||||
Gerichtsstand ist nach unserer Wahl Stuttgart, der Erfüllungsort der Lieferverpflichtung oder das für den
|
||||
Sitz des Bestellers zuständige Gericht, sofern der Besteller Kaufmann, juristische Person des öffentlichen
|
||||
Rechts oder öffentlich-rechtliches Sondervermögen ist oder keinen allgemeinen Gerichtsstand im Inland hat.
|
||||
</p>
|
||||
<p>
|
||||
Mit der Veröffentlichung der vorliegenden L&Z im Internet werden alle von uns früher verwendeten
|
||||
Bedingungen gegenstandslos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-footer" style={{ marginTop: '2rem' }}>
|
||||
<p>Remshalden, 22.1.2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
56
app/api/contact/route.ts
Normal file
56
app/api/contact/route.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const { name, email, company, message, website } = await req.json();
|
||||
|
||||
// Honeypot check
|
||||
if (website) {
|
||||
console.log('Spam detected (honeypot)');
|
||||
return NextResponse.json({ message: 'Ok' });
|
||||
}
|
||||
|
||||
// Validation
|
||||
if (!name || name.length < 2 || name.length > 100) {
|
||||
return NextResponse.json({ error: 'Ungültiger Name' }, { status: 400 });
|
||||
}
|
||||
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
|
||||
return NextResponse.json({ error: 'Ungültige E-Mail' }, { status: 400 });
|
||||
}
|
||||
if (!message || message.length < 20 || message.length > 4000) {
|
||||
return NextResponse.json({ error: 'Nachricht zu kurz oder zu lang' }, { status: 400 });
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: parseInt(process.env.SMTP_PORT || '587'),
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: process.env.SMTP_FROM,
|
||||
to: process.env.CONTACT_RECIPIENT,
|
||||
replyTo: email,
|
||||
subject: `Kontaktanfrage von ${name}`,
|
||||
text: `
|
||||
Name: ${name}
|
||||
Firma: ${company || 'Nicht angegeben'}
|
||||
E-Mail: ${email}
|
||||
Zeitpunkt: ${new Date().toISOString()}
|
||||
|
||||
Nachricht:
|
||||
${message}
|
||||
`,
|
||||
});
|
||||
|
||||
return NextResponse.json({ message: 'Ok' });
|
||||
} catch (error) {
|
||||
console.error('SMTP Error:', error);
|
||||
return NextResponse.json({ error: 'Interner Serverfehler' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
22
app/datenschutz/page.tsx
Normal file
22
app/datenschutz/page.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function Privacy() {
|
||||
return (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1 className="no-underline">Datenschutzerklärung</h1>
|
||||
<div className="legal-content">
|
||||
<h2>1. Datenschutz auf einen Blick</h2>
|
||||
<p>Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.</p>
|
||||
|
||||
<h2>2. Hosting</h2>
|
||||
<p>Unsere Website wird bei Hetzner Online GmbH gehostet. Der Serverstandort ist Deutschland. Wir haben einen Vertrag über Auftragsverarbeitung (AVV) mit Hetzner geschlossen.</p>
|
||||
|
||||
<h2>3. Kontaktformular</h2>
|
||||
<p>Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.</p>
|
||||
|
||||
<h2>4. Server-Log-Dateien</h2>
|
||||
<p>Der Provider der Seiten erhebt und speichert automatisch Informationen in sogenannten Server-Log-Dateien, die Ihr Browser automatisch an uns übermittelt. Dies sind: Browsertyp und Browserversion, verwendetes Betriebssystem, Referrer URL, Hostname des zugreifenden Rechners, Uhrzeit der Serveranfrage, IP-Adresse.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
203
app/globals.css
Normal file
203
app/globals.css
Normal file
@@ -0,0 +1,203 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--color-primary: #0E2A47;
|
||||
--color-accent-green: #2FA66A;
|
||||
--color-bg-color: #F8F9FA;
|
||||
--color-secondary-bg: #E6E9ED;
|
||||
--color-text-primary: #1F2933;
|
||||
--color-text-secondary: #6B7280;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-color: #0E2A47;
|
||||
--bg-color: #F8F9FA;
|
||||
--secondary-bg: #E6E9ED;
|
||||
--text-secondary: #6B7280;
|
||||
--text-primary: #1F2933;
|
||||
--accent-green: #2FA66A;
|
||||
--font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
--transition-fast: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--header-height: 80px;
|
||||
--bottom-nav-height: 0px;
|
||||
--safe-area-bottom: env(safe-area-inset-bottom);
|
||||
--safe-area-top: env(safe-area-inset-top);
|
||||
--safe-area-left: env(safe-area-inset-left);
|
||||
--safe-area-right: env(safe-area-inset-right);
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 0.75rem;
|
||||
--spacing-md: 1rem;
|
||||
--spacing-lg: 1.5rem;
|
||||
--spacing-xl: 2rem;
|
||||
--spacing-2xl: 3rem;
|
||||
--spacing-3xl: 4rem;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
letter-spacing: -0.02em;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2rem, 8vw, 3.5rem);
|
||||
hyphens: auto;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.5rem, 6vw, 2.5rem);
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-xl);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h2:not(.no-underline)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
left: 0;
|
||||
width: 48px;
|
||||
height: 4px;
|
||||
background: var(--accent-green);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-lg);
|
||||
}
|
||||
|
||||
section {
|
||||
padding: clamp(3rem, 8vw, 6rem) 0;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
transition: all var(--transition-fast);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 8px;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--header-height: 64px;
|
||||
--bottom-nav-height: 72px;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(var(--bottom-nav-height) + var(--safe-area-bottom));
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
backdrop-filter: blur(24px);
|
||||
border-top: 0.5px solid rgba(0, 0, 0, 0.08);
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
z-index: 1000;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.mobile-nav-link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #9ca3af;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
flex: 1;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.mobile-nav-link.active {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-top-btn {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
z-index: 900;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all var(--transition-smooth);
|
||||
}
|
||||
|
||||
.scroll-top-btn.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
37
app/impressum/page.tsx
Normal file
37
app/impressum/page.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
export default function Legal() {
|
||||
return (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1 className="no-underline">Impressum</h1>
|
||||
<div className="legal-content">
|
||||
<p><strong>Angaben gemäß § 5 TMG</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
MB Grid Solutions & Services GmbH<br />
|
||||
Raiffeisenstraße 22<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
<p><strong>Vertreten durch:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
Michael Bodemer<br />
|
||||
Klaus Mintel
|
||||
</p>
|
||||
<p><strong>Kontakt:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
E-Mail: <a href="mailto:info@mb-grid-solutions.com">info@mb-grid-solutions.com</a><br />
|
||||
Web: <a href="https://www.mb-grid-solutions.com">www.mb-grid-solutions.com</a>
|
||||
</p>
|
||||
<p><strong>Registereintrag:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
Eintragung im Handelsregister.<br />
|
||||
Registergericht: Amtsgericht Stuttgart<br />
|
||||
Registernummer: HRB 803379
|
||||
</p>
|
||||
<p><strong>Urheberrecht:</strong></p>
|
||||
<p>
|
||||
Alle auf der Website veröffentlichten Texte, Bilder und sonstigen Informationen unterliegen – sofern nicht anders gekennzeichnet – dem Urheberrecht. Jede Vervielfältigung, Verbreitung, Speicherung, Übermittlung, Wiedergabe bzw. Weitergabe der Inhalte ohne schriftliche Genehmigung ist ausdrücklich untersagt.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
196
app/kontakt/page.tsx
Normal file
196
app/kontakt/page.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
'use client';
|
||||
|
||||
import { CheckCircle, Mail, MapPin, Send } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function Contact() {
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (response.ok) {
|
||||
setSubmitted(true);
|
||||
} else {
|
||||
const err = await response.json();
|
||||
alert(`Fehler: ${err.error || 'Es gab einen Fehler beim Senden Ihrer Nachricht.'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Es gab einen Fehler beim Senden Ihrer Nachricht.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<section
|
||||
className="relative min-h-[40vh] md:min-h-[50vh] flex items-center overflow-hidden py-6 md:py-12"
|
||||
style={{
|
||||
background: 'linear-gradient(to right, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0.7) 100%), url("/media/laying/iStock-1282259999.jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<div className="max-w-[700px]">
|
||||
<span className="inline-block text-accent-green text-xs font-bold uppercase tracking-[0.15em] mb-3">Kontakt</span>
|
||||
<h1 className="text-primary text-4xl md:text-5xl font-extrabold mb-4 tracking-tight">
|
||||
Kontakt
|
||||
</h1>
|
||||
<p className="text-text-secondary text-lg md:text-xl leading-relaxed max-w-[600px]">
|
||||
Haben Sie Fragen zu einem Projekt oder benötigen Sie technische Beratung? Wir freuen uns auf Ihre Nachricht.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-12 md:py-24">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-16">
|
||||
<div className="flex flex-col gap-8">
|
||||
<a
|
||||
href="mailto:info@mb-grid-solutions.com"
|
||||
className="flex gap-6 items-start p-6 bg-white border border-secondary-bg rounded-2xl hover:border-primary transition-all duration-200"
|
||||
>
|
||||
<div className="text-accent-green p-4 border border-secondary-bg rounded-xl shrink-0">
|
||||
<Mail size={24} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm md:text-base font-semibold mb-1">E-Mail</h4>
|
||||
<span className="text-primary text-lg md:text-xl font-medium">
|
||||
info@mb-grid-solutions.com
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<div className="flex gap-6 items-start p-6 bg-white border border-secondary-bg rounded-2xl">
|
||||
<div className="text-accent-green p-4 border border-secondary-bg rounded-xl shrink-0">
|
||||
<MapPin size={24} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm md:text-base font-semibold mb-1">Anschrift</h4>
|
||||
<p className="text-primary text-lg md:text-xl font-medium leading-relaxed">
|
||||
MB Grid Solutions GmbH<br />
|
||||
Raiffeisenstraße 22<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-8 md:p-12 border border-secondary-bg rounded-2xl shadow-sm">
|
||||
{submitted ? (
|
||||
<div className="text-center py-8 md:py-12">
|
||||
<div className="text-accent-green mb-6 flex justify-center">
|
||||
<CheckCircle size={64} strokeWidth={2} />
|
||||
</div>
|
||||
<h3 className="text-2xl md:text-3xl font-bold mb-4">Nachricht gesendet</h3>
|
||||
<p className="text-text-secondary text-base md:text-lg leading-relaxed mb-8">
|
||||
Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setSubmitted(false)}
|
||||
className="bg-primary text-white px-8 py-4 rounded-lg font-bold uppercase tracking-widest text-xs hover:bg-text-primary transition-all"
|
||||
>
|
||||
Weitere Nachricht
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="text-sm font-semibold">Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
minLength={2}
|
||||
maxLength={100}
|
||||
placeholder="Ihr Name"
|
||||
className="w-full p-4 border border-secondary-bg rounded-xl focus:outline-none focus:border-primary focus:ring-3 focus:ring-accent-green/10 transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="company" className="text-sm font-semibold">Firma</label>
|
||||
<input
|
||||
type="text"
|
||||
id="company"
|
||||
name="company"
|
||||
placeholder="Ihr Unternehmen"
|
||||
className="w-full p-4 border border-secondary-bg rounded-xl focus:outline-none focus:border-primary focus:ring-3 focus:ring-accent-green/10 transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="email" className="text-sm font-semibold">E-Mail *</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
required
|
||||
placeholder="ihre@email.de"
|
||||
className="w-full p-4 border border-secondary-bg rounded-xl focus:outline-none focus:border-primary focus:ring-3 focus:ring-accent-green/10 transition-all"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="message" className="text-sm font-semibold">Nachricht *</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
required
|
||||
minLength={20}
|
||||
maxLength={4000}
|
||||
rows={6}
|
||||
placeholder="Wie können wir Ihnen helfen?"
|
||||
className="w-full p-4 border border-secondary-bg rounded-xl focus:outline-none focus:border-primary focus:ring-3 focus:ring-accent-green/10 transition-all resize-y min-h-[120px]"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div className="hidden">
|
||||
<label htmlFor="website">Website (bitte leer lassen)</label>
|
||||
<input
|
||||
type="text"
|
||||
id="website"
|
||||
name="website"
|
||||
tabIndex={-1}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="self-start bg-primary text-white px-8 py-4 rounded-lg font-bold uppercase tracking-widest text-xs hover:bg-text-primary transition-all flex items-center gap-3 disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Wird gesendet...' : 'Nachricht senden'} <Send size={18} strokeWidth={2.5} />
|
||||
</button>
|
||||
<p className="text-[11px] md:text-xs text-text-secondary leading-relaxed mt-2">
|
||||
* Pflichtfelder. Mit dem Absenden erklären Sie sich mit unserer{' '}
|
||||
<Link href="/datenschutz" className="underline font-medium">
|
||||
Datenschutzerklärung
|
||||
</Link>{' '}
|
||||
einverstanden.
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
app/layout.tsx
Normal file
24
app/layout.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import Layout from "@/components/Layout";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "MB Grid Solutions",
|
||||
description: "Ihr Partner für Energiekabelprojekte bis 110 kV.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body>
|
||||
<Layout>
|
||||
{children}
|
||||
</Layout>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
188
app/page.tsx
Normal file
188
app/page.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { ArrowRight, Shield, Zap, BarChart3, CheckCircle2 } from 'lucide-react';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="bg-bg-color">
|
||||
<section
|
||||
className="relative min-h-[70vh] md:min-h-[80vh] flex items-center overflow-hidden py-8 md:py-16"
|
||||
style={{
|
||||
background: 'linear-gradient(to right, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0.7) 100%), url("/media/business/iStock-1068752548.jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<div className="max-w-[700px]">
|
||||
<span className="inline-block text-accent-green text-xs font-bold uppercase tracking-[0.15em] mb-3">Engineering Excellence</span>
|
||||
<h1 className="text-primary text-4xl md:text-6xl font-extrabold mb-6 tracking-tight leading-[1.15]">
|
||||
Spezialisierter Partner für Energiekabelprojekte
|
||||
</h1>
|
||||
<p className="text-text-secondary text-lg md:text-xl leading-relaxed max-w-[600px] mb-10">
|
||||
Herstellerneutrale technische Beratung ihrer Projekte für Mittel - und Hochspannungsnetze bis zu 110 kV
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-6 items-center">
|
||||
<Link href="/kontakt" className="bg-primary text-white px-8 py-4 rounded-lg font-bold uppercase tracking-widest text-xs hover:bg-text-primary transition-all flex items-center gap-3">
|
||||
Projekt anfragen <ArrowRight size={18} strokeWidth={2.5} />
|
||||
</Link>
|
||||
<Link
|
||||
href="/ueber-uns"
|
||||
className="text-primary font-bold text-xs uppercase tracking-widest flex items-center gap-2 hover:text-accent-green transition-all"
|
||||
>
|
||||
Mehr erfahren <ArrowRight size={16} strokeWidth={2.5} />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-16 md:py-24 bg-[#f8fafc]">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="mb-12 md:mb-20">
|
||||
<span className="inline-block text-accent-green text-xs font-bold uppercase tracking-[0.15em] mb-3">Portfolio</span>
|
||||
<h2 className="text-primary text-3xl md:text-4xl font-bold mb-6 relative inline-block after:content-[''] after:absolute after:-bottom-3 after:left-0 after:w-12 after:h-1 after:bg-accent-green after:rounded-sm">Unsere Leistungen</h2>
|
||||
<p className="text-text-secondary text-lg md:text-xl leading-relaxed max-w-[600px] mt-8">
|
||||
Beratung durch unabhängige Experten mit jahrzehntelanger Erfahrung aus Engineering, Normengremien, Planung und Produktion
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{[
|
||||
{ icon: <Zap size={32} strokeWidth={2} />, title: 'Technische Beratung', desc: 'Individuelle Konzepte, Vergleiche, Risikobetrachtung und Empfehlungen für Ihre Kabelinfrastruktur.' },
|
||||
{ icon: <Shield size={32} strokeWidth={2} />, title: 'Projektbegleitung', desc: 'Trotz bester Planung entstehen bei der Verlegung und Installation oft zusätzliche Herausforderungen, wo wir sie gerne begleiten' },
|
||||
{ icon: <BarChart3 size={32} strokeWidth={2} />, title: 'Produktbeschaffung', desc: 'Herstellerneutrale Marktanalyse und Unterstützung bei der Komponentenwahl in Hinblick auf Qualität, Lieferzeit, Preis und Nachhaltigkeit' }
|
||||
].map((item, i) => (
|
||||
<div key={i} className="bg-white p-8 md:p-10 border border-secondary-bg rounded-2xl shadow-sm hover:border-primary hover:shadow-md transition-all duration-300">
|
||||
<div className="text-accent-green mb-6">
|
||||
{item.icon}
|
||||
</div>
|
||||
<h3 className="text-primary text-xl font-bold mb-4">{item.title}</h3>
|
||||
<p className="text-text-secondary leading-relaxed text-base md:text-lg">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-16 md:py-24 bg-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 items-center gap-12 md:gap-16">
|
||||
<img
|
||||
src="/media/cables/HS Kabel.png"
|
||||
alt="Technical Engineering and Cable Infrastructure"
|
||||
className="w-full h-[300px] md:h-[400px] object-cover rounded-2xl"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div>
|
||||
<span className="inline-block text-accent-green text-xs font-bold uppercase tracking-[0.15em] mb-3">Expertise</span>
|
||||
<h2 className="text-primary text-3xl md:text-4xl font-bold mb-6 relative inline-block after:content-[''] after:absolute after:-bottom-3 after:left-0 after:w-12 after:h-1 after:bg-accent-green after:rounded-sm">Anwendungen & Zielgruppen</h2>
|
||||
<p className="text-text-secondary text-lg md:text-xl leading-relaxed mb-10 mt-8">
|
||||
Wir unterstützen Akteure der Energiewende bei der Realisierung komplexer Kabelprojekte.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{[
|
||||
'Energieversorger',
|
||||
'Ingenieurbüros',
|
||||
'Tiefbauunternehmen',
|
||||
'Industrie',
|
||||
'Projektierer EE',
|
||||
'Planungsbüros'
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 p-4 bg-white border border-accent-green/15 border-l-4 border-l-accent-green rounded-xl shadow-sm hover:translate-x-1 transition-all"
|
||||
>
|
||||
<CheckCircle2 size={18} strokeWidth={2.5} className="text-accent-green shrink-0" />
|
||||
<span className="text-primary font-semibold text-sm md:text-base">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="relative py-16 md:py-24 text-white overflow-hidden"
|
||||
style={{
|
||||
background: 'linear-gradient(rgba(15, 23, 42, 0.85), rgba(15, 23, 42, 0.85)), url("/media/drums/iStock-487538226 (1).jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<div className="mb-12 md:mb-20">
|
||||
<span className="inline-block text-white/60 text-xs font-bold uppercase tracking-[0.15em] mb-3">Expertise</span>
|
||||
<h2 className="text-white text-3xl md:text-4xl font-bold mb-6">Technische Spezifikationen</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{[
|
||||
{ label: 'Kabeltypen im Hochspannungsbereich wie beispielsweise', value: 'N2XS(FL)2Y, N2X(F)KLD2Y, NA2XS(FL)2Y, NA2X(F)KLD2Y', desc: 'Umfassende Expertise des optimalen Designs gängiger Hochspannungskabel.' },
|
||||
{ label: 'Spannungsebenen', value: '64/110 kV & Mittelspannung', desc: 'Spezialisierte Beratung für die 110-kV-Ebene und komplexe Mittelspannungsprojekte.' },
|
||||
{ label: 'Leitertechnologie', value: 'Massiv-, Mehrdraht- & Millikenleiter', desc: 'Optimierung des Leiterdesigns hinsichtlich Stromtragfähigkeit.' }
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="bg-white/5 p-8 md:p-10 border border-white/10 rounded-2xl backdrop-blur-sm"
|
||||
>
|
||||
<h4 className="text-[10px] md:text-xs uppercase text-accent-green font-bold tracking-[0.2em] mb-4">
|
||||
{item.label}
|
||||
</h4>
|
||||
<p className="text-white text-lg md:text-xl font-bold mb-4 leading-tight">
|
||||
{item.value}
|
||||
</p>
|
||||
<p className="text-white/60 text-sm md:text-base leading-relaxed">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-16 md:py-24 bg-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="mb-12 md:mb-20">
|
||||
<span className="inline-block text-accent-green text-xs font-bold uppercase tracking-[0.15em] mb-3">Werte</span>
|
||||
<h2 className="text-primary text-3xl md:text-4xl font-bold mb-6 relative inline-block after:content-[''] after:absolute after:-bottom-3 after:left-0 after:w-12 after:h-1 after:bg-accent-green after:rounded-sm">Unsere Leitprinzipien</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
|
||||
{[
|
||||
{ title: 'Exzellenz', desc: 'Höchste technische Präzision in jedem Detail. Wir suchen die optimale Lösung in Einklang mit Normen, technischen Spezifikation und den Umgebungsparametern.' },
|
||||
{ title: 'Nachhaltigkeit', desc: 'Zukunftssichere Lösungen für die Infrastruktur. Wir denken in Lebenszyklen und Zuverlässigkeit.' },
|
||||
{ title: 'Transparenz', desc: 'Ehrliche Beratung auf Augenhöhe. Wir kommunizieren klar und herstellerneutral.' }
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="border-l-4 border-l-accent-green pl-8"
|
||||
>
|
||||
<h3 className="text-primary text-lg md:text-xl font-bold uppercase tracking-widest mb-4">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-text-secondary leading-relaxed text-base md:text-lg">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="relative py-16 md:py-24 bg-primary overflow-hidden">
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<h2 className="text-white text-3xl md:text-5xl font-bold mb-6 tracking-tight leading-tight">
|
||||
Bereit für Ihr nächstes Projekt?
|
||||
</h2>
|
||||
<p className="text-white/85 text-lg md:text-xl leading-relaxed max-w-[700px] mb-10">
|
||||
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.
|
||||
</p>
|
||||
<Link href="/kontakt" className="inline-flex bg-white text-primary px-8 py-4 rounded-lg font-bold uppercase tracking-widest text-xs hover:bg-accent-green hover:text-white transition-all items-center gap-3">
|
||||
Jetzt Kontakt aufnehmen <ArrowRight size={18} strokeWidth={2.5} />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
337
app/ueber-uns/page.tsx
Normal file
337
app/ueber-uns/page.tsx
Normal file
@@ -0,0 +1,337 @@
|
||||
import { Award, Clock, Lightbulb, Linkedin, MessageSquare, ShieldCheck, Truck } from 'lucide-react';
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<div>
|
||||
<section style={{
|
||||
background: 'linear-gradient(to right, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0.7) 100%), url("/media/drums/iStock-487538226 (1).jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
minHeight: 'clamp(60vh, 70vh, 80vh)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start'
|
||||
}}>
|
||||
<div className="container" style={{ margin: '0 auto', display: 'flex', justifyContent: 'flex-start' }}>
|
||||
<div className="about-hero-content" style={{ maxWidth: '700px', textAlign: 'left', paddingLeft: 0, marginLeft: 0 }}>
|
||||
<h1 className="no-underline">Über uns</h1>
|
||||
<p style={{
|
||||
fontSize: 'clamp(1.125rem, 2.5vw, 1.25rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
marginBottom: 'var(--spacing-xl)',
|
||||
lineHeight: 1.6
|
||||
}}>
|
||||
Wir verbinden Energie, Know-how und Innovation, um die Infrastruktur der Zukunft zu gestalten.
|
||||
</p>
|
||||
<p style={{
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
lineHeight: 1.7,
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)'
|
||||
}}>
|
||||
MB Grid Solution steht für technische Exzellenz in der Energiekabeltechnologie. Wir verstehen uns als Ihr technischer Lotse, der mit jahrzehntelanger Erfahrung und einem klaren Blick für zukunftsweisende Entwicklungen komplexe Projekte sicher zum Ziel führt.
|
||||
</p>
|
||||
<p style={{
|
||||
lineHeight: 1.7,
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)'
|
||||
}}>
|
||||
Unsere Wurzeln liegen in der tiefen praktischen Erfahrung unserer technischen Berater und unserer Netzwerke im globalem Kabelmarkt. Wir vereinen Tradition mit modernster Innovation, um zuverlässige Energielösungen für Projekte bis 110 kV und bei Bedarf darüber hinaus zu realisieren.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
marginBottom: 'clamp(3rem, 6vw, 4rem)'
|
||||
}}>
|
||||
<h2 className="no-underline">Die Köpfe und Koordinatoren hinter MB Grid Solution</h2>
|
||||
</div>
|
||||
<div className="grid team-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))',
|
||||
gap: 'var(--spacing-xl)'
|
||||
}}>
|
||||
<div className="card">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem' }}>
|
||||
<h3 style={{ marginBottom: 0 }}>Michael Bodemer</h3>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/michael-bodemer-33b493122/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#0077b5', display: 'flex' }}
|
||||
aria-label="Michael Bodemer auf LinkedIn"
|
||||
>
|
||||
<Linkedin size={20} />
|
||||
</a>
|
||||
</div>
|
||||
<p style={{
|
||||
color: 'var(--accent-green)',
|
||||
fontWeight: 600,
|
||||
fontSize: 'clamp(0.8125rem, 2vw, 0.85rem)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em',
|
||||
}}>
|
||||
Geschäftsführung und Inhaber
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem' }}>
|
||||
<h3 style={{ marginBottom: 0 }}>Klaus Mintel</h3>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klaus-mintel-b80a8b193/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#0077b5', display: 'flex' }}
|
||||
aria-label="Klaus Mintel auf LinkedIn"
|
||||
>
|
||||
<Linkedin size={20} />
|
||||
</a>
|
||||
</div>
|
||||
<p style={{
|
||||
color: 'var(--accent-green)',
|
||||
fontWeight: 600,
|
||||
fontSize: 'clamp(0.8125rem, 2vw, 0.85rem)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em',
|
||||
}}>
|
||||
Geschäftsführung
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
marginBottom: 'clamp(3rem, 8vw, 5rem)'
|
||||
}}>
|
||||
<h2 className="no-underline">Unser Manifest</h2>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)'
|
||||
}}>
|
||||
Werte, die unsere tägliche Arbeit leiten.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid manifest-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
gap: 'var(--spacing-2xl)'
|
||||
}}>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Award size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
1. Kompetenz
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Jahrzehntelange Erfahrung kombiniert mit europaweitem Know-how. Wir arbeiten mit Partnern für modernste Anlagen und Testlabore bis 525 kV.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Clock size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
2. Verfügbarkeit
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Schnelle und verlässliche Unterstützung ohne unnötige Verzögerungen. Wir sind für Sie da, wenn es darauf ankommt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Lightbulb size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
3. Lösungen
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Wir stellen die richtigen Fragen – an Sie, an Hersteller und an uns selbst. Nur wer hinterfragt, findet die technisch und wirtschaftlich beste Lösung. Und reduziert Risiken.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Truck size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
4. Logistik & Überwachung
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Von der Fertigungsüberwachung bis zum Fracht-Tracking und der termingerechten Anlieferung – wir steuern den gesamten Prozess professionell.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<MessageSquare size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
5. Offenheit
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Wir hören zu und passen unsere Prozesse kontinuierlich an. Stillstand ist für uns keine Option – wir optimieren für Ihren Erfolg.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<ShieldCheck size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
6. Zuverlässigkeit
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Wir halten, was wir versprechen – ohne Ausnahme. Verbindlichkeit ist das Fundament unserer Zusammenarbeit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div className="card" style={{
|
||||
background: 'var(--primary-color)',
|
||||
color: 'white',
|
||||
textAlign: 'left',
|
||||
padding: 'clamp(3rem, 6vw, 4rem)'
|
||||
}}>
|
||||
<h2
|
||||
className="no-underline"
|
||||
style={{
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
fontSize: 'clamp(1.5rem, 6vw, 2rem)',
|
||||
position: 'relative',
|
||||
textAlign: 'left'
|
||||
}}
|
||||
>
|
||||
Bereit für Ihr nächstes Projekt?
|
||||
</h2>
|
||||
<p style={{
|
||||
marginBottom: 'var(--spacing-2xl)',
|
||||
opacity: 0.9,
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
lineHeight: 1.65,
|
||||
textAlign: 'left'
|
||||
}}>
|
||||
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.
|
||||
</p>
|
||||
<a
|
||||
href="/kontakt"
|
||||
className="cta-button"
|
||||
style={{
|
||||
background: 'white',
|
||||
color: 'var(--primary-color)'
|
||||
}}
|
||||
aria-label="Jetzt Kontakt aufnehmen"
|
||||
>
|
||||
Jetzt Kontakt aufnehmen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
147
components/Layout.tsx
Normal file
147
components/Layout.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
'use client';
|
||||
|
||||
import { ArrowUp, Home, Info, Mail } from 'lucide-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
const pathname = usePathname();
|
||||
const [showScrollTop, setShowScrollTop] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setShowScrollTop(window.scrollY > 400);
|
||||
};
|
||||
|
||||
const throttledScroll = () => {
|
||||
window.requestAnimationFrame(handleScroll);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', throttledScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', throttledScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const isActive = (path: string) => pathname === path;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<header className="sticky top-0 z-[100] bg-bg-color/95 backdrop-blur-xl border-b border-secondary-bg pt-[env(safe-area-top)]">
|
||||
<div className="container mx-auto px-4 h-20 flex justify-between items-center">
|
||||
<Link
|
||||
href="/"
|
||||
className="flex items-center"
|
||||
aria-label="MB Grid Solutions - Zur Startseite"
|
||||
>
|
||||
<img
|
||||
src="/assets/logo.png"
|
||||
alt="MB Grid Solutions"
|
||||
className="h-12 md:h-40 transition-all"
|
||||
loading="eager"
|
||||
/>
|
||||
</Link>
|
||||
<nav className="hidden md:flex gap-8" aria-label="Hauptnavigation">
|
||||
<Link href="/" className={`text-xs font-semibold uppercase tracking-widest transition-colors ${isActive('/') ? 'text-accent-green' : 'text-text-primary hover:text-accent-green'}`}>
|
||||
Startseite
|
||||
</Link>
|
||||
<Link href="/ueber-uns" className={`text-xs font-semibold uppercase tracking-widest transition-colors ${isActive('/ueber-uns') ? 'text-accent-green' : 'text-text-primary hover:text-accent-green'}`}>
|
||||
Über uns
|
||||
</Link>
|
||||
<Link href="/kontakt" className={`text-xs font-semibold uppercase tracking-widest transition-colors ${isActive('/kontakt') ? 'text-accent-green' : 'text-text-primary hover:text-accent-green'}`}>
|
||||
Kontakt
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="flex-grow pb-[calc(72px+env(safe-area-bottom))] md:pb-0">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className={`fixed bottom-24 md:bottom-8 right-5 md:right-8 w-12 md:w-14 h-12 md:h-14 bg-primary text-white rounded-full flex items-center justify-center cursor-pointer z-[900] shadow-lg transition-all duration-300 hover:-translate-y-1 hover:bg-accent-green ${showScrollTop ? 'opacity-100 visible translate-y-0' : 'opacity-0 invisible translate-y-2'}`}
|
||||
aria-label="Nach oben scrollen"
|
||||
aria-hidden={!showScrollTop}
|
||||
tabIndex={showScrollTop ? 0 : -1}
|
||||
>
|
||||
<ArrowUp size={24} strokeWidth={2.5} />
|
||||
</button>
|
||||
|
||||
<nav className="md:hidden fixed bottom-0 left-0 w-full h-[calc(72px+env(safe-area-bottom))] bg-white/95 backdrop-blur-2xl border-t border-black/10 flex justify-around items-start pt-2 z-[1000] px-2" aria-label="Mobile Navigation">
|
||||
<Link
|
||||
href="/"
|
||||
className={`flex flex-col items-center gap-1 text-[10px] font-semibold uppercase tracking-wider flex-1 py-2 transition-colors ${isActive('/') ? 'text-primary' : 'text-gray-400'}`}
|
||||
aria-label="Startseite"
|
||||
>
|
||||
<Home size={22} strokeWidth={2} className={isActive('/') ? 'text-accent-green scale-110' : ''} />
|
||||
<span>Start</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/ueber-uns"
|
||||
className={`flex flex-col items-center gap-1 text-[10px] font-semibold uppercase tracking-wider flex-1 py-2 transition-colors ${isActive('/ueber-uns') ? 'text-primary' : 'text-gray-400'}`}
|
||||
aria-label="Über uns"
|
||||
>
|
||||
<Info size={22} strokeWidth={2} className={isActive('/ueber-uns') ? 'text-accent-green scale-110' : ''} />
|
||||
<span>Über uns</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/kontakt"
|
||||
className={`flex flex-col items-center gap-1 text-[10px] font-semibold uppercase tracking-wider flex-1 py-2 transition-colors ${isActive('/kontakt') ? 'text-primary' : 'text-gray-400'}`}
|
||||
aria-label="Kontakt"
|
||||
>
|
||||
<Mail size={22} strokeWidth={2} className={isActive('/kontakt') ? 'text-accent-green scale-110' : ''} />
|
||||
<span>Kontakt</span>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<footer className="bg-white border-t border-secondary-bg py-8 md:py-16 mt-8 md:mt-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 md:gap-12 mb-8 md:mb-12">
|
||||
<div className="flex flex-col items-center md:items-start text-center md:text-left">
|
||||
<img
|
||||
src="/assets/logo.png"
|
||||
alt="MB Grid Solutions"
|
||||
className="h-16 md:h-20 mb-6 grayscale"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p className="text-text-secondary text-sm md:text-base leading-relaxed">
|
||||
Ihr Partner für Energiekabelprojekte bis 110 kV.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:items-start text-center md:text-left">
|
||||
<h4 className="text-sm md:text-base font-semibold mb-4">Navigation</h4>
|
||||
<nav className="flex flex-col gap-3" aria-label="Footer Navigation">
|
||||
<Link href="/" className="text-sm md:text-[15px] hover:text-accent-green transition-colors">Startseite</Link>
|
||||
<Link href="/ueber-uns" className="text-sm md:text-[15px] hover:text-accent-green transition-colors">Über uns</Link>
|
||||
<Link href="/kontakt" className="text-sm md:text-[15px] hover:text-accent-green transition-colors">Kontakt</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:items-start text-center md:text-left">
|
||||
<h4 className="text-sm md:text-base font-semibold mb-4">Rechtliches</h4>
|
||||
<nav className="flex flex-col gap-3" aria-label="Legal Navigation">
|
||||
<Link href="/impressum" className="text-sm md:text-[15px] hover:text-accent-green transition-colors">Impressum</Link>
|
||||
<Link href="/datenschutz" className="text-sm md:text-[15px] hover:text-accent-green transition-colors">Datenschutz</Link>
|
||||
<Link href="/agb" className="text-sm md:text-[15px] hover:text-accent-green transition-colors">AGB</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-secondary-bg pt-8 flex flex-col md:flex-row justify-between items-center gap-4 text-text-secondary text-xs md:text-sm text-center">
|
||||
<div>© 2026 MB Grid Solutions GmbH. Alle Rechte vorbehalten.</div>
|
||||
<div>Made with precision.</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -6,26 +6,16 @@ services:
|
||||
- "3000"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.mb-grid-solutions.rule=Host(`mb-grid-solutions.com`) || Host(`www.mb-grid-solutions.com`)"
|
||||
- "traefik.http.routers.mb-grid-solutions.rule=(Host(`mb-grid-solutions.com`) || Host(`www.mb-grid-solutions.com`))"
|
||||
- "traefik.http.routers.mb-grid-solutions.entrypoints=websecure"
|
||||
- "traefik.http.routers.mb-grid-solutions.tls.certresolver=le"
|
||||
- "traefik.http.services.mb-grid-solutions.loadbalancer.server.port=3000"
|
||||
- "traefik.http.routers.mb-grid-solutions.middlewares=auth@docker"
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "const http = require('http'); const options = { host: '127.0.0.1', port: 3000, path: '/health', timeout: 2000 }; const request = http.request(options, (res) => { if (res.statusCode === 200) { process.exit(0); } else { process.exit(1); } }); request.on('error', (err) => { process.exit(1); }); request.end();"]
|
||||
test: ["CMD", "node", "-e", "fetch('http://localhost:3000/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"]
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
deploy:
|
||||
replicas: 2
|
||||
update_config:
|
||||
order: start-first
|
||||
failure_action: rollback
|
||||
delay: 5s
|
||||
rollback_config:
|
||||
parallelism: 1
|
||||
order: stop-first
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
networks:
|
||||
- infra
|
||||
|
||||
|
||||
61
index.html
61
index.html
@@ -1,61 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/assets/logo.png" />
|
||||
|
||||
<!-- Mobile Optimization -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#0E2A47" media="(prefers-color-scheme: light)" />
|
||||
<meta name="theme-color" content="#0E2A47" media="(prefers-color-scheme: dark)" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="MB Grid Solutions" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="format-detection" content="telephone=yes" />
|
||||
<meta name="format-detection" content="email=yes" />
|
||||
|
||||
<!-- Performance -->
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
<!-- SEO -->
|
||||
<title>MB Grid Solutions | Energiekabelprojekte bis 110 kV</title>
|
||||
<meta name="description" content="Spezialisierter Partner für Energiekabelprojekte bis 110 kV. Herstellerneutrale technische Beratung und Projektbegleitung." />
|
||||
<meta name="keywords" content="Energiekabel, Hochspannung, 110 kV, Kabelinfrastruktur, Projektbegleitung, technische Beratung" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="MB Grid Solutions | Energiekabelprojekte bis 110 kV" />
|
||||
<meta property="og:description" content="Spezialisierter Partner für Energiekabelprojekte bis 110 kV. Herstellerneutrale technische Beratung und Projektbegleitung." />
|
||||
<meta property="og:site_name" content="MB Grid Solutions" />
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/assets/logo.png" />
|
||||
|
||||
<style>
|
||||
/* Critical CSS for initial render */
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
/* Prevent FOUC */
|
||||
body {
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.3s ease-in forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
6
next-env.d.ts
vendored
Normal file
6
next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
6528
package-lock.json
generated
6528
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
54
package.json
54
package.json
@@ -2,49 +2,37 @@
|
||||
"name": "mb-grid-solutions.com",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "dist/backend/server.js",
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
},
|
||||
"scripts": {
|
||||
"dev:frontend": "vite",
|
||||
"dev:backend": "nodemon --exec ts-node-esm server.ts",
|
||||
"build:frontend": "vite build",
|
||||
"build:backend": "tsc -p tsconfig.server.json",
|
||||
"build": "npm run build:frontend && npm run build:backend",
|
||||
"start": "node dist/backend/server.js",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.2.1",
|
||||
"helmet": "^8.1.0",
|
||||
"nodemailer": "^7.0.12"
|
||||
"framer-motion": "^12.29.2",
|
||||
"lucide-react": "^0.562.0",
|
||||
"next": "^16.1.6",
|
||||
"nodemailer": "^7.0.12",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^25.0.8",
|
||||
"@types/nodemailer": "^7.0.5",
|
||||
"@types/react": "^19.2.8",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/react": "^19.2.10",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react-swc": "^3.11.0",
|
||||
"framer-motion": "^12.26.2",
|
||||
"lucide-react": "^0.562.0",
|
||||
"nodemon": "^3.1.11",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-router-dom": "^7.12.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"autoprefixer": "^10.4.23",
|
||||
"jsdom": "^27.4.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.4.1",
|
||||
"vite-plugin-checker": "^0.12.0"
|
||||
"vitest": "^4.0.18"
|
||||
}
|
||||
}
|
||||
|
||||
110
server.ts
110
server.ts
@@ -1,110 +0,0 @@
|
||||
import express from 'express';
|
||||
import * as path from 'path';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import cors from 'cors';
|
||||
import * as dotenv from 'dotenv';
|
||||
import helmet from 'helmet';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
// Security
|
||||
app.use(helmet({
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
...helmet.contentSecurityPolicy.getDefaultDirectives(),
|
||||
"script-src": ["'self'", "'sha256-YX4iJw93x5SU0ple+RI+95HNdNBZSA60gR8a5v7HfOA='", "'sha256-ieoeWczDHkReVBsRBqaal5AFMlBtNjMzgwKvLqi/tSU='"],
|
||||
"img-src": ["'self'", "data:", "blob:"],
|
||||
"style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
||||
"font-src": ["'self'", "https://fonts.gstatic.com"],
|
||||
},
|
||||
},
|
||||
}));
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// Rate limiting for API
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 10 * 60 * 1000, // 10 minutes
|
||||
max: 5, // limit each IP to 5 requests per windowMs
|
||||
message: 'Zu viele Anfragen von dieser IP, bitte versuchen Sie es später erneut.',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
// API Endpoint
|
||||
app.post('/api/contact', apiLimiter, async (req, res) => {
|
||||
const { name, email, company, message, website } = req.body;
|
||||
|
||||
// Honeypot check
|
||||
if (website) {
|
||||
console.log('Spam detected (honeypot)');
|
||||
return res.status(200).json({ message: 'Ok' }); // Generic success
|
||||
}
|
||||
|
||||
// Validation
|
||||
if (!name || name.length < 2 || name.length > 100) {
|
||||
return res.status(400).json({ error: 'Ungültiger Name' });
|
||||
}
|
||||
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
|
||||
return res.status(400).json({ error: 'Ungültige E-Mail' });
|
||||
}
|
||||
if (!message || message.length < 20 || message.length > 4000) {
|
||||
return res.status(400).json({ error: 'Nachricht zu kurz oder zu lang' });
|
||||
}
|
||||
|
||||
try {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: parseInt(process.env.SMTP_PORT || '587'),
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: process.env.SMTP_FROM,
|
||||
to: process.env.CONTACT_RECIPIENT,
|
||||
replyTo: email,
|
||||
subject: `Kontaktanfrage von ${name}`,
|
||||
text: `
|
||||
Name: ${name}
|
||||
Firma: ${company || 'Nicht angegeben'}
|
||||
E-Mail: ${email}
|
||||
Zeitpunkt: ${new Date().toISOString()}
|
||||
|
||||
Nachricht:
|
||||
${message}
|
||||
`,
|
||||
});
|
||||
|
||||
res.status(200).json({ message: 'Ok' });
|
||||
} catch (error) {
|
||||
console.error('SMTP Error:', error);
|
||||
res.status(500).json({ error: 'Interner Serverfehler' });
|
||||
}
|
||||
});
|
||||
|
||||
// Health check
|
||||
app.get('/health', (req, res) => {
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
|
||||
// Serve static files from the React app
|
||||
const distPath = path.join(process.cwd(), 'dist/frontend');
|
||||
app.use(express.static(distPath));
|
||||
|
||||
// The "catchall" handler: for any request that doesn't
|
||||
// match one above, send back React's index.html file.
|
||||
app.get(/.*/, (req, res) => {
|
||||
res.sendFile(path.join(distPath, 'index.html'));
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
});
|
||||
27
src/App.tsx
27
src/App.tsx
@@ -1,27 +0,0 @@
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import Layout from './components/Layout';
|
||||
import Home from './pages/Home';
|
||||
import About from './pages/About';
|
||||
import Contact from './pages/Contact';
|
||||
import Legal from './pages/Legal';
|
||||
import Privacy from './pages/Privacy';
|
||||
import AGB from './pages/AGB';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Layout>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/ueber-uns" element={<About />} />
|
||||
<Route path="/kontakt" element={<Contact />} />
|
||||
<Route path="/impressum" element={<Legal />} />
|
||||
<Route path="/datenschutz" element={<Privacy />} />
|
||||
<Route path="/agb" element={<AGB />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,175 +0,0 @@
|
||||
import { ArrowUp, Home, Info, Mail } from 'lucide-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Link, NavLink, useLocation } from 'react-router-dom';
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
const location = useLocation();
|
||||
const [showScrollTop, setShowScrollTop] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setShowScrollTop(window.scrollY > 400);
|
||||
};
|
||||
|
||||
const throttledScroll = () => {
|
||||
window.requestAnimationFrame(handleScroll);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', throttledScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', throttledScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [location.pathname]);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="layout">
|
||||
<header>
|
||||
<div className="container">
|
||||
<Link
|
||||
to="/"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
aria-label="MB Grid Solutions - Zur Startseite"
|
||||
>
|
||||
<img
|
||||
src="/assets/logo.png"
|
||||
alt="MB Grid Solutions"
|
||||
style={{ height: '160px' }}
|
||||
loading="eager"
|
||||
/>
|
||||
</Link>
|
||||
<nav style={{ display: 'flex', gap: '2rem' }} aria-label="Hauptnavigation">
|
||||
<NavLink to="/" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
|
||||
Startseite
|
||||
</NavLink>
|
||||
<NavLink to="/ueber-uns" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
|
||||
Über uns
|
||||
</NavLink>
|
||||
<NavLink to="/kontakt" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
|
||||
Kontakt
|
||||
</NavLink>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className={`scroll-top-btn ${showScrollTop ? 'visible' : ''}`}
|
||||
aria-label="Nach oben scrollen"
|
||||
aria-hidden={!showScrollTop}
|
||||
tabIndex={showScrollTop ? 0 : -1}
|
||||
>
|
||||
<ArrowUp size={24} strokeWidth={2.5} />
|
||||
</button>
|
||||
|
||||
<nav className="mobile-nav" aria-label="Mobile Navigation">
|
||||
<NavLink
|
||||
to="/"
|
||||
className={({ isActive }) => `mobile-nav-link ${isActive ? 'active' : ''}`}
|
||||
aria-label="Startseite"
|
||||
>
|
||||
<Home size={22} strokeWidth={2} />
|
||||
<span>Start</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/ueber-uns"
|
||||
className={({ isActive }) => `mobile-nav-link ${isActive ? 'active' : ''}`}
|
||||
aria-label="Über uns"
|
||||
>
|
||||
<Info size={22} strokeWidth={2} />
|
||||
<span>Über uns</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/kontakt"
|
||||
className={({ isActive }) => `mobile-nav-link ${isActive ? 'active' : ''}`}
|
||||
aria-label="Kontakt"
|
||||
>
|
||||
<Mail size={22} strokeWidth={2} />
|
||||
<span>Kontakt</span>
|
||||
</NavLink>
|
||||
</nav>
|
||||
|
||||
<footer style={{
|
||||
borderTop: '1px solid var(--secondary-bg)',
|
||||
padding: 'clamp(2rem, 4vw, 4rem) 0',
|
||||
marginTop: 'clamp(2rem, 4vw, 4rem)',
|
||||
background: 'white'
|
||||
}}>
|
||||
<div className="container">
|
||||
<div className="grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
||||
marginBottom: 'clamp(1.5rem, 3vw, 3rem)'
|
||||
}}>
|
||||
<div>
|
||||
<img
|
||||
src="/assets/logo.png"
|
||||
alt="MB Grid Solutions"
|
||||
style={{
|
||||
height: 'clamp(60px, 10vw, 80px)',
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
filter: 'grayscale(1)'
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
lineHeight: 1.6
|
||||
}}>
|
||||
Ihr Partner für Energiekabelprojekte bis 110 kV.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
fontSize: 'clamp(0.9rem, 2vw, 1rem)',
|
||||
marginBottom: 'var(--spacing-md)',
|
||||
fontWeight: 600
|
||||
}}>Navigation</h4>
|
||||
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.625rem' }} aria-label="Footer Navigation">
|
||||
<Link to="/" style={{ fontSize: 'clamp(0.875rem, 2vw, 0.95rem)' }}>Startseite</Link>
|
||||
<Link to="/ueber-uns" style={{ fontSize: 'clamp(0.875rem, 2vw, 0.95rem)' }}>Über uns</Link>
|
||||
<Link to="/kontakt" style={{ fontSize: 'clamp(0.875rem, 2vw, 0.95rem)' }}>Kontakt</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
fontSize: 'clamp(0.9rem, 2vw, 1rem)',
|
||||
marginBottom: 'var(--spacing-md)',
|
||||
fontWeight: 600
|
||||
}}>Rechtliches</h4>
|
||||
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.625rem' }} aria-label="Legal Navigation">
|
||||
<Link to="/impressum" style={{ fontSize: 'clamp(0.875rem, 2vw, 0.95rem)' }}>Impressum</Link>
|
||||
<Link to="/datenschutz" style={{ fontSize: 'clamp(0.875rem, 2vw, 0.95rem)' }}>Datenschutz</Link>
|
||||
<Link to="/agb" style={{ fontSize: 'clamp(0.875rem, 2vw, 0.95rem)' }}>AGB</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{
|
||||
borderTop: '1px solid var(--secondary-bg)',
|
||||
paddingTop: 'var(--spacing-xl)',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
color: 'var(--text-secondary)',
|
||||
fontSize: 'clamp(0.75rem, 2vw, 0.85rem)',
|
||||
gap: 'var(--spacing-md)',
|
||||
flexWrap: 'wrap'
|
||||
}}>
|
||||
<div>© 2026 MB Grid Solutions GmbH. Alle Rechte vorbehalten.</div>
|
||||
<div>Made with precision.</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
814
src/index.css
814
src/index.css
@@ -1,814 +0,0 @@
|
||||
:root {
|
||||
--primary-color: #0E2A47;
|
||||
--bg-color: #F8F9FA;
|
||||
--secondary-bg: #E6E9ED;
|
||||
--text-secondary: #6B7280;
|
||||
--text-primary: #1F2933;
|
||||
--accent-green: #2FA66A;
|
||||
--font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
--transition-fast: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--header-height: 80px;
|
||||
--bottom-nav-height: 0px;
|
||||
--safe-area-bottom: env(safe-area-inset-bottom);
|
||||
--safe-area-top: env(safe-area-inset-top);
|
||||
--safe-area-left: env(safe-area-inset-left);
|
||||
--safe-area-right: env(safe-area-inset-right);
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 0.75rem;
|
||||
--spacing-md: 1rem;
|
||||
--spacing-lg: 1.5rem;
|
||||
--spacing-xl: 2rem;
|
||||
--spacing-2xl: 3rem;
|
||||
--spacing-3xl: 4rem;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-tap-highlight-color: rgba(47, 166, 106, 0.1);
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-feature-settings: 'kern' 1;
|
||||
font-kerning: normal;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
letter-spacing: -0.02em;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2rem, 8vw, 3.5rem);
|
||||
hyphens: auto;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
h1.no-underline::after,
|
||||
h2.no-underline::after,
|
||||
.no-underline::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.5rem, 6vw, 2.5rem);
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-xl);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h2:not(.no-underline)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
left: 0;
|
||||
width: 48px;
|
||||
height: 4px;
|
||||
background: var(--accent-green);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: clamp(1.2rem, 4vw, 1.4rem);
|
||||
letter-spacing: -0.01em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
-webkit-touch-callout: default;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-lg);
|
||||
padding-left: max(var(--spacing-lg), var(--safe-area-left));
|
||||
padding-right: max(var(--spacing-lg), var(--safe-area-right));
|
||||
}
|
||||
|
||||
section {
|
||||
padding: clamp(3rem, 8vw, 6rem) 0;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
transition: all var(--transition-fast);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 8px;
|
||||
min-height: 48px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background-color: var(--text-primary);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.cta-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--secondary-bg);
|
||||
background: white;
|
||||
font-family: inherit;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
transition: all var(--transition-fast);
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(47, 166, 106, 0.1);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
padding: var(--spacing-xl);
|
||||
border: 1px solid var(--secondary-bg);
|
||||
transition: all var(--transition-smooth);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.img-placeholder {
|
||||
background: #e2e8f0;
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid var(--secondary-bg);
|
||||
border-radius: 8px;
|
||||
content-visibility: auto;
|
||||
padding: var(--spacing-md);
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: block;
|
||||
color: var(--accent-green);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.15em;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: rgba(248, 249, 250, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--secondary-bg);
|
||||
padding-top: var(--safe-area-top);
|
||||
}
|
||||
|
||||
header .container {
|
||||
height: var(--header-height);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-top-btn {
|
||||
position: fixed;
|
||||
bottom: calc(var(--bottom-nav-height) + var(--safe-area-bottom) + 20px);
|
||||
right: 20px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
z-index: 900;
|
||||
box-shadow: 0 8px 24px rgba(14, 42, 71, 0.2);
|
||||
transition: all var(--transition-smooth);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.scroll-top-btn.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.scroll-top-btn:hover {
|
||||
transform: translateY(-4px);
|
||||
background: var(--accent-green);
|
||||
box-shadow: 0 12px 32px rgba(47, 166, 106, 0.3);
|
||||
}
|
||||
|
||||
.scroll-top-btn:active {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.scroll-top-btn {
|
||||
bottom: calc(var(--bottom-nav-height) + var(--safe-area-bottom) + 16px);
|
||||
right: 16px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--header-height: 64px;
|
||||
--bottom-nav-height: 72px;
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 0.75rem;
|
||||
--spacing-md: 1rem;
|
||||
--spacing-lg: 1.25rem;
|
||||
--spacing-xl: 2rem;
|
||||
--spacing-2xl: 2.5rem;
|
||||
--spacing-3xl: 3rem;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: clamp(2.5rem, 6vw, 3.5rem) 0;
|
||||
}
|
||||
|
||||
.grid {
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 var(--spacing-md);
|
||||
padding-left: max(var(--spacing-md), var(--safe-area-left));
|
||||
padding-right: max(var(--spacing-md), var(--safe-area-right));
|
||||
}
|
||||
|
||||
header .container {
|
||||
padding: 0 var(--spacing-md);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
header nav {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 52px !important;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(var(--bottom-nav-height) + var(--safe-area-bottom));
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
backdrop-filter: blur(24px);
|
||||
-webkit-backdrop-filter: blur(24px);
|
||||
border-top: 0.5px solid rgba(0, 0, 0, 0.08);
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
z-index: 1000;
|
||||
padding-top: 8px;
|
||||
padding-bottom: max(8px, var(--safe-area-bottom));
|
||||
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.mobile-nav-link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
color: #9ca3af;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
flex: 1;
|
||||
padding: 8px 4px;
|
||||
min-width: 64px;
|
||||
min-height: 56px;
|
||||
transition: all var(--transition-fast);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mobile-nav-link svg {
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.mobile-nav-link.active {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.mobile-nav-link.active svg {
|
||||
color: var(--accent-green);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.mobile-nav-link:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
main {
|
||||
padding-bottom: calc(var(--bottom-nav-height) + var(--safe-area-bottom));
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
width: 100%;
|
||||
padding: 1.125rem 1.5rem;
|
||||
font-size: 0.875rem;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 4px 12px rgba(14, 42, 71, 0.12);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.cta-button:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 0 !important;
|
||||
min-height: auto !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
padding: var(--spacing-2xl) var(--spacing-lg);
|
||||
text-align: left !important;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
font-size: clamp(1.875rem, 9vw, 2.5rem);
|
||||
line-height: 1.15;
|
||||
margin-bottom: var(--spacing-md);
|
||||
letter-spacing: -0.03em;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.hero-content p {
|
||||
font-size: 1.0625rem !important;
|
||||
line-height: 1.6;
|
||||
margin-bottom: var(--spacing-xl);
|
||||
color: #4b5563;
|
||||
text-align: left !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
justify-content: flex-start !important;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: var(--spacing-md) !important;
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
.hero-actions .secondary-link {
|
||||
padding: var(--spacing-md);
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero-img {
|
||||
display: block !important;
|
||||
width: 100vw !important;
|
||||
margin-left: calc(-1 * var(--spacing-md)) !important;
|
||||
margin-right: calc(-1 * var(--spacing-md)) !important;
|
||||
border-radius: 0 !important;
|
||||
order: 1;
|
||||
height: 50vh !important;
|
||||
max-height: 400px !important;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--spacing-3xl) !important;
|
||||
padding: 0 var(--spacing-sm);
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.section-header p {
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.65;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.section-header h2::after {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.portfolio-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.portfolio-card {
|
||||
padding: var(--spacing-xl) var(--spacing-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.split-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
.split-img {
|
||||
width: 100vw !important;
|
||||
margin-left: calc(-1 * var(--spacing-md)) !important;
|
||||
margin-right: calc(-1 * var(--spacing-md)) !important;
|
||||
height: 50vh !important;
|
||||
max-height: 350px !important;
|
||||
border-radius: 0 !important;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.split-content {
|
||||
text-align: center;
|
||||
padding: var(--spacing-2xl) var(--spacing-md);
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.split-content h2 {
|
||||
font-size: clamp(1.5rem, 7vw, 1.875rem);
|
||||
}
|
||||
|
||||
.split-content p {
|
||||
font-size: 1.0625rem;
|
||||
margin-bottom: var(--spacing-xl);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.target-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
text-align: left;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
gap: var(--spacing-sm) !important;
|
||||
}
|
||||
|
||||
.target-item {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||
border: 1px solid rgba(47, 166, 106, 0.15);
|
||||
border-left: 3px solid var(--accent-green);
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||
transition: all var(--transition-smooth);
|
||||
}
|
||||
|
||||
.target-item:active {
|
||||
transform: translateX(4px);
|
||||
box-shadow: 0 4px 12px rgba(47, 166, 106, 0.15);
|
||||
border-left-width: 4px;
|
||||
}
|
||||
|
||||
.spec-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.spec-card {
|
||||
padding: var(--spacing-2xl) var(--spacing-lg) !important;
|
||||
text-align: center;
|
||||
border-radius: 20px;
|
||||
background: rgba(255,255,255,0.04) !important;
|
||||
border: 1px solid rgba(255,255,255,0.1) !important;
|
||||
}
|
||||
|
||||
.principles-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: var(--spacing-lg) !important;
|
||||
}
|
||||
|
||||
.principle-item {
|
||||
text-align: left;
|
||||
border-left: 4px solid var(--accent-green) !important;
|
||||
border-top: none !important;
|
||||
padding: var(--spacing-lg) var(--spacing-md) var(--spacing-lg) var(--spacing-lg) !important;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.04);
|
||||
transition: all var(--transition-smooth);
|
||||
}
|
||||
|
||||
.principle-item:active {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
padding: var(--spacing-3xl) 0 !important;
|
||||
}
|
||||
|
||||
.cta-section h2 {
|
||||
font-size: clamp(1.5rem, 8vw, 2rem) !important;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.cta-section p {
|
||||
font-size: 1.0625rem !important;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: var(--spacing-lg) 0 var(--spacing-xs) 0 !important;
|
||||
margin-top: var(--spacing-lg) !important;
|
||||
}
|
||||
|
||||
footer .grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: var(--spacing-md) !important;
|
||||
margin-bottom: var(--spacing-sm) !important;
|
||||
}
|
||||
|
||||
footer .grid div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer .grid div h4 {
|
||||
font-size: 0.875rem !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
footer .grid div nav {
|
||||
gap: 0.375rem !important;
|
||||
}
|
||||
|
||||
footer .grid div nav a {
|
||||
font-size: 0.8125rem !important;
|
||||
}
|
||||
|
||||
footer .grid div img {
|
||||
height: 56px !important;
|
||||
margin-bottom: var(--spacing-sm) !important;
|
||||
}
|
||||
|
||||
footer .grid div p {
|
||||
font-size: 0.8125rem !important;
|
||||
}
|
||||
|
||||
footer .container > div:last-child {
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
text-align: center;
|
||||
padding-top: var(--spacing-sm) !important;
|
||||
font-size: 0.75rem !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: var(--spacing-xl) var(--spacing-lg);
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.portfolio-card {
|
||||
padding: var(--spacing-2xl) var(--spacing-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about-hero-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 0 !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about-hero-grid > div:first-child {
|
||||
order: 2;
|
||||
padding: var(--spacing-2xl) var(--spacing-md);
|
||||
}
|
||||
|
||||
.about-hero-grid h1 {
|
||||
font-size: clamp(2rem, 9vw, 2.5rem);
|
||||
}
|
||||
|
||||
.about-hero-grid p {
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.about-hero-img {
|
||||
width: 100vw !important;
|
||||
margin-left: calc(-1 * var(--spacing-md)) !important;
|
||||
margin-right: calc(-1 * var(--spacing-md)) !important;
|
||||
height: 50vh !important;
|
||||
max-height: 400px !important;
|
||||
border-radius: 0 !important;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.team-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.team-card-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: var(--spacing-md) !important;
|
||||
}
|
||||
|
||||
.manifest-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: var(--spacing-lg) !important;
|
||||
}
|
||||
|
||||
.manifest-item {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: var(--spacing-xl) var(--spacing-lg);
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(0,0,0,0.04);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
.contact-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: var(--spacing-2xl) !important;
|
||||
}
|
||||
|
||||
.contact-form-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.contact-info-item {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
background: white;
|
||||
padding: var(--spacing-xl) var(--spacing-lg);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.03);
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.contact-info-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.08em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: clamp(1.125rem, 4vw, 1.25rem);
|
||||
}
|
||||
|
||||
.legal-content {
|
||||
padding: var(--spacing-lg);
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
.legal-content h2 {
|
||||
font-size: clamp(1.25rem, 5vw, 1.5rem);
|
||||
margin-top: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.legal-content p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.legal-content ul, .legal-content ol {
|
||||
padding-left: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.legal-content li {
|
||||
margin-bottom: var(--spacing-xs);
|
||||
line-height: 1.75;
|
||||
}
|
||||
}
|
||||
10
src/main.tsx
10
src/main.tsx
@@ -1,10 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -1,311 +0,0 @@
|
||||
const AGB = () => (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1 className="no-underline">Liefer- und Zahlungsbedingungen</h1>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: '2rem', flexWrap: 'wrap', gap: '1rem' }}>
|
||||
<p className="subtitle" style={{ margin: 0 }}>Stand Januar 2026</p>
|
||||
<a
|
||||
href="/assets/AGB MB Grid 1-2026.pdf"
|
||||
download
|
||||
className="button"
|
||||
style={{ fontSize: '0.9rem', padding: '0.5rem 1rem' }}
|
||||
>
|
||||
Als PDF herunterladen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="legal-content">
|
||||
<div className="legal-section">
|
||||
<h2>1. Allgemeines</h2>
|
||||
<p>
|
||||
Diese Liefer- und Zahlungsbedingungen (L&Z) der MB Grid Solutions & Services gelten ausschließlich;
|
||||
entgegenstehende oder von unseren Bedingungen abweichende Bedingungen des Kunden erkennen wir nicht an,
|
||||
es sei denn, wir hätten ausdrücklich schriftlich ihrer Geltung zugestimmt. Unsere L&Z gelten auch dann,
|
||||
wenn wir in Kenntnis entgegenstehender oder von unseren L&Z abweichender Bedingungen des Bestellers die
|
||||
Lieferung an diesen vorbehaltlos ausführen. Unsere L&Z gelten nur gegenüber Unternehmern im Sinn von
|
||||
§ 310 Abs. 1 BGB sowie juristischen Personen des öffentlichen Rechts oder öffentlich-rechtliches Sondervermögen.
|
||||
</p>
|
||||
<p>
|
||||
Nebenabreden, Vorbehalte, Änderungen, Ergänzungen usw. bedürfen zu ihrer Wirksamkeit unserer schriftlichen Bestätigung.
|
||||
</p>
|
||||
<p>
|
||||
Hinweise auf die Geltung gesetzlicher Vorschriften haben nur klarstellende Bedeutung. Auch ohne eine
|
||||
derartige Klarstellung gelten daher die gesetzlichen Vorschriften, soweit sie in diesen L&Z nicht
|
||||
unmittelbar abgeändert oder ausdrücklich ausgeschlossen werden. Bezüglich Beratungsleistungen weisen
|
||||
wir ausdrücklich auf Punkt 17 hin.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>2. Angebote</h2>
|
||||
<p>
|
||||
Sofern nicht ausdrücklich als bindend bezeichnet, sind unsere Angebote freibleibend; die Bestellung
|
||||
des Kunden ist als Angebot gemäß § 145 BGB zu qualifizieren.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>3. Preise</h2>
|
||||
<p>
|
||||
Die Preise gelten für den in unseren Angeboten und Auftragsbestätigungen aufgeführten Leistungs- und
|
||||
Lieferumfang. Mehrleistungen werden gesondert berechnet. Die Hohlpreise verstehen sich in Euro zuzüglich
|
||||
Metallzuschlag, gegebenenfalls Verpackung, auftragsspezifischer Schnittkosten und der gesetzlichen Mehrwertsteuer.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>4. Metallnotierung</h2>
|
||||
<p>
|
||||
Basis zur Kupferabrechnung ist die Notierung „LME Copper official price cash offer“, Durchschnitt des
|
||||
Liefervormonats zuzüglich der dann aktuellen von uns benannten Kupfer-Prämie.
|
||||
</p>
|
||||
<p>
|
||||
Basis zur Aluminiumabrechnung ist die Notierung „LME Aluminium official price cash offer“, Durchschnitt
|
||||
des Liefervormonats zuzüglich der dann von uns benannten Aluminium-Prämie. USD werden auf Basis des
|
||||
EUR/USD LME-FX-Rate (MTLE) in EUR umgerechnet. Die entsprechenden Notierungen können Sie der Web-Seite
|
||||
<a href="https://www.westmetall.com" target="_blank" rel="noopener noreferrer"> www.westmetall.com</a> entnehmen.
|
||||
Die Prämienzuschläge können stark variieren und MB Grid Solutions & Services behält sich das Recht vor,
|
||||
diese fristgerecht anzupassen, ungeachtet der Angebotslegung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>5. Metallzahl</h2>
|
||||
<p>
|
||||
Die von uns ausgewiesene Metallzahl ist eine rein kaufmännische Berechnungsgröße für den Metallinhalt,
|
||||
die in die Berechnung des Gesamtpreises eines Kabels eingeht. Damit entsprechen wir Ihrem Wunsch eine
|
||||
Vergleichbarkeit in ihrem System auf Hohlpreisbasis zu ermöglichen. Die Metallzahl gibt damit nicht das
|
||||
Gewicht des tatsächlich im Kabel enthaltenen Leitermetalls an. Sie ist ein rein kalkulatorischer
|
||||
Berechnungsfaktor, der jedoch keine unmittelbaren Rückschlüsse auf die im Kabel verwendeten Kupfer- bzw.
|
||||
Aluminiummengen zulässt. Wir weisen ausdrücklich darauf hin, final nur den Vollpreis für Vergleichszwecke
|
||||
heranzuziehen. Soweit Sie es wünschen andere Metallzahlen zu Grunde zu legen, sind wir gerne dazu bereit,
|
||||
das Angebot in den Bestandteilen umzurechnen. Bei jeglicher Änderung bleibt aber der Vollpreis der gleiche Betrag.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>6. Auftragsänderung / Auftragsstorno</h2>
|
||||
<p>
|
||||
Nach Auftragsbestätigung werden Änderungen an bestätigten Aufträgen nur nach Prüfung und gesonderter
|
||||
ausdrücklicher Zustimmung durch uns akzeptiert. Wir behalten uns bei allen Auftragsänderungen das Recht vor,
|
||||
einen durch die Änderung entstandenen Mehraufwand, wie z.B. Bearbeitungskosten oder Entsorgungskosten in
|
||||
Rechnung zu stellen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>7. Eigentumsvorbehalt</h2>
|
||||
<p>
|
||||
Wir behalten uns an den von uns gelieferten Waren – nachfolgend: Vorbehaltsware – bis zur vollständigen
|
||||
Begleichung aller unserer Forderungen aus den Geschäftsbeziehungen mit dem Besteller, das Eigentum vor.
|
||||
Der Eigentumsvorbehalt bleibt auch dann bestehen, wenn einzelne Forderungen in eine laufende Rechnung
|
||||
aufgenommen werden (Kontokorrentvorbehalt).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>8. Zahlungsbedingungen | Aufrechnung | Zurückbehaltungsrechte</h2>
|
||||
<p>
|
||||
Unsere Rechnungen sind 10 Tage nach Rechnungsdatum ohne jeden Abzug zahlbar. Bei Nichteinhaltung der
|
||||
vereinbarten Zahlungsbedingungen sind wir berechtigt, Zinsen in Höhe von 7 %-Punkten über dem Basiszinssatz
|
||||
zu berechnen; das Recht zur Geltendmachung weitergehender Schäden, insbesondere nachgewiesener höherer
|
||||
Zinsen, bleibt hiervon unberührt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>9. Liefervorbehalt | Teillieferungen</h2>
|
||||
<p>
|
||||
Sämtliche Lieferzusagen unsererseits stehen, sofern nichts anderes ausdrücklich schriftlich vereinbart ist,
|
||||
unter dem Vorbehalt der richtigen und rechtzeitigen Belieferung durch unsere Produzenten. Wir behalten uns
|
||||
jederzeit Teillieferungen vor. Darüber hinaus behalten wir uns branchenübliche Über- oder Unterlieferungen
|
||||
bis zu 10 % der bestellten Menge vor.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>10. Lieferfristen und Liefertermine</h2>
|
||||
<p>
|
||||
Die Lieferfrist wird individuell vereinbart bzw. von uns bei Annahme der Bestellung angegeben. Sofern wir
|
||||
verbindliche Lieferfristen aus Gründen, die wir nicht zu vertreten haben, nicht einhalten können
|
||||
(Nichtverfügbarkeit der Leistung), werden wir den Besteller hierüber unverzüglich informieren und
|
||||
gleichzeitig die voraussichtliche, neue Lieferfrist mitteilen. Ist die Leistung auch innerhalb der neuen
|
||||
Lieferfrist nicht verfügbar, sind wir berechtigt, ganz oder teilweise vom Vertrag zurückzutreten. Eine
|
||||
bereits erbrachte Gegenleistung des Bestellers werden wir unverzüglich erstatten. Nichtverfügbarkeit der
|
||||
Leistung liegt beispielsweise vor bei nicht rechtzeitiger Selbstbelieferung durch unseren Zulieferer, wenn
|
||||
wir ein kongruentes Deckungsgeschäft abgeschlossen haben, bei sonstigen Störungen in der Lieferkette etwa
|
||||
aufgrund höherer Gewalt oder wenn wir im Einzelfall zur Beschaffung nicht verpflichtet sind.
|
||||
</p>
|
||||
<p>
|
||||
Der Eintritt unseres Lieferverzugs bestimmt sich nach den gesetzlichen Vorschriften. In jedem Fall ist
|
||||
aber eine Mahnung durch den Käufer erforderlich.
|
||||
</p>
|
||||
<p>
|
||||
Die gesetzlichen Rechte bleiben im Übrigen unberührt.
|
||||
</p>
|
||||
<p>
|
||||
Fixgeschäfte setzen die ausdrückliche schriftliche Bezeichnung als solche voraus. Ansonsten ist der
|
||||
Besteller stets verpflichtet, uns schriftlich eine angemessene Nachfrist zu setzen, wenn von uns
|
||||
zugesagte Termine und/ oder Fristen nicht eingehalten werden. Wird auch die Nachfrist nicht eingehalten,
|
||||
ist der Besteller berechtigt, vom Vertrag zurückzutreten. Im Fall höherer Gewalt und/oder sonstiger von
|
||||
uns nicht vorhersehbarer außergewöhnlicher und/oder unverschuldeter Umstände, auch wenn sie bei unserem
|
||||
Vorlieferanten eintreten, verlängert sich eine von uns zugesagte Lieferfrist bis zur Behebung des
|
||||
vorerwähnten Ereignisses. Ist dieser Zeitpunkt nicht überblickbar, sind sowohl der Besteller als auch
|
||||
wir berechtigt, von dem abgeschlossenen Vertrag zurückzutreten. In diesem Fall sind beiderseits
|
||||
Schadensersatzansprüche ausgeschlossen. Wir verpflichten uns, bei Bekanntwerden vorerwähnter Umstände
|
||||
den Besteller hiervon unverzüglich zu benachrichtigen.
|
||||
</p>
|
||||
<p>
|
||||
Ist die Einhaltung eines Termins davon abhängig, dass uns seitens des Bestellers bestimmte Angaben
|
||||
und/oder Pläne, Freigabeerklärungen oder ähnliches erteilt werden, beginnt die Lieferfrist erst von dem
|
||||
Zeitpunkt an zu laufen, zu dem uns die vollständigen Angaben des Bestellers schriftlich vorliegen.
|
||||
Wird die Anlieferung auf Wunsch des Bestellers über den vertraglich vorgesehenen Zeitpunkt hinausgeschoben,
|
||||
kann von uns beginnend mit einer Frist von frühestens 10 Werktagen nach Anzeige der Versandbereitschaft
|
||||
dem Besteller ein Lagergeld in Höhe von 2 % des Rechnungsbetrages für jeden angefangenen Monat, maximal
|
||||
jedoch 10 % insgesamt berechnet werden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>11. Abrufaufträge</h2>
|
||||
<p>
|
||||
Wird uns ein Abrufauftrag erteilt und werden über die Abruftermine keine gesonderten schriftlichen
|
||||
Vereinbarungen getroffen, ist der Besteller verpflichtet, uns die einzelnen Abruftermine so mitzuteilen,
|
||||
dass zwischen Eingang der Abrufmitteilung bei uns und Auslieferung mindestens 14 Werktage und die letzte
|
||||
Auslieferung spätestens 90 Tage nach unserer Auftragsbestätigung liegt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>12. Maß- und Gewichtsangaben</h2>
|
||||
<p>
|
||||
Alle Angaben über Durchmesser, Gewicht, technische Gestaltung, Herstellung und Umfang der von uns zu
|
||||
liefernden Ware stehen unter dem Vorbehalt der Abweichung innerhalb der handelsüblichen zulässigen
|
||||
Toleranzen. Darüber hinaus behalten wir uns Änderungen, die einer technischen Verbesserung dienen,
|
||||
jederzeit vor. Farbabweichungen und/oder Abweichungen in der äußeren Beschaffenheit der von uns zu
|
||||
liefernden Ware, die jedoch deren Qualität und technische Wirksamkeit unbeeinflusst lässt, begründen
|
||||
keine Mängelhaftungsansprüche des Bestellers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>13. Gefahrübergang und -tragung</h2>
|
||||
<p>
|
||||
Die Lieferung erfolgt DAP frei Bestimmungsort Deutschland, wo auch der Erfüllungsort für die Lieferung
|
||||
und eine etwaige Nacherfüllung ist.
|
||||
</p>
|
||||
<p>
|
||||
Wird die bestellte Ware von uns versandbereit gestellt und/oder verzögert sich die Versendung und/oder
|
||||
der Abruf aus Gründen, die vom Besteller zu vertreten sind, sind wir berechtigt, Ersatz des hieraus
|
||||
entstehenden Schadens einschließlich Mehraufwendungen zu verlangen. Hierfür berechnen wir eine pauschale
|
||||
Entschädigung i.H. von 2% des Rechnungsbetrages für jeden angefangenen Monat, maximal jedoch 10 %
|
||||
insgesamt beginnend mit der Lieferfrist bzw. – mangels einer Lieferfrist – mit der Mitteilung der
|
||||
Versandbereitschaft der Ware. Der Nachweis eines höheren Schadens und unsere gesetzlichen Ansprüche
|
||||
(insbesondere Ersatz von Mehraufwendungen, angemessene Entschädigung, Kündigung) bleiben unberührt;
|
||||
die Pauschale ist aber auf weitergehende Geldansprüche anzurechnen. Dem Besteller bleibt der Nachweis
|
||||
gestattet, dass uns überhaupt kein oder nur ein wesentlich geringerer Schaden als vorstehende Pauschale
|
||||
entstanden ist. Rücksendungen an uns, die nicht vorher von uns schriftlich bestätigt worden sind,
|
||||
erfolgen auf alleinige Gefahr des Bestellers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>14. Mängelhaftung</h2>
|
||||
<p>
|
||||
Wir haften nur dann für die Einhaltung objektiver Anforderungen an der Ware, wenn und soweit zwischen
|
||||
dem Besteller und uns keine Beschaffenheitsvereinbarung getroffen wurde. Die einzuhaltenden subjektiven
|
||||
Anforderungen gehen den einzuhaltenden objektiven Anforderungen vor. Im Zweifel ergeben sich die
|
||||
vereinbarten Anforderungen an die Ware aus dem von uns bereitgestellten Datenblatt. Einzelne, nicht
|
||||
immer auszuschließende marginale Abweichungen, dürfen durch Reparaturen, wie zum Beispiel
|
||||
Mantelmanschetten nachgebessert werden.
|
||||
</p>
|
||||
<p>
|
||||
Jedwede Mängelhaftungsansprüche des Bestellers setzen voraus, dass dieser die ihm übersandte Ware
|
||||
unverzüglich, d. h. in der Regel sofort bei Anlieferung (noch in Anwesenheit des Transporteurs) auf
|
||||
ihre ordnungsgemäße Beschaffenheit hin überprüft und uns zu verzeichnende sichtbare Mängel unmittelbar
|
||||
nach Erhalt der Ware und verdeckte Mängel unmittelbar nach deren Feststellung schriftlich mitteilt.
|
||||
Soweit ein rechtzeitig gerügter, nicht nur unerheblicher Mangel der Kaufsache vorliegt, sind wir nach
|
||||
unserer Wahl zur Mangelbeseitigung oder zur Ersatzlieferung (Nacherfüllung) berechtigt.
|
||||
</p>
|
||||
<p>
|
||||
Wir übernehmen im Rahmen der Nacherfüllung in keinem Fall Ein- oder Ausbaukosten, wenn und soweit die
|
||||
Mangelhaftigkeit der Ware zum Zeitpunkt des Einbaus dem Besteller bekannt oder grob fahrlässig unbekannt
|
||||
geblieben ist. Sind wir zur Mangelbeseitigung/Ersatzlieferung nicht bereit oder nicht in der Lage oder
|
||||
verzögert sich diese über angemessene Fristen hinaus aus Gründen, die wir zu vertreten haben, oder
|
||||
schlägt sie in sonstiger Weise fehl, so ist der Besteller nach seiner Wahl berechtigt, vom Vertrag
|
||||
zurückzutreten oder eine entsprechende Minderung des Kaufpreises zu verlangen.
|
||||
</p>
|
||||
<p>
|
||||
Weitergehende Ansprüche des Bestellers, gleich aus welchem Rechtsgrund, sind nach näherer Maßgabe der
|
||||
Regelungen in nachstehender Ziffer 15 ausgeschlossen bzw. beschränkt. Die Verjährungsfristen für
|
||||
Mängelhaftungsansprüche beträgt 24 Monate ab Übergabe der Ware.
|
||||
</p>
|
||||
<p>
|
||||
Sollte es bei einer Mängelrüge zu unterschiedlichen Meinungen bezüglich des Kabelschaden kommen, gilt
|
||||
hier im Zweifelsfall nur die Expertise des VDE-Instituts selbst. Andere, auch akkreditierte Testlabore,
|
||||
akzeptieren wir nicht. Wir weisen ausdrücklich daraufhin, dass beim Verlegen des Kabels in den Graben
|
||||
oder in Rohren, bzw. in Bauwerke eine ständige Sichtkontrolle durch den Kabelverleger vorzunehmen ist,
|
||||
ob Auffälligkeiten zu vermerken sind. Eine spätere Reklamation, die fahrlässiges Verhalten vermuten lässt,
|
||||
schränkt sich damit ein. Dies gilt auch bei der Annahme der Ware, wo offensichtliche Beschädigungen
|
||||
direkt zu kommunizieren sind. Spätere Ansprüche nach Akzeptanz einer einwandfreien Belieferung sind
|
||||
detailliert zu beweisen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>15. Schadenersatz | Gesamthaftung</h2>
|
||||
<p>
|
||||
Wir haften unbeschränkt nur für Vorsatz und grobe Fahrlässigkeit sowie für Schäden aus einer Verletzung
|
||||
von Leben, Körper oder Gesundheit, die auf mindestens fahrlässiger Pflichtverletzung unsererseits oder
|
||||
unserer gesetzlichen Vertreter oder Erfüllungsgehilfen beruhen; ebenso haften wir unbeschränkt im Fall
|
||||
von uns übernommenen bzw. abgegebenen Garantien und Zusicherungen, sofern ein davon umfasster Mangel
|
||||
unsere Haftung auslöst sowie im Fall einer Haftung nach dem Produkthaftungsgesetz oder sonstigen
|
||||
Gefährdungshaftungstatbeständen. Im Fall sonstiger schuldhafter Verletzung wesentlicher Vertragspflichten
|
||||
(„Kardinalpflichten“) ist unsere verbleibende Haftung auf den vertragstypischen vorhersehbaren Schaden
|
||||
beschränkt. Mangelfolgeschäden sowie entgangener Gewinn schließen wir grundsätzlich aus.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>16. Kabeltrommeln</h2>
|
||||
<p>
|
||||
Unsere Kabel werden auf stabilen Vollholztrommeln geliefert. Auf Wunsch vermitteln wir Ihnen Partner,
|
||||
die diese Trommeln gegen eine Gebühr abholen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>17. Technische Beratungsdienstleistungen</h2>
|
||||
<p>
|
||||
Die technische Unterstützung ersetzt weder die Fachplanung noch die Ausführungs- oder Prüfverantwortung
|
||||
des beauftragten Ingenieurbüros, Planers oder der ausführenden Fachfirma bzw. verantwortlichen Abteilung.
|
||||
</p>
|
||||
<p>
|
||||
Alle Hinweise, Einschätzungen und Empfehlungen der MB Grid Solutions and Services erfolgen ohne Gewähr
|
||||
und entbinden den jeweiligen Auftragnehmer nicht von seiner eigenen fachlichen Prüfung, Planung und Verantwortung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-section">
|
||||
<h2>18. Sonstiges</h2>
|
||||
<p>
|
||||
Es gilt ausschließlich das Recht der Bundesrepublik Deutschland unter Ausschluss des UN-Kaufrechts (CISG).
|
||||
Gerichtsstand ist nach unserer Wahl Stuttgart, der Erfüllungsort der Lieferverpflichtung oder das für den
|
||||
Sitz des Bestellers zuständige Gericht, sofern der Besteller Kaufmann, juristische Person des öffentlichen
|
||||
Rechts oder öffentlich-rechtliches Sondervermögen ist oder keinen allgemeinen Gerichtsstand im Inland hat.
|
||||
</p>
|
||||
<p>
|
||||
Mit der Veröffentlichung der vorliegenden L&Z im Internet werden alle von uns früher verwendeten
|
||||
Bedingungen gegenstandslos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="legal-footer" style={{ marginTop: '2rem' }}>
|
||||
<p>Remshalden, 22.1.2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default AGB;
|
||||
@@ -1,337 +0,0 @@
|
||||
import { Award, Clock, Lightbulb, Linkedin, MessageSquare, ShieldCheck, Truck } from 'lucide-react';
|
||||
|
||||
const About = () => (
|
||||
<div>
|
||||
<section style={{
|
||||
background: 'linear-gradient(to right, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0.7) 100%), url("/media/drums/iStock-487538226 (1).jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
minHeight: 'clamp(60vh, 70vh, 80vh)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start'
|
||||
}}>
|
||||
<div className="container" style={{ margin: '0 auto', display: 'flex', justifyContent: 'flex-start' }}>
|
||||
<div className="about-hero-content" style={{ maxWidth: '700px', textAlign: 'left', paddingLeft: 0, marginLeft: 0 }}>
|
||||
<h1 className="no-underline">Über uns</h1>
|
||||
<p style={{
|
||||
fontSize: 'clamp(1.125rem, 2.5vw, 1.25rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
marginBottom: 'var(--spacing-xl)',
|
||||
lineHeight: 1.6
|
||||
}}>
|
||||
Wir verbinden Energie, Know-how und Innovation, um die Infrastruktur der Zukunft zu gestalten.
|
||||
</p>
|
||||
<p style={{
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
lineHeight: 1.7,
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)'
|
||||
}}>
|
||||
MB Grid Solution steht für technische Exzellenz in der Energiekabeltechnologie. Wir verstehen uns als Ihr technischer Lotse, der mit jahrzehntelanger Erfahrung und einem klaren Blick für zukunftsweisende Entwicklungen komplexe Projekte sicher zum Ziel führt.
|
||||
</p>
|
||||
<p style={{
|
||||
lineHeight: 1.7,
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)'
|
||||
}}>
|
||||
Unsere Wurzeln liegen in der tiefen praktischen Erfahrung unserer technischen Berater und unserer Netzwerke im globalem Kabelmarkt. Wir vereinen Tradition mit modernster Innovation, um zuverlässige Energielösungen für Projekte bis 110 kV und bei Bedarf darüber hinaus zu realisieren.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
marginBottom: 'clamp(3rem, 6vw, 4rem)'
|
||||
}}>
|
||||
<h2 className="no-underline">Die Köpfe und Koordinatoren hinter MB Grid Solution</h2>
|
||||
</div>
|
||||
<div className="grid team-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))',
|
||||
gap: 'var(--spacing-xl)'
|
||||
}}>
|
||||
<div className="card">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem' }}>
|
||||
<h3 style={{ marginBottom: 0 }}>Michael Bodemer</h3>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/michael-bodemer-33b493122/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#0077b5', display: 'flex' }}
|
||||
aria-label="Michael Bodemer auf LinkedIn"
|
||||
>
|
||||
<Linkedin size={20} />
|
||||
</a>
|
||||
</div>
|
||||
<p style={{
|
||||
color: 'var(--accent-green)',
|
||||
fontWeight: 600,
|
||||
fontSize: 'clamp(0.8125rem, 2vw, 0.85rem)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em',
|
||||
}}>
|
||||
Geschäftsführung und Inhaber
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem' }}>
|
||||
<h3 style={{ marginBottom: 0 }}>Klaus Mintel</h3>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klaus-mintel-b80a8b193/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#0077b5', display: 'flex' }}
|
||||
aria-label="Klaus Mintel auf LinkedIn"
|
||||
>
|
||||
<Linkedin size={20} />
|
||||
</a>
|
||||
</div>
|
||||
<p style={{
|
||||
color: 'var(--accent-green)',
|
||||
fontWeight: 600,
|
||||
fontSize: 'clamp(0.8125rem, 2vw, 0.85rem)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em',
|
||||
}}>
|
||||
Geschäftsführung
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
marginBottom: 'clamp(3rem, 8vw, 5rem)'
|
||||
}}>
|
||||
<h2 className="no-underline">Unser Manifest</h2>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)'
|
||||
}}>
|
||||
Werte, die unsere tägliche Arbeit leiten.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid manifest-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
gap: 'var(--spacing-2xl)'
|
||||
}}>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Award size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
1. Kompetenz
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Jahrzehntelange Erfahrung kombiniert mit europaweitem Know-how. Wir arbeiten mit Partnern für modernste Anlagen und Testlabore bis 525 kV.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Clock size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
2. Verfügbarkeit
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Schnelle und verlässliche Unterstützung ohne unnötige Verzögerungen. Wir sind für Sie da, wenn es darauf ankommt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Lightbulb size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
3. Lösungen
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Wir stellen die richtigen Fragen – an Sie, an Hersteller und an uns selbst. Nur wer hinterfragt, findet die technisch und wirtschaftlich beste Lösung. Und reduziert Risiken.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Truck size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
4. Logistik & Überwachung
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Von der Fertigungsüberwachung bis zum Fracht-Tracking und der termingerechten Anlieferung – wir steuern den gesamten Prozess professionell.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<MessageSquare size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
5. Offenheit
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Wir hören zu und passen unsere Prozesse kontinuierlich an. Stillstand ist für uns keine Option – wir optimieren für Ihren Erfolg.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manifest-item" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<ShieldCheck size={32} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
6. Zuverlässigkeit
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
Wir halten, was wir versprechen – ohne Ausnahme. Verbindlichkeit ist das Fundament unserer Zusammenarbeit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div className="card" style={{
|
||||
background: 'var(--primary-color)',
|
||||
color: 'white',
|
||||
textAlign: 'left',
|
||||
padding: 'clamp(3rem, 6vw, 4rem)'
|
||||
}}>
|
||||
<h2
|
||||
className="no-underline"
|
||||
style={{
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
fontSize: 'clamp(1.5rem, 6vw, 2rem)',
|
||||
position: 'relative',
|
||||
textAlign: 'left'
|
||||
}}
|
||||
>
|
||||
Bereit für Ihr nächstes Projekt?
|
||||
</h2>
|
||||
<p style={{
|
||||
marginBottom: 'var(--spacing-2xl)',
|
||||
opacity: 0.9,
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
lineHeight: 1.65,
|
||||
textAlign: 'left'
|
||||
}}>
|
||||
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.
|
||||
</p>
|
||||
<a
|
||||
href="/kontakt"
|
||||
className="cta-button"
|
||||
style={{
|
||||
background: 'white',
|
||||
color: 'var(--primary-color)'
|
||||
}}
|
||||
aria-label="Jetzt Kontakt aufnehmen"
|
||||
>
|
||||
Jetzt Kontakt aufnehmen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default About;
|
||||
@@ -1,340 +0,0 @@
|
||||
import { CheckCircle, Mail, MapPin, Phone, Send } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Contact = () => {
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (response.ok) {
|
||||
setSubmitted(true);
|
||||
} else {
|
||||
const err = await response.json();
|
||||
alert(`Fehler: ${err.error || 'Es gab einen Fehler beim Senden Ihrer Nachricht.'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Es gab einen Fehler beim Senden Ihrer Nachricht.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<section
|
||||
className="hero"
|
||||
style={{
|
||||
minHeight: 'clamp(40vh, 50vh, 60vh)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
background: 'linear-gradient(to right, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0.7) 100%), url("/media/laying/iStock-1282259999.jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
padding: 'clamp(1.5rem, 4vw, 3rem) 0',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<div className="container" style={{ position: 'relative', zIndex: 1, margin: '0 auto', display: 'flex', justifyContent: 'flex-start' }}>
|
||||
<div className="hero-content" style={{ maxWidth: '700px', textAlign: 'left', paddingLeft: 0, marginLeft: 0 }}>
|
||||
<span className="badge">Kontakt</span>
|
||||
<h1 style={{ marginBottom: 'var(--spacing-md)' }}>
|
||||
Kontakt
|
||||
</h1>
|
||||
<p style={{
|
||||
fontSize: 'clamp(1rem, 2vw, 1.1rem)',
|
||||
marginBottom: 'var(--spacing-xl)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.6,
|
||||
maxWidth: '600px'
|
||||
}}>
|
||||
Haben Sie Fragen zu einem Projekt oder benötigen Sie technische Beratung? Wir freuen uns auf Ihre Nachricht.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div className="grid contact-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))',
|
||||
gap: 'clamp(2rem, 6vw, 4rem)'
|
||||
}}>
|
||||
<div>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'var(--spacing-xl)'
|
||||
}}>
|
||||
<a
|
||||
href="mailto:info@mb-grid-solutions.com"
|
||||
className="contact-info-item"
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)',
|
||||
alignItems: 'flex-start',
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
aria-label="E-Mail an info@mb-grid-solutions.com senden"
|
||||
>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
background: 'white',
|
||||
padding: 'var(--spacing-md)',
|
||||
border: '1px solid var(--secondary-bg)',
|
||||
borderRadius: '12px',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Mail size={24} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: '0.25rem',
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
E-Mail
|
||||
</h4>
|
||||
<span style={{
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 500,
|
||||
color: 'var(--primary-color)'
|
||||
}}>
|
||||
info@mb-grid-solutions.com
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<div
|
||||
className="contact-info-item"
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)',
|
||||
alignItems: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
background: 'white',
|
||||
padding: 'var(--spacing-md)',
|
||||
border: '1px solid var(--secondary-bg)',
|
||||
borderRadius: '12px',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<MapPin size={24} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{
|
||||
marginBottom: '0.25rem',
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
Anschrift
|
||||
</h4>
|
||||
<p style={{
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
fontWeight: 500,
|
||||
color: 'var(--primary-color)',
|
||||
lineHeight: 1.6
|
||||
}}>
|
||||
MB Grid Solutions GmbH<br />
|
||||
Raiffeisenstraße 22<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
{submitted ? (
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
padding: 'clamp(2rem, 6vw, 3rem) 0'
|
||||
}}>
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<CheckCircle size={64} strokeWidth={2} />
|
||||
</div>
|
||||
<h3 style={{
|
||||
fontSize: 'clamp(1.25rem, 4vw, 1.5rem)',
|
||||
marginBottom: 'var(--spacing-md)'
|
||||
}}>
|
||||
Nachricht gesendet
|
||||
</h3>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)',
|
||||
lineHeight: 1.65,
|
||||
marginBottom: 'var(--spacing-xl)'
|
||||
}}>
|
||||
Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setSubmitted(false)}
|
||||
className="cta-button"
|
||||
aria-label="Weitere Nachricht senden"
|
||||
>
|
||||
Weitere Nachricht
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}>
|
||||
<div className="grid contact-form-grid" style={{
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
gap: 'var(--spacing-md)'
|
||||
}}>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="name"
|
||||
style={{
|
||||
display: 'block',
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
fontWeight: 600
|
||||
}}
|
||||
>
|
||||
Name *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
minLength={2}
|
||||
maxLength={100}
|
||||
placeholder="Ihr Name"
|
||||
aria-required="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="company"
|
||||
style={{
|
||||
display: 'block',
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
fontWeight: 600
|
||||
}}
|
||||
>
|
||||
Firma
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="company"
|
||||
name="company"
|
||||
placeholder="Ihr Unternehmen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label
|
||||
htmlFor="email"
|
||||
style={{
|
||||
display: 'block',
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
fontWeight: 600
|
||||
}}
|
||||
>
|
||||
E-Mail *
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
required
|
||||
placeholder="ihre@email.de"
|
||||
aria-required="true"
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor="message"
|
||||
style={{
|
||||
display: 'block',
|
||||
marginBottom: 'var(--spacing-xs)',
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
fontWeight: 600
|
||||
}}
|
||||
>
|
||||
Nachricht *
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
required
|
||||
minLength={20}
|
||||
maxLength={4000}
|
||||
rows={6}
|
||||
placeholder="Wie können wir Ihnen helfen?"
|
||||
aria-required="true"
|
||||
></textarea>
|
||||
|
||||
<div className="visually-hidden" style={{ display: 'none' }}>
|
||||
<label htmlFor="website">Website (bitte leer lassen)</label>
|
||||
<input
|
||||
type="text"
|
||||
id="website"
|
||||
name="website"
|
||||
tabIndex={-1}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="cta-button"
|
||||
style={{ alignSelf: 'flex-start' }}
|
||||
aria-label={loading ? 'Nachricht wird gesendet' : 'Nachricht senden'}
|
||||
>
|
||||
{loading ? 'Wird gesendet...' : 'Nachricht senden'} <Send size={18} strokeWidth={2.5} />
|
||||
</button>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.6875rem, 2vw, 0.75rem)',
|
||||
marginTop: 'var(--spacing-lg)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.5
|
||||
}}>
|
||||
* Pflichtfelder. Mit dem Absenden erklären Sie sich mit unserer{' '}
|
||||
<Link
|
||||
to="/datenschutz"
|
||||
style={{
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
Datenschutzerklärung
|
||||
</Link>{' '}
|
||||
einverstanden.
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
@@ -1,327 +0,0 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ArrowRight, Shield, Zap, BarChart3, CheckCircle2 } from 'lucide-react';
|
||||
|
||||
const Home = () => (
|
||||
<div className="home-page">
|
||||
<section
|
||||
className="hero"
|
||||
style={{
|
||||
minHeight: 'clamp(70vh, 80vh, 90vh)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
background: 'linear-gradient(to right, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0.7) 100%), url("/media/business/iStock-1068752548.jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
padding: 'clamp(2rem, 6vw, 4rem) 0',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<div className="container" style={{ position: 'relative', zIndex: 1, margin: '0 auto', display: 'flex', justifyContent: 'flex-start', paddingLeft: 0 }}>
|
||||
<div className="hero-content" style={{ maxWidth: '700px', textAlign: 'left', paddingLeft: 0, marginLeft: 0 }}>
|
||||
<span className="badge">Engineering Excellence</span>
|
||||
<h1 style={{ marginBottom: 'var(--spacing-lg)' }}>
|
||||
Spezialisierter Partner für Energiekabelprojekte
|
||||
</h1>
|
||||
<p style={{
|
||||
fontSize: 'clamp(1.0625rem, 2.5vw, 1.2rem)',
|
||||
marginBottom: 'var(--spacing-2xl)',
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.6,
|
||||
maxWidth: '600px'
|
||||
}}>
|
||||
Herstellerneutrale technische Beratung ihrer Projekte für Mittel - und Hochspannungsnetze bis zu 110 kV
|
||||
</p>
|
||||
<div className="hero-actions" style={{
|
||||
display: 'flex',
|
||||
gap: 'var(--spacing-lg)',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap'
|
||||
}}>
|
||||
<Link to="/kontakt" className="cta-button" aria-label="Projekt anfragen">
|
||||
Projekt anfragen <ArrowRight size={18} strokeWidth={2.5} />
|
||||
</Link>
|
||||
<Link
|
||||
to="/ueber-uns"
|
||||
className="secondary-link"
|
||||
style={{
|
||||
fontWeight: 700,
|
||||
fontSize: '0.8rem',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
transition: 'all var(--transition-fast)'
|
||||
}}
|
||||
aria-label="Mehr über uns erfahren"
|
||||
>
|
||||
Mehr erfahren <ArrowRight size={16} strokeWidth={2.5} />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: '#f8fafc' }}>
|
||||
<div className="container">
|
||||
<div className="section-header" style={{ marginBottom: 'clamp(3rem, 8vw, 5rem)' }}>
|
||||
<span className="badge">Portfolio</span>
|
||||
<h2 style={{ border: 'none', padding: 0 }}>Unsere Leistungen</h2>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
maxWidth: '600px',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
lineHeight: 1.65
|
||||
}}>
|
||||
Beratung durch unabhängige Experten mit jahrzehntelanger Erfahrung aus Engineering, Normengremien, Planung und Produktion
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid portfolio-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
|
||||
gap: 'var(--spacing-xl)'
|
||||
}}>
|
||||
{[
|
||||
{ icon: <Zap size={32} strokeWidth={2} />, title: 'Technische Beratung', desc: 'Individuelle Konzepte, Vergleiche, Risikobetrachtung und Empfehlungen für Ihre Kabelinfrastruktur.' },
|
||||
{ icon: <Shield size={32} strokeWidth={2} />, title: 'Projektbegleitung', desc: 'Trotz bester Planung entstehen bei der Verlegung und Installation oft zusätzliche Herausforderungen, wo wir sie gerne begleiten' },
|
||||
{ icon: <BarChart3 size={32} strokeWidth={2} />, title: 'Produktbeschaffung', desc: 'Herstellerneutrale Marktanalyse und Unterstützung bei der Komponentenwahl in Hinblick auf Qualität, Lieferzeit, Preis und Nachhaltigkeit' }
|
||||
].map((item, i) => (
|
||||
<div key={i} className="card portfolio-card">
|
||||
<div style={{
|
||||
color: 'var(--accent-green)',
|
||||
marginBottom: 'var(--spacing-lg)'
|
||||
}}>
|
||||
{item.icon}
|
||||
</div>
|
||||
<h3 style={{ marginBottom: 'var(--spacing-md)' }}>{item.title}</h3>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.7,
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)'
|
||||
}}>
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div className="grid split-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))',
|
||||
alignItems: 'center',
|
||||
gap: 'clamp(2rem, 6vw, 4rem)'
|
||||
}}>
|
||||
<img
|
||||
src="/media/cables/HS Kabel.png"
|
||||
alt="Technical Engineering and Cable Infrastructure"
|
||||
className="split-img"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 'clamp(280px, 50vw, 400px)',
|
||||
objectFit: 'cover',
|
||||
marginBottom: 0,
|
||||
borderRadius: '12px'
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="split-content">
|
||||
<span className="badge">Expertise</span>
|
||||
<h2>Anwendungen & Zielgruppen</h2>
|
||||
<p style={{
|
||||
marginBottom: 'var(--spacing-2xl)',
|
||||
color: 'var(--text-secondary)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
lineHeight: 1.65
|
||||
}}>
|
||||
Wir unterstützen Akteure der Energiewende bei der Realisierung komplexer Kabelprojekte.
|
||||
</p>
|
||||
<div className="grid target-grid" style={{
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
gap: 'var(--spacing-md)'
|
||||
}}>
|
||||
{[
|
||||
'Energieversorger',
|
||||
'Ingenieurbüros',
|
||||
'Tiefbauunternehmen',
|
||||
'Industrie',
|
||||
'Projektierer EE',
|
||||
'Planungsbüros'
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="target-item"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.75rem',
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.95rem)',
|
||||
fontWeight: 600,
|
||||
color: 'var(--primary-color)'
|
||||
}}
|
||||
>
|
||||
<CheckCircle2 size={18} strokeWidth={2.5} style={{ color: 'var(--accent-green)', flexShrink: 0 }} />
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{
|
||||
background: 'linear-gradient(rgba(15, 23, 42, 0.85), rgba(15, 23, 42, 0.85)), url("/media/drums/iStock-487538226 (1).jpg")',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
color: 'white',
|
||||
padding: 'clamp(4rem, 10vw, 6rem) 0'
|
||||
}}>
|
||||
<div className="container">
|
||||
<div className="section-header" style={{ marginBottom: 'clamp(3rem, 8vw, 5rem)' }}>
|
||||
<span className="badge" style={{ color: 'white', opacity: 0.6 }}>Expertise</span>
|
||||
<h2 style={{ color: 'white', border: 'none', padding: 0 }}>Technische Spezifikationen</h2>
|
||||
</div>
|
||||
<div className="grid spec-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
|
||||
gap: 'var(--spacing-xl)'
|
||||
}}>
|
||||
{[
|
||||
{ label: 'Kabeltypen im Hochspannungsbereich wie beispielsweise', value: 'N2XS(FL)2Y, N2X(F)KLD2Y, NA2XS(FL)2Y, NA2X(F)KLD2Y', desc: 'Umfassende Expertise des optimalen Designs gängiger Hochspannungskabel.' },
|
||||
{ label: 'Spannungsebenen', value: '64/110 kV & Mittelspannung', desc: 'Spezialisierte Beratung für die 110-kV-Ebene und komplexe Mittelspannungsprojekte.' },
|
||||
{ label: 'Leitertechnologie', value: 'Massiv-, Mehrdraht- & Millikenleiter', desc: 'Optimierung des Leiterdesigns hinsichtlich Stromtragfähigkeit.' }
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="spec-card"
|
||||
style={{
|
||||
background: 'rgba(255,255,255,0.05)',
|
||||
padding: 'var(--spacing-2xl)',
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
borderRadius: '16px'
|
||||
}}
|
||||
>
|
||||
<h4 style={{
|
||||
fontSize: 'clamp(0.6875rem, 2vw, 0.75rem)',
|
||||
textTransform: 'uppercase',
|
||||
color: 'var(--accent-green)',
|
||||
marginBottom: 'var(--spacing-md)',
|
||||
letterSpacing: '0.2em',
|
||||
fontWeight: 700
|
||||
}}>
|
||||
{item.label}
|
||||
</h4>
|
||||
<p style={{
|
||||
fontWeight: 700,
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.15rem)',
|
||||
marginBottom: 'var(--spacing-md)',
|
||||
color: 'white',
|
||||
lineHeight: 1.4
|
||||
}}>
|
||||
{item.value}
|
||||
</p>
|
||||
<p style={{
|
||||
fontSize: 'clamp(0.875rem, 2vw, 0.9rem)',
|
||||
color: 'rgba(255,255,255,0.6)',
|
||||
lineHeight: 1.7
|
||||
}}>
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div className="section-header" style={{ marginBottom: 'clamp(3rem, 8vw, 5rem)' }}>
|
||||
<span className="badge">Werte</span>
|
||||
<h2 style={{ border: 'none', padding: 0 }}>Unsere Leitprinzipien</h2>
|
||||
</div>
|
||||
<div className="grid principles-grid" style={{
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
gap: 'var(--spacing-2xl)'
|
||||
}}>
|
||||
{[
|
||||
{ title: 'Exzellenz', desc: 'Höchste technische Präzision in jedem Detail. Wir suchen die optimale Lösung in Einklang mit Normen, technischen Spezifikation und den Umgebungsparametern.' },
|
||||
{ title: 'Nachhaltigkeit', desc: 'Zukunftssichere Lösungen für die Infrastruktur. Wir denken in Lebenszyklen und Zuverlässigkeit.' },
|
||||
{ title: 'Transparenz', desc: 'Ehrliche Beratung auf Augenhöhe. Wir kommunizieren klar und herstellerneutral.' }
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="principle-item"
|
||||
style={{
|
||||
borderLeft: '4px solid var(--accent-green)',
|
||||
paddingLeft: 'var(--spacing-xl)'
|
||||
}}
|
||||
>
|
||||
<h3 style={{
|
||||
marginBottom: 'var(--spacing-md)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)'
|
||||
}}>
|
||||
{item.title}
|
||||
</h3>
|
||||
<p style={{
|
||||
color: 'var(--text-secondary)',
|
||||
lineHeight: 1.75,
|
||||
fontSize: 'clamp(0.9375rem, 2vw, 1rem)'
|
||||
}}>
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
className="cta-section"
|
||||
style={{
|
||||
background: 'var(--primary-color)',
|
||||
padding: 'clamp(3rem, 8vw, 6rem) 0',
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<div className="container" style={{ position: 'relative', zIndex: 1, textAlign: 'left' }}>
|
||||
<h2
|
||||
className="no-underline"
|
||||
style={{
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
marginBottom: 'var(--spacing-lg)',
|
||||
fontSize: 'clamp(1.5rem, 6vw, 2.5rem)',
|
||||
lineHeight: 1.2,
|
||||
position: 'relative',
|
||||
textAlign: 'left'
|
||||
}}
|
||||
>
|
||||
Bereit für Ihr nächstes Projekt?
|
||||
</h2>
|
||||
<p style={{
|
||||
color: 'rgba(255,255,255,0.85)',
|
||||
marginBottom: 'var(--spacing-2xl)',
|
||||
fontSize: 'clamp(1rem, 2.5vw, 1.1rem)',
|
||||
maxWidth: '700px',
|
||||
margin: '0 0 var(--spacing-2xl) 0',
|
||||
lineHeight: 1.65,
|
||||
textAlign: 'left'
|
||||
}}>
|
||||
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.
|
||||
</p>
|
||||
<Link to="/kontakt" className="cta-button" aria-label="Jetzt Kontakt aufnehmen">
|
||||
Jetzt Kontakt aufnehmen <ArrowRight size={18} strokeWidth={2.5} />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Home;
|
||||
@@ -1,37 +0,0 @@
|
||||
const Legal = () => (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1 className="no-underline">Impressum</h1>
|
||||
<div className="legal-content">
|
||||
<p><strong>Angaben gemäß § 5 TMG</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
MB Grid Solutions & Services GmbH<br />
|
||||
Raiffeisenstraße 22<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
<p><strong>Vertreten durch:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
Michael Bodemer<br />
|
||||
Klaus Mintel
|
||||
</p>
|
||||
<p><strong>Kontakt:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
E-Mail: <a href="mailto:info@mb-grid-solutions.com">info@mb-grid-solutions.com</a><br />
|
||||
Web: <a href="https://www.mb-grid-solutions.com">www.mb-grid-solutions.com</a>
|
||||
</p>
|
||||
<p><strong>Registereintrag:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
Eintragung im Handelsregister.<br />
|
||||
Registergericht: Amtsgericht Stuttgart<br />
|
||||
Registernummer: HRB 803379
|
||||
</p>
|
||||
<p><strong>Urheberrecht:</strong></p>
|
||||
<p>
|
||||
Alle auf der Website veröffentlichten Texte, Bilder und sonstigen Informationen unterliegen – sofern nicht anders gekennzeichnet – dem Urheberrecht. Jede Vervielfältigung, Verbreitung, Speicherung, Übermittlung, Wiedergabe bzw. Weitergabe der Inhalte ohne schriftliche Genehmigung ist ausdrücklich untersagt.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Legal;
|
||||
@@ -1,22 +0,0 @@
|
||||
const Privacy = () => (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1 className="no-underline">Datenschutzerklärung</h1>
|
||||
<div className="legal-content">
|
||||
<h2>1. Datenschutz auf einen Blick</h2>
|
||||
<p>Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.</p>
|
||||
|
||||
<h2>2. Hosting</h2>
|
||||
<p>Unsere Website wird bei Hetzner Online GmbH gehostet. Der Serverstandort ist Deutschland. Wir haben einen Vertrag über Auftragsverarbeitung (AVV) mit Hetzner geschlossen.</p>
|
||||
|
||||
<h2>3. Kontaktformular</h2>
|
||||
<p>Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.</p>
|
||||
|
||||
<h2>4. Server-Log-Dateien</h2>
|
||||
<p>Der Provider der Seiten erhebt und speichert automatisch Informationen in sogenannten Server-Log-Dateien, die Ihr Browser automatisch an uns übermittelt. Dies sind: Browsertyp und Browserversion, verwendetes Betriebssystem, Referrer URL, Hostname des zugreifenden Rechners, Uhrzeit der Serveranfrage, IP-Adresse.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Privacy;
|
||||
25
tests/home.test.tsx
Normal file
25
tests/home.test.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import Home from '../app/page'
|
||||
|
||||
describe('Home Page', () => {
|
||||
it('renders the hero section with correct title', () => {
|
||||
render(<Home />)
|
||||
expect(screen.getByText(/Spezialisierter Partner für Energiekabelprojekte/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('contains the CTA button', () => {
|
||||
render(<Home />)
|
||||
const ctaButton = screen.getByRole('link', { name: /Projekt anfragen/i })
|
||||
expect(ctaButton).toBeInTheDocument()
|
||||
expect(ctaButton).toHaveAttribute('href', '/kontakt')
|
||||
})
|
||||
|
||||
it('renders the portfolio section', () => {
|
||||
render(<Home />)
|
||||
expect(screen.getByText(/Unsere Leistungen/i)).toBeInTheDocument()
|
||||
// Use getAllByText because it appears in both hero description and card title
|
||||
const elements = screen.getAllByText(/Technische Beratung/i)
|
||||
expect(elements.length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
12
tests/setup.ts
Normal file
12
tests/setup.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import '@testing-library/jest-dom'
|
||||
import { vi } from 'vitest'
|
||||
|
||||
// Mock next/navigation
|
||||
vi.mock('next/navigation', () => ({
|
||||
usePathname: () => '/',
|
||||
useRouter: () => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
prefetch: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
@@ -1,21 +1,41 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "ES2019"],
|
||||
"allowJs": false,
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"outDir": "dist/backend"
|
||||
},
|
||||
"include": ["server.ts"]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: 'dist/frontend',
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': 'http://localhost:3000'
|
||||
}
|
||||
}
|
||||
})
|
||||
17
vitest.config.ts
Normal file
17
vitest.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: './tests/setup.ts',
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './'),
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user