Ceritain mau jadi siapa.>}
sub="3 langkah, kurang dari 2 menit. Brief masuk langsung ke kotak masuk kami — bukan robot, bukan auto-responder. Dijawab sama orang yang akan ngerjain proyek kamu."
/>
{step === 1 && (
)}
{step === 2 && (
)}
{step === 3 && (
)}
{step > 1 ? (
setStep(step - 1)} className="btn btn-ghost btn-sm">← Kembali
) : }
{step < 3 ? (
setStep(step + 1)} className="btn btn-primary"
disabled={(step === 1 && !canNext1) || (step === 2 && !canNext2)}
style={{ opacity: ((step === 1 && !canNext1) || (step === 2 && !canNext2)) ? 0.4 : 1 }}>
Lanjut →
) : (
Kirim brief →
)}
>
);
}
function ContactSide() {
return (
// or just reach out
// what happens next
{[
["Kami baca brief kamu (1×24 jam)"],
["Reply via WA/email — minta klarifikasi kalau perlu"],
["Booking discovery call 30-60 menit (free)"],
["Kirim proposal + estimasi tertulis"],
].map((s, i) => (
0{i+1}
{s[0]}
))}
);
}
function ContactRow({ icon, label, sub, href }) {
const icons = {
wa: ,
mail: ,
pin: ,
};
const inner = (
<>
{icons[icon]}
>
);
return href ? (
{inner}
) : (
{inner}
);
}
function StepBar({ step, total }) {
return (
// step {step} of {total}
{Array.from({ length: total }).map((_, i) => (
))}
);
}
function Step1({ data, set }) {
return (
<>
Apa yang mau dibangun?
Pilih yang paling dekat. Bisa diubah nanti pas diskusi.
{CONTACT_SERVICES.map((s) => (
set("service", s.id)}
style={{
all: "unset", cursor: "pointer",
display: "flex", alignItems: "center", gap: 14,
padding: "16px 18px", borderRadius: 8,
border: `1.5px solid ${data.service === s.id ? "var(--labs-accent)" : "var(--labs-line-2)"}`,
background: data.service === s.id ? "rgba(240,208,80,0.08)" : "transparent",
transition: "all 150ms var(--ease-out)",
}}>
{data.service === s.id && }
))}
>
);
}
function Step2({ data, set }) {
return (
<>
Kenalan dulu.
Biar kami bisa nyebut nama kamu pas balas.
set("name", v)} placeholder="Indra" />
set("email", v)} placeholder="kamu@email.com" />
set("company", v)} placeholder="Cafe Hira" />
>
);
}
function Step3({ data, set }) {
return (
<>
Sedikit konteks.
Budget & timeline kami pakai buat assess fit — bukan untuk nge-judge.
Budget
{BUDGETS.map((b) => (
set("budget", b)} />
))}
Timeline
{TIMELINES.map((b) => (
set("timeline", b)} />
))}
Brief — apa yang mau dibangun?
>
);
}
function Field({ label, value, onChange, placeholder, type = "text" }) {
return (
{label}
onChange(e.target.value)} placeholder={placeholder} />
);
}
function Chip({ label, active, onClick }) {
return (
{label}
);
}
Object.assign(window, {
PageHeader, ServiceDetailPage, PortfolioPage, ContactPage, WorkCard,
SERVICE_DETAILS, WORKS, CATEGORIES, CONTACT_SERVICES, BUDGETS, TIMELINES,
});