mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 19:51:17 +00:00
443 lines
27 KiB
TypeScript
443 lines
27 KiB
TypeScript
"use client"
|
|
|
|
import { AnimatePresence, motion } from "motion/react"
|
|
import { useEffect, useState } from "react"
|
|
import {
|
|
ArrowRightIcon,
|
|
BanknoteIcon,
|
|
BriefcaseIcon,
|
|
CalendarDaysIcon,
|
|
CheckIcon,
|
|
GraduationCapIcon,
|
|
MapPinIcon,
|
|
PartyPopperIcon,
|
|
PlayIcon,
|
|
PlusIcon,
|
|
TabletSmartphoneIcon,
|
|
TruckIcon,
|
|
} from "lucide-react"
|
|
import { cn } from "@/lib/utils"
|
|
import type { TemplateProps } from "../../lib/types"
|
|
import { TField, TSelect } from "../shared/fields"
|
|
import { splitList } from "../shared/hooks"
|
|
import { TImage } from "../shared/media"
|
|
import { Counter, Reveal, RevealWords, StepPanel, ease, useScrollNav } from "../shared/motion"
|
|
import { TemplateShell } from "../shared/shell"
|
|
import { karriere, type KarriereData } from "./definition"
|
|
import { fonts } from "./fonts"
|
|
|
|
const em = "text-t-accent"
|
|
const container = "mx-auto w-full max-w-[1320px] px-5 md:px-8"
|
|
const heading = "font-t-display font-bold tracking-[-0.03em]"
|
|
const icons = [BanknoteIcon, CalendarDaysIcon, TruckIcon, GraduationCapIcon, PartyPopperIcon, TabletSmartphoneIcon]
|
|
const INITIATIVE = "Initiativ bewerben"
|
|
|
|
function Eyebrow({ children }: { children: string }) {
|
|
return <p className="mb-4 inline-block -rotate-1 rounded-lg bg-t-accent px-3 py-1 text-sm font-bold text-t-accent-fg">{children}</p>
|
|
}
|
|
|
|
export function KarriereTemplate({ content }: TemplateProps<KarriereData>) {
|
|
const { brand, hero, benefits, facts, jobs, voices, process, apply } = content.data
|
|
const show = (key: string) => !content.hidden.includes(key)
|
|
const [job, setJob] = useState<string | null>(null)
|
|
const links = [
|
|
{ href: "#benefits", label: "Benefits", visible: show("benefits") },
|
|
{ href: "#stellen", label: "Stellen", visible: true },
|
|
{ href: "#team", label: "Team", visible: show("voices") },
|
|
].filter((link) => link.visible)
|
|
|
|
return (
|
|
<TemplateShell def={karriere} content={content} fonts={fonts[content.fonts] ?? fonts.freundlich}>
|
|
<Nav name={brand.name} links={links} />
|
|
<Hero hero={hero} openJobs={jobs.items.length} />
|
|
|
|
{show("benefits") && (
|
|
<section id="benefits" className={cn(container, "py-20 md:py-28")}>
|
|
<Reveal><Eyebrow>{benefits.eyebrow}</Eyebrow></Reveal>
|
|
<RevealWords text={benefits.title} emClassName={em} className={cn(heading, "text-[clamp(2.4rem,5vw,4.5rem)] leading-[1]")} />
|
|
<div className="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
{benefits.items.map((item, i) => {
|
|
const Icon = icons[i % icons.length]
|
|
return (
|
|
<Reveal key={i} delay={(i % 3) * 0.08}>
|
|
<motion.article
|
|
whileHover={{ rotate: i % 2 ? 1.5 : -1.5, y: -4 }}
|
|
transition={{ type: "spring", stiffness: 300, damping: 18 }}
|
|
className="h-full rounded-3xl border-2 border-t-fg/10 bg-t-bg p-7 transition-colors hover:border-t-accent"
|
|
>
|
|
<span className="grid size-12 place-items-center rounded-2xl bg-t-accent text-t-accent-fg">
|
|
<Icon className="size-6" />
|
|
</span>
|
|
<h3 className={cn(heading, "mt-5 text-xl")}>{item.title}</h3>
|
|
<p className="mt-2 text-t-muted">{item.text}</p>
|
|
</motion.article>
|
|
</Reveal>
|
|
)
|
|
})}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{show("facts") && (
|
|
<section className="bg-t-fg py-14 text-t-bg">
|
|
<dl className={cn(container, "grid grid-cols-2 gap-8 md:grid-cols-4")}>
|
|
{facts.items.map((fact, i) => (
|
|
<div key={i}>
|
|
<dd className={cn(heading, "text-5xl md:text-6xl")}>
|
|
<Counter value={fact.value} />
|
|
<span className="text-t-accent">{fact.suffix}</span>
|
|
</dd>
|
|
<dt className="mt-1 text-sm opacity-70">{fact.label}</dt>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
</section>
|
|
)}
|
|
|
|
<Jobs jobs={jobs} onApply={setJob} />
|
|
|
|
{show("voices") && (
|
|
<section id="team" className="bg-t-surface py-20 md:py-28">
|
|
<div className={container}>
|
|
<Reveal><Eyebrow>{voices.eyebrow}</Eyebrow></Reveal>
|
|
<RevealWords text={voices.title} emClassName={em} className={cn(heading, "text-[clamp(2.4rem,5vw,4.5rem)] leading-[1]")} />
|
|
<div className="mt-12 grid gap-5 md:grid-cols-3">
|
|
{voices.items.map((voice, i) => (
|
|
<Reveal key={i} delay={i * 0.1}>
|
|
<figure className="group h-full overflow-hidden rounded-3xl bg-t-bg">
|
|
<div className="relative aspect-[4/3] overflow-hidden">
|
|
<TImage src={voice.image} alt={voice.name} sizes="(min-width: 768px) 33vw, 100vw" className="transition-transform duration-700 group-hover:scale-105" />
|
|
<span className="absolute bottom-4 left-4 grid size-12 place-items-center rounded-full bg-t-accent text-t-accent-fg shadow-lg transition-transform group-hover:scale-110">
|
|
<PlayIcon className="ml-0.5 size-5 fill-current" />
|
|
</span>
|
|
</div>
|
|
<blockquote className="p-6 text-lg leading-snug">„{voice.quote}“</blockquote>
|
|
<figcaption className="px-6 pb-6 text-sm">
|
|
<span className="font-bold">{voice.name}</span> <span className="text-t-muted">· {voice.role}</span>
|
|
</figcaption>
|
|
</figure>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{show("process") && (
|
|
<section className={cn(container, "py-20 md:py-28")}>
|
|
<Reveal><Eyebrow>{process.eyebrow}</Eyebrow></Reveal>
|
|
<RevealWords text={process.title} emClassName={em} className={cn(heading, "text-[clamp(2.4rem,5vw,4.5rem)] leading-[1]")} />
|
|
<ol className="relative mt-14 grid gap-10 md:grid-cols-4 md:gap-6">
|
|
<span aria-hidden className="absolute top-7 right-[12%] left-[12%] hidden border-t-2 border-dashed border-t-fg/20 md:block" />
|
|
{process.items.map((step, i) => (
|
|
<Reveal key={i} delay={i * 0.12}>
|
|
<li className="relative md:text-center">
|
|
<span className={cn(heading, "relative grid size-14 place-items-center rounded-full bg-t-accent text-xl text-t-accent-fg md:mx-auto")}>{i + 1}</span>
|
|
<p className={cn(heading, "mt-5 text-xl")}>{step.title}</p>
|
|
<p className="mt-1 text-t-muted">{step.text}</p>
|
|
</li>
|
|
</Reveal>
|
|
))}
|
|
</ol>
|
|
</section>
|
|
)}
|
|
|
|
<Apply apply={apply} jobs={jobs.items} job={job} onJob={setJob} />
|
|
|
|
<footer className={cn(container, "flex flex-col gap-4 border-t border-t-fg/10 py-10 text-sm text-t-muted md:flex-row md:justify-between")}>
|
|
<p>{brand.name} · {brand.suffix} · {brand.email}</p>
|
|
<div className="flex gap-6">
|
|
<a href="#" className="hover:text-t-fg">Impressum</a>
|
|
<a href="#" className="hover:text-t-fg">Datenschutz</a>
|
|
</div>
|
|
</footer>
|
|
</TemplateShell>
|
|
)
|
|
}
|
|
|
|
function Nav({ name, links }: { name: string; links: { href: string; label: string }[] }) {
|
|
const { scrolled, hidden } = useScrollNav()
|
|
|
|
return (
|
|
<motion.header
|
|
animate={{ y: hidden ? "-100%" : "0%" }}
|
|
transition={{ duration: 0.45, ease }}
|
|
className={cn("fixed inset-x-0 top-0 z-50 border-b transition-colors duration-500", scrolled ? "border-t-fg/10 bg-t-bg/90 backdrop-blur-xl" : "border-transparent")}
|
|
>
|
|
<nav className={cn(container, "flex h-20 items-center justify-between gap-6")}>
|
|
<a href="#top" className="flex items-center gap-2">
|
|
<span className={cn(heading, "text-xl")}>{name}</span>
|
|
<span className="rounded-full bg-t-accent px-2.5 py-0.5 text-xs font-bold text-t-accent-fg">Karriere</span>
|
|
</a>
|
|
<div className="hidden items-center gap-8 font-medium md:flex">
|
|
{links.map((link) => (
|
|
<a key={link.href} href={link.href} className="text-t-fg/70 transition-colors hover:text-t-fg">{link.label}</a>
|
|
))}
|
|
</div>
|
|
<a href="#bewerben" className="rounded-full bg-t-fg px-5 py-2.5 text-sm font-bold text-t-bg transition-transform hover:scale-105">Jetzt bewerben</a>
|
|
</nav>
|
|
</motion.header>
|
|
)
|
|
}
|
|
|
|
function Hero({ hero, openJobs }: { hero: KarriereData["hero"]; openJobs: number }) {
|
|
const roles = splitList(hero.roles)
|
|
const [index, setIndex] = useState(0)
|
|
|
|
useEffect(() => {
|
|
if (roles.length < 2) return
|
|
const timer = setInterval(() => setIndex((i) => i + 1), 2200)
|
|
return () => clearInterval(timer)
|
|
}, [roles.length])
|
|
|
|
const role = roles.length ? roles[index % roles.length] : ""
|
|
|
|
return (
|
|
<section id="top" className={cn(container, "grid items-center gap-14 pt-32 pb-20 lg:grid-cols-[1.1fr_1fr] lg:pt-40")}>
|
|
<div>
|
|
<motion.p initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, ease }} className="mb-6 inline-flex items-center gap-2 rounded-full border-2 border-t-fg/10 px-4 py-1.5 text-sm font-medium">
|
|
<span className="size-2 animate-pulse rounded-full bg-emerald-500" /> {hero.eyebrow}
|
|
</motion.p>
|
|
<RevealWords as="h1" onMount delay={0.15} text={hero.title} emClassName={em} className={cn(heading, "text-[clamp(3.25rem,8vw,7.5rem)] leading-[0.92]")} />
|
|
{role && (
|
|
<div className={cn(heading, "mt-6 flex flex-wrap items-center gap-3 text-2xl md:text-3xl")}>
|
|
<span className="text-t-muted">Wir suchen</span>
|
|
<span className="relative inline-flex h-[1.4em] overflow-hidden rounded-xl bg-t-accent px-3 text-t-accent-fg">
|
|
<AnimatePresence mode="popLayout" initial={false}>
|
|
<motion.span key={role} initial={{ y: "100%" }} animate={{ y: "0%" }} exit={{ y: "-100%" }} transition={{ duration: 0.45, ease }} className="leading-[1.4em]">
|
|
{role}
|
|
</motion.span>
|
|
</AnimatePresence>
|
|
</span>
|
|
</div>
|
|
)}
|
|
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, ease, delay: 0.6 }}>
|
|
<p className="mt-8 max-w-lg text-lg leading-relaxed text-t-muted">{hero.text}</p>
|
|
<div className="mt-9 flex flex-wrap gap-3">
|
|
<a href="#stellen" className="inline-flex items-center gap-2 rounded-full bg-t-fg px-7 py-4 font-bold text-t-bg transition-transform hover:scale-105">
|
|
{hero.cta} <span className="rounded-full bg-t-accent px-2 text-sm text-t-accent-fg">{openJobs}</span>
|
|
</a>
|
|
<a href="#bewerben" className="inline-flex items-center gap-2 rounded-full border-2 border-t-fg px-7 py-4 font-bold transition-colors hover:bg-t-fg hover:text-t-bg">
|
|
{hero.ctaSecondary} <ArrowRightIcon className="size-4" />
|
|
</a>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
|
|
<div className="relative mx-auto aspect-square w-full max-w-[560px]">
|
|
{[
|
|
{ src: hero.imageSecondary, className: "top-0 left-0 w-[46%] -rotate-6", delay: 0.4 },
|
|
{ src: hero.image, className: "top-[12%] right-0 w-[58%] rotate-3", delay: 0.25 },
|
|
{ src: hero.imageThird, className: "bottom-0 left-[14%] w-[52%] -rotate-2", delay: 0.55 },
|
|
].map((photo, i) => (
|
|
<motion.div
|
|
key={i}
|
|
initial={{ opacity: 0, y: 40, rotate: 0 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.9, ease, delay: photo.delay }}
|
|
className={cn("absolute aspect-[4/5] overflow-hidden rounded-3xl border-[6px] border-t-bg bg-t-surface shadow-xl", photo.className)}
|
|
>
|
|
<TImage src={photo.src} eager={i === 1} sizes="(min-width: 1024px) 30vw, 60vw" />
|
|
</motion.div>
|
|
))}
|
|
<motion.div
|
|
initial={{ scale: 0 }}
|
|
animate={{ scale: 1, rotate: [0, 360] }}
|
|
transition={{ scale: { type: "spring", bounce: 0.4, delay: 1 }, rotate: { duration: 22, repeat: Infinity, ease: "linear" } }}
|
|
className="absolute right-[6%] bottom-[8%] grid size-28 place-items-center rounded-full bg-t-accent p-3 text-center text-sm leading-tight font-black text-t-accent-fg shadow-xl md:size-32"
|
|
>
|
|
{hero.badge}
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
function Jobs({ jobs, onApply }: { jobs: KarriereData["jobs"]; onApply: (job: string) => void }) {
|
|
const types = ["Alle", ...new Set(jobs.items.map((item) => item.type))]
|
|
const [filter, setFilter] = useState("Alle")
|
|
const [open, setOpen] = useState<number | null>(0)
|
|
const visible = jobs.items.map((item, i) => ({ item, i })).filter(({ item }) => filter === "Alle" || item.type === filter)
|
|
|
|
return (
|
|
<section id="stellen" className={cn(container, "py-20 md:py-28")}>
|
|
<div className="flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
|
<div>
|
|
<Reveal><Eyebrow>{jobs.eyebrow}</Eyebrow></Reveal>
|
|
<RevealWords text={jobs.title} emClassName={em} className={cn(heading, "text-[clamp(2.4rem,5vw,4.5rem)] leading-[1]")} />
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{types.map((type) => (
|
|
<button
|
|
key={type}
|
|
type="button"
|
|
aria-pressed={filter === type}
|
|
onClick={() => setFilter(type)}
|
|
className={cn("cursor-pointer rounded-full border-2 px-4 py-2 text-sm font-bold transition-colors", filter === type ? "border-t-fg bg-t-fg text-t-bg" : "border-t-fg/15 hover:border-t-fg")}
|
|
>
|
|
{type}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<ul className="mt-10 space-y-3">
|
|
{visible.map(({ item, i }) => {
|
|
const isOpen = open === i
|
|
return (
|
|
<motion.li layout key={item.title} className={cn("overflow-hidden rounded-3xl border-2 transition-colors", isOpen ? "border-t-fg" : "border-t-fg/10")}>
|
|
<button type="button" aria-expanded={isOpen} onClick={() => setOpen(isOpen ? null : i)} className="flex w-full cursor-pointer items-center gap-4 p-5 text-left md:p-7">
|
|
<span className="hidden size-12 shrink-0 place-items-center rounded-2xl bg-t-surface sm:grid">
|
|
<BriefcaseIcon className="size-5" />
|
|
</span>
|
|
<span className="min-w-0 flex-1">
|
|
<span className={cn(heading, "block text-lg md:text-2xl")}>{item.title}</span>
|
|
<span className="mt-2 flex flex-wrap gap-2 text-xs font-bold">
|
|
<span className="rounded-full bg-t-accent px-2.5 py-1 text-t-accent-fg">{item.type}</span>
|
|
<span className="rounded-full bg-t-surface px-2.5 py-1">{item.area}</span>
|
|
<span className="flex items-center gap-1 rounded-full bg-t-surface px-2.5 py-1"><MapPinIcon className="size-3" /> {item.location}</span>
|
|
</span>
|
|
</span>
|
|
<PlusIcon className={cn("size-6 shrink-0 transition-transform duration-300", isOpen && "rotate-45")} />
|
|
</button>
|
|
<AnimatePresence initial={false}>
|
|
{isOpen && (
|
|
<motion.div initial={{ height: 0 }} animate={{ height: "auto" }} exit={{ height: 0 }} transition={{ duration: 0.4, ease }}>
|
|
<div className="flex flex-col gap-6 px-5 pb-6 md:flex-row md:items-end md:justify-between md:px-7 md:pb-7">
|
|
<ul className="space-y-2">
|
|
{splitList(item.tasks, "\n").map((task) => (
|
|
<li key={task} className="flex items-start gap-2.5">
|
|
<CheckIcon className="mt-1 size-4 shrink-0 text-t-accent" /> {task}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<a href="#bewerben" onClick={() => onApply(item.title)} className="inline-flex shrink-0 items-center gap-2 rounded-full bg-t-accent px-6 py-3.5 font-bold text-t-accent-fg">
|
|
Jetzt bewerben <ArrowRightIcon className="size-4" />
|
|
</a>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</motion.li>
|
|
)
|
|
})}
|
|
</ul>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
function Apply({ apply, jobs, job, onJob }: { apply: KarriereData["apply"]; jobs: KarriereData["jobs"]["items"]; job: string | null; onJob: (job: string | null) => void }) {
|
|
const [step, setStep] = useState(0)
|
|
const [experience, setExperience] = useState<string | null>(null)
|
|
const [done, setDone] = useState(false)
|
|
const levels = splitList(apply.experience)
|
|
const current = job && step === 0 ? 1 : step
|
|
const progress = done ? 100 : ((current + 1) / 3) * 100
|
|
|
|
return (
|
|
<section id="bewerben" className="bg-t-accent py-20 text-t-accent-fg md:py-28">
|
|
<div className={cn(container, "grid items-center gap-12 lg:grid-cols-2")}>
|
|
<div>
|
|
<RevealWords text={apply.title} emClassName="underline decoration-4 underline-offset-8" className={cn(heading, "text-[clamp(2.6rem,6vw,5.5rem)] leading-[0.95]")} />
|
|
<Reveal delay={0.1}><p className="mt-6 max-w-md text-lg opacity-80">{apply.text}</p></Reveal>
|
|
</div>
|
|
<div className="relative rounded-[2rem] bg-t-bg p-6 text-t-fg shadow-2xl md:p-9">
|
|
<div className="mb-8 h-2 overflow-hidden rounded-full bg-t-surface">
|
|
<motion.div className="h-full rounded-full bg-t-accent" animate={{ width: `${progress}%` }} transition={{ duration: 0.5, ease }} />
|
|
</div>
|
|
{done ? (
|
|
<div className="relative py-10 text-center">
|
|
{Array.from({ length: 18 }, (_, i) => {
|
|
const angle = (i / 18) * Math.PI * 2
|
|
return (
|
|
<motion.span
|
|
key={i}
|
|
aria-hidden
|
|
className={cn("absolute top-1/2 left-1/2 size-2.5 rounded-sm", i % 3 === 0 ? "bg-t-accent" : i % 3 === 1 ? "bg-t-fg" : "bg-t-muted")}
|
|
initial={{ x: 0, y: 0, opacity: 1, rotate: 0 }}
|
|
animate={{ x: Math.cos(angle) * 160, y: Math.sin(angle) * 110, opacity: 0, rotate: 200 }}
|
|
transition={{ duration: 1.2, ease: "easeOut" }}
|
|
/>
|
|
)
|
|
})}
|
|
<p className={cn(heading, "text-4xl")}>Stark! 🎉</p>
|
|
<p className="mt-3 text-t-muted">Wir melden uns innerhalb von 48 Stunden bei dir.</p>
|
|
<p className="mt-6 text-sm opacity-60">Demo-Hinweis: Live landet die Bewerbung direkt im Postfach der Personalabteilung.</p>
|
|
</div>
|
|
) : (
|
|
<StepPanel step={current}>
|
|
{current === 0 && (
|
|
<div>
|
|
<p className={cn(heading, "text-2xl")}>Welche Stelle?</p>
|
|
<div className="mt-5 grid gap-2">
|
|
{[...jobs.map((item) => item.title), INITIATIVE].map((title) => (
|
|
<button
|
|
key={title}
|
|
type="button"
|
|
onClick={() => {
|
|
onJob(title)
|
|
setStep(1)
|
|
}}
|
|
className="flex cursor-pointer items-center justify-between rounded-2xl border-2 border-t-fg/10 px-4 py-3.5 text-left font-medium transition-colors hover:border-t-accent"
|
|
>
|
|
{title} <ArrowRightIcon className="size-4 shrink-0" />
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{current === 1 && (
|
|
<div>
|
|
<p className="text-sm text-t-muted">{job}</p>
|
|
<p className={cn(heading, "mt-1 text-2xl")}>Wie viel Erfahrung bringst du mit?</p>
|
|
<div className="mt-5 grid grid-cols-2 gap-2">
|
|
{levels.map((level) => (
|
|
<button
|
|
key={level}
|
|
type="button"
|
|
onClick={() => {
|
|
setExperience(level)
|
|
setStep(2)
|
|
}}
|
|
className={cn("cursor-pointer rounded-2xl border-2 px-4 py-5 font-bold transition-colors", experience === level ? "border-t-accent bg-t-accent text-t-accent-fg" : "border-t-fg/10 hover:border-t-accent")}
|
|
>
|
|
{level}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
onJob(null)
|
|
setStep(0)
|
|
}}
|
|
className="mt-5 cursor-pointer text-sm text-t-muted hover:text-t-fg"
|
|
>
|
|
Andere Stelle wählen
|
|
</button>
|
|
</div>
|
|
)}
|
|
{current === 2 && (
|
|
<form
|
|
className="grid gap-4"
|
|
onSubmit={(event) => {
|
|
event.preventDefault()
|
|
setDone(true)
|
|
}}
|
|
>
|
|
<p className={cn(heading, "text-2xl")}>Wie erreichen wir dich?</p>
|
|
<TField label="Vorname und Name" name="name" required autoComplete="name" variant="soft" />
|
|
<TField label="Handynummer" name="phone" type="tel" required autoComplete="tel" variant="soft" />
|
|
<TSelect label="Wann passt ein Anruf?" name="time" options={["Vormittags", "Mittags", "Nachmittags", "Abends"]} variant="soft" />
|
|
<button type="submit" className="mt-2 inline-flex cursor-pointer items-center justify-center gap-2 rounded-full bg-t-fg py-4 font-bold text-t-bg">
|
|
Bewerbung abschicken <ArrowRightIcon className="size-4" />
|
|
</button>
|
|
<button type="button" onClick={() => setStep(1)} className="cursor-pointer text-sm text-t-muted hover:text-t-fg">Zurück</button>
|
|
</form>
|
|
)}
|
|
</StepPanel>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|