Browse Source

feat : utm/uatm 3번째 섹션 작업중

remotes/origin/main
이시연 3 weeks ago
parent
commit
0048243cd9
  1. BIN
      public/images/utm_intro_icon1.png
  2. BIN
      public/images/utm_intro_icon2.png
  3. BIN
      public/images/utm_intro_icon3.png
  4. BIN
      public/images/utm_intro_icon4.png
  5. BIN
      public/images/utm_what_img.png
  6. 92
      src/css/common.css
  7. 373
      src/pages/utm/IntroPage.jsx

BIN
public/images/utm_intro_icon1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

BIN
public/images/utm_intro_icon2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

BIN
public/images/utm_intro_icon3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

BIN
public/images/utm_intro_icon4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
public/images/utm_what_img.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

92
src/css/common.css

@ -1103,4 +1103,94 @@ body{overflow-x:hidden;}
.mt-service-card:nth-child(n+2) { border-top:1px solid var(--color-primary-soft-border); } .mt-service-card:nth-child(n+2) { border-top:1px solid var(--color-primary-soft-border); }
.mt-service-card:nth-child(5) { grid-column:span 1; } .mt-service-card:nth-child(5) { grid-column:span 1; }
} }
/* ================================================================
utm intro Page
================================================================ */
.utm-intro { display: flex; flex-direction: column; gap: 52px; padding: 80px 0;}
.utm-intro__left { flex: 1; }
.utm-intro__top { max-width: 560px; }
.utm-intro__title { font-size: 38px; font-weight: 700; line-height: 1.4; color: #111; margin: 16px 0 18px; word-break: keep-all; }
.utm-intro__title em { color: var(--color-primary); font-style: normal; }
.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-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-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__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: block; font-size: 12px; font-weight: 500; letter-spacing: 2px; text-transform: uppercase; color: #E8193C; margin-bottom: 20px; }
.utm-what__title { font-size: 40px; font-weight: 700; line-height: 1.35; color: #fff; margin-bottom: 24px; word-break: keep-all; }
.utm-what__desc { font-size: 14px; color: rgba(255,255,255,0.55); line-height: 1.9; margin-bottom: 36px; word-break: keep-all; }
.utm-what__list { list-style: none; display: flex; flex-direction: column; gap: 14px; }
.utm-what__list li { display: flex; align-items: center; gap: 12px; font-size: 14px; color: rgba(255,255,255,0.75); font-weight: 500; }
.utm-what__list-dot { width: 6px; height: 6px; border-radius: 50%; background: #E8193C; flex-shrink: 0; }
.utm-what__right { flex: 1; background-size: contain; background-position: center right; background-repeat: no-repeat; min-height: 620px; }
.utm-what__img { width: 100%; max-width: 480px; object-fit: cover; }
.utm-what__img-wrap { position: relative; width: 100%; min-height: 620px; background-size: contain; background-position: center right; background-repeat: no-repeat; }
.utm-what__paths { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.drone { position: absolute; display: flex; align-items: center; justify-content: center; }
.drone--1 { top: 28%; left: 38%; animation: dronefloat1 6s ease-in-out infinite; }
.drone--2 { top: 55%; left: 60%; animation: dronefloat2 8s ease-in-out infinite; }
.drone--3 { top: 40%; left: 52%; animation: dronefloat3 7s ease-in-out infinite; }
.drone__bg { width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; position: relative; z-index: 2; }
.drone__bg--red { background: rgba(232,25,60,0.25); border: 1.5px solid rgba(232,25,60,0.8); box-shadow: 0 0 16px rgba(232,25,60,0.6), 0 0 32px rgba(232,25,60,0.3); }
.drone__bg--blue { background: rgba(59,130,246,0.25); border: 1.5px solid rgba(59,130,246,0.8); box-shadow: 0 0 16px rgba(59,130,246,0.6), 0 0 32px rgba(59,130,246,0.3); }
.drone__bg--purple { background: rgba(167,139,250,0.25); border: 1.5px solid rgba(59,130,246,0.8); box-shadow: 0 0 16px rgba(167,139,250,0.6), 0 0 32px rgba(167,139,250,0.3); }
.drone__ping { position: absolute; width: 48px; height: 48px; border-radius: 50%; z-index: 1; animation: ping 2.5s ease-out infinite; }
.drone__ping--red { border: 1.5px solid rgba(232,25,60,0.6); }
.drone__ping--blue { border: 1.5px solid rgba(59,130,246,0.6); }
.drone__ping--purple { border: 1.5px solid rgba(167,139,250,0.6); }
.drone__ping--delay { animation-delay: 1s; }
.drone-alert { position: absolute; bottom: 120px; left: 198px; width: 200px; background: rgba(255,255,255,0.95); border-radius: 10px; padding: 12px 14px; backdrop-filter: blur(8px); display: flex; gap: 10px; align-items: flex-start; animation: alertAppear 0.5s ease forwards; opacity: 0; animation-delay: 1s; box-shadow: 0 0 20px rgba(232,25,60,0.2); }
.drone-alert__dot { width: 8px; height: 8px; border-radius: 50%; background: #E8193C; flex-shrink: 0; margin-top: 3px; animation: dotBlink 1s ease-in-out infinite; }
.drone-alert__title { font-size: 12px; font-weight: 600; color: #E8193C; margin-bottom: 4px; }
.drone-alert__desc { font-size: 11px; color: #444; line-height: 1.5; margin-bottom: 4px; }
.drone-alert__pos { font-size: 10px; color: #888; font-variant-numeric: tabular-nums; }
@keyframes alertAppear { from{opacity:0; transform:translateY(-8px);} to{opacity:1; transform:translateY(0);} }
@keyframes dotBlink { 0%,100%{opacity:1;} 50%{opacity:0.2;} }
@keyframes dronefloat1 { 0%,100%{transform:translate(0,0) rotate(0deg);} 33%{transform:translate(12px,-10px) rotate(3deg);} 66%{transform:translate(-8px,6px) rotate(-2deg);} }
@keyframes dronefloat2 { 0%,100%{transform:translate(0,0) rotate(0deg);} 33%{transform:translate(-10px,-14px) rotate(-3deg);} 66%{transform:translate(8px,8px) rotate(2deg);} }
@keyframes dronefloat3 { 0%,100%{transform:translate(0,0) rotate(0deg);} 50%{transform:translate(10px,-12px) rotate(4deg);} }
@keyframes ping { 0%{transform:scale(1);opacity:0.8;} 100%{transform:scale(2.8);opacity:0;} }
.utm-evo { display: flex; gap: 80px; align-items: flex-start; padding: 100px 48px; }
.utm-evo__left { flex: 0 0 420px; }
.utm-evo__eyebrow { font-size: 11px; font-weight: 500; letter-spacing: 2px; color: #E8193C; text-transform: uppercase; display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
.utm-evo__eyebrow::before { content: ''; width: 20px; height: 1.5px; background: #E8193C; }
.utm-evo__title { font-size: 36px; font-weight: 700; line-height: 1.35; color: #111; margin-bottom: 20px; word-break: keep-all; }
.utm-evo__title em { color: var(--color-primary); font-style: normal; }
.utm-evo__desc { font-size: 14px; color: #777; line-height: 1.85; margin-bottom: 32px; word-break: keep-all; }
.utm-evo__chip { display: inline-flex; align-items: center; gap: 8px; background: #111; color: #fff; font-size: 12px; font-weight: 600; padding: 10px 18px; border-radius: 8px; }
.utm-evo__chip-dot { width: 7px; height: 7px; border-radius: 50%; background: #E8193C; flex-shrink: 0; }
.utm-evo__right { flex: 1; }
.utm-evo__track { display: flex; flex-direction: column; position: relative; }
.utm-evo__track::before { content: ''; position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: #EBEBEB; transform: translateX(-50%); z-index: 0; }
.utm-evo__item { display: grid; grid-template-columns: 1fr 56px 1fr; align-items: center; padding: 20px 0; }
.utm-evo__empty { flex: 1; }
.utm-evo__card-wrap--left { padding-right: 28px; }
.utm-evo__card-wrap--right { padding-left: 28px; }
.utm-evo__dot-wrap { display: flex; justify-content: center; align-items: center; z-index: 1; position: relative; }
.utm-evo__dot { width: 44px; height: 44px; border-radius: 50%; background: #F4F4F6; border: 3px solid #fff; box-shadow: 0 0 0 1.5px #E0E0E0; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; color: #bbb; }
.utm-evo__item--active .utm-evo__dot { background: #E8193C; box-shadow: 0 0 0 8px rgba(232,25,60,0.1); color: #fff; }
.utm-evo__card { background: #F8F8FC; border-radius: 14px; padding: 22px 20px; border: 1px solid #EBEBEB; }
.utm-evo__item--active .utm-evo__card { background: #fff; border-color: #E8193C; box-shadow: 0 6px 32px rgba(232,25,60,0.1); }
.utm-evo__card-name { font-size: 17px; font-weight: 700; color: #111; margin-bottom: 6px; }
.utm-evo__item--active .utm-evo__card-name { color: #E8193C; }
.utm-evo__card-desc { font-size: 12px; color: #999; line-height: 1.7; }
.utm-evo__item--active .utm-evo__card-desc { color: #555; }
.utm-evo__card-tag { display: inline-block; margin-top: 10px; font-size: 11px; font-weight: 500; color: #bbb; background: #EFEFEF; padding: 3px 10px; border-radius: 20px; }
.utm-evo__item--active .utm-evo__card-tag { background: rgba(232,25,60,0.08); color: #E8193C; }
.utm-evo__pal-flag { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 11px; font-weight: 600; color: #E8193C; }

373
src/pages/utm/IntroPage.jsx

@ -1,12 +1,37 @@
import { useRef } from "react";
import SubHero from "../../components/SubHero"; import SubHero from "../../components/SubHero";
import useFadeIn from "../../hooks/useFadeIn"; import useFadeIn from "../../hooks/useFadeIn";
import { motion, useInView } from "framer-motion";
import { MapPin, ShieldAlert, Network, BadgeCheck } 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` },
];
const UTM_WHAT_LIST = [
"실시간 비행체 위치 감시 및 추적",
"AI 기반 충돌 위험 사전 분석·경보",
"기상청 연계 기상 정보 자동 반영",
"동적 공역 설정 및 지오펜싱 관리",
];
function IntroPage() { function IntroPage() {
const ref = useFadeIn(); const ref = useFadeIn();
const UTM_NAV = [ const UTM_NAV = [
{ label: "UTM/UATM 소개", to: "/utm/intro" }, { label: "UTM/UATM 소개", to: "/utm/intro" },
{ label: "도입사례", to: "/utm/case" }, { label: "도입사례", to: "/utm/case" },
]; ];
const introRef = useRef(null);
const introInView = useInView(introRef, { once: true, margin: "-60px" });
const whatRef = useRef(null);
const whatInView = useInView(whatRef, { once: true, margin: "-60px" });
return ( return (
<article ref={ref}> <article ref={ref}>
<SubHero <SubHero
@ -23,28 +48,340 @@ function IntroPage() {
/> />
<div className="sub-content"> <div className="sub-content">
<section className="sub-section"> <div className="inner-wrap">
<span className="sub-section-eyebrow sub-fade-in">UTM/UATM 소개</span> <section className="utm-intro" ref={introRef}>
{/* <main className="sub-page"> {/* ── 상단 텍스트 블록 ── */}
<section className="sub-visual"> <div className="utm-intro__top">
<div className="inner"> <motion.span
<h2>UTM/UATM 소개</h2> className="fc-eyebrow"
<p>Urban Air Mobility &amp; UTM Air Traffic Management</p> initial={{ opacity: 0, y: 16 }}
</div> animate={introInView ? { opacity: 1, y: 0 } : {}}
</section> transition={{ duration: 0.6, ease }}
>
Overview
</motion.span>
<motion.h2
className="utm-intro__title"
initial={{ opacity: 0, y: 20 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, delay: 0.1, ease }}
>
PAL Networks의 UTM/UATM은
<br />
<em>도심 항공 관제의 새로운 기준</em>입니다.
</motion.h2>
<section className="sub-content-section"> <motion.p
<div className="inner"> className="utm-intro__desc"
<h3>도심 항공 모빌리티 핵심 기술</h3> initial={{ opacity: 0, y: 16 }}
<p> animate={introInView ? { opacity: 1, y: 0 } : {}}
안전한 도심 상공 운항과 통합 관제를 실현하는 transition={{ duration: 0.6, delay: 0.2, ease }}
>
UAM 기체 운용과 UTM 관제 플랫폼을 동시에 보유한 국내 유일의 통합
사업자입니다.
<br /> 운용 데이터로 검증된 시스템으로{" "}
<strong>안전한 저고도 공역 관리</strong> 실현합니다.
</motion.p>
</div>
{/* ── 4개 카드 그리드 ── */}
<ul className="utm-intro__cards">
{INTRO_CARDS.map((card, i) => (
<motion.li
key={card.label}
className="utm-intro__card"
initial={{ opacity: 0, y: 24 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.15 + i * 0.08, ease }}
>
<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"
/>
<p className="utm-intro__card-label">{card.label}</p>
</motion.li>
))}
</ul>
</section>
</div>
{/* ── UTM 소개 섹션 ── */}
<section className="utm-what" ref={whatRef}>
<div className="inner-wrap">
<div className="utm-what__inner">
<div className="utm-what__left">
<motion.span
className="utm-what__eyebrow"
initial={{ opacity: 0, y: 16 }}
animate={whatInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
What is UTM
</motion.span>
<motion.h2
className="utm-what__title"
initial={{ opacity: 0, y: 20 }}
animate={whatInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, delay: 0.1, ease }}
>
UTM,
<br /> <br />
PAL Networks의 UTM/UATM 기술을 소개합니다. 무엇을 있나요?
</p> </motion.h2>
<motion.p
className="utm-what__desc"
initial={{ opacity: 0, y: 16 }}
animate={whatInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2, ease }}
>
UTM은 단순한 관제 시스템을 넘어, 도심 저고도 공역 전체를
디지털로 운영하는 통합 플랫폼입니다. 실시간 감시부터 충돌 위험
분석, 기상 연계, 공역 관리까지 비행의 모든 순간을 안전하게
연결합니다.
</motion.p>
<motion.ul
className="utm-what__list"
initial={{ opacity: 0, y: 16 }}
animate={whatInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.3, ease }}
>
{UTM_WHAT_LIST.map((item) => (
<li key={item}>
<span className="utm-what__list-dot" />
{item}
</li>
))}
</motion.ul>
</div> </div>
</section> <motion.div
</main> */} className="utm-what__right"
initial={{ opacity: 0, x: 32 }}
animate={whatInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.8, delay: 0.2, ease }}
>
<div
className="utm-what__img-wrap"
style={{
backgroundImage: `url(${basePath}images/utm_what_img.png)`,
}}
>
{/* 드론들 */}
<div className="drone drone--1">
<div className="drone__bg drone__bg--red">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"
fill="#fff"
/>
<path
d="M4 4l4 4M20 4l-4 4M4 20l4-4M20 20l-4-4"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round"
/>
<circle cx="4" cy="4" r="2" fill="#fff" />
<circle cx="20" cy="4" r="2" fill="#fff" />
<circle cx="4" cy="20" r="2" fill="#fff" />
<circle cx="20" cy="20" r="2" fill="#fff" />
</svg>
</div>
<div className="drone__ping drone__ping--red" />
<div className="drone__ping drone__ping--red drone__ping--delay" />
</div>
<div className="drone drone--2">
<div className="drone__bg drone__bg--blue">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"
fill="#fff"
/>
<path
d="M4 4l4 4M20 4l-4 4M4 20l4-4M20 20l-4-4"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round"
/>
<circle cx="4" cy="4" r="2" fill="#fff" />
<circle cx="20" cy="4" r="2" fill="#fff" />
<circle cx="4" cy="20" r="2" fill="#fff" />
<circle cx="20" cy="20" r="2" fill="#fff" />
</svg>
</div>
<div className="drone__ping drone__ping--blue" />
<div className="drone__ping drone__ping--blue drone__ping--delay" />
</div>
<div className="drone drone--3">
<div className="drone__bg drone__bg--blue">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"
fill="#fff"
/>
<path
d="M4 4l4 4M20 4l-4 4M4 20l4-4M20 20l-4-4"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round"
/>
<circle cx="4" cy="4" r="2" fill="#fff" />
<circle cx="20" cy="4" r="2" fill="#fff" />
<circle cx="4" cy="20" r="2" fill="#fff" />
<circle cx="20" cy="20" r="2" fill="#fff" />
</svg>
</div>
<div className="drone__ping drone__ping--blue" />
<div className="drone__ping drone__ping--blue drone__ping--delay" />
</div>
<div className="drone-alert">
<div className="drone-alert__dot" />
<div className="drone-alert__content">
<p className="drone-alert__title"> 불법 드론 감지</p>
<p className="drone-alert__desc">
식별 불가 기체 접근 <br /> GND-150m
</p>
<p className="drone-alert__pos">위치 37.5°N 126.9°E</p>
</div>
</div>
</div>
</motion.div>
</div>
</div>
</section> </section>
<div className="inner-wrap">
{/* ── DRONE → UATM 진화 섹션 ── */}
<section className="utm-evo">
<div className="utm-evo__left">
{" "}
<motion.span
className="fc-eyebrow"
initial={{ opacity: 0, y: 16 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
Evolution
</motion.span>
<h2 className="utm-evo__title">
UTM에서 UATM으로,
<br />
<em>하늘길의 패러다임이 바뀝니다</em>
</h2>
<p className="utm-evo__desc">
드론 대의 비행에서 시작된 기술이, 이제 도심 전체의 하늘을
통합 관제하는 UATM으로 진화했습니다. PAL Networks는 변화의
최전선에서 기체와 관제를 함께 운영합니다.
</p>
</div>
<div className="utm-evo__right">
<div className="utm-evo__track">
<div className="utm-evo__item">
<div className="utm-evo__card-wrap utm-evo__card-wrap--left">
<div className="utm-evo__card">
<div className="utm-evo__card-name">🚁 DRONE</div>
<div className="utm-evo__card-desc">
개별 드론 운용 단계. 단순 비행 임무 수행 중심으로 체계적
관리 체계 부재.
</div>
<span className="utm-evo__card-tag">단일 기체 운용</span>
</div>
</div>
<div className="utm-evo__dot-wrap">
<div className="utm-evo__dot">01</div>
</div>
<div className="utm-evo__empty" />
</div>
<div className="utm-evo__item">
<div className="utm-evo__empty" />
<div className="utm-evo__dot-wrap">
<div className="utm-evo__dot">02</div>
</div>
<div className="utm-evo__card-wrap utm-evo__card-wrap--right">
<div className="utm-evo__card">
<div className="utm-evo__card-name">📡 UTM</div>
<div className="utm-evo__card-desc">
무인기 교통관리 시스템 도입. 비행 경로 승인·충돌
방지·공역 관리 체계화.
</div>
<span className="utm-evo__card-tag">
공역 관리 시스템
</span>
</div>
</div>
</div>
<div className="utm-evo__item">
<div className="utm-evo__card-wrap utm-evo__card-wrap--left">
<div className="utm-evo__card">
<div className="utm-evo__card-name"> UAM</div>
<div className="utm-evo__card-desc">
도심항공모빌리티 등장. 유인 eVTOL 기체가 도심 저고도
공역을 비행.
</div>
<span className="utm-evo__card-tag">
도심 항공 모빌리티
</span>
</div>
</div>
<div className="utm-evo__dot-wrap">
<div className="utm-evo__dot">03</div>
</div>
<div className="utm-evo__empty" />
</div>
<div className="utm-evo__item utm-evo__item--active">
<div className="utm-evo__empty" />
<div className="utm-evo__dot-wrap">
<div className="utm-evo__dot">04</div>
</div>
<div className="utm-evo__card-wrap utm-evo__card-wrap--right">
<div className="utm-evo__card">
<div className="utm-evo__card-name">🛸 UATM</div>
<div className="utm-evo__card-desc">
UAM과 UTM의 완전한 통합. 기체 운용부터 관제까지 하나의
플랫폼으로 실현.
</div>
<span className="utm-evo__card-tag">
통합 관제 플랫폼
</span>
<div className="utm-evo__pal-flag">
PAL Networks가 실현합니다
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div> </div>
</article> </article>
); );

Loading…
Cancel
Save