|
|
|
@ -1,49 +1,13 @@ |
|
|
|
import useFadeIn from "../../hooks/useFadeIn"; |
|
|
|
import useFadeIn from "../../hooks/useFadeIn"; |
|
|
|
import SubHero from "../../components/SubHero"; |
|
|
|
import SubHero from "../../components/SubHero"; |
|
|
|
import { useRef, useState } from "react"; |
|
|
|
import { useRef } from "react"; |
|
|
|
import { motion, useInView, AnimatePresence } from "framer-motion"; |
|
|
|
import { motion, useInView } from "framer-motion"; |
|
|
|
import { |
|
|
|
import { Search, Armchair, CalendarClock, Wallet, Ticket } from "lucide-react"; |
|
|
|
Search, |
|
|
|
|
|
|
|
Armchair, |
|
|
|
|
|
|
|
CalendarClock, |
|
|
|
|
|
|
|
Wallet, |
|
|
|
|
|
|
|
Ticket, |
|
|
|
|
|
|
|
ArrowUpRight, |
|
|
|
|
|
|
|
} from "lucide-react"; |
|
|
|
|
|
|
|
const ease = [0.22, 1, 0.36, 1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const FUNCTIONS = [ |
|
|
|
|
|
|
|
{ num: "01", img: "./images/s1-01.jpg", label: "비행가능 지역 및 공역표출" }, |
|
|
|
|
|
|
|
{ num: "02", img: "./images/s1-02.jpg", label: "비행체 위치 표출" }, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
num: "03", |
|
|
|
|
|
|
|
img: "./images/s1-03.jpg", |
|
|
|
|
|
|
|
label: "항공기 비행경로 및 히스토리 표출", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ num: "04", img: "./images/s1-04.jpg", label: "항공기 비행정보 표출" }, |
|
|
|
|
|
|
|
{ num: "05", img: "./images/s1-05.jpg", label: "비행계획서 조회" }, |
|
|
|
|
|
|
|
{ num: "06", img: "./images/s1-06.jpg", label: "비정상 상황의 경보 표출" }, |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const FLOW = [ |
|
|
|
const ease = [0.22, 1, 0.36, 1]; |
|
|
|
{ |
|
|
|
|
|
|
|
step: "01", |
|
|
|
|
|
|
|
label: "비행체 식별", |
|
|
|
|
|
|
|
desc: "항공기·드론·선박 실시간 신호 수집", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
step: "02", |
|
|
|
|
|
|
|
label: "데이터 수집", |
|
|
|
|
|
|
|
desc: "위치·속도·고도·식별코드 통합 처리", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ step: "03", label: "관제 서버", desc: "통합 관제 서버 분석 및 상황 판단" }, |
|
|
|
|
|
|
|
{ step: "04", label: "모니터링", desc: "관제사 화면 실시간 현황 표출" }, |
|
|
|
|
|
|
|
{ step: "05", label: "경보·대응", desc: "이상 상황 감지 즉시 경보 및 대응" }, |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function IbePage() { |
|
|
|
function IbePage() { |
|
|
|
const basePath = import.meta.env.BASE_URL; |
|
|
|
const basePath = import.meta.env.BASE_URL; |
|
|
|
|
|
|
|
|
|
|
|
const ref = useFadeIn(); |
|
|
|
const ref = useFadeIn(); |
|
|
|
const introRef = useRef(null); |
|
|
|
const introRef = useRef(null); |
|
|
|
const introInView = useInView(introRef, { once: true, margin: "-60px" }); |
|
|
|
const introInView = useInView(introRef, { once: true, margin: "-60px" }); |
|
|
|
@ -51,33 +15,21 @@ function IbePage() { |
|
|
|
const channelInView = useInView(channelRef, { once: true, margin: "-60px" }); |
|
|
|
const channelInView = useInView(channelRef, { once: true, margin: "-60px" }); |
|
|
|
const bookingRef = useRef(null); |
|
|
|
const bookingRef = useRef(null); |
|
|
|
const bookingInView = useInView(bookingRef, { once: true, margin: "-60px" }); |
|
|
|
const bookingInView = useInView(bookingRef, { once: true, margin: "-60px" }); |
|
|
|
const funcRef = useRef(null); |
|
|
|
|
|
|
|
const funcInView = useInView(funcRef, { once: true, margin: "-80px" }); |
|
|
|
|
|
|
|
const flowRef = useRef(null); |
|
|
|
|
|
|
|
const flowInView = useInView(flowRef, { once: true, margin: "-60px" }); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const SOLUTION_NAV = [ |
|
|
|
const SOLUTION_NAV = [ |
|
|
|
{ label: "비행상황관리 시스템", to: "/solution/flight-control" }, |
|
|
|
{ label: "비행상황관리 시스템", to: "/solution/flight-control" }, |
|
|
|
{ label: "IBE", to: "/solution/ibe" }, |
|
|
|
{ label: "IBE", to: "/solution/ibe" }, |
|
|
|
// { label: "스마트 관광 예약 플랫폼", to: "/solution/smart-tour" }, |
|
|
|
|
|
|
|
// { label: "KT G-cloud 인천총판", to: "/solution/kt-gcloud" }, |
|
|
|
|
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
const [activeIdx, setActiveIdx] = useState(0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<article ref={ref}> |
|
|
|
<article ref={ref}> |
|
|
|
<SubHero |
|
|
|
<SubHero |
|
|
|
label="SOLUTION" |
|
|
|
label="SOLUTION" |
|
|
|
title={ |
|
|
|
title={ |
|
|
|
<> |
|
|
|
<> |
|
|
|
{/* <span style={{ color: "#111" }}>통합 항공 예약 시스템</span> |
|
|
|
|
|
|
|
<br /> */} |
|
|
|
|
|
|
|
<em>IBE</em> |
|
|
|
<em>IBE</em> |
|
|
|
</> |
|
|
|
</> |
|
|
|
} |
|
|
|
} |
|
|
|
// desc="온라인 항공 예약과 발권을 위한 통합 엔진으로 |
|
|
|
|
|
|
|
// 안정적이고 확장 가능한 예약 플랫폼을 제공합니다." |
|
|
|
|
|
|
|
navItems={SOLUTION_NAV} |
|
|
|
navItems={SOLUTION_NAV} |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
@ -112,7 +64,7 @@ function IbePage() { |
|
|
|
> |
|
|
|
> |
|
|
|
IBE(Internet Booking Engine)는 항공권 검색부터 예약, 결제, |
|
|
|
IBE(Internet Booking Engine)는 항공권 검색부터 예약, 결제, |
|
|
|
<br /> |
|
|
|
<br /> |
|
|
|
발권까지 모든 프로세스를 지원하는 차세대 예약 엔진 입니다.{" "} |
|
|
|
발권까지 모든 프로세스를 지원하는 차세대 예약 엔진 입니다. |
|
|
|
<br /> |
|
|
|
<br /> |
|
|
|
다양한 채널과 시스템을 유연하게 연동하여 최적의 예약 경험을 |
|
|
|
다양한 채널과 시스템을 유연하게 연동하여 최적의 예약 경험을 |
|
|
|
제공합니다. |
|
|
|
제공합니다. |
|
|
|
@ -139,7 +91,6 @@ function IbePage() { |
|
|
|
))} |
|
|
|
))} |
|
|
|
</motion.div> |
|
|
|
</motion.div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<motion.div |
|
|
|
<motion.div |
|
|
|
className="fc-intro__right" |
|
|
|
className="fc-intro__right" |
|
|
|
initial={{ opacity: 0, x: 40 }} |
|
|
|
initial={{ opacity: 0, x: 40 }} |
|
|
|
@ -148,7 +99,7 @@ function IbePage() { |
|
|
|
> |
|
|
|
> |
|
|
|
<img |
|
|
|
<img |
|
|
|
src={`${basePath}images/ibe_computer.png`} |
|
|
|
src={`${basePath}images/ibe_computer.png`} |
|
|
|
alt="비행상황관리 시스템" |
|
|
|
alt="IBE" |
|
|
|
className="fc-intro__monitor" |
|
|
|
className="fc-intro__monitor" |
|
|
|
/> |
|
|
|
/> |
|
|
|
</motion.div> |
|
|
|
</motion.div> |
|
|
|
@ -174,6 +125,20 @@ function IbePage() { |
|
|
|
</motion.h2> |
|
|
|
</motion.h2> |
|
|
|
|
|
|
|
|
|
|
|
<div className="ibe-booking__flow"> |
|
|
|
<div className="ibe-booking__flow"> |
|
|
|
|
|
|
|
<svg width="0" height="0" style={{ position: "absolute" }}> |
|
|
|
|
|
|
|
<defs> |
|
|
|
|
|
|
|
<linearGradient |
|
|
|
|
|
|
|
id="ibe-icon-grad" |
|
|
|
|
|
|
|
x1="0%" |
|
|
|
|
|
|
|
y1="0%" |
|
|
|
|
|
|
|
x2="100%" |
|
|
|
|
|
|
|
y2="100%" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<stop offset="0%" stopColor="#60a5fa" /> |
|
|
|
|
|
|
|
<stop offset="100%" stopColor="#f472b6" /> |
|
|
|
|
|
|
|
</linearGradient> |
|
|
|
|
|
|
|
</defs> |
|
|
|
|
|
|
|
</svg> |
|
|
|
{[ |
|
|
|
{[ |
|
|
|
{ |
|
|
|
{ |
|
|
|
num: "01", |
|
|
|
num: "01", |
|
|
|
@ -203,7 +168,7 @@ function IbePage() { |
|
|
|
num: "05", |
|
|
|
num: "05", |
|
|
|
icon: Ticket, |
|
|
|
icon: Ticket, |
|
|
|
label: "발권", |
|
|
|
label: "발권", |
|
|
|
desc: "E-ticket 발행 및\n예약 확인을 통해\n예약 완료", |
|
|
|
desc: "E-Ticket 발행 후\n예약 정보를 확인하고\n예약 완료", |
|
|
|
}, |
|
|
|
}, |
|
|
|
].map((item, i) => { |
|
|
|
].map((item, i) => { |
|
|
|
const Icon = item.icon; |
|
|
|
const Icon = item.icon; |
|
|
|
@ -217,24 +182,6 @@ function IbePage() { |
|
|
|
transition={{ duration: 0.6, ease, delay: 0.2 + i * 0.1 }} |
|
|
|
transition={{ duration: 0.6, ease, delay: 0.2 + i * 0.1 }} |
|
|
|
> |
|
|
|
> |
|
|
|
<div className="ibe-booking__circle"> |
|
|
|
<div className="ibe-booking__circle"> |
|
|
|
<svg |
|
|
|
|
|
|
|
width="0" |
|
|
|
|
|
|
|
height="0" |
|
|
|
|
|
|
|
style={{ position: "absolute" }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<defs> |
|
|
|
|
|
|
|
<linearGradient |
|
|
|
|
|
|
|
id="ibe-icon-grad" |
|
|
|
|
|
|
|
x1="0%" |
|
|
|
|
|
|
|
y1="0%" |
|
|
|
|
|
|
|
x2="100%" |
|
|
|
|
|
|
|
y2="100%" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<stop offset="0%" stopColor="#a78bfa" /> |
|
|
|
|
|
|
|
<stop offset="100%" stopColor="#ec4899" /> |
|
|
|
|
|
|
|
</linearGradient> |
|
|
|
|
|
|
|
</defs> |
|
|
|
|
|
|
|
</svg> |
|
|
|
|
|
|
|
<Icon |
|
|
|
<Icon |
|
|
|
size={32} |
|
|
|
size={32} |
|
|
|
strokeWidth={1.5} |
|
|
|
strokeWidth={1.5} |
|
|
|
@ -266,6 +213,7 @@ function IbePage() { |
|
|
|
})} |
|
|
|
})} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
{/* MULTI CHANNEL INTEGRATION */} |
|
|
|
{/* MULTI CHANNEL INTEGRATION */} |
|
|
|
<section className="ibe-channel-section" ref={channelRef}> |
|
|
|
<section className="ibe-channel-section" ref={channelRef}> |
|
|
|
<motion.span |
|
|
|
<motion.span |
|
|
|
@ -329,7 +277,6 @@ function IbePage() { |
|
|
|
className="ibe-channel__path" |
|
|
|
className="ibe-channel__path" |
|
|
|
d="M830 245 C980 245 1020 335 1190 335" |
|
|
|
d="M830 245 C980 245 1020 335 1190 335" |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
<circle cx="350" cy="95" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="350" cy="95" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="350" cy="215" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="350" cy="215" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="350" cy="335" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="350" cy="335" r="4" className="ibe-channel__dot" /> |
|
|
|
@ -337,6 +284,7 @@ function IbePage() { |
|
|
|
<circle cx="1190" cy="215" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="1190" cy="215" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="1190" cy="335" r="4" className="ibe-channel__dot" /> |
|
|
|
<circle cx="1190" cy="335" r="4" className="ibe-channel__dot" /> |
|
|
|
</svg> |
|
|
|
</svg> |
|
|
|
|
|
|
|
|
|
|
|
<div className="ibe-channel__cols-wrap"> |
|
|
|
<div className="ibe-channel__cols-wrap"> |
|
|
|
<div className="ibe-channel__col ibe-channel__col--left"> |
|
|
|
<div className="ibe-channel__col ibe-channel__col--left"> |
|
|
|
{[ |
|
|
|
{[ |
|
|
|
@ -344,7 +292,13 @@ function IbePage() { |
|
|
|
{ img: "ibe_pal_icon2.png", label: "GDS" }, |
|
|
|
{ img: "ibe_pal_icon2.png", label: "GDS" }, |
|
|
|
{ img: "ibe_pal_icon3.png", label: "호텔" }, |
|
|
|
{ img: "ibe_pal_icon3.png", label: "호텔" }, |
|
|
|
].map((item, i) => ( |
|
|
|
].map((item, i) => ( |
|
|
|
<motion.div key={i} className="ibe-channel__card"> |
|
|
|
<motion.div |
|
|
|
|
|
|
|
key={i} |
|
|
|
|
|
|
|
className="ibe-channel__card" |
|
|
|
|
|
|
|
initial={{ opacity: 0, x: -24 }} |
|
|
|
|
|
|
|
animate={channelInView ? { opacity: 1, x: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, ease, delay: 0.4 + i * 0.1 }} |
|
|
|
|
|
|
|
> |
|
|
|
<img |
|
|
|
<img |
|
|
|
src={`${basePath}images/${item.img}`} |
|
|
|
src={`${basePath}images/${item.img}`} |
|
|
|
alt={item.label} |
|
|
|
alt={item.label} |
|
|
|
@ -355,10 +309,13 @@ function IbePage() { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div className="ibe-channel__center"> |
|
|
|
<div className="ibe-channel__center"> |
|
|
|
<img |
|
|
|
<motion.img |
|
|
|
src={`${basePath}images/ibe_pal_item.png`} |
|
|
|
src={`${basePath}images/ibe_pal_item.png`} |
|
|
|
alt="PAL IBE" |
|
|
|
alt="PAL IBE" |
|
|
|
className="ibe-channel__center-img" |
|
|
|
className="ibe-channel__center-img" |
|
|
|
|
|
|
|
initial={{ opacity: 0, scale: 0.8 }} |
|
|
|
|
|
|
|
animate={channelInView ? { opacity: 1, scale: 1 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.8, ease, delay: 0.5 }} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
@ -368,7 +325,13 @@ function IbePage() { |
|
|
|
{ img: "ibe_pal_icon5.png", label: "여행사/OTA" }, |
|
|
|
{ img: "ibe_pal_icon5.png", label: "여행사/OTA" }, |
|
|
|
{ img: "ibe_pal_icon6.png", label: "보험" }, |
|
|
|
{ img: "ibe_pal_icon6.png", label: "보험" }, |
|
|
|
].map((item, i) => ( |
|
|
|
].map((item, i) => ( |
|
|
|
<motion.div key={i} className="ibe-channel__card"> |
|
|
|
<motion.div |
|
|
|
|
|
|
|
key={i} |
|
|
|
|
|
|
|
className="ibe-channel__card" |
|
|
|
|
|
|
|
initial={{ opacity: 0, x: 24 }} |
|
|
|
|
|
|
|
animate={channelInView ? { opacity: 1, x: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, ease, delay: 0.4 + i * 0.1 }} |
|
|
|
|
|
|
|
> |
|
|
|
<img |
|
|
|
<img |
|
|
|
src={`${basePath}images/${item.img}`} |
|
|
|
src={`${basePath}images/${item.img}`} |
|
|
|
alt={item.label} |
|
|
|
alt={item.label} |
|
|
|
@ -380,129 +343,6 @@ function IbePage() { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</motion.div> |
|
|
|
</motion.div> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
{/* 4. 주요기능 */} |
|
|
|
|
|
|
|
<section className="fc-functions" ref={funcRef}> |
|
|
|
|
|
|
|
<motion.span |
|
|
|
|
|
|
|
className="fc-section-title" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
|
|
|
animate={funcInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
주요기능 |
|
|
|
|
|
|
|
</motion.span> |
|
|
|
|
|
|
|
<div className="fc-functions__body"> |
|
|
|
|
|
|
|
<ul className="fc-func-list"> |
|
|
|
|
|
|
|
{FUNCTIONS.map(({ num, label }, i) => ( |
|
|
|
|
|
|
|
<motion.li |
|
|
|
|
|
|
|
key={num} |
|
|
|
|
|
|
|
className={`fc-func-item${activeIdx === i ? " is-active" : ""}`} |
|
|
|
|
|
|
|
onMouseEnter={() => setActiveIdx(i)} |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
|
|
|
animate={funcInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.5, ease, delay: 0.05 * i }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<span className="fc-func-item__num">{num}</span> |
|
|
|
|
|
|
|
<span className="fc-func-item__label">{label}</span> |
|
|
|
|
|
|
|
<motion.span |
|
|
|
|
|
|
|
className="fc-func-item__arrow" |
|
|
|
|
|
|
|
initial={{ opacity: 0, x: -6 }} |
|
|
|
|
|
|
|
animate={ |
|
|
|
|
|
|
|
activeIdx === i |
|
|
|
|
|
|
|
? { opacity: 1, x: 0 } |
|
|
|
|
|
|
|
: { opacity: 0, x: -6 } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
transition={{ duration: 0.2 }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<ArrowUpRight size={16} strokeWidth={1.5} /> |
|
|
|
|
|
|
|
</motion.span> |
|
|
|
|
|
|
|
<motion.div |
|
|
|
|
|
|
|
className="fc-func-item__line" |
|
|
|
|
|
|
|
animate={{ scaleX: activeIdx === i ? 1 : 0 }} |
|
|
|
|
|
|
|
transition={{ duration: 0.35, ease }} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</motion.li> |
|
|
|
|
|
|
|
))} |
|
|
|
|
|
|
|
</ul> |
|
|
|
|
|
|
|
<div className="fc-func-display"> |
|
|
|
|
|
|
|
<AnimatePresence mode="wait"> |
|
|
|
|
|
|
|
<motion.div |
|
|
|
|
|
|
|
key={activeIdx} |
|
|
|
|
|
|
|
className="fc-func-display__inner" |
|
|
|
|
|
|
|
initial={{ opacity: 0 }} |
|
|
|
|
|
|
|
animate={{ opacity: 1 }} |
|
|
|
|
|
|
|
exit={{ opacity: 0 }} |
|
|
|
|
|
|
|
transition={{ duration: 0.35, ease: "easeInOut" }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<img |
|
|
|
|
|
|
|
src={FUNCTIONS[activeIdx].img} |
|
|
|
|
|
|
|
alt={FUNCTIONS[activeIdx].label} |
|
|
|
|
|
|
|
className="fc-func-display__img" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
<div className="fc-func-display__caption"> |
|
|
|
|
|
|
|
<motion.span |
|
|
|
|
|
|
|
key={`num-${activeIdx}`} |
|
|
|
|
|
|
|
className="fc-func-display__num" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 6 }} |
|
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }} |
|
|
|
|
|
|
|
transition={{ duration: 0.25 }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{FUNCTIONS[activeIdx].num} |
|
|
|
|
|
|
|
</motion.span> |
|
|
|
|
|
|
|
<motion.span |
|
|
|
|
|
|
|
key={`label-${activeIdx}`} |
|
|
|
|
|
|
|
className="fc-func-display__label" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 6 }} |
|
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }} |
|
|
|
|
|
|
|
transition={{ duration: 0.25, delay: 0.05 }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{FUNCTIONS[activeIdx].label} |
|
|
|
|
|
|
|
</motion.span> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</motion.div> |
|
|
|
|
|
|
|
</AnimatePresence> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* 5. 시스템 구성 흐름 */} |
|
|
|
|
|
|
|
<section className="fc-flow" ref={flowRef}> |
|
|
|
|
|
|
|
<motion.span |
|
|
|
|
|
|
|
className="fc-section-title" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
|
|
|
animate={flowInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
시스템 구성 |
|
|
|
|
|
|
|
</motion.span> |
|
|
|
|
|
|
|
<div className="fc-flow__row"> |
|
|
|
|
|
|
|
{FLOW.map(({ step, label, desc }, i) => ( |
|
|
|
|
|
|
|
<motion.div |
|
|
|
|
|
|
|
key={step} |
|
|
|
|
|
|
|
className="fc-flow__item" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
|
|
|
animate={flowInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.5, ease, delay: 0.08 * i }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<span className="fc-flow__step">{step}</span> |
|
|
|
|
|
|
|
<span className="fc-flow__label">{label}</span> |
|
|
|
|
|
|
|
<p className="fc-flow__desc">{desc}</p> |
|
|
|
|
|
|
|
{i < FLOW.length - 1 && ( |
|
|
|
|
|
|
|
<motion.div |
|
|
|
|
|
|
|
className="fc-flow__arrow" |
|
|
|
|
|
|
|
initial={{ opacity: 0, scaleX: 0 }} |
|
|
|
|
|
|
|
animate={flowInView ? { opacity: 1, scaleX: 1 } : {}} |
|
|
|
|
|
|
|
transition={{ |
|
|
|
|
|
|
|
duration: 0.4, |
|
|
|
|
|
|
|
ease, |
|
|
|
|
|
|
|
delay: 0.08 * i + 0.3, |
|
|
|
|
|
|
|
}} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</motion.div> |
|
|
|
|
|
|
|
))} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</section> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</article> |
|
|
|
</article> |
|
|
|
|