/* @jsx React.createElement */ // Sub-page components (services, portfolio, contact) + shared bits. const { useState: useStateS, useEffect: useEffectS } = React; // ============================================================ // Page header (sub-page hero) // ============================================================ function PageHeader({ eye, title, sub }) { return (
{eye}

{title}

{sub &&

{sub}

}
); } // ============================================================ // SERVICES DETAIL PAGE — long sections per service // ============================================================ const SERVICE_DETAILS = [ { id: "web", n: "01", title: "Web Development", sub: "Landing · Marketing · Brand", desc: "Website yang loading-nya cepet, mobile-friendly, dan gak bikin tim brand kamu pusing pas mau update konten.", deliver: [ "Design hi-fi (mobile + desktop)", "Build dengan stack modern (Next.js / Astro)", "CMS sederhana (Sanity / Notion-as-CMS)", "Setup analytics + SEO basic", "Deploy ke Vercel / Cloudflare", ], stack: ["Next.js", "Astro", "Tailwind", "Sanity CMS", "Vercel"], timeline: "2–4 minggu", starting: "Rp 12.500.000", examples: ["Brand site", "Portfolio agency", "Campaign micro-site", "Multi-language ID/EN/JP"], color: "var(--labs-accent)", }, { id: "webapp", n: "02", title: "Web App", sub: "POS · Dashboard · Internal tools", desc: "Aplikasi internal yang harian. Bukan demo cantik — tools yang benar-benar bikin tim kamu kerja lebih cepat.", deliver: [ "Discovery: mapping user flow & data model", "Wireframe → mockup → prototype", "Build dengan auth, role, audit log", "Integrasi printer thermal / payment / WA", "Training tim + dokumentasi", ], stack: ["Next.js", "Supabase", "PostgreSQL", "Prisma", "Stripe / Midtrans"], timeline: "6–12 minggu", starting: "Rp 35.000.000", examples: ["POS cafe & restoran", "Dashboard sales", "CRM internal", "Inventory & supplier"], color: "var(--indie-cyan-500)", }, { id: "ai", n: "03", title: "AI Integration", sub: "Chatbot · Automation · LLM", desc: "Kamu udah punya prompt yang work. Notebook yang akurat. Kita bantu kemas jadi produk hidup yang dipakai customer harian.", deliver: [ "Audit prototype kamu (notebook / n8n / Make)", "Frontend: chat, form, wizard, dashboard", "Backend: auth, rate-limit, billing, log", "Prompt management & evaluasi", "Hosting + monitoring + cost tracking", ], stack: ["Claude / GPT", "Vercel AI SDK", "LangChain", "Pinecone / pgvector", "Inngest"], timeline: "4–10 minggu", starting: "Rp 25.000.000", examples: ["Chatbot konsultasi WA", "Auto-tag CRM", "Q&A from PDF", "AI writing assistant internal"], color: "var(--indie-orange-500)", }, { id: "consult", n: "04", title: "IT Consultant", sub: "Tech stack · Architecture · Audit", desc: "Kadang yang dibutuhin bukan tim baru — cuma second opinion yang jujur. Half-day session, jelas, actionable.", deliver: [ "Pre-call: kamu kirim konteks 1 halaman", "Sesi 3-4 jam (Zoom atau Jakarta on-site)", "Tech stack & architecture review", "Cost audit (cloud, SaaS, vendor)", "Written report + action items", ], stack: ["Cloud audit", "DB review", "Codebase walkthrough", "Vendor selection", "Hiring plan"], timeline: "1–2 minggu", starting: "Rp 2.500.000", examples: ["Founder pre-MVP review", "Migrasi WP → headless", "Audit infra Google Cloud", "Hiring CTO / tech lead"], color: "var(--indie-cyan-500)", }, ]; function ServiceDetailPage() { return ( <> Empat jalan,
dikerjakan teliti.} sub="Tiap jalan punya scope yang jelas — timeline, tahap, biaya. Pilih satu yang paling dekat dengan posisi kamu sekarang, atau ngobrol dulu sama kami." />
{SERVICE_DETAILS.map((s, i) => ( ))}
Empat tahap.
Empat minggu pertama jelas.} desc="Kami pegang transparansi sebagai default — kamu akan tahu kapan keputusan ada di tangan kamu." />
{PROCESS.map((p) => (
{p.n}

{p.t}

{p.j}

{p.d}

))}
); } function ServiceBlock({ s, flip }) { return (
{s.n}
{s.sub}

{s.title}

{s.desc}

Diskusi {s.title} →
// what you get
    {s.deliver.map((d) => (
  • {d}
  • ))}
// usually built with
{s.stack.map((x) => ( {x} ))}
// example projects
{s.examples.map((x) => ( {x} ))}
); } function Meta({ label, value, mono }) { return (
{label}
{value}
); } // ============================================================ // PORTFOLIO PAGE — placeholder grid + filter // ============================================================ const WORKS = [ { tag: "POS", title: "Order & kitchen display untuk specialty cafe", client: "Cafe Hira · Bandung", year: "2026", color: "var(--indie-orange-500)", brand: "indiecafe", category: "Web App" }, { tag: "AI", title: "Chatbot konsultasi visa via WhatsApp", client: "Visa & Co · Jakarta", year: "2026", color: "var(--labs-accent)", brand: "indielabs", category: "AI Integration" }, { tag: "Dashboard", title: "Inventory & sales tracker untuk UKM packaging", client: "Pak Made Pack · Bali", year: "2025", color: "var(--indie-cyan-500)", brand: "indiepack", category: "Web App" }, { tag: "Web", title: "Brand site multi-language untuk butik renovasi", client: "INDIE Ruma", year: "2025", color: "var(--indie-cyan-500)", brand: "indieruma", category: "Web" }, { tag: "AI", title: "Auto-tag CRM customer dengan LLM", client: "Kost Network ID", year: "2025", color: "var(--indie-cyan-500)", brand: "indiekost", category: "AI Integration" }, { tag: "Web", title: "Campaign micro-site Yappari! vending", client: "Yappari!", year: "2024", color: "var(--indie-yellow-400)", brand: "yappari", category: "Web" }, { tag: "Consult", title: "Tech stack audit + hiring plan founder F&B", client: "Anonymous (NDA)", year: "2024", color: "var(--indie-orange-500)", brand: "indiebrand", category: "Consult" }, { tag: "Web App", title: "Booking & schedule untuk music school", client: "INDIE Music School", year: "2024", color: "var(--indie-red-500)", brand: "ims", category: "Web App" }, { tag: "Web", title: "Portfolio + shop untuk paper goods studio", client: "indiepaper", year: "2024", color: "var(--indie-yellow-400)", brand: "indiepaper", category: "Web" }, ]; const CATEGORIES = ["All", "Web", "Web App", "AI Integration", "Consult"]; function PortfolioPage() { const [cat, setCat] = useStateS("All"); const filtered = cat === "All" ? WORKS : WORKS.filter((w) => w.category === cat); return ( <> Brand yang sudah
jadi versi-nya.} sub="Sebagian besar di bawah NDA — yang di sini bisa kami pamerkan. Klik untuk baca cerita siapa mereka sebelum dan sesudah. Yang lain bisa diceritain pas ngobrol." />
// filter {CATEGORIES.map((c) => ( ))}
{filtered.length} {filtered.length === 1 ? "project" : "projects"}
{filtered.map((w, i) => )}
// next

