Frontend migration

This commit is contained in:
2025-04-27 17:33:20 +00:00
parent 01bb308740
commit c3f43016c6
94 changed files with 10729 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import Section from "@/components/Section";
import AboutHero from "@/components/About/Section/AboutHero";
import AboutTimeline from "@/components/About/Section/AboutTimeline";
import TeamSection from "@/components/About/Section/TeamSection";
import AboutIntro from "@/components/About/Section/AboutIntro";
import AboutProcess from "@/components/About/Section/AboutProcess";
const AboutContent = () => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<AboutHero/>
</Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<AboutIntro/>
</Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<AboutProcess/>
</Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<AboutTimeline/>
</Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<TeamSection/>
</Section>
</motion.div>
);
};
export default AboutContent;

View File

@@ -0,0 +1,19 @@
'use client';
import React from "react";
import SmallHero from "@/components/Helper/SmallHero";
const AboutHero = () => {
return (
<div className="relative overflow-hidden">
<SmallHero
title="Über uns"
subtitle="Digitaler Partner für individuelle Softwarelösungen."
backgroundImage="/images/contact.png"
blurBackground
/>
</div>
);
};
export default AboutHero;

View File

@@ -0,0 +1,68 @@
'use client';
import {motion} from 'framer-motion';
import {useThemeColors} from '@/utils/useThemeColors';
const About = () => {
const colors = useThemeColors();
return (
<section
className="relative w-full py-24 transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<div className="flex flex-col">
{/* Text */}
<div className="p-0 max-w-4xl">
<motion.p
className="text-base md:text-lg leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir sind Rhein-Software Ihr Partner für digitale Produkte und individuelle
Softwarelösungen.
Wir entwickeln skalierbare, wartbare Anwendungen mit klarem Fokus: Technik, die begeistert
von der Architektur bis zum Go-Live.
</motion.p>
<motion.p
className="mt-6 text-base md:text-lg leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}}
>
Ob Start-up oder etabliertes Unternehmen: Wir begleiten Sie mit einem flexiblen Netzwerk,
klarer Kommunikation und hohem Qualitätsanspruch agil, lösungsorientiert und nah am
Projekt.
</motion.p>
</div>
{/* CTA Button */}
<motion.div
className="mt-10 flex justify-end"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.5}}
>
{/*<Link href="/about">*/}
{/* <button*/}
{/* className="flex items-center gap-2 bg-blue-700 hover:bg-blue-900 text-white font-semibold px-5 py-2 rounded-full shadow-lg transition-all"*/}
{/* >*/}
{/* Mehr über uns <FiArrowRight size={18}/>*/}
{/* </button>*/}
{/*</Link>*/}
</motion.div>
</div>
</div>
</section>
);
};
export default About;

View File

@@ -0,0 +1,158 @@
'use client';
import React, {useState} from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const processSteps = [
{
title: "Beratung",
description: (
<>
In der <strong>Beratungsphase</strong> analysieren wir gemeinsam Ihre Anforderungen und
Geschäftsziele. Dabei identifizieren wir Herausforderungen und definieren die Zielsetzung
für Ihr Projekt.
</>
),
},
{
title: "Planung",
description: (
<>
Wir erarbeiten ein <strong>technisches Konzept</strong> mit klarer Struktur, Meilensteinen und
Ressourcenplanung. Eine solide Architektur bildet die Grundlage für ein skalierbares und wartbares
System.
</>
),
},
{
title: "Entwicklung",
description: (
<>
In iterativen Zyklen setzen wir das Projekt um. Regelmäßige <strong>Feedbackschleifen</strong>&nbsp;
sorgen dafür, dass das Ergebnis Ihren Erwartungen entspricht und flexibel angepasst werden kann.
</>
),
},
{
title: "Test",
description: (
<>
Durch umfangreiche <strong>Tests und Optimierungen</strong> stellen wir sicher, dass Ihre
Anwendung robust, performant und benutzerfreundlich ist noch vor dem Go-Live.
</>
),
},
{
title: "Go-Live",
description: (
<>
Wir begleiten Sie beim <strong>produktiven Einsatz</strong> Ihrer Anwendung und unterstützen Sie
auch nach dem Go-Live mit Support und Weiterentwicklungsmöglichkeiten.
</>
),
},
];
const AboutProcess: React.FC = () => {
const colors = useThemeColors();
const [activeIndex, setActiveIndex] = useState<number>(0);
return (
<section className="w-full px-6 sm:px-12 py-20 max-w-6xl mx-auto">
<h2
className="text-2xl sm:text-3xl font-bold text-left"
style={{color: colors.primaryText}}
>
Unser Prozess
</h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-12 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
{/* Mobile View: Tab buttons */}
<div className="block md:hidden mb-6">
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
{processSteps.map((step, idx) => (
<button
key={idx}
onClick={() => setActiveIndex(idx)}
className={`w-full px-4 py-2 text-sm border rounded-full transition-colors ${
activeIndex === idx
? 'bg-blue-600 text-white border-blue-600'
: 'border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
}`}
>
{step.title}
</button>
))}
</div>
</div>
{/* Desktop View: 2-column layout */}
<div className="hidden md:grid grid-cols-3 gap-8">
{/* Left: Step List */}
<div className="flex flex-col space-y-4">
{processSteps.map((step, idx) => (
<button
key={idx}
onClick={() => setActiveIndex(idx)}
className={`text-left px-4 py-3 border rounded-lg transition-colors ${
activeIndex === idx
? 'border-blue-600 bg-blue-50 dark:bg-gray-800'
: 'border-gray-300 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800'
}`}
style={{color: colors.primaryText}}
>
<span className="font-semibold">{idx + 1}. {step.title}</span>
</button>
))}
</div>
{/* Right: Step Content */}
<div className="md:col-span-2 p-6 border border-gray-300 dark:border-gray-700 rounded-lg"
style={{backgroundColor: colors.primaryBg}}>
<motion.div
key={activeIndex}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.4}}
>
<h3 className="text-xl font-bold mb-4" style={{color: colors.primaryText}}>
{processSteps[activeIndex].title}
</h3>
<div className="text-base space-y-1" style={{color: colors.secondaryText}}>
{processSteps[activeIndex].description}
</div>
</motion.div>
</div>
</div>
{/* Mobile View: Content Below Tabs */}
<div className="block md:hidden">
<div className="p-6 border border-gray-300 dark:border-gray-700 rounded-lg"
style={{backgroundColor: colors.primaryBg}}>
<motion.div
key={activeIndex + '-mobile'}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.4}}
>
<h3 className="text-xl font-bold mb-4" style={{color: colors.primaryText}}>
{activeIndex + 1}. {processSteps[activeIndex].title}
</h3>
<div className="text-base space-y-1" style={{color: colors.secondaryText}}>
{processSteps[activeIndex].description}
</div>
</motion.div>
</div>
</div>
</section>
);
};
export default AboutProcess;

View File

