[feat] landing: 카피 정직화(가이드 3탭·강화학습·이점)·실단가 반영, 비교 토글·FAQ 섹션 신설, 협상 콘솔 목표가 스파크라인·절감 델타 추가, 헤더 플리커·히어로 줄바꿈 수정
This commit is contained in:
parent
e93820a51b
commit
9ccf42cf6b
166
landing/app/components/sections/comparison.tsx
Normal file
166
landing/app/components/sections/comparison.tsx
Normal file
@ -0,0 +1,166 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { AnimatePresence, motion } from "motion/react"
|
||||
import { Bot, Clock, Database, Handshake, Scale, TrendingUp, UserRound, type LucideIcon } from "lucide-react"
|
||||
|
||||
import { Section } from "@/components/ui/section"
|
||||
import { SectionHeading } from "@/components/ui/section-heading"
|
||||
import { Typography } from "@/components/ui/typography"
|
||||
import { useFadeUp } from "@/lib/motion"
|
||||
|
||||
/** 기존 방식 ↔ 도입 후 토글 비교 — 화면 진입 시 자동으로 오가고, 사용자가 누르면 수동 고정. */
|
||||
export function Comparison() {
|
||||
const fadeUp = useFadeUp()
|
||||
const toggleFadeUp = useFadeUp(0.1)
|
||||
const [mode, setMode] = useState<Mode>("before")
|
||||
// 사용자가 직접 토글을 누르면 자동 전환 중단
|
||||
const [locked, setLocked] = useState(false)
|
||||
const [inView, setInView] = useState(false)
|
||||
const sectionRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const el = sectionRef.current
|
||||
if (!el) return
|
||||
const observer = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), { threshold: 0.35 })
|
||||
observer.observe(el)
|
||||
return () => observer.disconnect()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!inView || locked) return
|
||||
const timer = setInterval(() => setMode((m) => (m === "before" ? "after" : "before")), 3000)
|
||||
return () => clearInterval(timer)
|
||||
}, [inView, locked])
|
||||
|
||||
return (
|
||||
<Section id="comparison" bordered>
|
||||
<motion.div {...fadeUp} className="mb-12">
|
||||
<SectionHeading
|
||||
align="center"
|
||||
eyebrow="WHY NEGOTIUM"
|
||||
title="협상 방식을 바꾸면, 무엇이 달라질까요"
|
||||
description="기존 가격 협상의 문제는 사람이 아니라 구조입니다. 버튼을 눌러 직접 비교해 보세요."
|
||||
descriptionClassName="mt-4 text-[17px] max-w-2xl"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* 모드 토글 — 이용 가이드 탭과 같은 세그먼트 문법 */}
|
||||
<motion.div {...toggleFadeUp} className="flex justify-center mb-14">
|
||||
<div className="p-1 bg-fill rounded-[14px] inline-flex gap-1">
|
||||
{MODES.map((m) => (
|
||||
<button
|
||||
key={m.key}
|
||||
onClick={() => {
|
||||
setLocked(true)
|
||||
setMode(m.key)
|
||||
}}
|
||||
className={`px-5 py-2.5 rounded-[10px] text-xs sm:text-sm font-bold transition-all flex items-center gap-2 cursor-pointer ${
|
||||
mode === m.key ? "bg-white text-primary shadow-sm" : "text-ink-soft hover:text-ink"
|
||||
}`}
|
||||
>
|
||||
{m.key === "after" && <Bot className="w-4 h-4" />}
|
||||
<span>{m.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div ref={sectionRef} className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{ITEMS.map((item, idx) => (
|
||||
<CompareCard key={item.label} item={item} mode={mode} index={idx} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
type Mode = "before" | "after"
|
||||
|
||||
const MODES: { key: Mode; label: string }[] = [
|
||||
{ key: "before", label: "기존 방식" },
|
||||
{ key: "after", label: "negotium 도입 후" },
|
||||
]
|
||||
|
||||
type CompareItem = { label: string; icon: LucideIcon; before: string; after: string }
|
||||
|
||||
function CompareCard({ item, mode, index }: { item: CompareItem; mode: Mode; index: number }) {
|
||||
const fadeUp = useFadeUp(0.06 * (index + 1))
|
||||
const active = mode === "after"
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
{...fadeUp}
|
||||
className={`p-7 rounded-[28px] flex flex-col gap-5 transition-colors duration-500 ${
|
||||
active ? "bg-primary-soft/60" : "bg-surface"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div
|
||||
className={`w-11 h-11 rounded-2xl flex items-center justify-center transition-colors duration-500 ${
|
||||
active ? "bg-primary/10 text-primary" : "bg-ink-muted/10 text-ink-muted"
|
||||
}`}
|
||||
>
|
||||
<item.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<Typography variant="micro" className={active ? "text-primary" : ""}>
|
||||
{item.label}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
{/* 전환 시 레이아웃 점프 방지용 최소 높이 */}
|
||||
<div className="min-h-[72px]">
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={mode}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -8 }}
|
||||
transition={{ duration: 0.28, delay: 0.05 * index }}
|
||||
>
|
||||
<Typography variant="small" className={active ? "text-ink font-semibold" : "text-ink-muted"}>
|
||||
{active ? item.after : item.before}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
const ITEMS: CompareItem[] = [
|
||||
{
|
||||
label: "거래 시간",
|
||||
icon: Clock,
|
||||
before: "일일 거래의 한계 — 사람의 인력과 시간만큼만 협상합니다.",
|
||||
after: "24시간 연중무휴, 전 품목·전 공급사와 동시에 협상합니다.",
|
||||
},
|
||||
{
|
||||
label: "협상 성과",
|
||||
icon: TrendingUp,
|
||||
before: "담당자의 역량·감정·컨디션에 따라 성과가 흔들립니다.",
|
||||
after: "전략 강화 학습으로 기준선이 상승하고, 점진적으로 개선됩니다.",
|
||||
},
|
||||
{
|
||||
label: "노하우",
|
||||
icon: Database,
|
||||
before: "잘 깎는 노하우가 개인의 머릿속에만 남습니다.",
|
||||
after: "모든 협상이 데이터로 축적되어 다음 전략에 재활용됩니다.",
|
||||
},
|
||||
{
|
||||
label: "사람",
|
||||
icon: UserRound,
|
||||
before: "반복 흥정과 감정 노동에 지쳐 고급 인력이 이탈합니다.",
|
||||
after: "감정 소모는 봇이 맡고, 사람은 전략 업무와 대형 거래에 집중합니다.",
|
||||
},
|
||||
{
|
||||
label: "파트너 관계",
|
||||
icon: Handshake,
|
||||
before: "감정 과열과 실랑이로 장기 파트너십이 흔들립니다.",
|
||||
after: "악역은 봇이 — 압박 없는 상시 협상으로 관계가 개선됩니다.",
|
||||
},
|
||||
{
|
||||
label: "의사 결정",
|
||||
icon: Scale,
|
||||
before: "개인 편향과 컴플라이언스 리스크에 노출됩니다.",
|
||||
after: "객관적·데이터 기반의 투명한 프로세스로 기록됩니다.",
|
||||
},
|
||||
]
|
||||
@ -78,7 +78,7 @@ export function Contact() {
|
||||
type="text"
|
||||
name="companyName"
|
||||
required
|
||||
placeholder="예: 주식회사 네고시움"
|
||||
placeholder="예: (주)한빛유통"
|
||||
value={formData.companyName}
|
||||
onChange={handleChange}
|
||||
disabled={submitting}
|
||||
|
||||
@ -17,7 +17,7 @@ export function CoreValues() {
|
||||
align="center"
|
||||
eyebrow="BEYOND EXPECTED VALUE"
|
||||
title="가격 그 이상의 이점"
|
||||
description="단순 업무 긴축을 넘어선 진정한 구매 거버넌스 혁신"
|
||||
description="절감액은 시작일 뿐 — 관계, 투명성, 그리고 사람의 시간까지 지킵니다."
|
||||
descriptionClassName="mt-4 text-[17px]"
|
||||
/>
|
||||
</motion.div>
|
||||
@ -56,24 +56,24 @@ const VALUES: Value[] = [
|
||||
icon: Users,
|
||||
title: "파트너사 관계 수호",
|
||||
description:
|
||||
"단가를 조율하는 과정에서 수반되는 피로감과 실랑이는 온전히 봇이 감당합니다. 담당 부서는 협력사 담당자와 신뢰할 수 있는 인간적 유대를 단단히 하고 동반 성장을 도모하는 큰 틀의 파트너십에만 집중할 수 있습니다.",
|
||||
"단가를 깎는 악역과 감정 소모는 봇이 맡습니다. 담당자는 협력사와의 신뢰와 동반 성장, 큰 틀의 파트너십에만 집중하세요.",
|
||||
},
|
||||
{
|
||||
icon: Lock,
|
||||
title: "완벽하고 투명한 거버넌스",
|
||||
title: "투명한 거버넌스",
|
||||
description:
|
||||
"모든 제안 접수와 흥정 로그, 조율 타임라인이 위변조가 차단된 고유 원장에 실시간 적재됩니다. 감사 대응이나 의사결정 추적 분석 시 완벽에 가까운 거버넌스를 보장합니다.",
|
||||
"모든 제안과 협상 로그, 조율 타임라인이 한 줄도 빠짐없이 기록됩니다. 감사 대응도, 의사결정 추적도 클릭 한 번이면 됩니다.",
|
||||
},
|
||||
{
|
||||
icon: Briefcase,
|
||||
title: "핵심 전략에만 집중",
|
||||
description:
|
||||
"수많은 이메일 교환, 메신저 단가 실랑이 등 정형적이고 반복적인 흥정 수작업에서 구매 부서를 구해냅니다. 공급망 위기 대처, 우량 소스 발굴과 같은 진정 가치 있는 혁신적 과업에 인재들을 투입하세요.",
|
||||
"이메일·메신저로 반복되던 흥정 수작업은 봇이 전담합니다. 인력은 공급망 위기 대처, 우량 공급처 발굴 같은 전략 업무와 대형 거래에 투입하세요.",
|
||||
},
|
||||
{
|
||||
icon: Clock,
|
||||
title: "협상 리드타임 대폭 긴축",
|
||||
title: "협상 리드타임 단축",
|
||||
description:
|
||||
"수백 공급사와 동시에 병렬 조율을 자동으로 전개하므로 업무량의 한계가 없습니다. 통상 수주일 동안 지루하게 이어지던 최종 가격 타결 주기를 단 수일 안으로 긴축 단축시킬 수 있습니다.",
|
||||
"수백 공급사와 동시에 협상하므로 사람 수에 얽매이지 않습니다. 수주씩 걸리던 가격 타결을 수일 안에 끝냅니다.",
|
||||
},
|
||||
]
|
||||
|
||||
112
landing/app/components/sections/faq.tsx
Normal file
112
landing/app/components/sections/faq.tsx
Normal file
@ -0,0 +1,112 @@
|
||||
import { useState } from "react"
|
||||
import { AnimatePresence, motion } from "motion/react"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
|
||||
import { Section } from "@/components/ui/section"
|
||||
import { SectionHeading } from "@/components/ui/section-heading"
|
||||
import { Typography } from "@/components/ui/typography"
|
||||
import { EASE_OUT_EXPO, useFadeUp } from "@/lib/motion"
|
||||
|
||||
/** 도입 검토 FAQ 아코디언 — 첫 항목 기본 열림, 한 번에 하나만 펼침. */
|
||||
export function Faq() {
|
||||
const fadeUp = useFadeUp()
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(0)
|
||||
|
||||
return (
|
||||
<Section id="faq" width="sm" bordered>
|
||||
<motion.div {...fadeUp} className="mb-24">
|
||||
<SectionHeading
|
||||
align="center"
|
||||
eyebrow="FAQ"
|
||||
title="자주 묻는 질문"
|
||||
description="도입 검토에서 가장 많이 받는 질문들입니다."
|
||||
descriptionClassName="mt-4 text-[17px]"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{FAQS.map((faq, idx) => (
|
||||
<FaqItem
|
||||
key={faq.question}
|
||||
faq={faq}
|
||||
open={openIndex === idx}
|
||||
onToggle={() => setOpenIndex(openIndex === idx ? null : idx)}
|
||||
delay={0.1 * (idx + 1)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
type FaqEntry = { question: string; answer: string }
|
||||
|
||||
function FaqItem({
|
||||
faq,
|
||||
open,
|
||||
onToggle,
|
||||
delay,
|
||||
}: {
|
||||
faq: FaqEntry
|
||||
open: boolean
|
||||
onToggle: () => void
|
||||
delay: number
|
||||
}) {
|
||||
const fadeUp = useFadeUp(delay)
|
||||
|
||||
return (
|
||||
<motion.div {...fadeUp} className="bg-surface rounded-[28px] overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
aria-expanded={open}
|
||||
className="w-full flex items-center justify-between gap-6 text-left px-8 py-7 cursor-pointer"
|
||||
>
|
||||
<Typography variant="cardTitle" as="span">
|
||||
{faq.question}
|
||||
</Typography>
|
||||
<motion.span
|
||||
animate={{ rotate: open ? 180 : 0 }}
|
||||
transition={{ duration: 0.4, ease: EASE_OUT_EXPO }}
|
||||
className="text-ink-muted shrink-0"
|
||||
aria-hidden
|
||||
>
|
||||
<ChevronDown className="w-5 h-5" />
|
||||
</motion.span>
|
||||
</button>
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
{open && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.5, ease: EASE_OUT_EXPO }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<Typography variant="small" className="px-8 pb-8">
|
||||
{faq.answer}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
const FAQS: FaqEntry[] = [
|
||||
{
|
||||
question: "공급사가 봇과의 협상을 싫어하지 않을까요?",
|
||||
answer:
|
||||
"오히려 반대입니다. 이 거래들 대부분은 지금껏 협상 테이블에 오르지도 못하던 건입니다. 봇은 24시간 원하는 시간에, 압박 없이, 늘 같은 기준으로 응대합니다. 글로벌 동종 서비스의 공급사 만족도는 82%에 이릅니다.",
|
||||
},
|
||||
{
|
||||
question: "봇이 우리 기준을 벗어나 합의해 버리면요?",
|
||||
answer:
|
||||
"그럴 수 없습니다. 봇은 견적을 만들 때 정한 목표가와 낙찰 기준 밖으로 나가지 않고, 최종 낙찰 규칙도 사용자가 정합니다.",
|
||||
},
|
||||
{
|
||||
question: "기존 시스템과 연동되나요?",
|
||||
answer: "견적·발주 데이터 연동을 지원합니다. 도입 상담에서 사내 ERP 환경에 맞는 연동 방식을 안내해 드립니다.",
|
||||
},
|
||||
]
|
||||
@ -18,7 +18,7 @@ export function Header() {
|
||||
return (
|
||||
<header
|
||||
className={`fixed top-0 left-0 right-0 z-40 transition-all duration-300 ${
|
||||
scrolled ? "py-4 bg-white/80 backdrop-blur-xl border-b border-line" : "py-6 bg-transparent"
|
||||
scrolled ? "py-4 bg-white/80 backdrop-blur-xl border-b border-line" : "py-6 bg-transparent border-b border-transparent"
|
||||
}`}
|
||||
>
|
||||
<div className="max-w-5xl mx-auto px-6 flex items-center justify-between">
|
||||
@ -48,5 +48,5 @@ const NAV_LINKS = [
|
||||
{ href: "#how-it-works-demo", label: "이용 가이드" },
|
||||
{ href: "#core-values", label: "솔루션 이점" },
|
||||
{ href: "#reinforcement", label: "강화학습" },
|
||||
{ href: "#benchmarks", label: "보안 및 신뢰성" },
|
||||
{ href: "#faq", label: "자주 묻는 질문" },
|
||||
]
|
||||
|
||||
@ -45,7 +45,7 @@ export function HeroGlassmorphic() {
|
||||
transition={{ duration: 0.9, delay: 0.1, ease: EASE_OUT_EXPO }}
|
||||
>
|
||||
<Typography variant="display" className="mb-8">
|
||||
구매 협상, 이제 <span className="text-primary">봇이 알아서</span> <br />
|
||||
구매 협상, 이제 <span className="text-primary">봇이 알아서</span> <br className="hidden sm:inline" />
|
||||
흥정부터 낙찰까지 자동으로
|
||||
</Typography>
|
||||
</motion.div>
|
||||
@ -57,7 +57,7 @@ export function HeroGlassmorphic() {
|
||||
>
|
||||
<Typography variant="lead" className="md:text-[20px] max-w-2xl mb-12">
|
||||
협상할수록 강화학습으로 흥정 전략을 알아서 다듬어갑니다. <br className="hidden sm:inline" />
|
||||
파트너사 이탈(결렬) 없이, 최선의 계약 조건만 완벽하게 성사시키세요.
|
||||
감정 없이, 지치지 않고, 24시간 연중무휴 — 결렬 대신 성사로 이끕니다.
|
||||
</Typography>
|
||||
</motion.div>
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { motion } from "motion/react"
|
||||
import { ArrowDown, ArrowRight, Sparkles } from "lucide-react"
|
||||
import { ArrowDown, ArrowRight } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { PhoneFrame } from "@/components/ui/device-mockups"
|
||||
import { Typography } from "@/components/ui/typography"
|
||||
|
||||
@ -13,19 +14,10 @@ export function HeroNeumorphic() {
|
||||
<div className="max-w-5xl mx-auto px-6 w-full relative z-10 grid grid-cols-1 lg:grid-cols-12 gap-16 items-center">
|
||||
{/* 좌: 카피 + CTA */}
|
||||
<div className="lg:col-span-7 space-y-8 text-left">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7 }}
|
||||
className="inline-flex items-center gap-2 px-3.5 py-1.5 bg-surface-neu rounded-full text-xs font-bold text-primary shadow-[3px_3px_6px_#dbdee3,-3px_-3px_6px_#ffffff]"
|
||||
>
|
||||
<Sparkles className="w-3.5 h-3.5" />
|
||||
<span>LIVE NEGOTIATION DEMO</span>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, delay: 0.1 }}>
|
||||
<Typography variant="display" className="text-4xl sm:text-5xl lg:text-[54px] leading-[1.15]">
|
||||
구매 협상, 이제 <span className="text-primary">봇이 알아서</span> <br />
|
||||
구매 협상, <br />
|
||||
이제 <span className="text-primary">봇이 알아서</span> <br />
|
||||
흥정부터 낙찰까지 자동으로
|
||||
</Typography>
|
||||
</motion.div>
|
||||
@ -43,20 +35,17 @@ export function HeroNeumorphic() {
|
||||
transition={{ duration: 0.8, delay: 0.3 }}
|
||||
className="flex flex-col sm:flex-row gap-5 pt-4"
|
||||
>
|
||||
<a
|
||||
<Button
|
||||
href="#contact-section"
|
||||
className="px-8 py-4.5 bg-primary hover:bg-primary-deep text-white text-base font-bold rounded-[20px] cursor-pointer flex items-center justify-center gap-2 group transition-all shadow-[6px_6px_12px_rgba(49,130,246,0.25),-6px_-6px_12px_#ffffff] hover:shadow-[4px_4px_8px_rgba(49,130,246,0.25),-4px_-4px_8px_#ffffff] active:shadow-none translate-y-0 active:translate-y-0.5"
|
||||
className="w-full sm:w-auto group shadow-[0_8px_24px_rgba(49,130,246,0.15)] hover:scale-[1.02] active:scale-[0.98]"
|
||||
>
|
||||
<span>도입 문의 상담 받기</span>
|
||||
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" />
|
||||
</a>
|
||||
<a
|
||||
href="#how-it-works"
|
||||
className="px-8 py-4.5 bg-surface-neu text-ink-soft hover:text-ink text-base font-bold rounded-[20px] transition-all flex items-center justify-center gap-1.5 shadow-[6px_6px_12px_#dbdee3,-6px_-6px_12px_#ffffff] hover:shadow-[4px_4px_8px_#dbdee3,-4px_-4px_8px_#ffffff] active:shadow-[inset_3px_3px_6px_#dbdee3,inset_-3px_-3px_6px_#ffffff] translate-y-0 active:translate-y-0.5"
|
||||
>
|
||||
</Button>
|
||||
<Button href="#how-it-works" variant="glass" className="w-full sm:w-auto gap-1.5">
|
||||
<span>작동 방식 보기</span>
|
||||
<ArrowDown className="w-4 h-4 text-ink-soft" />
|
||||
</a>
|
||||
</Button>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ export function HowItWorksDemo() {
|
||||
align="center"
|
||||
className="mb-20"
|
||||
eyebrow="SERVICE DEMONSTRATION"
|
||||
title="견적 생성부터 AI 협상까지 한눈에 보기"
|
||||
title="견적 생성부터 낙찰까지 한눈에 보기"
|
||||
description="복잡해 보이는 구매 과정이 어떻게 자동화되는지 실제 작동 화면(GIF)을 통해 쉽고 직관적으로 확인해 보세요."
|
||||
descriptionClassName="text-[17px] max-w-2xl"
|
||||
/>
|
||||
@ -176,15 +176,15 @@ const DEMO_TABS: DemoTab[] = [
|
||||
),
|
||||
paragraphs: [
|
||||
<>
|
||||
품목 명과 시중 시장 가격을 입력하면, negotium의 지능형 <b>LPS(Lowest Price Scanning)</b> 기술이 실시간 시세를 자동
|
||||
연동합니다.
|
||||
품목명과 시중 시장 가격을 입력하면, negotium이 <b>인터넷 최저가(LPS)</b> 데이터를 상시 반영해{" "}
|
||||
<b className="text-primary">시장 바닥값을 쥐고 시작합니다.</b>
|
||||
</>,
|
||||
<>
|
||||
과거 거래 데이터 분석을 바탕으로, 파트너사가 반발심을 가질 확률을 최저로 억제하면서 사내 마진율을 방어할 수 있는{" "}
|
||||
<b className="text-primary">최적 목표 단가(Target Price)</b> 가이드라인을 기획해냅니다.
|
||||
시중 최저가와 매입 이력, 목표 마진율을 종합해 — 사내 마진은 지키면서 공급사가 받아들일 수 있는{" "}
|
||||
<b className="text-primary">최적 목표 단가(Target Price)</b>를 자동으로 잡아줍니다.
|
||||
</>,
|
||||
],
|
||||
checks: ["원자재 및 공정 시장 실시간 시세 연동", "이탈 저항 임계 모델 기반 가이드 자동 완성"],
|
||||
checks: ["인터넷 최저가(LPS) 시세 상시 반영", "마진을 지키는 최적 목표 단가 자동 완성"],
|
||||
mockup: "browser",
|
||||
url: "https://console.negotium.ai/estimates/new",
|
||||
gif: "/gifs/quote_generation.gif",
|
||||
@ -205,13 +205,13 @@ const DEMO_TABS: DemoTab[] = [
|
||||
checkClassName: "text-emerald-500",
|
||||
heading: (
|
||||
<>
|
||||
로그인도 필요 없이 <br />
|
||||
전용 링크로 간편하게 밀당 조율
|
||||
공급사는 설치 없이 <br />
|
||||
모바일 웹으로 언제든 협상
|
||||
</>
|
||||
),
|
||||
paragraphs: [
|
||||
<>
|
||||
각 파트너사 담당자는 번거로운 가입 절차 없이 발송된 <b>비대면 협상 모바일 포털</b>에 접속해 단가를 실시간 조율합니다.
|
||||
각 파트너사 담당자는 초대 메일로 <b>비대면 협상 모바일 포털</b>에 접속해, 언제든 단가를 조율합니다.
|
||||
</>,
|
||||
<>
|
||||
단순 마진 깎기가 아닌, <b>"물량 보증"</b> 혹은 <b>"지불 주기 단축"</b> 등의 와일드카드 거래 카드를 연동 제안하여
|
||||
@ -244,15 +244,14 @@ const DEMO_TABS: DemoTab[] = [
|
||||
),
|
||||
paragraphs: [
|
||||
<>
|
||||
협상이 실시간 완료되면, 구매 관리자는 AI가 도출해 낸 파트너사별 최종 타결 단가와 절감된 사내 재무 지표 리포트를
|
||||
대시보드에서 즉시 확인합니다.
|
||||
협상이 끝나면, 파트너사별 <b>최종 타결 단가와 절감액 리포트</b>를 대시보드에서 즉시 확인합니다.
|
||||
</>,
|
||||
<>
|
||||
어떤 파트너사가 어느 카드(정산 단축, 물량 보증)를 수용하여 단가를 낮추었는지 시각적으로 분석되어 최저 계약 체결을
|
||||
전면 검토할 수 있습니다.
|
||||
어떤 제안에 단가가 움직였는지 <b>협상 전 과정이 로그로 남고</b>, 공급사별 투찰가를 한눈에 비교해 계약을 검토할 수
|
||||
있습니다.
|
||||
</>,
|
||||
],
|
||||
checks: ["최종 단가 조율 결과 및 계약 체결 자동 확정", "총 누적 절감액(Savings) 및 마진 방어 통계 실시간 표기"],
|
||||
checks: ["낙찰 결과·절감액 자동 집계", "누적 절감액·협상 통계 대시보드"],
|
||||
mockup: "browser",
|
||||
url: "https://console.negotium.ai/dashboard/reports",
|
||||
gif: "/gifs/negotiation_result.gif",
|
||||
|
||||
@ -3,6 +3,7 @@ import { AnimatePresence, motion } from "motion/react"
|
||||
import { Bot, Handshake, ShieldCheck } from "lucide-react"
|
||||
|
||||
import { SlateRenderer } from "@/components/ui/slate-renderer"
|
||||
import { Typography } from "@/components/ui/typography"
|
||||
import { EASE_OUT_EXPO } from "@/lib/motion"
|
||||
import type { DialogueStep } from "@/types"
|
||||
|
||||
@ -10,7 +11,7 @@ import type { DialogueStep } from "@/types"
|
||||
export function NegotiationConsole() {
|
||||
// -1 = 아직 섹션 진입 전(온보딩 가이드 표시)
|
||||
const [activeStep, setActiveStep] = useState(-1)
|
||||
const [animatedPrice, setAnimatedPrice] = useState(1200)
|
||||
const [animatedPrice, setAnimatedPrice] = useState(1200000)
|
||||
const sectionRef = useRef<HTMLDivElement>(null)
|
||||
const chatContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@ -39,7 +40,7 @@ export function NegotiationConsole() {
|
||||
|
||||
// 단가 오도미터 애니메이션 (500ms ease-out)
|
||||
useEffect(() => {
|
||||
const targetPrice = activeStep === -1 ? 1200 : NEGOTIATION_STEPS[activeStep].price
|
||||
const targetPrice = activeStep === -1 ? 1200000 : NEGOTIATION_STEPS[activeStep].price
|
||||
if (animatedPrice === targetPrice) return
|
||||
|
||||
const start = animatedPrice
|
||||
@ -68,6 +69,16 @@ export function NegotiationConsole() {
|
||||
className="relative bg-surface text-ink w-full font-sans border-t border-line h-[240vh] md:h-[280vh]"
|
||||
>
|
||||
<div className="sticky top-0 h-screen flex items-center justify-center w-full overflow-hidden px-6 md:px-8">
|
||||
{/* 섹션 헤딩 — sticky 상단 고정 (모바일은 공간상 생략) */}
|
||||
<div className="absolute top-24 left-0 right-0 text-center hidden md:block">
|
||||
<Typography variant="eyebrow" className="block">
|
||||
Live Replay
|
||||
</Typography>
|
||||
<Typography variant="heading" as="h2" className="mt-1">
|
||||
봇이 깎아내는 과정, 그대로 재생
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<div className="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-12 gap-12 md:gap-16 items-center w-full">
|
||||
{/* 좌: 실시간 단가 패널 */}
|
||||
<div className="md:col-span-5 flex flex-col justify-center space-y-6">
|
||||
@ -75,10 +86,35 @@ export function NegotiationConsole() {
|
||||
<span className="text-[11px] font-bold text-ink-muted uppercase tracking-wider block mb-1">실시간 협상 단가</span>
|
||||
<div className="flex items-baseline gap-1.5">
|
||||
<span className="text-3xl font-bold text-primary">₩</span>
|
||||
<span className="text-5xl md:text-6xl font-black tracking-tight text-primary">
|
||||
<span className="text-4xl md:text-5xl font-black tracking-tight text-primary">
|
||||
{animatedPrice.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
{/* 시작가 대비 누적 절감 델타 — 높이 고정으로 레이아웃 점프 방지 */}
|
||||
<div className="h-6 mt-1.5">
|
||||
{animatedPrice < START_PRICE && (
|
||||
<span className="inline-flex items-baseline gap-1.5 text-positive font-bold text-sm">
|
||||
▼ {(START_PRICE - animatedPrice).toLocaleString()}원
|
||||
<span className="text-xs font-semibold">
|
||||
(−{(((START_PRICE - animatedPrice) / START_PRICE) * 100).toFixed(1)}%)
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 계단식 하락 스파크라인 + 목표가 기준선 */}
|
||||
<div>
|
||||
<PriceSparkline activeStep={activeStep} />
|
||||
<div className="flex justify-between mt-2">
|
||||
<Typography variant="micro">시작가 {START_PRICE.toLocaleString()}</Typography>
|
||||
<Typography variant="micro" className="text-primary">
|
||||
목표가 {TARGET_PRICE.toLocaleString()}
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography variant="caption" className="mt-3">
|
||||
실제 협상 화면을 재구성한 데모입니다.
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<div className="h-10">
|
||||
@ -99,7 +135,7 @@ export function NegotiationConsole() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우: 대화 말풍선 (파트너 좌 / 봇 우) */}
|
||||
{/* 우: 대화 말풍선 (봇 좌 / 파트너 우) */}
|
||||
<div className="md:col-span-7 flex flex-col justify-end space-y-5 h-[340px] md:h-[420px] relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-surface via-transparent to-transparent h-12 pointer-events-none z-10" />
|
||||
|
||||
@ -141,9 +177,9 @@ export function NegotiationConsole() {
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: -10, scale: 0.97 }}
|
||||
transition={{ duration: 0.4, ease: EASE_OUT_EXPO }}
|
||||
className={`flex flex-col ${isBot ? "items-end" : "items-start"} w-full`}
|
||||
className={`flex flex-col ${isBot ? "items-start" : "items-end"} w-full`}
|
||||
>
|
||||
<div className={`flex items-center gap-1.5 mb-1 text-[11px] font-bold text-ink-muted ${isBot ? "flex-row-reverse" : ""}`}>
|
||||
<div className={`flex items-center gap-1.5 mb-1 text-[11px] font-bold text-ink-muted ${isBot ? "" : "flex-row-reverse"}`}>
|
||||
<span className={`flex items-center gap-1 ${isBot ? "text-primary" : "text-ink-soft"}`}>
|
||||
{isBot ? (
|
||||
<>
|
||||
@ -161,7 +197,7 @@ export function NegotiationConsole() {
|
||||
|
||||
<div
|
||||
className={`max-w-[85%] rounded-[20px] p-4 text-sm leading-relaxed font-semibold shadow-[0_2px_8px_rgba(0,0,0,0.02)] transition-all duration-300 ${
|
||||
isBot ? "bg-primary-soft text-ink rounded-tr-none" : "bg-white text-ink rounded-tl-none"
|
||||
isBot ? "bg-primary-soft text-ink rounded-tl-none" : "bg-white text-ink rounded-tr-none"
|
||||
}`}
|
||||
>
|
||||
<SlateRenderer nodes={item.editorNodes} />
|
||||
@ -180,11 +216,63 @@ export function NegotiationConsole() {
|
||||
)
|
||||
}
|
||||
|
||||
// 데모 대화 시나리오 — 1,200원 제시에서 1,050원 낙찰까지 6스텝
|
||||
// 시작가(첫 제시가)·목표가 — 좌측 패널 델타·스파크라인 기준값
|
||||
const START_PRICE = 1200000
|
||||
const TARGET_PRICE = 1080000
|
||||
|
||||
/** 대화 진행에 맞춰 그려지는 계단식 가격 하락 라인 + 목표가 점선. */
|
||||
function PriceSparkline({ activeStep }: { activeStep: number }) {
|
||||
const W = 100
|
||||
const H = 44
|
||||
// 위아래 여백을 둔 가격 범위 매핑
|
||||
const MAX = 1215000
|
||||
const MIN = 1035000
|
||||
const y = (price: number) => ((MAX - price) / (MAX - MIN)) * H
|
||||
|
||||
const prices = NEGOTIATION_STEPS.map((step) => step.price)
|
||||
const stepX = (i: number) => (i / (prices.length - 1)) * W
|
||||
|
||||
// step-after 계단 경로
|
||||
let d = `M 0 ${y(prices[0]).toFixed(1)}`
|
||||
for (let i = 1; i < prices.length; i++) {
|
||||
d += ` L ${stepX(i).toFixed(1)} ${y(prices[i - 1]).toFixed(1)} L ${stepX(i).toFixed(1)} ${y(prices[i]).toFixed(1)}`
|
||||
}
|
||||
|
||||
const progress = activeStep < 0 ? 0 : Math.min((activeStep + 1) / prices.length, 1)
|
||||
|
||||
return (
|
||||
<svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" className="w-full h-16 overflow-visible" aria-hidden>
|
||||
<defs>
|
||||
<clipPath id="nego-spark-clip">
|
||||
<rect x="0" y="-4" width={progress * W} height={H + 8} style={{ transition: "width 0.5s ease-out" }} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
{/* 목표가 기준선 */}
|
||||
<g className="text-primary/40">
|
||||
<line
|
||||
x1="0"
|
||||
y1={y(TARGET_PRICE)}
|
||||
x2={W}
|
||||
y2={y(TARGET_PRICE)}
|
||||
stroke="currentColor"
|
||||
strokeWidth="1"
|
||||
strokeDasharray="4 4"
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
</g>
|
||||
{/* 가격 하락 계단 — 진행률만큼 클립으로 드러남 */}
|
||||
<g clipPath="url(#nego-spark-clip)" className="text-primary">
|
||||
<path d={d} fill="none" stroke="currentColor" strokeWidth="2.5" vectorEffect="non-scaling-stroke" strokeLinejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// 데모 대화 시나리오 — 1,200,000원 제시에서 1,050,000원 낙찰까지 6스텝
|
||||
const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
{
|
||||
step: 1,
|
||||
price: 1200,
|
||||
price: 1200000,
|
||||
editorNodes: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
@ -192,14 +280,14 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
children: [
|
||||
{ text: '협력사', bold: true },
|
||||
{ text: ' : ' },
|
||||
{ text: '현재 글로벌 원재료 상승 요인으로 제안할 수 있는 최선의 단가는 1,200원입니다. 이 이하로는 마진 확보가 어렵습니다.' },
|
||||
{ text: '현재 글로벌 원재료 상승 요인으로 제안할 수 있는 최선의 단가는 1,200,000원입니다. 이 이하로는 마진 확보가 어렵습니다.' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
step: 2,
|
||||
price: 1150,
|
||||
price: 1150000,
|
||||
editorNodes: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
@ -207,7 +295,7 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
children: [
|
||||
{ text: 'AI 흥정 봇', bold: true },
|
||||
{ text: ' : ' },
|
||||
{ text: '제시해주신 1,200원은 당사 타 유사 품목 이력 및 시중 원가 인덱스 데이터 대비 약 8.3% 높게 책정되어 있습니다. ', italic: true },
|
||||
{ text: '제시해주신 1,200,000원은 당사 타 유사 품목 이력 및 시중 원가 인덱스 데이터 대비 약 8.3% 높게 책정되어 있습니다. ', italic: true },
|
||||
{ text: '상호 호혜적 장기 계약 체결을 전제로 조율가 범위를 반영해 제안해 드립니다.', code: true },
|
||||
],
|
||||
},
|
||||
@ -215,7 +303,7 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
},
|
||||
{
|
||||
step: 3,
|
||||
price: 1150,
|
||||
price: 1150000,
|
||||
editorNodes: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
@ -223,14 +311,14 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
children: [
|
||||
{ text: '협력사', bold: true },
|
||||
{ text: ' : ' },
|
||||
{ text: '제조 공정상 급격한 인하는 무리가 있으나, 상생 협력 차원에서 1,150원까지는 즉시 조정해 드릴 용의가 있습니다.' },
|
||||
{ text: '제조 공정상 급격한 인하는 무리가 있으나, 상생 협력 차원에서 1,150,000원까지는 즉시 조정해 드릴 용의가 있습니다.' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
step: 4,
|
||||
price: 1080,
|
||||
price: 1080000,
|
||||
editorNodes: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
@ -238,14 +326,14 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
children: [
|
||||
{ text: 'AI 흥정 봇', bold: true },
|
||||
{ text: ' : ' },
|
||||
{ text: '적극적인 협조에 감사드립니다. 만약 연간 최소 발주 수량을 보증하고 공급망 일정을 다소 유연화해주신다면, 목표가인 1,080원 선까지 맞출 수 있을까요?', italic: true },
|
||||
{ text: '적극적인 협조에 감사드립니다. 만약 연간 최소 발주 수량을 보증하고 공급망 일정을 다소 유연화해주신다면, 목표가인 1,080,000원 선까지 맞출 수 있을까요?', italic: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
step: 5,
|
||||
price: 1050,
|
||||
price: 1050000,
|
||||
editorNodes: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
@ -254,14 +342,14 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
{ text: '협력사', bold: true },
|
||||
{ text: ' : ' },
|
||||
{ text: '좋습니다. 제안하신 연간 개런티 확보 및 대금 현금 결제 기한 단축을 승인해 주시는 조건으로, ' },
|
||||
{ text: '최종 조율가 1,050원으로 맞춰서 계약을 체결하겠습니다.', bold: true },
|
||||
{ text: '최종 조율가 1,050,000원으로 맞춰서 계약을 체결하겠습니다.', bold: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
step: 6,
|
||||
price: 1050,
|
||||
price: 1050000,
|
||||
badge: '낙찰 성공 · 12.5% 예산 절감',
|
||||
editorNodes: [
|
||||
{
|
||||
@ -270,7 +358,7 @@ const NEGOTIATION_STEPS: DialogueStep[] = [
|
||||
children: [
|
||||
{ text: 'AI 흥정 봇', bold: true },
|
||||
{ text: ' : ' },
|
||||
{ text: '최종 합의 접수 완료 — 가이드 상한가(1,200원) 대비 합의 낙찰가 1,050원으로 최종 계약 승인 처리 완료되었습니다. ', bold: true },
|
||||
{ text: '최종 합의 접수 완료 — 가이드 상한가(1,200,000원) 대비 합의 낙찰가 1,050,000원으로 최종 계약 승인 처리 완료되었습니다. ', bold: true },
|
||||
{ text: '본 흥정 마일스톤 및 단가 타결 히스토리는 사내 투명성 보증을 위해 보존 기록됩니다.', code: true },
|
||||
],
|
||||
},
|
||||
|
||||
@ -18,10 +18,10 @@ export function Reinforcement() {
|
||||
title="협상할수록, 더 좋은 조건으로"
|
||||
description={
|
||||
<>
|
||||
negotium은 진행된 모든 거래 데이터와 파트너 거절 피드백을{" "}
|
||||
<span className="font-bold text-primary">강화학습 기술</span>에 투입합니다. 무턱대고 가격을 후려쳐 상대방의
|
||||
반발만 사고 결렬로 치닫는 경직된 협상이 아니라, 파트너사가 충분히 받아들일 수 있는 범위 내에서 최선의 마진율을
|
||||
뽑아내도록 조율 페이스를 학습합니다.
|
||||
잘 깎는 노하우는 담당자 머릿속에만 남고, 성과는 그날의 감정과 컨디션에 흔들립니다. negotium은 진행된 모든 거래
|
||||
데이터와 파트너 거절 피드백을 <span className="font-bold text-primary">강화학습 기술</span>에 투입합니다.
|
||||
무턱대고 가격을 후려쳐 상대방의 반발만 사고 결렬로 치닫는 경직된 협상이 아니라, 파트너사가 충분히 받아들일 수
|
||||
있는 범위 내에서 최선의 마진율을 뽑아내도록 조율 페이스를 학습합니다.
|
||||
</>
|
||||
}
|
||||
descriptionClassName="mt-8 max-w-3xl"
|
||||
@ -62,17 +62,17 @@ function PillarCard({ pillar, delay }: { pillar: Pillar; delay: number }) {
|
||||
const PILLARS: Pillar[] = [
|
||||
{
|
||||
icon: Scale,
|
||||
title: "흥정의 일관성 보존",
|
||||
description: "개인 감정이나 친분에 휘둘리지 않고 사내 재무 원칙과 가이드라인을 완벽히 관철시킵니다.",
|
||||
title: "협상 기준의 일관성",
|
||||
description: "개인 감정이나 친분에 휘둘리지 않고, 사내 기준과 가이드라인대로만 협상합니다.",
|
||||
},
|
||||
{
|
||||
icon: RefreshCw,
|
||||
title: "지속적인 전략 최적화",
|
||||
description: "매 조율 피드백을 축적하여 상대방의 마진 수용성 확률 지도를 고도화해 나갑니다.",
|
||||
description: "전략 강화 학습을 통해 기준선이 상승하고, 점진적으로 개선됩니다. 시간이 곧 협상력이 됩니다.",
|
||||
},
|
||||
{
|
||||
icon: GitMerge,
|
||||
title: "낙찰 성사율 극대화",
|
||||
description: "단순한 협상 결렬(No-deal) 리스크를 상시 추산해 안전하면서도 최고 이익률의 균형점을 확보합니다.",
|
||||
description: "무리하게 후려쳐 관계를 깨는 대신, 성사되는 선에서 최대한 끌어냅니다.",
|
||||
},
|
||||
]
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { Comparison } from "@/components/sections/comparison";
|
||||
import { Contact } from "@/components/sections/contact";
|
||||
import { CoreValues } from "@/components/sections/core-values";
|
||||
import { Faq } from "@/components/sections/faq";
|
||||
import { FinalCTA } from "@/components/sections/final-cta";
|
||||
import { Footer } from "@/components/sections/footer";
|
||||
import { Header } from "@/components/sections/header";
|
||||
@ -10,7 +12,6 @@ import { HeroNeumorphic } from "@/components/sections/hero-neumorphic";
|
||||
import { HowItWorksDemo } from "@/components/sections/how-it-works-demo";
|
||||
import { NegotiationConsole } from "@/components/sections/negotiation-console";
|
||||
import { Reinforcement } from "@/components/sections/reinforcement";
|
||||
import { ROISimulator } from "@/components/sections/roi-simulator";
|
||||
|
||||
const TITLE = "negotium — AI 구매 협상 자동화";
|
||||
const DESCRIPTION =
|
||||
@ -38,9 +39,8 @@ export default function Home() {
|
||||
<div className="min-h-screen bg-white text-ink font-sans antialiased selection:bg-primary/10 selection:text-primary">
|
||||
<Header />
|
||||
|
||||
{import.meta.env.DEV && <HeroThemeSwitcher value={heroTheme} onChange={setHeroTheme} />}
|
||||
|
||||
<div className="relative">
|
||||
{import.meta.env.DEV && <HeroThemeSwitcher value={heroTheme} onChange={setHeroTheme} />}
|
||||
{heroTheme === "neumorphic" && <HeroNeumorphic />}
|
||||
{heroTheme === "glassmorphic" && <HeroGlassmorphic />}
|
||||
</div>
|
||||
@ -49,7 +49,8 @@ export default function Home() {
|
||||
<HowItWorksDemo />
|
||||
<Reinforcement />
|
||||
<CoreValues />
|
||||
<ROISimulator />
|
||||
<Comparison />
|
||||
<Faq />
|
||||
<FinalCTA />
|
||||
<Contact />
|
||||
<Footer />
|
||||
@ -58,13 +59,13 @@ export default function Home() {
|
||||
}
|
||||
|
||||
const HERO_OPTIONS: { value: HeroTheme; label: string }[] = [
|
||||
{ value: "neumorphic", label: "Neumorphic" },
|
||||
{ value: "neumorphic", label: "Phone Demo" },
|
||||
{ value: "glassmorphic", label: "Soft Ambient" },
|
||||
];
|
||||
|
||||
function HeroThemeSwitcher({ value, onChange }: { value: HeroTheme; onChange: (theme: HeroTheme) => void }) {
|
||||
return (
|
||||
<div className="fixed top-24 left-1/2 -translate-x-1/2 z-50 flex items-center gap-1.5 p-1 rounded-full shadow-[0_4px_20px_rgba(0,0,0,0.06)] border border-line-strong bg-white/95 text-ink-soft backdrop-blur-md text-xs font-semibold">
|
||||
<div className="absolute top-24 left-1/2 -translate-x-1/2 z-30 flex items-center gap-1.5 p-1 rounded-full shadow-[0_4px_20px_rgba(0,0,0,0.06)] border border-line-strong bg-white/95 text-ink-soft backdrop-blur-md text-xs font-semibold">
|
||||
<span className="pl-3.5 pr-1.5 text-[10px] uppercase tracking-wider text-ink-muted font-bold">Hero Design:</span>
|
||||
{HERO_OPTIONS.map((option) => (
|
||||
<button
|
||||
|
||||
Loading…
Reference in New Issue
Block a user