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:
2026-04-03 07:24:30 +09:00
parent 3537862c99
commit c7bf0253e3
6 changed files with 579 additions and 120 deletions

View File

@@ -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>