@@ -0,0 +1,94 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const timeline = [
{
date: "Oktober 2024",
title: "Projektgründung",
description: "Entwicklung der Idee und erste Umsetzungsschritte inspiriert durch Technik und Nachhaltigkeit.",
current: false,
},
{
date: "Mai 2025",
title: "Go-Live",
description: "Offizieller Start mit Kundenprojekten und einem umfassenden Full-Service-Angebot.",
current: true,
},
];
const AboutTimeline3 = () => {
const colors = useThemeColors();
return (
<div className="relative w-full px-6 sm:px-12 py-8 max-w-5xl mx-auto">
<h2
className="text-2xl sm:text-3xl font-bold mt-10"
style={{color: colors.primaryText}}
>
Von der Idee bis heute
</h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-12 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<div className="relative border-l-2 border-gray-300 dark:border-gray-700 ml-6">
{timeline.map((item, idx) => (
<motion.div
key={idx}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: idx * 0.2}}
className="relative mb-10 pl-12"
>
{/* Timeline dot */}
<div className="absolute left-[-22px] top-0 z-10">
<div
className={`w-10 h-10 rounded-full border-2 flex items-center justify-center bg-white dark:bg-gray-900 ${
item.current
? "border-blue-600"
: "border-gray-400 dark:border-gray-600"
}`}
>
{item.current && (
<motion.span
className="absolute w-10 h-10 rounded-full bg-blue-600 opacity-40"
animate={{scale: [1, 1.6, 1], opacity: [0.4, 0, 0.4]}}
transition={{repeat: Infinity, duration: 1.6}}
/>
)}
</div>
</div>
{/* Timeline card */}
<motion.div
whileHover={{scale: 1.02, translateX: 4}}
transition={{type: "spring", stiffness: 260, damping: 20}}
className="bg-white dark:bg-gray-900 rounded-lg shadow-md p-5 border border-gray-200 dark:border-gray-700 cursor-default"
>
<div className="text-sm text-blue-600 font-semibold mb-1">{item.date}</div>
<div
className="text-lg font-bold mb-2"
style={{color: colors.primaryText}}
>
{item.title}
</div>
<div className="text-sm" style={{color: colors.secondaryText}}>
{item.description}
</div>
</motion.div>
</motion.div>
))}
</div>
</div>
);
};
export default AboutTimeline3;

View File

@@ -0,0 +1,97 @@
'use client';
import React from "react";
import Image from "next/image";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const team = [
{
name: "Thatsaphorn",
role: "Gründer & Entwickler",
picture: "",
},
{
name: "Anonym",
role: "Vertrieb",
picture: "",
},
];
const fallbackImage = "/images/team/default-avatar.jpg";
const TeamSection = () => {
const colors = useThemeColors();
return (
<section className="w-full px-6 sm:px-12 py-16 max-w-6xl mx-auto">
<motion.h2
className="text-2xl sm:text-3xl font-bold text-left"
style={{color: colors.primaryText}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
>
Das Team
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-12 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<div className="flex justify-center">
<div
className={`grid gap-8
grid-cols-1
sm:grid-cols-${Math.min(team.length, 2)}
md:grid-cols-${Math.min(team.length, 3)}
lg:grid-cols-${Math.min(team.length, 4)}`}
>
{team.map((member, idx) => (
<motion.div
key={member.name}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: idx * 0.1}}
whileHover={{scale: 1.015}}
className="flex flex-col items-center text-center
rounded-xl border border-gray-200 dark:border-gray-700
shadow-md hover:shadow-lg transition-all p-6"
style={{backgroundColor: colors.secondaryBg}}
>
<motion.div
whileHover={{scale: 1.05}}
transition={{type: "spring", stiffness: 300, damping: 20}}
className="w-28 h-28 relative mb-4"
>
<Image
src={member.picture || fallbackImage}
alt={member.name}
fill
sizes="112px"
className="rounded-full object-cover shadow"
/>
</motion.div>
<div className="h-px w-8 bg-gray-300 dark:bg-gray-600 my-4"/>
<div className="text-lg font-semibold" style={{color: colors.primaryText}}>
{member.name}
</div>
<div className="text-sm mt-1" style={{color: colors.secondaryText}}>
{member.role}
</div>
</motion.div>
))}
</div>
</div>
</section>
);
};
export default TeamSection;

View File

@@ -0,0 +1,31 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import ContactHero from "@/components/Contact/Section/ContactHero";
import ContactFormSection from "@/components/Contact/Section/ContactFormSection";
import Section from "@/components/Section";
const Contact = () => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ContactHero/>
</Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<ContactFormSection/>
</Section>
</motion.div>
);
};
export default Contact;

View File

@@ -0,0 +1,192 @@
'use client';
import React, { useState } from "react";
import { motion } from "framer-motion";
import { useThemeColors } from "@/utils/useThemeColors";
import HCaptcha from "@hcaptcha/react-hcaptcha";
const ContactFormSection = () => {
const colors = useThemeColors();
const [form, setForm] = useState({
name: "",
email: "",
company: "",
phone: "",
website: "",
message: "",
});
const [captchaToken, setCaptchaToken] = useState("");
const [submitted, setSubmitted] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const isDev = process.env.NODE_ENV === "development";
const hCaptchaSiteKey = isDev
? "10000000-ffff-ffff-ffff-000000000001" // hCaptcha test sitekey
: "ES_ff59a664dc764f92870bf2c7b4eab7c5";
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError("");
if (!captchaToken) {
setError("Bitte löse das CAPTCHA, um fortzufahren.");
setLoading(false);
return;
}
try {
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...form, captcha: captchaToken }),
});
if (res.ok) {
setSubmitted(true);
setForm({ name: "", email: "", company: "", phone: "", website: "", message: "" });
} else {
const resJson = await res.json();
setError(resJson?.error || "Ein Fehler ist aufgetreten. Bitte versuche es später erneut.");
}
} catch (_err) {
setError("Serverfehler. Bitte versuche es später erneut.");
}
setLoading(false);
};
return (
<div className="w-full px-6 sm:px-12 py-20 text-left transition-theme">
<motion.h2
className="text-2xl sm:text-3xl font-bold mb-2"
style={{ color: colors.primaryText }}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
>
Schreib uns eine Nachricht
</motion.h2>
<motion.p
className="text-sm mb-8 max-w-xl"
style={{ color: colors.secondaryText }}
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.2 }}
>
Wir freuen uns über dein Interesse und melden uns schnellstmöglich bei dir zurück.
</motion.p>
{submitted ? (
<div className="text-green-600 font-semibold text-lg"> Deine Nachricht wurde erfolgreich gesendet!</div>
) : (
<form className="space-y-6" onSubmit={handleSubmit}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{[
{ label: "Dein Name *", name: "name", type: "text", required: true, placeholder: "Max Mustermann" },
{ label: "Deine E-Mail *", name: "email", type: "email", required: true, placeholder: "max@example.com" },
{ label: "Firmenname (optional)", name: "company", type: "text", required: false, placeholder: "Mustermann GmbH" },
{ label: "Telefonnummer (optional)", name: "phone", type: "tel", required: false, placeholder: "+49 123 456789" },
{ label: "Webseite (optional)", name: "website", type: "url", required: false, placeholder: "https://..." },
].map((field, index) => (
<motion.div
key={field.name}
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<label className="block font-semibold mb-1" style={{ color: colors.primaryText }}>
{field.label}
</label>
<input
type={field.type}
name={field.name}
value={form[field.name as keyof typeof form]}
onChange={handleChange}
required={field.required}
placeholder={field.placeholder}
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
style={{
backgroundColor: colors.inputFieldBg,
border: `1px solid ${colors.inputBorder}`,
color: colors.primaryText,
}}
/>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.6 }}
>
<label className="block font-semibold mb-1" style={{ color: colors.primaryText }}>
Deine Nachricht *
</label>
<textarea
name="message"
rows={4}
required
value={form.message}
onChange={handleChange}
placeholder="Worum geht es?"
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
style={{
backgroundColor: colors.inputFieldBg,
border: `1px solid ${colors.inputBorder}`,
color: colors.primaryText,
}}
/>
</motion.div>
<motion.div
className="pt-2"
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.7 }}
>
<HCaptcha sitekey={hCaptchaSiteKey} onVerify={setCaptchaToken} />
</motion.div>
{error && (
<div className="text-red-600 font-medium pt-2">
{error}
</div>
)}
<motion.div
className="pt-4 flex justify-end"
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.8 }}
>
<button
type="submit"
disabled={loading}
className="px-6 py-3 bg-blue-600 text-white text-sm sm:text-base font-semibold rounded-lg shadow-md hover:bg-blue-700 transition-all disabled:opacity-50"
>
{loading ? "Sende..." : "📩 Nachricht senden"}
</button>
</motion.div>
</form>
)}
</div>
);
};
export default ContactFormSection;

View File