Mau jadi case berikutnya?

Ngobrol dulu sambil ngopi. Kami dengerin, kasih opini, dan kalau cocok — bangun bareng.

Mulai diskusi →
); } function WorkCard({ w }) { return (
{ e.currentTarget.style.transform = "translateY(-2px)"; }} onMouseLeave={(e) => { e.currentTarget.style.transform = "translateY(0)"; }}>
{w.tag}
{w.year}
{w.title}
{w.client}
// {w.category} Read →
); } // ============================================================ // CONTACT PAGE — multi-step brief form // ============================================================ const CONTACT_SERVICES = [ { id: "web", label: "Website", desc: "Landing / brand / portfolio" }, { id: "webapp", label: "Web App", desc: "POS, dashboard, internal tools" }, { id: "ai", label: "AI Integration", desc: "Chatbot, automation, LLM" }, { id: "consult", label: "Consultation", desc: "Half-day audit & opinion" }, { id: "other", label: "Not sure yet", desc: "Let's figure it out together" }, ]; const BUDGETS = ["< 15jt", "15–35jt", "35–100jt", "100jt+", "Belum tau"]; const TIMELINES = ["ASAP (<1 bln)", "1–3 bulan", "3–6 bulan", "Fleksibel"]; function ContactPage() { const [step, setStep] = useStateS(1); const [data, setData] = useStateS({ service: "", name: "", email: "", company: "", budget: "", timeline: "", brief: "", }); const [sent, setSent] = useStateS(false); const set = (k, v) => setData({ ...data, [k]: v }); const canNext1 = !!data.service; const canNext2 = data.name.trim() && data.email.trim().includes("@"); const canSend = data.brief.trim().length > 10; const submit = () => { setSent(true); // In real prod we'd POST. For prototype, open WA with prefilled message. const lines = [ `Halo INDIE Labs, saya tertarik diskusi proyek.`, ``, `Service: ${CONTACT_SERVICES.find(s => s.id === data.service)?.label}`, `Nama: ${data.name}`, `Email: ${data.email}`, data.company && `Company: ${data.company}`, `Budget: ${data.budget || "—"}`, `Timeline: ${data.timeline || "—"}`, ``, `Brief:`, data.brief, ].filter(Boolean).join("\n"); setTimeout(() => { window.open(WA_LINK(lines), "_blank"); }, 600); }; if (sent) { return ( <>

Kami akan balas dalam 1×24 jam.

Sambil nunggu, mampir portfolio kami dulu.

Back to home
); } return ( <> 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 ? ( ) : } {step < 3 ? ( ) : ( )}
); } 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) => (
  1. 0{i+1} {s[0]}
  2. ))}
); } function ContactRow({ icon, label, sub, href }) { const icons = { wa: , mail: , pin: , }; const inner = ( <>
{icons[icon]}
{label}
{sub &&
{sub}
}
); 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) => ( ))}
); } 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?