Integrate React components and update homepage layout: added Hero, Feature, Stats, Pricing, FAQ, Contact, and Footer sections; updated global styles and dependencies for React support.

This commit is contained in:
Matthias
2026-04-21 10:11:13 +02:00
parent 54590826c7
commit ccd1cc46dc
19 changed files with 2416 additions and 498 deletions

View File

@@ -1,9 +1,10 @@
// @ts-check
import { defineConfig } from 'astro/config';
import react from "@astrojs/react";
import tailwindcss from '@tailwindcss/vite';
// https://astro.build/config
export default defineConfig({
integrations: [react()],
vite: {
plugins: [tailwindcss()],
},

View File

@@ -12,16 +12,24 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/react": "^5.0.3",
"@fontsource-variable/geist": "^5.2.8",
"@hookform/resolvers": "^5.2.2",
"@tailwindcss/vite": "^4.2.3",
"astro": "^6.1.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.38.0",
"lucide-react": "^1.8.0",
"motion": "^12.38.0",
"radix-ui": "^1.4.3",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-hook-form": "^7.73.1",
"shadcn": "^4.3.1",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.3",
"tw-animate-css": "^1.4.0"
"tw-animate-css": "^1.4.0",
"zod": "^4.3.6"
}
}

1212
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { CornerDownRight, LoaderIcon } from "lucide-react";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Field,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
const contactFormSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z
.string()
.min(1, "Email is required")
.email("Please enter a valid email"),
message: z.string().min(1, "Message is required"),
});
type ContactFormData = z.infer<typeof contactFormSchema>;
interface Contact21Props {
className?: string;
onSubmit?: (data: ContactFormData) => Promise<void>;
}
const Contact21 = ({ className, onSubmit }: Contact21Props) => {
const [isSubmitted, setIsSubmitted] = useState(false);
const [showSuccess, setShowSuccess] = useState(false);
const form = useForm<ContactFormData>({
resolver: zodResolver(contactFormSchema),
mode: "onSubmit",
reValidateMode: "onSubmit",
defaultValues: {
name: "",
email: "",
message: "",
},
});
const handleFormSubmit = async (data: ContactFormData) => {
try {
if (onSubmit) {
await onSubmit(data);
} else {
console.log("Form submitted:", data);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
setIsSubmitted(true);
setShowSuccess(true);
form.reset();
setTimeout(() => setShowSuccess(false), 4500);
setTimeout(() => setIsSubmitted(false), 5000);
} catch {
form.setError("root", {
message: "Something went wrong. Please try again.",
});
}
};
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="mt-20 flex flex-col justify-between gap-15 md:gap-10 lg:flex-row">
<div className="flex w-full max-w-lg flex-col justify-between gap-10">
<p className="indent-[22%] text-3xl font-medium tracking-tight text-muted-foreground/50 lg:text-4xl">
We are a team of creators, thinkers, and builders who believe in
crafting experiences that truly connect.{" "}
<span className="text-foreground">We're here to help</span>
</p>
<div className="mt-5 flex items-center gap-4 lg:mt-20">
<img
src="https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/avatar3.png"
className="size-12"
alt="avatar"
/>
<div>
<h3 className="text-lg font-medium tracking-tight">John Doe</h3>
<p className="text-sm text-foreground/40">Creative Director</p>
</div>
</div>
</div>
<div className="col-span-4 flex w-full flex-col gap-2 lg:pl-10">
<h1 className="mb-7 text-6xl font-semibold tracking-tight lg:text-5xl">
Get in Touch
</h1>
{isSubmitted && (
<div
className={cn(
"mb-4 rounded-lg border border-green-500/20 bg-green-500/10 p-4 text-center transition-opacity duration-500",
showSuccess ? "opacity-100" : "opacity-0",
)}
>
<p className="text-sm font-medium text-green-600 dark:text-green-400">
Thank you! We'll be in touch soon.
</p>
</div>
)}
<form onSubmit={form.handleSubmit(handleFormSubmit)}>
<FieldGroup className="gap-0">
<Controller
control={form.control}
name="name"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="sr-only">
Name
</FieldLabel>
<Input
{...field}
id={field.name}
aria-invalid={fieldState.invalid}
placeholder="Name*"
className="h-15 rounded-none border-0 border-b border-b-foreground/25 !bg-transparent shadow-none placeholder:text-foreground/20 focus-visible:ring-0 lg:text-base"
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="email"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="sr-only">
Email
</FieldLabel>
<Input
{...field}
id={field.name}
type="email"
aria-invalid={fieldState.invalid}
placeholder="Email*"
className="h-15 rounded-none border-0 border-b border-b-foreground/25 !bg-transparent shadow-none placeholder:text-foreground/20 focus-visible:ring-0 lg:text-base"
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="message"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="sr-only">
Message
</FieldLabel>
<Input
{...field}
id={field.name}
aria-invalid={fieldState.invalid}
placeholder="Message (Tell us about your project)"
className="h-15 rounded-none border-0 border-b border-b-foreground/25 !bg-transparent shadow-none placeholder:text-foreground/20 focus-visible:ring-0 lg:text-base"
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
{form.formState.errors.root && (
<p className="text-sm text-destructive">
{form.formState.errors.root.message}
</p>
)}
<Button
className="mt-15 flex items-center justify-start gap-2 rounded-none !px-8 lg:h-12 lg:text-base"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<>
<LoaderIcon className="size-5 animate-spin" />
Sending...
</>
) : (
<>
<CornerDownRight className="size-5" />
Get in touch
</>
)}
</Button>
</FieldGroup>
</form>
</div>
</div>
</div>
</section>
);
};
export { Contact21 };