@@ -0,0 +1,19 @@
'use client';
import React from "react";
import SmallHero from "@/components/Helper/SmallHero";
const ContactHero = () => {
return (
<div className="relative overflow-hidden">
<SmallHero
title="Kontakt"
subtitle="Du hast Fragen oder möchtest ein Projekt besprechen? Schreib uns!"
backgroundImage="/images/contact.png"
blurBackground
/>
</div>
);
};
export default ContactHero;

View File

@@ -0,0 +1,97 @@
'use client';
import React from 'react';
import Link from 'next/link';
import {motion} from 'framer-motion';
const Footer = () => {
return (
<motion.footer
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.6, ease: 'easeOut'}}
className="py-10 transition-theme text-white"
style={{
backgroundColor: '#16171f', // modern dark blue-purple tone
}}
>
<div className="w-[90%] mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-8">
{/* Logo and description */}
<div>
<h1 className="text-xl md:text-2xl font-bold text-white">
<span className="text-3xl md:text-4xl text-pink-700">R</span>hein Software
</h1>
</div>
{/* Informationen */}
<div>
<h3 className="text-lg font-semibold text-white">Informationen</h3>
<ul className="mt-4 space-y-4 text-sm font-semibold text-gray-400">
<li>
<Link href="/contact">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Kontakt
</p>
</Link>
</li>
{/*<li>*/}
{/* <Link href="/contact">*/}
{/* <p className="nav_link transition-all duration-300 ease-in-out hover:text-white">*/}
{/* Zahlung und Versand*/}
{/* </p>*/}
{/* </Link>*/}
{/*</li>*/}
</ul>
</div>
{/* Rechtliches */}
<div>
<h3 className="text-lg font-semibold text-white">Rechtliches</h3>
<ul className="mt-4 space-y-4 text-sm font-semibold text-gray-400">
{/*<li>*/}
{/* <Link href="/legal/terms-of-use">*/}
{/* <p className="nav_link transition-all duration-300 ease-in-out hover:text-white">*/}
{/* AGB*/}
{/* </p>*/}
{/* </Link>*/}
{/*</li>*/}
{/*<li>*/}
{/* <Link href="/legal/revocation">*/}
{/* <p className="nav_link transition-all duration-300 ease-in-out hover:text-white">*/}
{/* Widerruf*/}
{/* </p>*/}
{/* </Link>*/}
{/*</li>*/}
<li>
<Link href="/legal/privacy">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Datenschutz
</p>
</Link>
</li>
<li>
<Link href="/legal/imprint">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Impressum
</p>
</Link>
</li>
</ul>
</div>
</div>
{/* Bottom Section */}
<div
className="mt-8 border-t border-gray-600 pt-8 flex flex-col md:flex-row justify-between items-center text-sm text-gray-400">
<p className="text-center md:text-left">
© 2025 Rhein Software Development. All rights reserved.
</p>
</div>
</div>
</motion.footer>
);
};
export default Footer;

View File

@@ -0,0 +1,34 @@
export const SectionDivider1 = () => {
return (
<div
className="w-full h-20 transition-all duration-500 ease-in-out"
style={{
background: `linear-gradient(to bottom, var(--primary-bg), var(--secondary-bg))`
}}
/>
);
};
export const SectionDivider2 = () => {
return (
<div
className="w-full h-20 transition-all duration-500 ease-in-out"
style={{
background: `linear-gradient(to bottom, var(--secondary-bg), var(--primary-bg))`
}}
/>
);
};
export const SectionDivider3 = () => {
return (
<div
className="w-full h-20 transition-all duration-500 ease-in-out"
style={{
background: `linear-gradient(to bottom, var(--primary-bg), var(--footer-bg))`
}}
/>
);
};

View File

@@ -0,0 +1,58 @@
'use client';
import React, {useContext} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
import {motion} from "framer-motion";
type SmallHeroProps = {
title: string;
subtitle?: string;
backgroundImage?: string;
blurBackground?: boolean;
};
const SmallHero = ({title, subtitle, backgroundImage, blurBackground}: SmallHeroProps) => {
const {theme} = useContext(ThemeContext);
const colors = themeColors[theme];
const primaryTextColor = backgroundImage ? "#ffffff" : colors.primaryText;
const secondaryTextColor = backgroundImage ? "rgba(255, 255, 255, 0.8)" : "#6B7280"; // Tailwind gray-500
return (
<div className="relative w-full py-36 overflow-hidden">
{backgroundImage && blurBackground && (
<div
className="absolute inset-0 bg-cover bg-center blur-sm scale-[1.05] z-0 will-change-transform"
style={{backgroundImage: `url(${backgroundImage})`}}
/>
)}
{/* Text content */}
<div className="relative z-10 px-6 sm:px-12 max-w-5xl mx-auto">
<motion.h1
className="text-3xl sm:text-4xl font-bold text-left"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6}}
style={{color: primaryTextColor}}
>
{title}
</motion.h1>
{subtitle && (
<motion.p
className="mt-3 text-lg text-left"
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, delay: 0.2}}
style={{color: secondaryTextColor}}
>
{subtitle}
</motion.p>
)}
</div>
</div>
);
};
export default SmallHero;

View File

@@ -0,0 +1,34 @@
export const themeColors: Record<
"light" | "dark",
{
primaryBg: string;
secondaryBg: string;
navBg: string;
footerBg: string;
primaryText: string;
secondaryText: string;
inputFieldBg: string;
inputBorder: string;
}
> = {
light: {
primaryBg: "#F3F4F6",
secondaryBg: "#eff1f3",
navBg: "#F9FAFB",
footerBg: "#E5E7EB",
primaryText: "#1E293B",
secondaryText: "#475569",
inputFieldBg: "#ffffff",
inputBorder: "#cbd5e1",
},
dark: {
primaryBg: "#1A1A23",
secondaryBg: "#22222C",
navBg: "#2A2A35",
footerBg: "#1F1F29",
primaryText: "#F0F0F3",
secondaryText: "#C0C2CC",
inputFieldBg: "#2D2D38",
inputBorder: "#4B4B5A",
},
};

View File

@@ -0,0 +1,45 @@
'use client';
import React from "react";
import ContactCTA from "@/components/Home/Sections/ContactCTA";
import HomeServices from "@/components/Home/Sections/HomeServices";
import TechStack from "@/components/Home/Sections/TechStack";
import Section from "@/components/Section";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import Hero from "@/components/Home/Sections/Hero";
const Home = () => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Section style={{backgroundColor: colors.primaryBg}}>
<Hero/>
</Section>
{/*<Section style={{backgroundColor: colors.secondaryBg}} shadow>*/}
{/* <About/>*/}
{/*</Section>*/}
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<HomeServices/>
</Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<TechStack/>
</Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ContactCTA/>
</Section>
</motion.div>
);
};
export default Home;

View File

@@ -0,0 +1,84 @@
'use client';
// import Link from 'next/link';
// import {FiArrowRight} from 'react-icons/fi';
import {motion} from 'framer-motion';
import {useThemeColors} from '@/utils/useThemeColors';
const About = () => {
const colors = useThemeColors();
return (
<section
className="relative w-full py-24 transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<div className="flex flex-col">
{/* Title */}
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left transition-colors duration-700 ease-in-out"
>
Über uns
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
{/* Text */}
<div className="p-0 max-w-4xl">
<motion.p
className="text-base md:text-lg leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir sind Rhein-Software ein Team, das sich auf individuelle Softwarelösungen und digitale
Services spezialisiert hat. Unsere Anwendungen sind technisch solide, skalierbar und
durchdacht gebaut für langfristigen Erfolg.
</motion.p>
<motion.p
className="mt-6 text-base md:text-lg leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}}
>
Von der ersten Idee bis zum Go-Live begleiten wir Unternehmen und Startups mit einem
flexiblen Netzwerk, klarer Kommunikation und einem hohen Anspruch an Qualität.
Unsere Lösungen sind intuitiv, effizient und genau auf deine Anforderungen zugeschnitten.
</motion.p>
</div>
{/* CTA Button */}
<motion.div
className="mt-10 flex justify-end"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.5}}
>
{/*<Link href="/about">*/}
{/* <button*/}
{/* className="flex items-center gap-2 bg-blue-700 hover:bg-blue-900 text-white font-semibold px-5 py-2 rounded-full shadow-lg transition-all"*/}
{/* >*/}
{/* Mehr über uns <FiArrowRight size={18}/>*/}
{/* </button>*/}
{/*</Link>*/}
</motion.div>
</div>
</div>
</section>
);
};
export default About;

