Browse Source

feat : utm/uatm 페이지 완료

remotes/origin/main
이시연 2 weeks ago
parent
commit
016ea1832d
  1. 29
      src/css/common.css
  2. 166
      src/pages/utm/IntroPage.jsx

29
src/css/common.css

@ -1111,19 +1111,21 @@ body{overflow-x:hidden;}
.utm-intro { display: flex; flex-direction: column; gap: 52px; padding: 80px 0;}
.utm-intro__left { flex: 1; }
.utm-intro__top { max-width: 560px; }
.utm-what__title { font-size: 40px; font-weight: 700; line-height: 1.35; color: #fff; margin-bottom: 20px; word-break: keep-all; }
.utm-what__title em { background: linear-gradient(90deg, #ff6b6b, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-style: normal; }
.utm-intro__title { font-size: 40px; font-weight: 700; line-height: 1.35; color: #111; margin: 12px 0 20px; word-break: keep-all; }
.utm-intro__title em { color: #1A1F5E; font-style: normal; -webkit-text-fill-color: #1A1F5E; }
.utm-intro__desc { font-size: 14px; color: #555; line-height: 1.85; word-break: keep-all; }
.utm-intro__desc strong { font-weight: 600; color: #111; }
.utm-intro__cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; list-style: none; margin-left: auto; }
.utm-intro__card { background: #fff; border-radius: 16px; padding: 28px 24px 32px; position: relative; overflow: hidden; min-height: 180px; display: flex; flex-direction: column; justify-content: flex-end; border: 1px solid #EBEBEB; box-shadow: 0 4px 16px rgba(0,0,0,0.06); width: 260px; height: 260px;}
.utm-intro__card { background: #fff; border-radius: 16px; padding: 28px 24px 32px; position: relative; overflow: hidden; min-height: 180px; display: flex; flex-direction: column; justify-content: flex-end; border: 1px solid #EBEBEB; box-shadow: 0 4px 16px rgba(0,0,0,0.06); width: 240px; height: 240px;}
.utm-intro__card-num { position: absolute; top: 16px; right: 20px; font-size: 56px; font-weight: 700; color: rgba(0,0,0,0.07); line-height: 1; }
.utm-intro__card-img { width: 64px; height: 64px; object-fit: contain; margin-bottom: 16px; }
.utm-intro__card-icon { margin-bottom: 14px; display: block; background: linear-gradient(135deg, #C850C0, #4158D0); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.utm-intro__card-label { font-size: 18px; font-weight: 600; color: #222; line-height: 1.4; word-break: keep-all; }
.utm-what { background: linear-gradient(135deg, #1A0A2E 0%, #0D0D1A 50%, #1A0820 100%); padding: 100px 0; }
.utm-what__top { text-align: center; margin-bottom: 64px; }
.utm-what__title { font-size: 40px; font-weight: 700; line-height: 1.35; color: #fff; margin-bottom: 20px; word-break: keep-all; }
.utm-what__title em { background: linear-gradient(90deg, #ff6b6b, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-style: normal; }
.utm-what__inner { display: flex; align-items: center; justify-content: space-between; gap: 80px; min-height: 500px; }
.utm-what__left { flex: 0 0 480px; }
.utm-what__eyebrow { display: inline-block; font-size: 12px; font-weight: 500; letter-spacing: 2px; text-transform: uppercase; color: rgba(255,255,255,0.4); margin-bottom: 20px; }
@ -1235,3 +1237,20 @@ body{overflow-x:hidden;}
.utm-evo__item--highlight .utm-evo__dot { border-color: transparent; color: #fff; }
.utm-evo__item--highlight .utm-evo__dot::before { opacity: 1; }
.utm-evo__item--highlight .utm-evo__card-tag { background: rgba(26,31,94,0.08); color: var(--navy); }
.utm-cap { padding: 80px 0; background: #F7F8FA; }
.utm-cap__top { max-width: 560px; }
.utm-cap .inner-wrap { display: flex; flex-direction: column; gap: 52px; }
.utm-cap__title { font-size: 40px; font-weight: 700; line-height: 1.35; color: #111; margin: 12px 0 20px; word-break: keep-all; }
.utm-cap__title em { color: var(--color-primary, #E8193C); font-style: normal; }
.utm-cap__desc { font-size: 14px; color: #555; line-height: 1.85; word-break: keep-all; }
.utm-cap__cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; width: 100%; background: #F7F8FA; border-radius: 20px; padding: 32px; }
.utm-cap__card { background: #fff; border-radius: 16px; padding: 28px 24px; position: relative; overflow: hidden; display: flex; flex-direction: column; justify-content: flex-start; border: 1px solid #EBEBEB; box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
.utm-cap__card-num { position: absolute; top: 16px; right: 20px; font-size: 72px; font-weight: 700; color: rgba(0,0,0,0.05); line-height: 1; }
.utm-cap__card-body { display: flex; flex-direction: column; gap: 16px; }
.utm-cap__card-name { font-size: 18px; font-weight: 700; color: #111; line-height: 1.35; word-break: keep-all; display: block; }
.utm-cap__card-name::before { content: ''; display: block; width: 24px; height: 2.5px; background: var(--cap-color); border-radius: 2px; margin-bottom: 14px; }
.utm-cap__card-tags { list-style: none; display: flex; flex-direction: column; gap: 7px; }
.utm-cap__card-tags li { font-size: 13px; color: #666; line-height: 1.5; display: flex; align-items: flex-start; gap: 8px; }
.utm-cap__card-tags li::before { content: ''; width: 4px; height: 4px; border-radius: 50%; background: var(--cap-color); flex-shrink: 0; margin-top: 6px; opacity: 0.6; }

166
src/pages/utm/IntroPage.jsx

@ -11,18 +11,20 @@ import {
FileCheck,
Database,
Layers,
Navigation,
CheckCircle,
Radio,
} from "lucide-react";
const ease = [0.22, 1, 0.36, 1];
const basePath = import.meta.env.BASE_URL;
const INTRO_CARDS = [
{ label: "비행 계획", img: `${basePath}/images/utm_intro_icon1.png` },
{ label: "비행 승인", img: `${basePath}/images/utm_intro_icon2.png` },
{ label: "실시간 관제", img: `${basePath}/images/utm_intro_icon3.png` },
{ label: "데이터 관리", img: `${basePath}/images/utm_intro_icon4.png` },
{ label: "비행 계획", icon: Navigation },
{ label: "비행 승인", icon: CheckCircle },
{ label: "실시간 관제", icon: Radio },
{ label: "데이터 관리", icon: Database },
];
const UTM_WHAT_LEFT = [
{ icon: MapPin, label: "실시간 위치 감시" },
{ icon: ShieldAlert, label: "충돌 위험 사전 분석" },
@ -61,7 +63,7 @@ const EVO_ITEMS = [
<>
무인기 교통관리 시스템 도입.
<br />
비행 경로 승인·충돌 방지· 공역 관리 체계화.
비행 경로 승인·충돌 방지·공역 관리 체계화.
</>
),
tag: "공역 관리 시스템",
@ -99,6 +101,49 @@ const EVO_ITEMS = [
active: true,
},
];
const SECTORS = [
{
id: 0,
num: "01",
name: "UAM 기체 운용",
color: "#E8193C",
tags: [
"eVTOL 기체 직접 운용",
"실 운용 데이터 기반 검증",
"국내 유일 통합 사업자",
"안전 관리 체계 구축",
],
},
{
id: 1,
num: "02",
name: "UTM 관제 기술",
color: "#1A1F5E",
tags: [
"실시간 공역 감시·관제",
"충돌 위험 사전 분석",
"비행 경로 승인 시스템",
"지오펜싱·공역 설정",
],
},
{
id: 2,
num: "03",
name: "통합 항공 데이터 플랫폼",
color: "#3D3D3D",
tags: [
"비행 데이터 수집·분석",
"UAM·드론 시스템 연동",
"기상 정보 자동 연계",
"통합 데이터 관리",
],
},
];
/*
EvoItem
*/
function EvoItem({ item, index }) {
const itemRef = useRef(null);
const itemInView = useInView(itemRef, { once: true, margin: "-80px" });
@ -140,8 +185,7 @@ function EvoItem({ item, index }) {
<div className="utm-evo__empty" />
<div className="utm-evo__dot-wrap">
<div className="utm-evo__dot">
<span>{String(index + 1).padStart(2, "0")}</span>{" "}
{/* 여기 span 추가 */}
<span>{String(index + 1).padStart(2, "0")}</span>
</div>
</div>
<div className="utm-evo__card-wrap utm-evo__card-wrap--right">
@ -163,6 +207,10 @@ function EvoItem({ item, index }) {
</motion.div>
);
}
/*
IntroPage
*/
function IntroPage() {
const ref = useFadeIn();
const UTM_NAV = [
@ -172,11 +220,17 @@ function IntroPage() {
const introRef = useRef(null);
const introInView = useInView(introRef, { once: true, margin: "-60px" });
const whatRef = useRef(null);
const whatInView = useInView(whatRef, { once: true, margin: "-200px" });
const whatInView = useInView(whatRef, { once: true, margin: "-100px" });
const evoRef = useRef(null);
const evoInView = useInView(evoRef, { once: true, margin: "-100px" });
const evoTrackRef = useRef(null);
const capRef = useRef(null);
const capInView = useInView(capRef, { once: true, margin: "-80px" });
const { scrollYProgress } = useScroll({
target: evoTrackRef,
offset: ["start center", "end center"],
@ -196,6 +250,7 @@ function IntroPage() {
/>
<div className="sub-content">
{/* ── 01 Overview ── */}
<div className="inner-wrap">
<section className="utm-intro" ref={introRef}>
<div className="utm-intro__top">
@ -215,7 +270,8 @@ function IntroPage() {
>
PAL Networks의 UTM/UATM은
<br />
<em>도심 항공 관제의 새로운 기준</em>입니다.
<em>도심 항공 관제의 {""}</em>
<em>새로운 기준입니다.</em>
</motion.h2>
<motion.p
className="utm-intro__desc"
@ -242,11 +298,27 @@ function IntroPage() {
<span className="utm-intro__card-num">
{String(i + 1).padStart(2, "0")}
</span>
<img
src={card.img}
alt={card.label}
className="utm-intro__card-img"
/>
<span className="utm-intro__card-icon">
<svg width="0" height="0">
<defs>
<linearGradient
id="icon-grad"
x1="0%"
y1="0%"
x2="100%"
y2="100%"
>
<stop offset="0%" stopColor="#C850C0" />
<stop offset="100%" stopColor="#4158D0" />
</linearGradient>
</defs>
</svg>
<card.icon
size={36}
strokeWidth={1.5}
stroke="url(#icon-grad)"
/>
</span>
<p className="utm-intro__card-label">{card.label}</p>
</motion.li>
))}
@ -254,7 +326,7 @@ function IntroPage() {
</section>
</div>
{/* ── UTM 소개 섹션 ── */}
{/* ── 02 What is UTM ── */}
<section className="utm-what" ref={whatRef}>
<div className="inner-wrap">
<motion.div
@ -286,7 +358,8 @@ function IntroPage() {
transition={{ duration: 0.6, delay: 0.2, ease }}
>
UTM은 단순한 관제 시스템을 넘어, 도심 저고도 공역 전체를
디지털로 운영하는 통합 플랫폼입니다. <br />
디지털로 운영하는 통합 플랫폼입니다.
<br />
실시간 감시부터 충돌 위험 분석, 기상 연계, 공역 관리까지
비행의 모든 순간을 안전하게 연결합니다.
</motion.p>
@ -417,8 +490,8 @@ function IntroPage() {
</div>
</section>
{/* ── 03 Evolution ── */}
<div className="inner-wrap">
{/* ── DRONE → UATM 진화 섹션 ── */}
<section className="utm-evo" ref={evoRef}>
<motion.div
className="utm-evo__left"
@ -460,6 +533,63 @@ function IntroPage() {
</div>
</section>
</div>
{/* ── 04 Capabilities ── */}
<section className="utm-cap" ref={capRef}>
<div className="inner-wrap">
<div className="utm-cap__top">
<motion.span
className="fc-eyebrow"
initial={{ opacity: 0, y: 16 }}
animate={capInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
Capabilities
</motion.span>
<motion.h2
className="utm-cap__title"
initial={{ opacity: 0, y: 20 }}
animate={capInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, delay: 0.1, ease }}
>
PAL Networks의 <br />
<em> 가지 핵심 역량</em>
</motion.h2>
<motion.p
className="utm-cap__desc"
initial={{ opacity: 0, y: 16 }}
animate={capInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2, ease }}
>
기체 운용부터 관제, 데이터까지 하나의 사업자가 모두 보유한
통합 역량입니다.
</motion.p>
</div>
<ul className="utm-cap__cards">
{SECTORS.map((s, i) => (
<motion.li
key={s.id}
className="utm-cap__card"
style={{ "--cap-color": s.color }}
initial={{ opacity: 0, y: 48 }}
animate={capInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, delay: 0.2 + i * 0.12, ease }}
>
<span className="utm-cap__card-num">{s.num}</span>
<div className="utm-cap__card-body">
<strong className="utm-cap__card-name">{s.name}</strong>
<ul className="utm-cap__card-tags">
{s.tags.map((tag) => (
<li key={tag}>{tag}</li>
))}
</ul>
</div>
</motion.li>
))}
</ul>
</div>
</section>
</div>
</article>
);

Loading…
Cancel
Save