82
src/components/faq7.tsx Normal file
View File

@@ -0,0 +1,82 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const faqs = [
{
question: "What is a FAQ and why is it important?",
answer:
"FAQ stands for Frequently Asked Questions. It is a list that provides answers to common questions people may have about a specific product, service, or topic.",
},
{
question: "Why should I use a FAQ on my website or app?",
answer:
"Utilizing a FAQ section on your website or app is a practical way to offer instant assistance to your users or customers. Instead of waiting for customer support responses, they can find quick answers to commonly asked questions. ",
},
{
question: "How do I effectively create a FAQ section?",
answer:
"Creating a FAQ section starts with gathering the most frequent questions you receive from your users or customers. Once you have a list, you need to write clear, detailed, and helpful answers to each question.",
},
{
question: "What are the benefits of having a well-maintained FAQ section?",
answer:
"There are numerous advantages to maintaining a robust FAQ section. Firstly, it provides immediate answers to common queries, which improves the user experience.",
},
{
question: "How do I effectively create a FAQ section?",
answer:
"Creating a FAQ section starts with gathering the most frequent questions you receive from your users or customers. Once you have a list, you need to write clear, detailed, and helpful answers to each question.",
},
];
interface Faq7Props {
className?: string;
}
const Faq7 = ({ className }: Faq7Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="mx-auto grid max-w-7xl gap-10 md:grid-cols-2">
<div className="flex flex-col gap-6">
<h2 className="text-4xl font-semibold">
Need Help?
<br />
<span className="text-muted-foreground/70">
We&apos;re here to assist.
</span>
</h2>
<p className="text-lg text-muted-foreground md:text-xl">
Still have questions? Feel free to contact our friendly
<a href="#" className="mx-1 whitespace-nowrap underline">
support team
</a>
specialists.
</p>
<Button size="lg" variant="outline" className="w-fit">
View all FAQs
</Button>
</div>
<Accordion type="multiple">
{faqs.map((faq, index) => (
<AccordionItem key={index} value={`item-${index}`}>
<AccordionTrigger className="text-left">
{faq.question}
</AccordionTrigger>
<AccordionContent>{faq.answer}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</div>
</section>
);
};
export { Faq7 };

View File

@@ -0,0 +1,96 @@
import { HelpCircleIcon } from "lucide-react";
import React from "react";
import { GlowingEffect } from "@/components/ui/glowing-effect";
import { cn } from "@/lib/utils";
const featureData = [
{
desc: "Lorem ipsum dolor sit amet consec adipisicing elit. Quisquam, quos.",
img: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img1.jpeg",
title: "Quality",
badgeTitle: "#1 Block",
gridClass: "md:col-span-1",
},
{
desc: "Consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore. Lorem ipsum dolor sit amet consec adipisicing elit.",
img: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img7.jpeg",
title: "Innovation",
badgeTitle: "#2 Block",
gridClass: "lg:col-span-2",
},
{
desc: "Ut enim ad minim veniam quis nostrud exercitation ullamco laboris.",
img: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img11.jpeg",
title: "Performance",
badgeTitle: "#3 Block",
gridClass: "md:col-span-1 lg:row-span-2 ",
},
{
desc: "Consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore. Lorem ipsum dolor sit amet consec adipisicing elit.",
img: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img2.jpeg",
title: "Innovation",
badgeTitle: "#2 Block",
gridClass: "lg:col-span-2",
},
{
desc: "Duis aute irure dolor in reprehenderit in voluptate velit esse.",
img: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/guri3/img4.jpeg",
title: "Reliability",
badgeTitle: "#4 Block",
gridClass: "md:col-span-1",
},
];
interface Feature284Props {
className?: string;
}
const Feature284 = ({ className }: Feature284Props) => {
return (
<section className={cn("h-full overflow-hidden py-32", className)}>
<div className="container flex h-full w-full items-center justify-center">
<div className="grid w-full max-w-6xl grid-cols-1 grid-rows-2 gap-4 md:grid-cols-2 lg:h-[800px] lg:grid-cols-4">
{featureData.map((feature, index) => (
<div
key={index}
className={cn(
"relative flex flex-col gap-2 rounded-3xl border p-4",
feature.gridClass,
)}
>
<GlowingEffect
spread={40}
glow={true}
disabled={false}
proximity={64}
inactiveZone={0.01}
/>
<div className="flex w-full items-center justify-between">
<p className="text-muted-foreground">{feature.badgeTitle}</p>
<HelpCircleIcon className="size-4 text-muted-foreground" />
</div>
<div
className={cn(
"w-full flex-1 overflow-hidden rounded-3xl bg-muted",
)}
>
<img
src={feature.img}
alt={feature.title}
className="pointer-events-none h-full w-full object-cover"
/>
</div>
<h3 className="mt-4 text-2xl font-semibold tracking-tight">
{feature.title}
</h3>
<p className="text-muted-foreground">{feature.desc}</p>
</div>
))}
</div>
</div>
</section>
);
};
export { Feature284 };

142
src/components/footer27.tsx Normal file
View File

@@ -0,0 +1,142 @@
"use client";
import { motion } from "framer-motion";
import { ArrowUpRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
interface Footer27Props {
className?: string;
}
const Footer27 = ({ className }: Footer27Props) => {
const socialLinks = [
{ name: "Instagram", href: "#" },
{ name: "X (Twitter)", href: "#" },
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
duration: 0.6,
staggerChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5 },
},
};
return (
<section className={cn("py-32", className)}>
<div className="container">
<footer>
<div>
<motion.div
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
className="flex flex-col justify-between md:flex-row md:items-center"
>
<div className="space-y-8">
<motion.div variants={itemVariants} className="space-y-6">
<h2 className="text-4xl leading-tight font-bold text-foreground lg:text-5xl">
Connect with Me
</h2>
<p className="max-w-md text-lg leading-relaxed text-muted-foreground">
No commitments. Just a quick chat to see if we click.
</p>
</motion.div>
<motion.div variants={itemVariants}>
<Button size="lg">Get in Touch</Button>
</motion.div>
</div>
<div className="mt-5 space-y-8 md:mt-0">
<motion.div variants={itemVariants}>
<div className="space-y-6">
{socialLinks.map((link) => (
<motion.div
key={link.name}
variants={itemVariants}
whileHover={{ x: 4 }}
transition={{
type: "spring",
stiffness: 300,
damping: 20,
}}
>
<a
href={link.href}
className="group flex items-center gap-2 py-2 text-foreground transition-colors hover:text-foreground/80"
>
<span className="text-xl font-medium">
{link.name}
</span>
<ArrowUpRight className="h-6 w-6 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</a>
</motion.div>
))}
</div>
</motion.div>
</div>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
className="mt-16"
>
<motion.div variants={itemVariants}>
<Separator className="mb-8" />
</motion.div>
<motion.div
variants={itemVariants}
className="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center"
>
<p className="text-sm text-muted-foreground">
© Copyright 2025. All rights Reserved.
</p>
<div className="flex items-center gap-6 text-sm">
<span className="text-muted-foreground">
Made by{" "}
<motion.a
href="https://x.com/shadcnblocks"
className="underline underline-offset-4 transition-colors hover:text-foreground"
whileHover={{ scale: 1.05 }}
transition={{
type: "spring",
stiffness: 300,
damping: 20,
}}
>
shadcnblocks
</motion.a>
</span>
</div>
</motion.div>
</motion.div>
</div>
</footer>
</div>
</section>
);
};
export { Footer27 };

View File

@@ -0,0 +1,96 @@
import { ArrowDown, BookOpen, LayoutGrid, Sparkles } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface Hero235Props {
className?: string;
}
const SERVICES = [
{
icon: Sparkles,
title: "Visual Identity",
description:
"We shape visual systems that blend craft and clarity to spark recognition and connection.",
},
{
icon: LayoutGrid,
title: "Interactive Experiences",
description:
"Experiences that tie design and technology together to tell your story in motion.",
},
{
icon: BookOpen,
title: "Workshops",
description:
"Hands-on sessions that adapt to your goals and help you grow with practical guidance.",
},
];
const Hero235 = ({ className }: Hero235Props) => {
return (
<section
className={cn(
"relative flex h-dvh items-center justify-center overflow-hidden bg-background py-32",
className,
)}
>
<div aria-hidden={true}>
<div className="absolute -right-0 -bottom-[30rem] size-[35rem] rounded-full bg-rose-400 opacity-40 blur-[5rem] md:-right-[2rem] md:-bottom-[50rem] md:size-[55rem] dark:opacity-20" />
<div className="absolute -right-[20rem] -bottom-[20rem] size-[35rem] rounded-full bg-sky-500 opacity-40 blur-[5rem] md:-right-[32rem] md:-bottom-[36rem] md:size-[55rem] dark:opacity-20" />
<div
className="pointer-events-none absolute inset-0 z-0 opacity-40"
style={{
backgroundImage:
"url(https://deifkwefumgah.cloudfront.net/shadcnblocks/block/noise.png)",
backgroundRepeat: "repeat",
}}
/>
</div>
<div className="relative container flex h-full flex-col justify-between">
<div className="flex flex-1 items-center justify-center">
<div className="mx-auto flex max-w-2xl flex-col items-center text-center">
<h1 className="text-4xl font-semibold tracking-tight text-balance text-foreground md:text-5xl lg:text-6xl">
Shadcnblocks. Digital craft with an eye for detail
</h1>
<p className="mt-8 max-w-xl text-pretty text-muted-foreground md:text-lg">
We build clear, thoughtful interfaces that work as well as they
look. If you want a partner who turns your vision into something
real, you&apos;re in the right place.
</p>
<Button size="lg" className="mt-10">
Explore our work
<ArrowDown className="shrink-0" aria-hidden />
</Button>
</div>
</div>
<div className="pt-16">
<div className="grid gap-12 lg:grid-cols-3 lg:gap-0">
{SERVICES.map((service) => {
const Icon = service.icon;
return (
<div
key={service.title}
className={cn(
"flex flex-col border-l border-primary px-6 md:px-8",
)}
>
<Icon className="mb-4 size-6" aria-hidden />
<h2 className="font-semibold text-foreground">
{service.title}
</h2>
<p className="mt-2 text-sm text-muted-foreground">
{service.description}
</p>
</div>
);
})}
</div>
</div>
</div>
</section>
);
};
export { Hero235 };

154
src/components/pricing4.tsx Normal file
View File

@@ -0,0 +1,154 @@
"use client";
import { Check } from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
interface PricingPlan {
name: string;
badge: string;
monthlyPrice: string;
yearlyPrice: string;
features: string[];
buttonText: string;
isPopular?: boolean;
}
interface Pricing4Props {
title?: string;
description?: string;
plans?: PricingPlan[];
className?: string;
}
const Pricing4 = ({
title = "Pricing",
description = "Check out our affordable pricing plans.",
plans = [
{
name: "Free",
badge: "Free",
monthlyPrice: "$0",
yearlyPrice: "$0",
features: [
"Unlimited Integrations",
"Windows, Linux, Mac support",
"24/7 Support",
"Free updates",
],
buttonText: "Get Started",
},
{
name: "Pro",
badge: "Pro",
monthlyPrice: "$29",
yearlyPrice: "$249",
features: [
"Everything in FREE",
"Live call support every month",
"Unlimited Storage",
],
buttonText: "Purchase",
},
{
name: "Elite",
badge: "Elite",
monthlyPrice: "$59",
yearlyPrice: "$549",
features: [
"Everything in PRO",
"Advanced analytics",
"Custom branding",
"Unlimited users",
],
buttonText: "Purchase",
isPopular: true,
},
],
className,
}: Pricing4Props) => {
const [isAnnually, setIsAnnually] = useState(false);
return (
<section className={cn("py-32", className)}>
<div className="container mx-auto">
<div className="flex flex-col gap-6">
<h2 className="text-4xl font-semibold text-pretty lg:text-6xl">
{title}
</h2>
<div className="flex flex-col justify-between gap-10 md:flex-row">
<p className="max-w-3xl text-muted-foreground lg:text-xl">
{description}
</p>
<Tabs
value={isAnnually ? "annually" : "monthly"}
onValueChange={(value: string) =>
setIsAnnually(value === "annually")
}
className="w-fit shrink-0"
aria-label="Billing period"
>
<TabsList className="grid h-11 w-max grid-cols-2 gap-0 rounded-md p-1 text-lg">
<TabsTrigger
value="monthly"
className="h-full min-h-0 px-7 py-0 font-semibold text-muted-foreground data-active:text-foreground"
>
Monthly
</TabsTrigger>
<TabsTrigger
value="annually"
className="h-full min-h-0 px-7 py-0 font-semibold text-muted-foreground data-active:text-foreground"
>
Yearly
</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div className="flex w-full flex-col items-stretch gap-6 md:flex-row">
{plans.map((plan) => (
<div
key={plan.name}
className={`flex w-full flex-col rounded-lg border p-6 text-left ${
plan.isPopular ? "bg-muted" : ""
}`}
>
<Badge className="mb-8 block w-fit uppercase">
{plan.badge}
</Badge>
<h3 className="font-mono text-4xl lg:text-5xl">
{isAnnually ? plan.yearlyPrice : plan.monthlyPrice}
</h3>
<p
className={`text-muted-foreground ${plan.monthlyPrice === "$0" ? "invisible" : ""}`}
>
{isAnnually ? "Per year" : "Per month"}
</p>
<Separator className="my-6" />
<div className="flex h-full flex-col justify-between gap-20">
<ul className="space-y-4 text-muted-foreground md:leading-snug">
{plan.features.map((feature, featureIndex) => (
<li
key={featureIndex}
className="flex items-center gap-2"
>
<Check className="size-4 shrink-0" aria-hidden="true" />
<span>{feature}</span>
</li>
))}
</ul>
<Button className="w-full">{plan.buttonText}</Button>
</div>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export { Pricing4 };

View File

@@ -0,0 +1,69 @@
import { cn } from "@/lib/utils";
interface Stats11Props {
className?: string;
}
const Stats11 = ({ className }: Stats11Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="relative isolate overflow-hidden bg-linear-to-b from-primary/10 to-transparent md:border-x md:border-border">
<div className="absolute right-0 -left-px -z-20 h-full w-full bg-[linear-gradient(90deg,var(--muted-foreground)_1px,transparent_1px)] [mask-image:linear-gradient(transparent_25%,black_25%,black_75%,transparent_75%)] [background-size:calc(100%/16)_100%] [mask-size:100%_16px] opacity-20 [-webkit-mask-image:linear-gradient(transparent_25%,black_25%,black_75%,transparent_75%)] [-webkit-mask-size:100%_16px]" />
<div>
<h2 className="mb-16 max-w-3xl text-3xl leading-10 font-semibold sm:mb-24 md:mx-10">
Revolutionizing healthcare with AI technology.
<span className="font-medium text-primary/50">
{" "}
Our advanced diagnostic platform helps doctors make accurate
diagnoses in seconds.
</span>
</h2>
<div className="relative grid max-w-2xl gap-4 border-x border-border pb-32 sm:grid-cols-2 sm:gap-10 sm:pb-44 md:ml-10 md:border-0">
<div className="absolute inset-0 -top-[1100px] -left-[calc(1000px-22vw)] -z-10 size-[1500px] rounded-full border border-primary bg-background sm:-top-[480%] sm:-left-[185%] sm:size-[2000px] md:-top-[906%] md:-left-[294%] md:size-[3500px] lg:-top-[1186%] lg:-left-[380%] lg:size-[4500px] xl:-top-[1200%] xl:-left-[350%] 2xl:-top-[1196%] 2xl:-left-[345%]"></div>
<div className="flex flex-col gap-2">
<span className="flex gap-5 text-3xl font-semibold">
<span className="relative -left-px w-px bg-primary/50"></span>
1,000,000+
</span>
<p className="pl-5 font-medium text-muted-foreground/80">
Diagnoses Made
</p>
</div>
<div className="flex flex-col gap-2">
<span className="flex gap-5 text-3xl font-semibold">
<span className="relative -left-px w-px bg-primary/50"></span>
95%
</span>
<p className="pl-5 font-medium text-muted-foreground/80">
Diagnostic Accuracy
</p>
</div>
<div className="flex flex-col gap-2">
<span className="flex gap-5 text-3xl font-semibold">
<span className="relative -left-px w-px bg-primary/50"></span>
3,000+
</span>
<p className="pl-5 font-medium text-muted-foreground/80">
Healthcare Providers
</p>
</div>
<div className="flex flex-col gap-2">
<span className="flex gap-5 text-3xl font-semibold">
<span className="relative -left-px w-px bg-primary/50"></span>
2.5s
</span>
<p className="pl-5 font-medium text-muted-foreground/80">
Latency
</p>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export { Stats11 };

View File

@@ -0,0 +1,79 @@
import * as React from "react"
import { Accordion as AccordionPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
function Accordion({
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return (
<AccordionPrimitive.Root
data-slot="accordion"
className={cn("flex w-full flex-col", className)}
{...props}
/>
)
}
function AccordionItem({
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return (
<AccordionPrimitive.Item
data-slot="accordion-item"
className={cn("not-last:border-b", className)}
{...props}
/>
)
}
function AccordionTrigger({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
return (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
"group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
className
)}
{...props}
>
{children}
<ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
<ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
}
function AccordionContent({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
return (
<AccordionPrimitive.Content
data-slot="accordion-content"
className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
{...props}
>
<div
className={cn(
"h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
className
)}
>
{children}
</div>
</AccordionPrimitive.Content>
)
}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

View File

@@ -0,0 +1,49 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
secondary:
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
destructive:
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
outline:
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
ghost:
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
link: "text-primary underline-offset-4 hover:underline",
},
},
defaultVariants: {
variant: "default",
},
}
)
function Badge({
className,
variant = "default",
asChild = false,
...props
}: React.ComponentProps<"span"> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot.Root : "span"
return (
<Comp
data-slot="badge"
data-variant={variant}
className={cn(badgeVariants({ variant }), className)}
{...props}
/>
)
}
export { Badge, badgeVariants }

236
src/components/ui/field.tsx Normal file
View File

@@ -0,0 +1,236 @@
import { useMemo } from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
return (
<fieldset
data-slot="field-set"
className={cn(
"flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
className
)}
{...props}
/>
)
}
function FieldLegend({
className,
variant = "legend",
...props
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
return (
<legend
data-slot="field-legend"
data-variant={variant}
className={cn(
"mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
className
)}
{...props}
/>
)
}
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-group"
className={cn(
"group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
className
)}
{...props}
/>
)
}
const fieldVariants = cva(
"group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
{
variants: {
orientation: {
vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
horizontal:
"flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
responsive:
"flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
},
},
defaultVariants: {
orientation: "vertical",
},
}
)
function Field({
className,
orientation = "vertical",
...props
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
return (
<div
role="group"
data-slot="field"
data-orientation={orientation}
className={cn(fieldVariants({ orientation }), className)}
{...props}
/>
)
}
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-content"
className={cn(
"group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
className
)}
{...props}
/>
)
}
function FieldLabel({
className,
...props
}: React.ComponentProps<typeof Label>) {
return (
<Label
data-slot="field-label"
className={cn(
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
className
)}
{...props}
/>
)
}
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-label"
className={cn(
"flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
className
)}
{...props}
/>
)
}
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
data-slot="field-description"
className={cn(
"text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
"last:mt-0 nth-last-2:-mt-1",
"[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
className
)}
{...props}
/>
)
}
function FieldSeparator({
children,
className,
...props
}: React.ComponentProps<"div"> & {
children?: React.ReactNode
}) {
return (
<div
data-slot="field-separator"
data-content={!!children}
className={cn(
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
className
)}
{...props}
>
<Separator className="absolute inset-0 top-1/2" />
{children && (
<span
className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
data-slot="field-separator-content"
>
{children}
</span>
)}
</div>
)
}
function FieldError({
className,
children,
errors,
...props
}: React.ComponentProps<"div"> & {
errors?: Array<{ message?: string } | undefined>
}) {
const content = useMemo(() => {
if (children) {
return children
}
if (!errors?.length) {
return null
}
const uniqueErrors = [
...new Map(errors.map((error) => [error?.message, error])).values(),
]
if (uniqueErrors?.length == 1) {
return uniqueErrors[0]?.message
}
return (
<ul className="ml-4 flex list-disc flex-col gap-1">
{uniqueErrors.map(
(error, index) =>
error?.message && <li key={index}>{error.message}</li>
)}
</ul>
)
}, [children, errors])
if (!content) {
return null
}
return (
<div
role="alert"
data-slot="field-error"
className={cn("text-sm font-normal text-destructive", className)}
{...props}
>
{content}
</div>
)
}
export {
Field,
FieldLabel,
FieldDescription,
FieldError,
FieldGroup,
FieldLegend,
FieldSeparator,
FieldSet,
FieldContent,
FieldTitle,
}