View File

@@ -0,0 +1,62 @@
'use client';
import Link from 'next/link';
import {motion} from 'framer-motion';
import {FiArrowRight} from 'react-icons/fi';
import {useThemeColors} from '@/utils/useThemeColors';
type ContactCTAProps = {
title?: string;
description?: string;
buttonLabel?: string;
};
const ContactCTA = ({
title = "Interesse geweckt?",
description = "Lass uns über dein Projekt sprechen. Wir freuen uns darauf, deine Ideen in die Realität umzusetzen.",
buttonLabel = "Jetzt Kontakt aufnehmen",
}: ContactCTAProps) => {
const colors = useThemeColors();
return (
<section
className="relative w-full py-24 overflow-hidden transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.primaryBg, color: colors.primaryText}}
>
<div className="w-full max-w-4xl px-6 md:px-10 mx-auto text-center">
<motion.h2 className="text-3xl md:text-4xl font-bold">
{title}
</motion.h2>
<motion.p
className="mt-4 text-sm md:text-base max-w-xl mx-auto"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.2}}
>
{description}
</motion.p>
<motion.div
className="mt-8 flex justify-center"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}}
>
<Link href="/contact">
<button
className="inline-flex items-center gap-2 px-6 py-3 text-sm md:text-base font-semibold rounded-full bg-blue-700 hover:bg-blue-900 text-white shadow-md transition-all duration-300"
>
{buttonLabel} <FiArrowRight size={18}/>
</button>
</Link>
</motion.div>
</div>
</section>
);
};
export default ContactCTA;

View File

@@ -0,0 +1,100 @@
'use client';
import Image from 'next/image';
import {Typewriter} from 'react-simple-typewriter';
import {motion} from 'framer-motion';
const Hero = () => {
return (
<div
className="relative w-full pt-[4vh] md:pt-[12vh] h-screen flex flex-col overflow-hidden"
style={{
backgroundColor: 'var(--primary-bg)',
color: 'white',
}}
>
{/* Background Image */}
<div className="absolute inset-0 z-0">
<Image
src="/images/home_hero.jpg"
alt="Rhein river aerial view"
layout="fill"
objectFit="cover"
className="blur-md scale-105"
priority
/>
<div className="absolute inset-0 bg-black/40"/>
</div>
{/* Main content */}
<div className="relative z-10 flex justify-center flex-col w-[90%] sm:w-[80%] h-full mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 items-center gap-12">
{/* Text Content */}
<div>
<motion.h1
className="text-3xl sm:text-4xl md:text-5xl mt-6 mb-6 font-bold text-white"
initial={{opacity: 0, y: 30}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, ease: 'easeOut'}}
>
Rhein-Software Development
</motion.h1>
<motion.p
className="text-lg md:text-xl text-gray-400"
initial={{opacity: 0, y: 30}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, delay: 0.2, ease: 'easeOut'}}
>
Digitale Lösungen für dein Unternehmen.
</motion.p>
<motion.p
className="mt-4 text-lg md:text-xl font-semibold text-white"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{delay: 0.9, duration: 0.6}}
>
<Typewriter
words={['Beratung', 'Entwicklung', 'Wartung', 'Fehlerbehebung']}
loop={true}
cursor
cursorStyle="_"
typeSpeed={60}
deleteSpeed={40}
delaySpeed={1500}
/>
</motion.p>
</div>
{/* Floating Image */}
<motion.div
className="hidden lg:block"
initial={{opacity: 0, y: 30}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.8, ease: 'easeOut', delay: 0.3}}
>
<motion.div
animate={{y: [0, -10, 0]}}
transition={{
duration: 4,
repeat: Infinity,
ease: 'easeInOut',
}}
className="animate-float"
>
<Image
src="/images/hero.png"
alt="hero graphic"
width={700}
height={700}
/>
</motion.div>
</motion.div>
</div>
</div>
</div>
);
};
export default Hero;

View File

@@ -0,0 +1,105 @@
'use client';
import {FiServer, FiTool, FiMonitor, FiZap, FiArrowRight} from 'react-icons/fi';
import {motion} from 'framer-motion';
import {useThemeColors} from '@/utils/useThemeColors';
const services = [
{
title: 'Beratung',
icon: <FiMonitor size={24}/>,
description:
'Strategische und technische Beratung rund um digitale Produkte und Prozesse. Wir analysieren bestehende Systeme, identifizieren Potenziale und helfen dir, die passende Architektur für dein Projekt zu finden.',
},
{
title: 'Entwicklung',
icon: <FiZap size={24}/>,
description:
'Individuelle Softwareentwicklung skalierbar, wartbar, zukunftssicher. Ob Web-App, API oder internes Tool: Wir setzen moderne Technologien ein, um genau die Lösung zu bauen, die du brauchst.',
},
{
title: 'Managed Services',
icon: <FiServer size={24}/>,
description:
'Wir betreuen Infrastruktur, Server und Systeme verlässlich und performant. Unser Team kümmert sich um Hosting, Monitoring, Backups und sorgt für einen reibungslosen Betrieb deiner Plattform.',
},
{
title: 'Fehlerbehebung',
icon: <FiTool size={24}/>,
description:
'Schnelle Hilfe bei Bugs, Performance-Problemen oder Sicherheitslücken. Wir analysieren Probleme, beheben sie gezielt und sorgen dafür, dass dein System wieder stabil läuft langfristig und zuverlässig.',
},
];
const HomeServices = () => {
const colors = useThemeColors();
return (
<section
className="w-full py-24 transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.primaryBg}}
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto" style={{color: colors.primaryText}}>
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left transition-colors duration-700 ease-in-out"
>
Leistungen
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-10 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{services.map((service, index) => (
<motion.div
key={service.title}
className="p-6 rounded-xl border shadow-md transition-colors duration-700 ease-in-out"
style={{
backgroundColor: colors.secondaryBg,
borderColor: colors.secondaryBg,
color: colors.primaryText,
}}
whileHover={{
scale: 1.03,
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
}}
initial={{opacity: 0, y: 30}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: index * 0.1}}
>
<div className="mb-3 text-blue-600">{service.icon}</div>
<h3 className="text-xl font-semibold mb-2">{service.title}</h3>
<p className="text-sm leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}>
{service.description}
</p>
</motion.div>
))}
</div>
<motion.div
className="mt-10 flex justify-end"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.3}}
>
<a
href="/services"
className="text-sm font-semibold text-blue-600 hover:underline flex items-center gap-1"
>
Weitere Leistungen <FiArrowRight size={16}/>
</a>
</motion.div>
</div>
</section>
);
};
export default HomeServices;

View File

