diff --git a/public/images/main-utm-background.png b/public/images/main-utm-background.png new file mode 100644 index 0000000..c6f85af Binary files /dev/null and b/public/images/main-utm-background.png differ diff --git a/public/images/main-utm-img1.png b/public/images/main-utm-img1.png new file mode 100644 index 0000000..1ee6093 Binary files /dev/null and b/public/images/main-utm-img1.png differ diff --git a/public/images/main-utm-img2.png b/public/images/main-utm-img2.png new file mode 100644 index 0000000..0a6d722 Binary files /dev/null and b/public/images/main-utm-img2.png differ diff --git a/public/images/main-utm-img3.png b/public/images/main-utm-img3.png new file mode 100644 index 0000000..cedd5d4 Binary files /dev/null and b/public/images/main-utm-img3.png differ diff --git a/public/images/main-utm-img4.png b/public/images/main-utm-img4.png new file mode 100644 index 0000000..052be0e Binary files /dev/null and b/public/images/main-utm-img4.png differ diff --git a/src/components/main/MainCameraTransition.jsx b/src/components/main/MainCameraTransition.jsx new file mode 100644 index 0000000..54836f5 --- /dev/null +++ b/src/components/main/MainCameraTransition.jsx @@ -0,0 +1,98 @@ +import { useEffect, useRef } from "react"; +import { gsap } from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; + +gsap.registerPlugin(ScrollTrigger); + +function MainAirspaceTransition() { + const sectionRef = useRef(null); + const leftRef = useRef(null); + const rightRef = useRef(null); + const lineRef = useRef([]); + const dotRef = useRef(null); + + useEffect(() => { + const ctx = gsap.context(() => { + gsap.set(rightRef.current, { xPercent: 100 }); + gsap.set(lineRef.current, { scaleX: 0, transformOrigin: "left center" }); + gsap.set(dotRef.current, { x: -220, opacity: 0 }); + + const tl = gsap.timeline({ + scrollTrigger: { + trigger: sectionRef.current, + start: "top top", + end: "+=1500", + scrub: 1, + pin: true, + anticipatePin: 1, + }, + }); + + tl.to(lineRef.current, { + scaleX: 1, + stagger: 0.08, + duration: 0.7, + ease: "none", + }); + + tl.to( + dotRef.current, + { + x: 0, + opacity: 1, + duration: 0.7, + ease: "none", + }, + "<0.1", + ); + + tl.to(leftRef.current, { + xPercent: -100, + duration: 1, + ease: "none", + }); + + tl.to( + rightRef.current, + { + xPercent: 0, + duration: 1, + ease: "none", + }, + "<", + ); + }, sectionRef); + + return () => ctx.revert(); + }, []); + + return ( +
+
+

UTM SYSTEM

+

드론 하늘길에서

+
+ +
+

UAM SYSTEM

+

도심 항공 네트워크로

+
+ +
+ {[0, 1, 2, 3].map((item) => ( + { + lineRef.current[item] = el; + }} + /> + ))} +
+ +
+
+ ); +} + +export default MainAirspaceTransition; diff --git a/src/components/main/MainUam.jsx b/src/components/main/MainUam.jsx index c1c3ef6..4341d13 100644 --- a/src/components/main/MainUam.jsx +++ b/src/components/main/MainUam.jsx @@ -4,322 +4,149 @@ import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); -const UAM_SPECS = [ - { - id: "01", - anchor: "navigation", - label: "자율 항법 시스템", - title: "AI 기반\n정밀 항법", - desc: "다중 센서 융합과 실시간 AI 연산으로 도심 상공에서도 센티미터 단위의 정밀 경로 제어를 실현합니다.", - stat: "±0.03m", - statLabel: "위치 정확도", - cx: "28%", - cy: "38%", - cardSide: "left", - }, - { - id: "02", - anchor: "comm", - label: "통합 통신 모듈", - title: "5G·위성\n이중 통신", - desc: "5G와 위성 통신을 동시에 운용하는 이중 링크 구조로 어떤 환경에서도 끊김 없는 데이터 채널을 보장합니다.", - stat: "<8ms", - statLabel: "지연 시간", - cx: "72%", - cy: "30%", - cardSide: "right", - }, - { - id: "03", - anchor: "power", - label: "하이브리드 추진", - title: "전기·수소\n하이브리드", - desc: "전기 모터와 수소 셀을 결합한 하이브리드 추진 시스템으로 항속 거리를 극대화하고 탄소 배출을 최소화합니다.", - stat: "320km", - statLabel: "최대 항속", - cx: "50%", - cy: "72%", - cardSide: "left", - }, - { - id: "04", - anchor: "sensor", - label: "능동 안전 센서", - title: "360° 장애물\n회피", - desc: "LiDAR·레이더·광학 카메라 트리플 센서가 360도 전방위를 실시간 스캔해 돌발 장애물에 즉각 대응합니다.", - stat: "0.12s", - statLabel: "반응 속도", - cx: "78%", - cy: "65%", - cardSide: "right", - }, -]; - function MainUam() { const sectionRef = useRef(null); - const eyebrowRef = useRef(null); + const overlayRef = useRef(null); const titleRef = useRef(null); - const subRef = useRef(null); - const aircraftRef = useRef(null); - const dotRefs = useRef([]); - const lineRefs = useRef([]); - const cardRefs = useRef([]); + const vehicleRef = useRef(null); + const panelsRef = useRef([]); + + const items = [ + { + label: "UAM NETWORK", + title: "Urban Air Mobility", + desc: "도심 내 버티포트, 운항 경로, 항공 교통 데이터를 연결해 미래형 도심 항공 이동 환경을 구축합니다.", + }, + { + label: "VERTIPORT", + title: "Vertiport Connection", + desc: "도심 곳곳의 이착륙 거점을 연결해 사람과 도시의 이동 흐름을 확장합니다.", + }, + { + label: "AIR CORRIDOR", + title: "Smart Air Corridor", + desc: "복잡한 도심 상공에서도 안전한 항로를 구성하고 실시간 운항 흐름을 관리합니다.", + }, + ]; useEffect(() => { const ctx = gsap.context(() => { - dotRefs.current = dotRefs.current.slice(0, UAM_SPECS.length); - lineRefs.current = lineRefs.current.slice(0, UAM_SPECS.length); - cardRefs.current = cardRefs.current.slice(0, UAM_SPECS.length); - - gsap.set(aircraftRef.current, { - opacity: 0, - scale: 0.72, - x: "-18vw", - y: "12vh", - }); - - gsap.set(eyebrowRef.current, { + gsap.set(overlayRef.current, { yPercent: 0 }); + gsap.set(titleRef.current, { y: 80, opacity: 0 }); + gsap.set(vehicleRef.current, { + xPercent: -120, + yPercent: 80, opacity: 0, - y: 28, - filter: "blur(8px)", }); - - gsap.set(titleRef.current, { - opacity: 0, - y: 36, - filter: "blur(8px)", - }); - - gsap.set(subRef.current, { - opacity: 0, - y: 24, - }); - - cardRefs.current.forEach((el) => { - if (el) gsap.set(el, { opacity: 0, y: 28, scale: 0.96, filter: "blur(6px)" }); - }); - - dotRefs.current.forEach((el) => { - if (el) gsap.set(el, { scale: 0, opacity: 0 }); + gsap.set(panelsRef.current, { y: 40, opacity: 0 }); + + const tl = gsap.timeline({ + scrollTrigger: { + trigger: sectionRef.current, + start: "top bottom", + end: "+=1400", + scrub: 1, + }, }); - lineRefs.current.forEach((el) => { - if (el) gsap.set(el, { scaleX: 0, opacity: 0 }); + tl.to(overlayRef.current, { + yPercent: -100, + duration: 1, + ease: "none", }); - ScrollTrigger.matchMedia({ - "(min-width: 992px)": () => { - const tl = gsap.timeline({ - scrollTrigger: { - trigger: sectionRef.current, - start: "top top", - end: "+=3200", - scrub: 1.05, - pin: true, - anticipatePin: 1, - invalidateOnRefresh: true, - }, - }); - - tl.to(eyebrowRef.current, { opacity: 1, y: 0, filter: "blur(0px)", ease: "none", duration: 0.5 }, 0).to(titleRef.current, { opacity: 1, y: 0, filter: "blur(0px)", ease: "none", duration: 0.65 }, 0.08).to(subRef.current, { opacity: 1, y: 0, ease: "none", duration: 0.45 }, 0.22).to( - aircraftRef.current, - { - opacity: 1, - scale: 1, - x: 0, - y: 0, - ease: "none", - duration: 1.8, - }, - 0.12, - ); - - UAM_SPECS.forEach((spec, i) => { - const baseTime = 0.5 + i * 0.52; - const origin = spec.cardSide === "left" ? "right center" : "left center"; - - tl.to( - dotRefs.current[i], - { - scale: 1, - opacity: 1, - ease: "none", - duration: 0.18, - }, - baseTime, - ) - .to( - lineRefs.current[i], - { - scaleX: 1, - opacity: 1, - transformOrigin: origin, - ease: "none", - duration: 0.24, - }, - baseTime + 0.08, - ) - .to( - cardRefs.current[i], - { - opacity: 1, - y: 0, - scale: 1, - filter: "blur(0px)", - ease: "none", - duration: 0.32, - }, - baseTime + 0.18, - ); - }); - - tl.to( - aircraftRef.current, - { - scale: 1.05, - ease: "none", - duration: 0.45, - }, - 2.7, - ); + tl.to( + titleRef.current, + { + y: 0, + opacity: 1, + duration: 0.8, + ease: "none", }, - - "(max-width: 991px)": () => { - const tl = gsap.timeline({ - scrollTrigger: { - trigger: sectionRef.current, - start: "top 80%", - end: "bottom 30%", - scrub: 0.8, - invalidateOnRefresh: true, - }, - }); - - tl.to(eyebrowRef.current, { opacity: 1, y: 0, filter: "blur(0px)", ease: "none", duration: 0.22 }, 0) - .to(titleRef.current, { opacity: 1, y: 0, filter: "blur(0px)", ease: "none", duration: 0.24 }, 0.05) - .to(subRef.current, { opacity: 1, y: 0, ease: "none", duration: 0.2 }, 0.1) - .to( - aircraftRef.current, - { - opacity: 1, - scale: 1, - x: 0, - y: 0, - ease: "none", - duration: 0.28, - }, - 0.16, - ) - .to( - dotRefs.current, - { - scale: 1, - opacity: 1, - stagger: 0.05, - ease: "none", - duration: 0.18, - }, - 0.24, - ) - .to( - lineRefs.current, - { - scaleX: 1, - opacity: 1, - stagger: 0.05, - ease: "none", - duration: 0.18, - }, - 0.3, - ) - .to( - cardRefs.current, - { - opacity: 1, - y: 0, - scale: 1, - filter: "blur(0px)", - stagger: 0.06, - ease: "none", - duration: 0.2, - }, - 0.36, - ); + "<0.25", + ); + + tl.to( + vehicleRef.current, + { + xPercent: 0, + yPercent: 0, + opacity: 1, + duration: 1, + ease: "none", }, - }); - - gsap.to(aircraftRef.current, { - y: -8, - duration: 2.8, - ease: "sine.inOut", - repeat: -1, - yoyo: true, - }); + "<0.2", + ); + + tl.to( + panelsRef.current, + { + y: 0, + opacity: 1, + stagger: 0.16, + duration: 0.8, + ease: "none", + }, + "<0.35", + ); }, sectionRef); return () => ctx.revert(); }, []); return ( -
-
-
-
+
+
+ FROM UTM + TO UAM +
+ +
+
+
+ +
+
+
+
+
+ +
V
+
P
+
C
-
-
-
- - UAM TECHNOLOGY +
+
+
-

- 도심 항공 모빌리티, +
+

UAM SYSTEM

+

+ 도심항공교통
- 기술로 완성합니다 + UAM

- -

- PAL NETWORKS의 4대 핵심 기술이 안전한 하늘길을 만듭니다 +

+ 드론 관제에서 확장된 하늘길은 이제 도시의 이동 네트워크로 + 이어집니다. UAM은 버티포트, 항로, 운항 데이터를 하나의 흐름으로 + 연결합니다.

-
-
- UAM 기체 -
-
- - {UAM_SPECS.map((spec, i) => ( -
-
(dotRefs.current[i] = el)} style={{ left: spec.cx, top: spec.cy }}> - - -
- -
(lineRefs.current[i] = el)} style={{ left: spec.cx, top: spec.cy }}>
- -
(cardRefs.current[i] = el)} style={{ left: spec.cx, top: `calc(${spec.cy} - 10px)` }}> -
{spec.id}
-
{spec.label}
- - - {spec.title.split("\n").map((line, j) => ( - - {line} - {j === 0 &&
} -
- ))} -
- -

{spec.desc}

- -
- {spec.stat} - {spec.statLabel} -
-
-
+
+ {items.map((item, index) => ( +
{ + panelsRef.current[index] = el; + }} + > + {item.label} +

{item.title}

+

{item.desc}

+
))}
diff --git a/src/components/main/MainUtm.jsx b/src/components/main/MainUtm.jsx index d456766..7518f78 100644 --- a/src/components/main/MainUtm.jsx +++ b/src/components/main/MainUtm.jsx @@ -6,109 +6,194 @@ gsap.registerPlugin(ScrollTrigger); function MainUtm() { const sectionRef = useRef(null); - const itemsRef = useRef([]); + const cardsRef = useRef([]); + + const items = [ + { + step: "01", + label: "비행 검토", + title: "비행가능여부 확인", + desc: "선택한 공역에서 드론 비행 가능 여부와 제한 조건을 실시간으로 확인합니다.", + }, + { + step: "02", + label: "승인 신청", + title: "자동비행승인 신청", + desc: "복잡한 승인 절차 없이 간편하게 비행 자동 승인을 신청할 수 있습니다.", + }, + { + step: "03", + label: "비행 관제", + title: "실시간 모니터링", + desc: "드론의 위치, 비행 경로, 운항 상태를 실시간으로 확인하고 모니터링합니다.", + }, + { + step: "04", + label: "통합 관리", + title: "비행 데이터 관리", + desc: "비행 이력과 운항 스케줄 데이터를 통합 관리하여 효율적인 드론 운영을 지원합니다.", + }, + ]; useEffect(() => { const ctx = gsap.context(() => { - itemsRef.current.forEach((el) => { - const text = el.querySelector(".main-utm-text"); - const visual = el.querySelector(".main-utm-visual"); + const cards = cardsRef.current.filter(Boolean); + + gsap.set(cards, { + yPercent: 110, + scale: 1, + opacity: 1, + rotateX: 0, + transformOrigin: "center bottom", + }); + + gsap.set(cards[0], { + yPercent: 0, + }); + + const tl = gsap.timeline({ + scrollTrigger: { + trigger: sectionRef.current, + start: "top top", + end: `+=${items.length * 900}`, + scrub: 1, + pin: true, + anticipatePin: 1, + }, + }); + + cards.forEach((card, index) => { + if (index === 0) return; + + const prevCard = cards[index - 1]; - gsap.fromTo( - text, - { y: 50, opacity: 0 }, + tl.to( + prevCard, { - y: 0, - opacity: 1, - duration: 0.8, - ease: "power3.out", - scrollTrigger: { - trigger: el, - start: "top 80%", - }, + yPercent: -18, + scale: 0.88, + opacity: 0.35, + rotateX: 8, + filter: "blur(3px)", + duration: 1, + ease: "none", }, + index - 0.85, ); - gsap.fromTo( - visual, - { x: el.classList.contains("reverse") ? -80 : 80, opacity: 0 }, + tl.to( + card, { - x: 0, - opacity: 1, - duration: 0.9, - ease: "power3.out", - scrollTrigger: { - trigger: el, - start: "top 80%", - }, + yPercent: 0, + duration: 1, + ease: "none", }, + index - 0.85, ); }); + + tl.to(cards[cards.length - 1], { + yPercent: -10, + scale: 0.94, + duration: 0.8, + ease: "none", + }); }, sectionRef); return () => ctx.revert(); - }, []); + }, [items.length]); return (
-
-
-

UTM SYSTEM

-

Unmanned Aircraft Traffic Management

-

드론 하늘길을 통제하는 관제 시스템

-
+
+
+ +
diff --git a/src/css/main.css b/src/css/main.css index f6ae19f..507489a 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -127,3 +127,384 @@ body{overflow-x:hidden;} .main-scroll-line{height:22px;} .main-scroll-text{font-size:10px;letter-spacing:.22em;} } + + +/* ─── Section ─── */ +.main-utm-section{position:relative;width:100%;min-height:100vh;padding-top:86px;overflow:hidden;background:radial-gradient(circle at 12% 18%,rgba(58,64,129,.08),transparent 32%),radial-gradient(circle at 88% 18%,rgba(58,64,129,.06),transparent 34%),linear-gradient(180deg,#f7f9ff 0%,#fff 100%);} +.main-utm-section::before{content:"";position:absolute;inset:0;background:linear-gradient(90deg,rgba(247,249,255,.82) 0%,rgba(247,249,255,.42) 24%,rgba(247,249,255,.12) 52%,rgba(247,249,255,.42) 78%,rgba(247,249,255,.82) 100%),url("/images/main-utm-background.png") no-repeat 50% 50% / cover;opacity:.52;pointer-events:none;z-index:0;} + +/* ─── Glow ─── */ +.main-utm-bg-glow{position:absolute;border-radius:999px;filter:blur(90px);pointer-events:none;} +.main-utm-bg-glow--a{width:520px;height:520px;top:-160px;right:-120px;background:rgba(58,64,129,.13);} +.main-utm-bg-glow--b{width:460px;height:460px;left:-140px;bottom:-160px;background:rgba(112,180,255,.12);} + +/* ─── Inner ─── */ +.main-utm-inner{position:relative;z-index:2;width:min(1440px,calc(100% - 120px));min-height:calc(100vh - 86px);margin:0 auto;padding-top:72px;padding-bottom:64px;} + +/* ─── Head ─── */ +.main-utm-head{position:relative;z-index:5;max-width:820px;margin-bottom:64px;} +.main-utm-eyebrow{margin:0 0 14px;font-size:12px;font-weight:800;letter-spacing:.24em;color:#3a4081;} +.main-utm-title{ + margin:0; + font-size:clamp(40px,4vw,64px); + font-weight:800; + line-height:.92; + letter-spacing:-0.08em; + color:#0e1120; +}.main-utm-desc{margin:22px 0 0;font-size:16px;line-height:1.6;letter-spacing:-.02em;color:#697083;} + +/* ─── Card Stack ─── */ +.main-utm-stack{position:relative;width:100%;height:min(430px,50vh);perspective:1400px;} +.main-utm-card{position:absolute;inset:0;display:grid;grid-template-columns:.9fr 1.1fr;align-items:center;gap:64px;padding:54px 62px;border-radius:34px;overflow:hidden;background:rgba(255,255,255,.86);border:1px solid rgba(58,64,129,.1);box-shadow:0 30px 80px rgba(14,17,32,.1),inset 0 1px 0 rgba(255,255,255,.9);backdrop-filter:blur(22px);will-change:transform,opacity,filter;} +.main-utm-card::before{content:"";position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.78),transparent 46%),radial-gradient(circle at 82% 28%,rgba(58,64,129,.11),transparent 34%);pointer-events:none;} +.main-utm-card-text,.main-utm-card-visual{position:relative;z-index:2;} +.main-utm-step{display:inline-flex;align-items:center;justify-content:center;width:58px;height:32px;margin-bottom:24px;border-radius:999px;background:rgba(58,64,129,.1);color:#3a4081;font-size:13px;font-weight:800;letter-spacing:.1em;} +.main-utm-label{margin:0 0 10px;font-size:12px;font-weight:800;letter-spacing:.18em;color:rgba(58,64,129,.62);} +.main-utm-card h3{margin:0;font-size:clamp(34px,3.2vw,54px);line-height:1.06;letter-spacing:-.055em;color:#101322;} +.main-utm-card-text > p:last-child{max-width:430px;margin:22px 0 0;font-size:17px;line-height:1.72;word-break:keep-all;color:#636b7e;} + +/* ─── Card Visual ─── */ +.main-utm-card-visual{ + position:relative; + height:100%; + min-height:300px; + border-radius:28px; + overflow:hidden; + background:#f7f8fc; + align-items:center; + overflow:hidden; + + justify-content:center; +} + +.main-utm-card-visual::after{ + content:""; + + position:absolute; + inset:18px; + + border-radius:24px; + + background:linear-gradient(180deg,rgba(255,255,255,.12),transparent 30%); + + pointer-events:none; +} + +.main-utm-img{ + width:100%; + height:100%; + + object-fit:cover; + + display:block; + + border-radius:24px; + + +box-shadow: + inset 0 0 0 1px rgba(58,64,129,.06), + 0 18px 40px rgba(17,22,40,.08), + 0 0 0 8px rgba(255,255,255,.42); + background:#fff; + overflow:hidden; +transform:translateZ(0); +} +.main-utm-orbit{position:absolute;inset:32px;border-radius:28px;} +.main-utm-orbit span{position:absolute;border:1px solid rgba(58,64,129,.14);border-radius:50%;} +.main-utm-orbit span:nth-child(1){width:340px;height:340px;left:50%;top:50%;transform:translate(-50%,-50%);} +.main-utm-orbit span:nth-child(2){width:230px;height:230px;left:18%;top:16%;} +.main-utm-orbit span:nth-child(3){width:170px;height:170px;right:10%;bottom:10%;} +.main-utm-map{position:absolute;inset:0;} +.main-utm-route{position:absolute;height:2px;border-radius:999px;background:linear-gradient(90deg,transparent,#3a4081,transparent);box-shadow:0 0 16px rgba(58,64,129,.32);opacity:.7;} +.main-utm-route--a{width:62%;left:18%;top:42%;transform:rotate(-18deg);} +.main-utm-route--b{width:48%;right:12%;top:62%;transform:rotate(22deg);} + + +/* ─── Floating Drones ─── */ +.utm-floating-air{position:absolute;top:120px;right:2%;width:42vw;height:210px;pointer-events:none;z-index:1;overflow:visible;animation:utmAirFlow 18s linear infinite;} +.utm-floating-drone{position:absolute;width:52px;height:52px;border-radius:50%;background:rgba(255,255,255,.42);backdrop-filter:blur(12px);box-shadow:0 0 0 8px rgba(58,64,129,.04),0 10px 24px rgba(58,64,129,.1);} +.utm-floating-drone svg{position:absolute;inset:9px;width:34px;height:34px;overflow:visible;} +.utm-floating-drone svg circle{fill:none;stroke:#3a4081;stroke-width:2.4;} +.utm-floating-drone svg circle:first-child{filter:drop-shadow(0 0 10px rgba(58,64,129,.34));} +.utm-floating-drone svg path{fill:none;stroke:#3a4081;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;} +.utm-floating-drone span{position:absolute;inset:-12px;border-radius:50%;border:1px solid rgba(58,64,129,.2);animation:utmDronePulse 2.8s ease-out infinite;} +.utm-floating-drone--a{left:10%;top:122px;animation:utmDroneMoveB 11s ease-in-out infinite,utmDronePulseFloat 6s ease-in-out infinite;} +.utm-floating-drone--a svg circle, +.utm-floating-drone--a svg path{ + stroke:#5B6CFF; +} + +.utm-floating-drone--a svg circle:first-child{ + filter:drop-shadow(0 0 10px rgba(91,108,255,.34)); +} +.utm-floating-drone--b{ + left:44%; + top:72px; + + box-shadow: + 0 0 0 8px rgba(243,199,104,.08), + 0 10px 24px rgba(243,199,104,.14); + + animation: + utmDroneMoveB 11s ease-in-out infinite, + utmDronePulseFloat 6s ease-in-out infinite; +} +.utm-floating-drone--b svg{ + animation:utmDroneColorShift 6s ease-in-out infinite; +} + +.utm-floating-drone--b svg circle, +.utm-floating-drone--b svg path{ + stroke:#F3C768; +} + +.utm-floating-drone--b svg circle:first-child{ + filter:drop-shadow(0 0 10px rgba(243,199,104,.34)); +} +.utm-floating-drone--c{right:4%;top:138px;animation:utmDroneMoveB 11s ease-in-out infinite,utmDronePulseFloat 6s ease-in-out infinite;animation-delay:-2.4s; box-shadow: + 0 0 0 8px rgba(255,123,123,.06), + 0 10px 24px rgba(255,123,123,.12); + + } + .utm-floating-drone--c svg circle, +.utm-floating-drone--c svg path{ + stroke:#FF7B7B; +} + +.utm-floating-drone--c svg circle:first-child{ + filter:drop-shadow(0 0 10px rgba(255,123,123,.32)); +} + + +/* ─── Responsive ─── */ +@media(max-width:1024px){ + .main-utm-section{padding-top:76px;} + .main-utm-inner{width:calc(100% - 56px);min-height:calc(100vh - 76px);padding-top:56px;} + .main-utm-head{margin-bottom:56px;} + .main-utm-card{grid-template-columns:1fr;gap:28px;padding:34px;} + .main-utm-card-visual{min-height:240px;} + .utm-floating-air{width:58vw;top:160px;opacity:.72;} +} +@media(max-width:768px){ + .main-utm-inner{width:calc(100% - 32px);padding-top:42px;} + .main-utm-title{font-size:38px;} + .main-utm-desc{font-size:14px;} + .main-utm-stack{height:64vh;} + .main-utm-card{padding:26px 22px;border-radius:24px;} + .main-utm-card h3{font-size:32px;} + .main-utm-card-text > p:last-child{font-size:14px;} + .main-utm-card-visual{min-height:210px;} + .utm-floating-air{display:none;} +} + +/* ─── Keyframes ─── */ +@keyframes utmAirFlow{0%,100%{transform:translateX(0);}50%{transform:translateX(-18px);}} +@keyframes utmDroneMoveA{0%,100%{transform:translate3d(0,0,0) scale(1);}50%{transform:translate3d(26px,-12px,0) scale(1.04);}} +@keyframes utmDroneMoveB{0%,100%{transform:translate3d(0,0,0) scale(.82);}50%{transform:translate3d(-20px,10px,0) scale(.88);}} +@keyframes utmDronePulse{0%{transform:scale(.7);opacity:.62;}100%{transform:scale(1.9);opacity:0;}} +@keyframes utmDronePulseFloat{0%,100%{filter:drop-shadow(0 0 0 rgba(58,64,129,0));}50%{filter:drop-shadow(0 0 18px rgba(58,64,129,.18));}} +@keyframes utmDroneColorShift{ + 0%{ + filter:none; + } + + 45%{ + filter:none; + } + + 50%{ + filter:drop-shadow(0 0 10px rgba(243,199,104,.42)); + } + + 55%{ + filter:drop-shadow(0 0 10px rgba(243,199,104,.42)); + } + + 100%{ + filter:none; + } +} + +.airspace-transition-section { + position: relative; + height: 100vh; + overflow: hidden; + + background: + radial-gradient(circle at 12% 18%, rgba(58,64,129,.08), transparent 32%), + radial-gradient(circle at 88% 18%, rgba(58,64,129,.06), transparent 34%), + linear-gradient(180deg, #f7f9ff 0%, #ffffff 100%); +} +.airspace-transition-section::before { + content: ""; + + position: absolute; + inset: 0; + + background: + linear-gradient( + 90deg, + rgba(255,255,255,.72) 0%, + rgba(255,255,255,.24) 20%, + rgba(255,255,255,.08) 50%, + rgba(255,255,255,.24) 80%, + rgba(255,255,255,.72) 100% + ); + + pointer-events: none; +} +.airspace-panel { + position: absolute; + inset: 0; + + display: flex; + flex-direction: column; + justify-content: center; + + padding: 0 8vw; +} + +.airspace-panel--utm { + background: transparent; +} + +.airspace-panel--uam { + background: + radial-gradient(circle at 72% 34%, rgba(112,180,255,.12), transparent 34%), + radial-gradient(circle at 42% 78%, rgba(58,64,129,.08), transparent 28%); +} + +.airspace-panel p { + margin: 0 0 18px; + font-size: 12px; + font-weight: 800; + letter-spacing: .26em; + color: #3a4081; +} + +.airspace-panel h2 { + margin: 0; + max-width: 760px; + + font-size: clamp(48px, 6vw, 96px); + font-weight: 800; + line-height: .96; + letter-spacing: -.08em; + color: #0e1120; +} + +.airspace-lines { + position: absolute; + inset: 0; + z-index: 5; + pointer-events: none; +} + +/* .airspace-line { + position: absolute; + left: 8vw; + + height: 1px; + border-radius: 999px; + + background: + linear-gradient( + 90deg, + rgba(58,64,129,0), + rgba(58,64,129,.06), + rgba(58,64,129,.14), + rgba(58,64,129,.06), + rgba(58,64,129,0) + ); + + filter: blur(.4px); + + opacity: .55; + + mix-blend-mode: multiply; + + transform-origin: left center; +} + +.airspace-line::after { + content: ""; + + position: absolute; + inset: 0; + + background: + linear-gradient( + 90deg, + transparent 0%, + rgba(255,255,255,.65) 18%, + rgba(255,255,255,0) 42% + ); + + mix-blend-mode: screen; +} +.airspace-line--1 { + top: 36%; + width: 62vw; + transform: rotate(-8deg); +} + +.airspace-line--2 { + top: 48%; + width: 74vw; + transform: rotate(3deg); +} + +.airspace-line--3 { + top: 60%; + width: 58vw; + transform: rotate(10deg); +} + +.airspace-line--4 { + top: 70%; + width: 46vw; + transform: rotate(-4deg); + opacity: .52; +} */ + +.airspace-moving-dot { + position: absolute; + left: 50%; + top: 49%; + z-index: 6; + + width: 54px; + height: 54px; + border-radius: 50%; + + background: rgba(255,255,255,.78); + border: 1px solid rgba(58,64,129,.14); + + box-shadow: + 0 0 0 12px rgba(58,64,129,.06), + 0 20px 50px rgba(58,64,129,.18); +} + +.airspace-moving-dot::before { + content: ""; + position: absolute; + inset: 16px; + border-radius: 50%; + background: #3a4081; + box-shadow: 0 0 18px rgba(58,64,129,.42); +} + +@media (max-width: 768px) { + .airspace-panel { + padding: 0 28px; + } + + .airspace-panel h2 { + font-size: 44px; + } + + .airspace-line { + left: 28px; + } +} + diff --git a/src/pages/MainPage.jsx b/src/pages/MainPage.jsx index 96c56a9..bb68273 100644 --- a/src/pages/MainPage.jsx +++ b/src/pages/MainPage.jsx @@ -1,12 +1,14 @@ import MainVisual from "../components/main/MainVisual"; import MainUtm from "../components/main/MainUtm"; import MainUam from "../components/main/MainUam"; +import MainCameraTransition from "../components/main/MainCameraTransition"; function MainPage() { return (
+ {/* */}
);