View File

@@ -0,0 +1,190 @@
"use client";
import { memo, useCallback, useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
import { animate } from "motion/react";
interface GlowingEffectProps {
blur?: number;
inactiveZone?: number;
proximity?: number;
spread?: number;
variant?: "default" | "white";
glow?: boolean;
className?: string;
disabled?: boolean;
movementDuration?: number;
borderWidth?: number;
}
const GlowingEffect = memo(
({
blur = 0,
inactiveZone = 0.7,
proximity = 0,
spread = 20,
variant = "default",
glow = false,
className,
movementDuration = 2,
borderWidth = 1,
disabled = true,
}: GlowingEffectProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const lastPosition = useRef({ x: 0, y: 0 });
const animationFrameRef = useRef<number>(0);
const handleMove = useCallback(
(e?: MouseEvent | { x: number; y: number }) => {
if (!containerRef.current) return;
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
animationFrameRef.current = requestAnimationFrame(() => {
const element = containerRef.current;
if (!element) return;
const { left, top, width, height } = element.getBoundingClientRect();
const mouseX = e?.x ?? lastPosition.current.x;
const mouseY = e?.y ?? lastPosition.current.y;
if (e) {
lastPosition.current = { x: mouseX, y: mouseY };
}
const center = [left + width * 0.5, top + height * 0.5];
const distanceFromCenter = Math.hypot(
mouseX - center[0],
mouseY - center[1]
);
const inactiveRadius = 0.5 * Math.min(width, height) * inactiveZone;
if (distanceFromCenter < inactiveRadius) {
element.style.setProperty("--active", "0");
return;
}
const isActive =
mouseX > left - proximity &&
mouseX < left + width + proximity &&
mouseY > top - proximity &&
mouseY < top + height + proximity;
element.style.setProperty("--active", isActive ? "1" : "0");
if (!isActive) return;
const currentAngle =
parseFloat(element.style.getPropertyValue("--start")) || 0;
let targetAngle =
(180 * Math.atan2(mouseY - center[1], mouseX - center[0])) /
Math.PI +
90;
const angleDiff = ((targetAngle - currentAngle + 180) % 360) - 180;
const newAngle = currentAngle + angleDiff;
animate(currentAngle, newAngle, {
duration: movementDuration,
ease: [0.16, 1, 0.3, 1],
onUpdate: (value) => {
element.style.setProperty("--start", String(value));
},
});
});
},
[inactiveZone, proximity, movementDuration]
);
useEffect(() => {
if (disabled) return;
const handleScroll = () => handleMove();
const handlePointerMove = (e: PointerEvent) => handleMove(e);
window.addEventListener("scroll", handleScroll, { passive: true });
document.body.addEventListener("pointermove", handlePointerMove, {
passive: true,
});
return () => {
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
window.removeEventListener("scroll", handleScroll);
document.body.removeEventListener("pointermove", handlePointerMove);
};
}, [handleMove, disabled]);
return (
<>
<div
className={cn(
"pointer-events-none absolute -inset-px hidden rounded-[inherit] border opacity-0 transition-opacity",
glow && "opacity-100",
variant === "white" && "border-white",
disabled && "!block"
)}
/>
<div
ref={containerRef}
style={
{
"--blur": `${blur}px`,
"--spread": spread,
"--start": "0",
"--active": "0",
"--glowingeffect-border-width": `${borderWidth}px`,
"--repeating-conic-gradient-times": "5",
"--gradient":
variant === "white"
? `repeating-conic-gradient(
from 236.84deg at 50% 50%,
var(--black),
var(--black) calc(25% / var(--repeating-conic-gradient-times))
)`
: `radial-gradient(circle, #dd7bbb 10%, #dd7bbb00 20%),
radial-gradient(circle at 40% 40%, #d79f1e 5%, #d79f1e00 15%),
radial-gradient(circle at 60% 60%, #5a922c 10%, #5a922c00 20%),
radial-gradient(circle at 40% 60%, #4c7894 10%, #4c789400 20%),
repeating-conic-gradient(
from 236.84deg at 50% 50%,
#dd7bbb 0%,
#d79f1e calc(25% / var(--repeating-conic-gradient-times)),
#5a922c calc(50% / var(--repeating-conic-gradient-times)),
#4c7894 calc(75% / var(--repeating-conic-gradient-times)),
#dd7bbb calc(100% / var(--repeating-conic-gradient-times))
)`,
} as React.CSSProperties
}
className={cn(
"pointer-events-none absolute inset-0 rounded-[inherit] opacity-100 transition-opacity",
glow && "opacity-100",
blur > 0 && "blur-[var(--blur)] ",
className,
disabled && "!hidden"
)}
>
<div
className={cn(
"glow",
"rounded-[inherit]",
'after:content-[""] after:rounded-[inherit] after:absolute after:inset-[calc(-1*var(--glowingeffect-border-width))]',
"after:[border:var(--glowingeffect-border-width)_solid_transparent]",
"after:[background:var(--gradient)] after:[background-attachment:fixed]",
"after:opacity-[var(--active)] after:transition-opacity after:duration-300",
"after:[mask-clip:padding-box,border-box]",
"after:[mask-composite:intersect]",
"after:[mask-image:linear-gradient(#0000,#0000),conic-gradient(from_calc((var(--start)-var(--spread))*1deg),#00000000_0deg,#fff,#00000000_calc(var(--spread)*2deg))]"
)}
/>
</div>
</>
);
}
);
GlowingEffect.displayName = "GlowingEffect";
export { GlowingEffect };

View File

@@ -0,0 +1,19 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
<input
type={type}
data-slot="input"
className={cn(
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className
)}
{...props}
/>
)
}
export { Input }