@@ -0,0 +1,139 @@
'use client';
import Image from 'next/image';
import {motion} from 'framer-motion';
import {useThemeColors} from "@/utils/useThemeColors";
const techStack = {
row1: [
{
category: 'Programmiersprachen & Frameworks Backend',
items: [
{id: 'java', label: 'Java'},
{id: 'dart', label: 'Dart'},
{id: 'kotlin', label: 'Kotlin'},
{id: 'spring', label: 'Spring'},
],
},
{
category: 'Programmiersprachen & Frameworks Frontend',
items: [
{id: 'html', label: 'HTML'},
{id: 'css', label: 'CSS'},
{id: 'bootstrap', label: 'Bootstrap'},
{id: 'nextjs', label: 'Next.js'},
{id: 'typescript', label: 'TypeScript'},
{id: 'flutter', label: 'Flutter'},
],
},
],
row2: [
{
category: 'Betriebssysteme',
items: [
{id: 'macos', label: 'macOS'},
{id: 'debian', label: 'Debian'},
{id: 'ubuntu', label: 'Ubuntu'},
],
},
{
category: 'Version Control & Collaboration',
items: [
{id: 'gitlab', label: 'GitLab'},
{id: 'outline', label: 'Outline'},
],
},
{
category: 'DevOps & Infrastruktur',
items: [
{id: 'gitlab-ci', label: 'GitLab CI'},
{id: 'docker', label: 'Docker'},
{id: 'proxmox', label: 'Proxmox'},
],
},
],
};
const TechStack = () => {
const colors = useThemeColors();
return (
<section
className="w-full py-20 transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.secondaryBg}}
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto" style={{color: colors.primaryText}}>
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left transition-colors duration-700 ease-in-out"
>
Technologien
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<motion.p
className="text-sm md:text-base mb-10 transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
>
Mit diesen Technologien realisieren wir moderne, leistungsstarke Softwarelösungen.
</motion.p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{techStack.row1.map((group, index) => (
<TechCard key={group.category} group={group} delay={index * 0.2} colors={colors}/>
))}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{techStack.row2.map((group, index) => (
<TechCard key={group.category} group={group} delay={index * 0.2 + 0.4} colors={colors}/>
))}
</div>
</div>
</section>
);
};
const TechCard = ({
group,
delay,
colors,
}: {
group: { category: string; items: { id: string; label: string }[] };
delay: number;
colors: ReturnType<typeof useThemeColors>;
}) => (
<motion.div
className="p-4 rounded-lg border shadow-md transition-colors duration-700 ease-in-out"
style={{
backgroundColor: colors.primaryBg,
borderColor: colors.primaryBg,
color: colors.primaryText,
}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
whileHover={{scale: 1.03, boxShadow: '0 10px 20px rgba(0,0,0,0.1)'}}
viewport={{once: true}}
transition={{duration: 0.4, delay}}
>
<h3 className="text-base font-semibold mb-4">{group.category}</h3>
<div className="grid grid-cols-3 gap-3">
{group.items.map(({id, label}) => (
<div key={id} className="flex flex-col items-center text-center">
<Image src={`/images/svg/${id}.svg`} alt={label} width={32} height={32} className="object-contain"/>
<span className="text-[10px] mt-1 transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}>
{label}
</span>
</div>
))}
</div>
</motion.div>
);
export default TechStack;

View File

@@ -0,0 +1,131 @@
"use client";
import React, {useContext} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
import {motion} from "framer-motion";
const fadeInUp = {
hidden: {opacity: 0, y: 30},
visible: (i: number) => ({
opacity: 1,
y: 0,
transition: {
duration: 0.6,
delay: i * 0.2,
ease: "easeOut",
},
}),
};
const ImprintComp = () => {
const {theme} = useContext(ThemeContext);
const colors = themeColors[theme];
return (
<div
className="overflow-hidden transition-colors duration-500"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}
>
{/* Imprint Content */}
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12 space-y-10 text-base leading-relaxed">
{[
{
title: "Impressum",
content: (
<>
Thatsaphorn Atchariyaphap<br/>
Rhein-Software (Einzelunternehmer)<br/>
Mühlenstrasse 13<br/>
79664 Wehr
</>
),
},
{
title: "Kontakt",
content: (
<>
Telefon: +49 (0) 151 24003632<br/>
E-Mail:{" "}
<a
href="mailto:contact@rhein-software.dev"
className="underline text-blue-500"
>
contact@rhein-software.dev
</a>
</>
),
},
{
title: "EU-Streitschlichtung",
content: (
<>
Die Europäische Kommission stellt eine Plattform zur
Online-Streitbeilegung (OS) bereit:{" "}
<a
href="https://ec.europa.eu/consumers/odr/"
target="_blank"
rel="noopener noreferrer"
className="underline text-blue-500"
>
https://ec.europa.eu/consumers/odr/
</a>
.<br/>
Unsere E-Mail-Adresse finden Sie oben im Impressum.
</>
),
},
{
title:
"Verbraucherstreitbeilegung / Universalschlichtungsstelle",
content: (
<>
Wir sind nicht bereit oder verpflichtet, an
Streitbeilegungsverfahren vor einer
Verbraucherschlichtungsstelle teilzunehmen.
</>
),
},
].map((section, i) => (
<motion.div
key={section.title}
custom={i}
initial="hidden"
animate="visible"
variants={fadeInUp}
>
<h2 className="text-2xl font-bold">{section.title}</h2>
<motion.div
className="w-6 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<p>{section.content}</p>
</motion.div>
))}
<motion.p
className="text-sm text-gray-500"
custom={4}
initial="hidden"
animate="visible"
variants={fadeInUp}
>
Quelle:{" "}
<a
href="https://www.e-recht24.de"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
www.e-recht24.de
</a>
</motion.p>
</div>
</div>
);
};
export default ImprintComp;

View File

@@ -0,0 +1,145 @@
"use client";
import React, {useContext} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
import {motion} from "framer-motion";
const fadeInUp = {
hidden: {opacity: 0, y: 30},
visible: (i: number) => ({
opacity: 1,
y: 0,
transition: {
duration: 0.6,
delay: i * 0.2,
ease: "easeOut",
},
}),
};
const PrivacyComp = () => {
const {theme} = useContext(ThemeContext);
const colors = themeColors[theme];
const sections = [
{
title: "1. Datenschutz auf einen Blick",
content: (
<>
<p>
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen
Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit
denen Sie persönlich identifiziert werden können.
</p>
<p>
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten
finden Sie im Abschnitt Hinweis zur Verantwortlichen Stelle.
</p>
</>
),
},
{
title: "2. Allgemeine Hinweise und Pflichtinformationen",
content: (
<>
<p>
Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen
Datenschutzvorschriften sowie dieser Datenschutzerklärung. Wir weisen darauf hin, dass die
Datenübertragung im Internet Sicherheitslücken aufweisen kann.
</p>
<p>
Verantwortlich für die Datenverarbeitung:
<br/>
Rhein-Software Development
<br/>
Mühlenstrasse 13
<br/>
79664 Wehr
<br/><br/>
Telefon: +49 (0) 151 24003632
<br/>
E-Mail:{" "}
<a
href="mailto:contact@rhein-software.dev"
className="underline text-blue-500"
>
contact@rhein-software.dev
</a>
</p>
</>
),
},
{
title: "3. Ihre Rechte",
content: (
<p>
Sie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung,
Datenübertragbarkeit sowie auf Widerspruch gegen die Verarbeitung Ihrer Daten. Zudem haben Sie
ein Beschwerderecht bei der zuständigen Aufsichtsbehörde.
</p>
),
},
{
title: "4. Analyse-Tools und Cookies",
content: (
<p>
Beim Besuch dieser Website kann Ihr Surfverhalten statistisch ausgewertet werden. Das geschieht
vor allem mit sogenannten Analyseprogrammen und Cookies. Detaillierte Informationen hierzu
entnehmen Sie bitte unserer vollständigen Datenschutzerklärung.
</p>
),
},
{
title: "5. Sicherheit",
content: (
<p>
Diese Seite nutzt eine SSL- bzw. TLS-Verschlüsselung. Eine verschlüsselte Verbindung erkennen
Sie an der Adresszeile des Browsers (https://“) und dem Schloss-Symbol.
</p>
),
},
{
title: "Quelle",
content: (
<p className="text-sm text-gray-500">
Quelle:{" "}
<a
href="https://www.e-recht24.de"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
www.e-recht24.de
</a>
</p>
),
},
];
return (
<div
className="overflow-hidden transition-colors duration-500"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}
>
{/* Privacy Content */}
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12 space-y-10 text-base leading-relaxed">
{sections.map((section, i) => (
<motion.div
key={section.title}
custom={i}
initial="hidden"
whileInView="visible"
viewport={{once: true, amount: 0.2}}
variants={fadeInUp}
>
<h2 className="text-2xl font-bold mb-4">{section.title}</h2>
<div className="space-y-4">{section.content}</div>
</motion.div>
))}
</div>
</div>
);
};
export default PrivacyComp;

