feat: 도구 쇼케이스 리디자인 + 서비스 페이지 스크롤 애니메이션 + followup 파이프라인
- /tools 페이지: Supanova 디자인 원칙 적용, 비대칭 레이아웃·지그재그 카드·CTA 리디자인 - /tools SEO: layout.tsx 분리하여 메타데이터·OG 태그 추가 - /services/prompt: 스크롤 리빌 애니메이션 (IntersectionObserver + stagger delay) - /services/automation: 스크롤 리빌 애니메이션 (전 섹션 적용) - /followup 커맨드: 지원서 팔로업 → 수주 클로징 파이프라인 신규 생성 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,31 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import ContactModal from '../../components/ContactModal';
|
||||
|
||||
function useScrollReveal() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
||||
);
|
||||
el.querySelectorAll('.reveal').forEach((child) => observer.observe(child));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
return ref;
|
||||
}
|
||||
|
||||
const tools = [
|
||||
{
|
||||
id: 'excel',
|
||||
@@ -163,6 +185,7 @@ const process = [
|
||||
export default function AutomationPage() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalService, setModalService] = useState('업무 자동화');
|
||||
const containerRef = useScrollReveal();
|
||||
|
||||
const openModal = (service: string) => {
|
||||
setModalService(service);
|
||||
@@ -170,7 +193,22 @@ export default function AutomationPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-full bg-[#f0f5ff]">
|
||||
<div ref={containerRef} className="min-h-full bg-[#f0f5ff]">
|
||||
<style>{`
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(1.5rem);
|
||||
transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.reveal-d1 { transition-delay: 80ms; }
|
||||
.reveal-d2 { transition-delay: 160ms; }
|
||||
.reveal-d3 { transition-delay: 240ms; }
|
||||
`}</style>
|
||||
<ContactModal
|
||||
isOpen={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
@@ -212,13 +250,13 @@ export default function AutomationPage() {
|
||||
{/* ─── 자동화 유형 ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-cyan-600 text-xs font-bold uppercase tracking-widest mb-2">AUTOMATION TYPES</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">자동화 유형</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{automationTypes.map((at) => (
|
||||
<div key={at.title} className={`bg-white rounded-2xl border-2 ${at.accentColor} p-5`}>
|
||||
{automationTypes.map((at, idx) => (
|
||||
<div key={at.title} className={`bg-white rounded-2xl border-2 ${at.accentColor} p-5 reveal reveal-d${(idx % 3) + 1}`}>
|
||||
<span className={`inline-block text-xs font-bold px-2 py-0.5 rounded-md border mb-3 ${at.labelColor}`}>{at.title.split(' ')[0]}</span>
|
||||
<h3 className="font-bold text-[#04102b] text-sm mb-2">{at.title}</h3>
|
||||
<p className="text-slate-500 text-xs leading-relaxed mb-3">{at.desc}</p>
|
||||
@@ -239,15 +277,15 @@ export default function AutomationPage() {
|
||||
{/* ─── 프로세스 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-cyan-600 text-xs font-bold uppercase tracking-widest mb-2">PROCESS</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">진행 프로세스</h2>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="hidden sm:block absolute top-10 left-[10%] right-[10%] h-0.5 bg-[#dbe8ff]" />
|
||||
<div className="grid grid-cols-1 sm:grid-cols-5 gap-4">
|
||||
{process.map((p) => (
|
||||
<div key={p.step} className="relative text-center">
|
||||
{process.map((p, idx) => (
|
||||
<div key={p.step} className={`relative text-center reveal reveal-d${(idx % 3) + 1}`}>
|
||||
<div className="w-20 h-20 mx-auto rounded-2xl bg-[#012030] border border-cyan-400/20 flex flex-col items-center justify-center mb-3">
|
||||
<span className="text-cyan-400 text-xs font-bold">STEP</span>
|
||||
<span className="text-white font-extrabold text-lg leading-none">{p.step}</span>
|
||||
@@ -264,13 +302,13 @@ export default function AutomationPage() {
|
||||
{/* ─── 예상 비용 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-[#1a56db] text-xs font-bold uppercase tracking-widest mb-2">PRICING</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">예상 비용</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{plans.map((plan) => (
|
||||
<div key={plan.name} className={`rounded-2xl border p-6 relative flex flex-col ${
|
||||
{plans.map((plan, idx) => (
|
||||
<div key={plan.name} className={`rounded-2xl border p-6 relative flex flex-col reveal reveal-d${idx + 1} ${
|
||||
plan.highlight
|
||||
? 'bg-[#012030] border-cyan-400/30 shadow-2xl shadow-cyan-900/20 scale-105'
|
||||
: 'bg-white border-[#dbe8ff]'
|
||||
@@ -304,7 +342,7 @@ export default function AutomationPage() {
|
||||
{/* ─── 프리미엄 툴 ─── */}
|
||||
<div className="px-6 pb-4 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<span className="inline-flex items-center gap-1.5 bg-amber-500/10 border border-amber-400/30 text-amber-700 text-xs font-bold px-3 py-1 rounded-full mb-3">
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||||
PREMIUM TOOLS
|
||||
@@ -313,9 +351,9 @@ export default function AutomationPage() {
|
||||
<p className="text-slate-500 text-sm">전문 분야별 고급 자동화 프로그램. 구매 후 소스코드 전달 + 1개월 무상 지원.</p>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 gap-6">
|
||||
{premiumTools.map((tool) => (
|
||||
{premiumTools.map((tool, idx) => (
|
||||
<div key={tool.id}
|
||||
className="rounded-2xl overflow-hidden border border-white/10 shadow-xl flex flex-col"
|
||||
className={`rounded-2xl overflow-hidden border border-white/10 shadow-xl flex flex-col reveal reveal-d${idx + 1}`}
|
||||
style={{ background: `linear-gradient(145deg, ${tool.bgFrom}, ${tool.bgTo})` }}>
|
||||
{/* 카드 헤더 */}
|
||||
<div className="p-6 pb-4">
|
||||
@@ -368,15 +406,15 @@ export default function AutomationPage() {
|
||||
{/* ─── 자동화 툴 무료 다운로드 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-cyan-600 text-xs font-bold uppercase tracking-widest mb-2">FREE TOOLS</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b] mb-2">자동화 도구 무료 다운로드</h2>
|
||||
<p className="text-slate-500 text-sm">직접 만들어 사용 중인 자동화 도구를 무료로 공유합니다.<br />필요에 맞게 수정해서 쓰실 수 있어요.</p>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{tools.map((tool) => (
|
||||
{tools.map((tool, idx) => (
|
||||
<div key={tool.id} style={{ borderColor: tool.borderColor, backgroundColor: tool.bgColor }}
|
||||
className="rounded-2xl border-2 p-5 flex flex-col relative">
|
||||
className={`rounded-2xl border-2 p-5 flex flex-col relative reveal reveal-d${idx + 1}`}>
|
||||
{!tool.ready && (
|
||||
<div className="absolute top-3 right-3 bg-slate-200 text-slate-500 text-[10px] font-bold px-2 py-0.5 rounded-full">준비중</div>
|
||||
)}
|
||||
@@ -411,7 +449,7 @@ export default function AutomationPage() {
|
||||
{/* ─── CTA ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="bg-[#012030] rounded-2xl border border-cyan-400/20 p-8 text-center" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 30px)' }}>
|
||||
<div className="bg-[#012030] rounded-2xl border border-cyan-400/20 p-8 text-center reveal" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 30px)' }}>
|
||||
<p className="text-cyan-400 text-xs font-bold uppercase tracking-widest mb-2">FREE CONSULTATION</p>
|
||||
<h3 className="text-white text-2xl font-extrabold mb-2">어떤 업무든 상담해보세요</h3>
|
||||
<p className="text-cyan-100/40 text-sm mb-6">자동화 가능한 업무라면 무엇이든 도와드립니다</p>
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import ContactModal from '../../components/ContactModal';
|
||||
const KAKAO_CHANNEL_URL = process.env.NEXT_PUBLIC_KAKAO_CHANNEL_URL ?? null;
|
||||
|
||||
function useScrollReveal() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
||||
);
|
||||
el.querySelectorAll('.reveal').forEach((child) => observer.observe(child));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
return ref;
|
||||
}
|
||||
|
||||
const CHECKLIST = [
|
||||
'주로 어떤 AI 도구를 사용하는지 (ChatGPT / Claude / Gemini)',
|
||||
'자동화하고 싶은 업무 유형 (이메일 / 보고서 / 코드 등)',
|
||||
@@ -347,6 +369,7 @@ const examples = [
|
||||
export default function PromptPage() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalService, setModalService] = useState('프롬프트 엔지니어링');
|
||||
const containerRef = useScrollReveal();
|
||||
|
||||
const openModal = (service: string) => {
|
||||
setModalService(service);
|
||||
@@ -354,7 +377,32 @@ export default function PromptPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-full bg-[#f0f5ff]">
|
||||
<div ref={containerRef} className="min-h-full bg-[#f0f5ff]">
|
||||
<style>{`
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(1.5rem);
|
||||
transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.reveal-d1 { transition-delay: 80ms; }
|
||||
.reveal-d2 { transition-delay: 160ms; }
|
||||
.reveal-d3 { transition-delay: 240ms; }
|
||||
.reveal-d4 { transition-delay: 320ms; }
|
||||
.reveal-d5 { transition-delay: 400ms; }
|
||||
.prompt-card {
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.prompt-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 40px -12px rgba(0,0,0,0.15);
|
||||
}
|
||||
`}</style>
|
||||
<ContactModal
|
||||
isOpen={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
@@ -396,7 +444,7 @@ export default function PromptPage() {
|
||||
{/* ─── 프리미엄 상품 ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<div className="inline-flex items-center gap-2 bg-fuchsia-500/10 border border-fuchsia-500/30 text-fuchsia-400 text-xs font-extrabold px-4 py-1.5 rounded-full uppercase tracking-widest mb-4">
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="currentColor"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" /></svg>
|
||||
PREMIUM PRODUCTS
|
||||
@@ -405,10 +453,10 @@ export default function PromptPage() {
|
||||
<p className="text-slate-500 text-sm mt-2">전문가가 직접 설계하고 검증한 완성형 프롬프트 패키지 — 구매 즉시 사용 가능</p>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-2 gap-6">
|
||||
{premiumProducts.map((product) => (
|
||||
{premiumProducts.map((product, idx) => (
|
||||
<div
|
||||
key={product.id}
|
||||
className="rounded-2xl overflow-hidden border"
|
||||
className={`rounded-2xl overflow-hidden border prompt-card reveal reveal-d${(idx % 4) + 1}`}
|
||||
style={{ borderColor: product.accentBorder, background: `linear-gradient(135deg, ${product.bgFrom}, ${product.bgTo})` }}
|
||||
>
|
||||
{/* 헤더 */}
|
||||
@@ -505,13 +553,13 @@ export default function PromptPage() {
|
||||
{/* ─── Before/After ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-violet-600 text-xs font-bold uppercase tracking-widest mb-2">BEFORE vs AFTER</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">이런 차이가 납니다</h2>
|
||||
</div>
|
||||
<div className="space-y-5">
|
||||
{examples.map((ex) => (
|
||||
<div key={ex.type} className="bg-white rounded-2xl border border-[#dbe8ff] overflow-hidden">
|
||||
{examples.map((ex, idx) => (
|
||||
<div key={ex.type} className={`bg-white rounded-2xl border border-[#dbe8ff] overflow-hidden reveal reveal-d${idx + 1}`}>
|
||||
<div className="bg-[#04102b] px-5 py-3 flex items-center justify-between">
|
||||
<span className="text-white/60 text-xs font-semibold font-mono">{ex.type} 예시</span>
|
||||
<span className="bg-violet-400/20 border border-violet-400/30 text-violet-300 text-xs px-3 py-1 rounded-full">{ex.improvement}</span>
|
||||
@@ -543,13 +591,13 @@ export default function PromptPage() {
|
||||
{/* ─── 활용 분야 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-violet-600 text-xs font-bold uppercase tracking-widest mb-2">USE CASES</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">활용 분야</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{useCases.map((uc, i) => (
|
||||
<div key={uc.label} className="bg-white rounded-2xl border border-[#dbe8ff] p-5 hover:border-violet-200 transition-colors">
|
||||
<div key={uc.label} className={`bg-white rounded-2xl border border-[#dbe8ff] p-5 hover:border-violet-200 transition-all duration-300 reveal reveal-d${(i % 3) + 1}`}>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-violet-50 border border-violet-200 flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-violet-600 font-extrabold text-xs">{String(i + 1).padStart(2, '0')}</span>
|
||||
@@ -568,13 +616,13 @@ export default function PromptPage() {
|
||||
{/* ─── 요금제 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-[#1a56db] text-xs font-bold uppercase tracking-widest mb-2">PRICING</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">요금제</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{plans.map((plan) => (
|
||||
<div key={plan.name} className={`rounded-2xl border p-6 relative flex flex-col ${
|
||||
{plans.map((plan, idx) => (
|
||||
<div key={plan.name} className={`rounded-2xl border p-6 relative flex flex-col reveal reveal-d${idx + 1} ${
|
||||
plan.highlight
|
||||
? 'bg-gradient-to-br from-[#0d0a2e] to-[#1a0f5c] border-violet-400/30 shadow-2xl shadow-violet-900/20 scale-105'
|
||||
: 'bg-white border-[#dbe8ff]'
|
||||
@@ -615,7 +663,7 @@ export default function PromptPage() {
|
||||
{/* ─── CTA ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-[#0d0a2e] to-[#1a0f5c] rounded-2xl border border-violet-400/20 p-8 text-center">
|
||||
<div className="bg-gradient-to-r from-[#0d0a2e] to-[#1a0f5c] rounded-2xl border border-violet-400/20 p-8 text-center reveal">
|
||||
<p className="text-violet-400 text-xs font-bold uppercase tracking-widest mb-2">GET STARTED</p>
|
||||
<h3 className="text-white text-2xl font-extrabold mb-2">AI를 제대로 활용하고 싶다면</h3>
|
||||
<p className="text-violet-100/40 text-sm mb-6">업무 분석 인터뷰 → 맞춤 설계 → 가이드 제공</p>
|
||||
|
||||
Reference in New Issue
Block a user