View File

@@ -0,0 +1,22 @@
import * as React from "react"
import { Label as LabelPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
function Label({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
<LabelPrimitive.Root
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}
/>
)
}
export { Label }

View File

@@ -0,0 +1,26 @@
import * as React from "react"
import { Separator as SeparatorPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
function Separator({
className,
orientation = "horizontal",
decorative = true,
...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
return (
<SeparatorPrimitive.Root
data-slot="separator"
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
className
)}
{...props}
/>
)
}
export { Separator }

View File

@@ -0,0 +1,90 @@
"use client"
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Tabs as TabsPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
function Tabs({
className,
orientation = "horizontal",
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
data-orientation={orientation}
className={cn(
"group/tabs flex gap-2 data-horizontal:flex-col",
className
)}
{...props}
/>
)
}
const tabsListVariants = cva(
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
{
variants: {
variant: {
default: "bg-muted",
line: "gap-1 bg-transparent",
},
},
defaultVariants: {
variant: "default",
},
}
)
function TabsList({
className,
variant = "default",
...props
}: React.ComponentProps<typeof TabsPrimitive.List> &
VariantProps<typeof tabsListVariants>) {
return (
<TabsPrimitive.List
data-slot="tabs-list"
data-variant={variant}
className={cn(tabsListVariants({ variant }), className)}
{...props}
/>
)
}
function TabsTrigger({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
className
)}
{...props}
/>
)
}
function TabsContent({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return (
<TabsPrimitive.Content
data-slot="tabs-content"
className={cn("flex-1 text-sm outline-none", className)}
{...props}
/>
)
}
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }

View File

@@ -1,5 +1,12 @@
---
import { Contact21 } from "@/components/contact21";
import { Faq7 } from "@/components/faq7";
import { Feature284 } from "@/components/feature284";
import { Footer27 } from "@/components/footer27";
import { Hero235 } from "@/components/hero235";
import { Pricing4 } from "@/components/pricing4";
import { Stats11 } from "@/components/stats11";
import "@/styles/global.css";
---
<html lang="de">
@@ -9,94 +16,22 @@
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Website im Aufbau</title>
<title>Website</title>
<script
src="https://rybbit.matthias.lol/api/script.js"
data-site-id="63eb61dcdf55"
defer
></script>
<style>
:root {
color-scheme: light;
font-family: "Georgia", "Times New Roman", serif;
background: linear-gradient(180deg, #f7f3eb 0%, #ece4d7 100%);
color: #1f1811;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 24px;
background:
radial-gradient(circle at top, rgba(255, 255, 255, 0.85), transparent 40%),
linear-gradient(180deg, #f7f3eb 0%, #ece4d7 100%);
}
main {
width: min(100%, 560px);
padding: 40px 32px;
border: 1px solid rgba(64, 43, 24, 0.14);
border-radius: 24px;
background: rgba(255, 252, 247, 0.88);
box-shadow: 0 20px 60px rgba(60, 41, 24, 0.12);
text-align: center;
}
h1 {
margin: 0 0 16px;
font-size: clamp(2rem, 5vw, 3.25rem);
line-height: 1.1;
}
p {
margin: 0;
font-size: 1.0625rem;
line-height: 1.7;
}
p + p {
margin-top: 16px;
}
a {
color: #6d2f14;
font-weight: 700;
text-decoration-thickness: 0.08em;
text-underline-offset: 0.18em;
word-break: break-word;
}
a:hover,
a:focus-visible {
color: #8a3a18;
}
@media (max-width: 640px) {
body {
padding: 16px;
}
main {
padding: 32px 22px;
border-radius: 20px;
}
}
</style>
</head>
<body>
<main>
<h1>Website in Arbeit</h1>
<p>Ich arbeite gerade an dieser Website.</p>
<p>
Bei Fragen oder Anliegen erreichen Sie mich unter
<a href="mailto:support@matthiasmeister.dev">support@matthiasmeister.dev</a>.
</p>
</main>
<Hero235 client:load />
<div class="mx-auto w-full max-w-7xl">
<Feature284 client:load />
<Stats11 client:load />
<Pricing4 client:load />
<Faq7 client:load />
<Contact21 client:load />
<Footer27 client:load />
</div>
</body>
</html>