View File

@@ -0,0 +1,98 @@
"use client";
import SmallHero from "@/components/Helper/SmallHero";
import React, {useContext, useEffect} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
import AOS from "aos";
const RevocationComp = () => {
const {theme} = useContext(ThemeContext);
const colors = themeColors[theme];
useEffect(() => {
AOS.init({
duration: 1000,
easing: "ease",
once: true,
anchorPlacement: "top-bottom",
});
}, []);
return (
<div className="overflow-hidden transition-colors duration-500"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
{/* Hero Section */}
<div className="mt-[10vh]">
<SmallHero
title="Widerruf"
subtitle=""
backgroundImage="/images/contact.png"
/>
</div>
{/* Contact Form */}
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
<h2 className="text-2xl md:text-3xl font-bold text-center"
data-aos="fade-up"
data-aos-delay="400"
>
Schreib uns eine Nachricht
</h2>
<p data-aos="fade-up" data-aos-delay="600"
className="text-center mt-3 text-[var(--secondary-text)]">
Wir melden uns schnellstmöglich bei dir!
</p>
<form className="mt-8 max-w-2xl mx-auto space-y-6">
{/* Name & Email */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{["Dein Name", "Deine E-Mail"].map((label, index) => (
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
<label className="block font-semibold">{label}</label>
<input
type={index === 0 ? "text" : "email"}
placeholder={index === 0 ? "Max Mustermann" : "max@example.com"}
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
style={{
backgroundColor: colors.inputFieldBg,
border: `1px solid ${colors.inputBorder}`,
color: colors.primaryText
}}
/>
</div>
))}
</div>
{/* Message */}
<div data-aos="fade-up" data-aos-delay="300">
<label className="block font-semibold">Deine Nachricht</label>
<textarea
rows={4}
placeholder="Schreibe deine Nachricht..."
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
style={{
backgroundColor: colors.inputFieldBg,
border: `1px solid ${colors.inputBorder}`,
color: colors.primaryText
}}
/>
</div>
{/* Submit Button */}
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
<button
type="submit"
className="px-6 py-3 bg-blue-600 text-white text-lg font-semibold rounded-lg shadow-md hover:bg-blue-700 transition-all"
>
📩 Nachricht senden
</button>
</div>
</form>
</div>
</div>
);};
export default RevocationComp;

View File

@@ -0,0 +1,99 @@
"use client";
import SmallHero from "@/components/Helper/SmallHero";
import React, {useContext, useEffect} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
import AOS from "aos";
const TermsOfUseComp = () => {
const {theme} = useContext(ThemeContext);
const colors = themeColors[theme];
useEffect(() => {
AOS.init({
duration: 1000,
easing: "ease",
once: true,
anchorPlacement: "top-bottom",
});
}, []);
return (
<div className="overflow-hidden transition-colors duration-500"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
{/* Hero Section */}
<div className="mt-[10vh]">
<SmallHero
title="AGB"
subtitle=""
backgroundImage="/images/contact.png"
/>
</div>
{/* Contact Form */}
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
<h2 className="text-2xl md:text-3xl font-bold text-center"
data-aos="fade-up"
data-aos-delay="400"
>
Schreib uns eine Nachricht
</h2>
<p data-aos="fade-up" data-aos-delay="600"
className="text-center mt-3 text-[var(--secondary-text)]">
Wir melden uns schnellstmöglich bei dir!
</p>
<form className="mt-8 max-w-2xl mx-auto space-y-6">
{/* Name & Email */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{["Dein Name", "Deine E-Mail"].map((label, index) => (
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
<label className="block font-semibold">{label}</label>
<input
type={index === 0 ? "text" : "email"}
placeholder={index === 0 ? "Max Mustermann" : "max@example.com"}
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
style={{
backgroundColor: colors.inputFieldBg,
border: `1px solid ${colors.inputBorder}`,
color: colors.primaryText
}}
/>
</div>
))}
</div>
{/* Message */}
<div data-aos="fade-up" data-aos-delay="300">
<label className="block font-semibold">Deine Nachricht</label>
<textarea
rows={4}
placeholder="Schreibe deine Nachricht..."
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
style={{
backgroundColor: colors.inputFieldBg,
border: `1px solid ${colors.inputBorder}`,
color: colors.primaryText
}}
/>
</div>
{/* Submit Button */}
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
<button
type="submit"
className="px-6 py-3 bg-blue-600 text-white text-lg font-semibold rounded-lg shadow-md hover:bg-blue-700 transition-all"
>
📩 Nachricht senden
</button>
</div>
</form>
</div>
</div>
);
};
export default TermsOfUseComp;

View File

@@ -0,0 +1,110 @@
"use client";
import {usePathname} from "next/navigation";
import Link from "next/link";
import React, {useContext, useEffect, useState} from "react";
import {HiBars3BottomRight} from "react-icons/hi2";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {useThemeColors} from "@/utils/useThemeColors";
import {navLinks} from "@/constant/Constant";
type Props = {
openNav: () => void;
};
const Nav = ({openNav}: Props) => {
const [navBg, setNavBg] = useState(false);
const [navHeight, setNavHeight] = useState("h-[10vh]");
const [contentSize, setContentSize] = useState("text-base md:text-lg");
const [buttonSize, setButtonSize] = useState("md:px-6 md:py-2 px-4 py-1 text-sm");
const {theme, toggleTheme} = useContext(ThemeContext);
const colors = useThemeColors();
const pathname = usePathname();
const navColorClass = theme === "dark" || !navBg ? "text-white" : "text-black";
useEffect(() => {
const handler = () => {
if (window.scrollY >= 90) {
setNavBg(true);
setNavHeight("h-[8vh]");
setContentSize("text-sm md:text-base");
setButtonSize("md:px-5 md:py-1.5 px-3 py-1 text-xs");
} else {
setNavBg(false);
setNavHeight("h-[10vh]");
setContentSize("text-base md:text-lg");
setButtonSize("md:px-6 md:py-2 px-4 py-1 text-sm");
}
};
window.addEventListener("scroll", handler);
return () => window.removeEventListener("scroll", handler);
}, []);
return (
<div
className={`fixed w-full transition-all duration-300 ease-in-out ${navHeight} z-[1000] ${
navBg ? "shadow-md" : ""
}`}
style={{backgroundColor: navBg ? colors.navBg : "transparent"}}
>
<div className="flex items-center h-full justify-between w-[90%] xl:w-[80%] mx-auto">
<Link href="/">
<h1 className={`${contentSize} font-bold cursor-pointer ${navColorClass}`}>
<span className="text-lg md:text-xl text-pink-700">R</span>hein Software
</h1>
</Link>
<div className="hidden lg:flex items-center space-x-6">
{navLinks.map((link) => (
<Link href={link.url} key={link.id}>
<p className={`relative group ${contentSize} uppercase ${getNavLinkClasses(pathname === link.url, navBg, theme, navColorClass)}`}>
{link.label}
{pathname !== link.url && (
<span
className="absolute bottom-0 left-0 w-full h-[2px] bg-current transform transition-transform duration-300 origin-right scale-x-0 group-hover:scale-x-100"/>
)}
</p>
</Link>
))}
</div>
<div className="flex items-center space-x-3">
<Link href="/contact">
<button
className={`${buttonSize} text-white font-semibold bg-blue-700 hover:bg-blue-900 rounded-full`}>
Kontakt
</button>
</Link>
<button
onClick={toggleTheme}
className={`w-7 h-7 flex items-center justify-center rounded-full ${navColorClass}`}
style={{backgroundColor: colors.secondaryBg}}
>
{theme === "dark" ? "🌙" : "☀️"}
</button>
<HiBars3BottomRight
onClick={openNav}
className={`w-6 h-6 cursor-pointer lg:hidden ${navColorClass}`}
/>
</div>
</div>
</div>
);
};
const getNavLinkClasses = (
isActive: boolean,
navBg: boolean,
theme: string,
navColorClass: string
): string => {
if (isActive) return !navBg ? "text-white font-bold" : `${navColorClass} font-bold`;
if (!navBg) return "text-white font-medium";
return theme === "dark" ? "text-gray-300 font-medium" : "text-gray-700 font-medium";
};
export default Nav;

View File

@@ -0,0 +1,62 @@
'use client';
import {navLinks} from "@/constant/Constant";
import Link from "next/link";
import React, {useContext} from "react";
import {CgClose} from "react-icons/cg";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {useThemeColors} from "@/utils/useThemeColors";
type Props = {
showNav: boolean;
closeNav: () => void;
};
const MobileNav = ({closeNav, showNav}: Props) => {
const navOpen = showNav ? "translate-y-0 opacity-100" : "-translate-y-20 opacity-0 pointer-events-none";
const {theme, toggleTheme} = useContext(ThemeContext);
const colors = useThemeColors();
const textClass = theme === "dark" ? "text-white" : "text-black";
return (
<div className="lg:hidden">
<div
className={`fixed inset-0 z-[10000] transition-opacity duration-500 ${
showNav ? "opacity-60 bg-black" : "opacity-0 pointer-events-none"
}`}
onClick={closeNav}
/>
<div
className={`fixed top-0 left-0 w-full z-[10006] transform ${navOpen} transition-all duration-500 ease-in-out shadow-md rounded-b-2xl`}
style={{backgroundColor: colors.navBg}}
>
<div className={`flex flex-col items-center justify-center py-8 space-y-4 px-4 relative ${textClass}`}>
<CgClose
onClick={closeNav}
className={`absolute top-4 right-6 sm:right-8 sm:w-7 sm:h-7 w-6 h-6 cursor-pointer p-1 ${textClass}`}
/>
{navLinks.map((link) => (
<Link href={link.url} key={link.id}>
<p className="nav__link uppercase text-[14px] sm:text-[16px] border-b pb-1 border-gray-400 transition-all duration-300 ease-in-out hover:scale-105">
{link.label}
</p>
</Link>
))}
<button
onClick={toggleTheme}
className={`mt-4 w-8 h-8 flex items-center justify-center rounded-full border border-gray-400 transition-all duration-300 ${textClass}`}
style={{backgroundColor: colors.secondaryBg}}
>
{theme === "dark" ? "🌙" : "☀️"}
</button>
</div>
</div>
</div>
);
};
export default MobileNav;

View File

@@ -0,0 +1,24 @@
"use client";
import React, {useState} from "react";
import DesktopNav from "./DesktopNav";
import MobileNav from "./MobileNav";
const Nav = () => {
const [showNav, setShowNav] = useState(false);
const handleNavShow = () => {
setShowNav(true);
};
const handleNavHide = () => {
setShowNav(false);
};
return (
<div>
<DesktopNav openNav={handleNavShow}/>
<MobileNav showNav={showNav} closeNav={handleNavHide}/>
</div>
);
};
export default Nav;

View File

@@ -0,0 +1,34 @@
'use client';
import React, {CSSProperties} from "react";
import clsx from "clsx";
interface SectionProps {
children: React.ReactNode;
className?: string;
style?: CSSProperties;
shadow?: boolean;
}
const Section: React.FC<SectionProps> = ({
children,
className = "",
style,
shadow = false,
}) => {
return (
<section
className={clsx("relative transition-colors duration-500", className)}
style={style}
>
<div className="relative z-10">{children}</div>
{shadow && (
<div
className="absolute bottom-0 left-0 w-full h-16 pointer-events-none"
/>
)}
</section>
);
};
export default Section;

View File

@@ -0,0 +1,81 @@
'use client';
import React, {useState} from 'react';
import {motion, AnimatePresence} from 'framer-motion';
import {FiZap, FiMonitor, FiServer, FiTool} from 'react-icons/fi';
import Development from "@/components/Services/Section/overview/Development";
import Consulting from "@/components/Services/Section/overview/Consulting";
import ManagedServices from "@/components/Services/Section/overview/ManagedServices";
import BugFixing from "@/components/Services/Section/overview/BugFixing";
import {useThemeColors} from "@/utils/useThemeColors";
const tabs = [
{key: 'entwicklung', label: 'Entwicklung', icon: <FiZap size={20}/>},
{key: 'beratung', label: 'Beratung', icon: <FiMonitor size={20}/>},
{key: 'services', label: 'Managed Services', icon: <FiServer size={20}/>},
{key: 'support', label: 'Fehlerbehebung', icon: <FiTool size={20}/>},
];
const tabContent: Record<string, React.ReactNode> = {
entwicklung: <Development/>,
beratung: <Consulting/>,
services: <ManagedServices/>,
support: <BugFixing/>,
};
const OverviewTabs = () => {
const [activeTab, setActiveTab] = useState("entwicklung");
const colors = useThemeColors();
return (
<div className="max-w-5xl mx-auto px-6 py-16 transition-theme text-left">
<h2 className="text-3xl font-bold mb-2" style={{color: colors.primaryText}}>
Was wir tun
</h2>
<motion.div
className="w-12 h-[2px] mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<p className="text-sm mb-10" style={{color: colors.secondaryText}}>
In diesem Abschnitt geben wir dir einen Überblick über unsere zentralen Leistungen von der technischen
Entwicklung über Beratung bis hin zu Betrieb und Support.
</p>
<div className="flex flex-wrap gap-4 mb-10">
{tabs.map((tab) => (
<button
key={tab.key}
onClick={() => setActiveTab(tab.key)}
className="flex-1 min-w-[150px] flex items-center justify-start gap-2 px-4 py-2 rounded-full border text-sm font-medium transition-all"
style={{
color: activeTab === tab.key ? '#ffffff' : colors.primaryText,
backgroundColor: activeTab === tab.key ? '#1D4ED8' : 'transparent',
borderColor: activeTab === tab.key ? '#1D4ED8' : colors.inputBorder,
}}
>
{tab.icon}
{tab.label}
</button>
))}
</div>
<AnimatePresence mode="wait">
<motion.div
key={activeTab}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
exit={{opacity: 0, y: -10}}
transition={{duration: 0.4}}
>
{tabContent[activeTab]}
</motion.div>
</AnimatePresence>
</div>
);
};
export default OverviewTabs;

View File

@@ -0,0 +1,19 @@
'use client';
import React from "react";
import SmallHero from "@/components/Helper/SmallHero";
const ServiceHero = () => {
return (
<div className="relative overflow-hidden">
<SmallHero
title="Unsere Leistungen"
subtitle="Wir bieten maßgeschneiderte Lösungen von der Beratung bis zum Betrieb."
backgroundImage="/images/contact.png"
blurBackground
/>
</div>
);
};
export default ServiceHero;

View File

@@ -0,0 +1,59 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const BugFixing = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] mx-auto grid grid-cols-1 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
🐞 Fehlerbehebung & Optimierung
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir analysieren und beheben Fehler in bestehenden Systemen, optimieren die Performance und sorgen
dafür, dass deine Software stabil und zuverlässig läuft.
</motion.p>
<motion.h3
className="mt-8 text-lg font-semibold"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🔎 Fokusbereiche
</motion.h3>
<ul
className="list-disc list-inside text-sm mt-4 space-y-1"
style={{color: colors.secondaryText}}
>
<li>Debugging & Troubleshooting</li>
<li>Performance-Analyse</li>
<li>Refactoring von Legacy-Code</li>
<li>Stabilitäts- und Sicherheitsupdates</li>
</ul>
</div>
</div>
);
};
export default BugFixing;

View File

@@ -0,0 +1,60 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const Consulting = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] mx-auto grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
🧠 Technische Beratung
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir unterstützen dich dabei, technische Entscheidungen fundiert zu treffen von der Auswahl
geeigneter Technologien bis hin zur Planung skalierbarer Architekturen. Gemeinsam finden wir den
effizientesten Weg von der Idee bis zur Umsetzung praxisnah, zielgerichtet und verständlich.
</motion.p>
<motion.h3
className="mt-8 text-lg font-semibold"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🔍 Themenbereiche
</motion.h3>
<ul
className="list-disc list-inside text-sm mt-4 space-y-1"
style={{color: colors.secondaryText}}
>
<li>Software-Architektur & Microservices</li>
<li>Technologie- und Framework-Auswahl</li>
<li>Prototyping & Machbarkeitsanalysen</li>
<li>Projektplanung und agile Methodik</li>
</ul>
</div>
</div>
);
};
export default Consulting;

View File

@@ -0,0 +1,210 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import Image from "next/image";
const techStack = {
row1: [
{
category: 'Programmiersprachen & Frameworks Backend',
items: [
{id: 'java', label: 'Java'},
{id: 'dart', label: 'Dart'},
{id: 'kotlin', label: 'Kotlin'},
{id: 'spring', label: 'Spring'},
],
},
{
category: 'Programmiersprachen & Frameworks Frontend',
items: [
{id: 'html', label: 'HTML'},
{id: 'css', label: 'CSS'},
{id: 'bootstrap', label: 'Bootstrap'},
{id: 'nextjs', label: 'Next.js'},
{id: 'typescript', label: 'TypeScript'},
{id: 'flutter', label: 'Flutter'},
],
},
],
row2: [
{
category: 'Betriebssysteme',
items: [
{id: 'macos', label: 'macOS'},
{id: 'debian', label: 'Debian'},
{id: 'ubuntu', label: 'Ubuntu'},
],
},
{
category: 'Version Control & Collaboration',
items: [
{id: 'gitlab', label: 'GitLab'},
{id: 'outline', label: 'Outline'},
],
},
{
category: 'DevOps & Infrastruktur',
items: [
{id: 'gitlab-ci', label: 'GitLab CI'},
{id: 'docker', label: 'Docker'},
{id: 'proxmox', label: 'Proxmox'},
],
},
],
};
const TechCard = ({group}: {
group: { category: string; items: { id: string; label: string }[] };
}) => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.4}}
className="p-4 rounded-lg border shadow-md transition-colors duration-700 ease-in-out"
style={{
backgroundColor: colors.primaryBg,
borderColor: colors.primaryBg,
color: colors.primaryText,
}}
>
<h3 className="text-base font-semibold mb-4">{group.category}</h3>
<div className="grid grid-cols-3 gap-3">
{group.items.map(({id, label}) => (
<div key={id} className="flex flex-col items-center text-center">
<Image
src={`/images/svg/${id}.svg`}
alt={label}
width={32}
height={32}
className="object-contain"
/>
<span
className="text-[10px] mt-1 transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
>
{label}
</span>
</div>
))}
</div>
</motion.div>
);
};
const Development = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] mx-auto grid grid-cols-1 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
💻 Full-Stack Entwicklung
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir entwickeln individuelle Softwarelösungen von der nativen Mobile-App über moderne Webseiten bis
hin zu internen Tools.
Unser Fokus liegt auf skalierbaren Architekturen, performanten Frontends und wartbaren Backends.
<br/><br/>
Egal ob API-Entwicklung, Admin-Dashboard oder komplexe Plattform wir setzen moderne Technologien
gezielt ein, um robuste, zukunftssichere Anwendungen zu realisieren.
</motion.p>
<motion.div
className="mt-6 text-sm font-medium space-y-3 pl-2"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.3}}
>
<div className="flex items-center gap-2">
<span className="text-xs">🚀</span>
<p>Native Mobile-Apps mit Flutter</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">🌐</span>
<p>Webseiten & Web-Portale mit Next.js</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">🧩</span>
<p>Skalierbare Backends mit Spring Boot</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">📊</span>
<p>Individuelle Dashboards & Admin-Panels</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">🔌</span>
<p>API-Entwicklung (REST & GraphQL)</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs"></span>
<p>Automatisierte interne Tools</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">📦</span>
<p>CI/CD & Container mit GitLab CI & Docker</p>
</div>
</motion.div>
<motion.h3
className="mt-10 text-lg font-semibold"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🔧 Unser Tech Stack im Überblick
</motion.h3>
<motion.p
className="text-sm font-medium mb-4"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Mit diesem Stack entwickeln wir robuste, moderne Softwarelösungen abgestimmt auf deine
Anforderungen.
</motion.p>
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.5, delay: 0.1}}
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{techStack.row1.map((group) => (
<TechCard key={group.category} group={group}/>
))}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{techStack.row2.map((group) => (
<TechCard key={group.category} group={group}/>
))}
</div>
</motion.div>
</div>
</div>
);
};
export default Development;

View File

@@ -0,0 +1,62 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const ManagedServices = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] mx-auto grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
🛠 Managed Services
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir übernehmen den Betrieb und die Wartung deiner Anwendungen zuverlässig, sicher und skalierbar.
So kannst du dich voll auf dein Geschäft konzentrieren.
</motion.p>
<motion.h3
className="mt-8 text-lg font-semibold"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🧰 Leistungen
</motion.h3>
<motion.ul
className="list-disc list-inside text-sm mt-4 space-y-1"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.3}}
>
<li>Monitoring & Logging</li>
<li>Security Updates & Wartung</li>
<li>Cloud Deployment & Hosting</li>
<li>24/7 Systemüberwachung</li>
</motion.ul>
</div>
</div>
);
};
export default ManagedServices;

View File

@@ -0,0 +1,38 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import ServiceHero from "@/components/Services/Section/ServiceHero";
import OverviewTabs from "@/components/Services/Section/OverviewTabs";
import ContactCTA from "@/components/Home/Sections/ContactCTA";
import Section from "@/components/Section";
const Home = () => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ServiceHero/>
</Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<OverviewTabs/>
</Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ContactCTA
title="Nichts Passendes gefunden?"
description="Nimm einfach Kontakt mit uns auf gemeinsam finden wir die passende Lösung für dein Vorhaben."
buttonLabel="Kontakt aufnehmen"
/>
</Section>
</motion.div>
);
};
export default Home;

View File

@@ -0,0 +1,50 @@
'use client';
import {createContext, useEffect, useState} from "react";
import Cookies from "js-cookie";
type ThemeType = "light" | "dark";
export const ThemeContext = createContext<{
theme: ThemeType;
toggleTheme: () => void;
}>({
theme: "light",
toggleTheme: () => {
},
});
export const ThemeProvider = ({children}: { children: React.ReactNode }) => {
const [theme, setTheme] = useState<ThemeType | null>(null);
useEffect(() => {
const saved = Cookies.get("theme") as ThemeType | undefined;
if (saved === "dark" || saved === "light") {
setTheme(saved);
} else {
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const defaultTheme: ThemeType = prefersDark ? "dark" : "light";
setTheme(defaultTheme);
Cookies.set("theme", defaultTheme, {expires: 365});
}
}, []);
useEffect(() => {
if (!theme) return;
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
const toggleTheme = () => {
const next = theme === "dark" ? "light" : "dark";
setTheme(next);
Cookies.set("theme", next, {expires: 365});
};
if (!theme) return null;
return (
<ThemeContext.Provider value={{theme, toggleTheme}}>
{children}
</ThemeContext.Provider>
);
};