Browse Source

히어로 수정

remotes/origin/main
김지은 4 weeks ago
parent
commit
f92add62c1
  1. BIN
      public/images/testbg01.png
  2. 75
      src/components/SubHero.jsx
  3. 115
      src/css/common.css
  4. 8
      src/pages/company/AboutPage.jsx
  5. 258
      src/pages/company/HistoryPage.jsx
  6. 33
      src/pages/company/LocationPage.jsx
  7. 152
      src/pages/company/PartnersPage.jsx

BIN
public/images/testbg01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

75
src/components/SubHero.jsx

@ -105,8 +105,7 @@ function NetworkGlobe() {
const d = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (d < 0.55) {
const depth = (a.sz + b.sz) * 0.5;
const alpha =
Math.max(0, 0.05 + (depth + 1) * 0.1) * (1 - d / 0.55);
const alpha = Math.max(0, 0.05 + (depth + 1) * 0.1) * (1 - d / 0.55);
const aHex = Math.round(Math.min(255, alpha * 255))
.toString(16)
.padStart(2, "0");
@ -135,14 +134,7 @@ function NetworkGlobe() {
.toString(16)
.padStart(2, "0");
const glow = ctx.createRadialGradient(
n.sx,
n.sy,
0,
n.sx,
n.sy,
r * 3.5,
);
const glow = ctx.createRadialGradient(n.sx, n.sy, 0, n.sx, n.sy, r * 3.5);
glow.addColorStop(0, n.color + gHex);
glow.addColorStop(1, n.color + "00");
ctx.beginPath();
@ -180,12 +172,7 @@ function NetworkGlobe() {
function TitleLine({ children, delay }) {
return (
<span className="sh4-title-line">
<motion.span
className="sh4-title-line-inner"
initial={{ y: "105%" }}
animate={{ y: "0%" }}
transition={{ duration: 1.1, delay, ease: [0.16, 1, 0.3, 1] }}
>
<motion.span className="sh4-title-line-inner" initial={{ y: "105%" }} animate={{ y: "0%" }} transition={{ duration: 1.1, delay, ease: [0.16, 1, 0.3, 1] }}>
{children}
</motion.span>
</span>
@ -215,9 +202,7 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
))
: // JSX: <br/> TitleLine
(() => {
const children = Array.isArray(title.props?.children)
? title.props.children
: [title];
const children = Array.isArray(title.props?.children) ? title.props.children : [title];
const lines = [];
let current = [];
children.forEach((child, i) => {
@ -243,14 +228,25 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
<section className={`sh4${rightSlot ? " sh4--split" : ""}`}>
<div className="sh4-inner">
<div className="sh4-left">
<motion.span
className="sh4-label"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
>
{menuMap["/" + pathname.split("/")[1]]?.label}
</motion.span>
{/* // 브레드크럼 */}
<motion.nav className="sh4-breadcrumb" initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} aria-label="breadcrumb">
<Link to="/" className="sh4-breadcrumb-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
</Link>
<span className="sh4-breadcrumb-sep">/</span>
<Link to={"/" + pathname.split("/")[1]} className="sh4-breadcrumb-item">
{menuMap["/" + pathname.split("/")[1]]?.label}
</Link>
{pathname.split("/")[2] && (
<>
<span className="sh4-breadcrumb-sep">/</span>
<span className="sh4-breadcrumb-item sh4-breadcrumb-item--active">{navItems?.find((n) => n.to === pathname)?.label}</span>
</>
)}
</motion.nav>
<h1 className="sh4-title">{titleContent}</h1>
{desc && (
<motion.p
@ -259,11 +255,7 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.6,
delay:
0.1 +
(typeof title === "string" ? title.split("\n").length : 2) *
0.1 +
0.15,
delay: 0.1 + (typeof title === "string" ? title.split("\n").length : 2) * 0.1 + 0.15,
ease: [0.16, 1, 0.3, 1],
}}
>
@ -273,12 +265,7 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
</div>
{rightSlot && (
<motion.div
className="sh4-right"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 0.3 }}
>
<motion.div className="sh4-right" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 1, delay: 0.3 }}>
{rightSlot}
</motion.div>
)}
@ -286,18 +273,10 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
</section>
{navItems?.length > 1 && (
<nav
ref={navRef}
className={`sh4-nav-wrap${isPill ? " is-pill" : ""}`}
aria-label="Sub Navigation"
>
<nav ref={navRef} className={`sh4-nav-wrap${isPill ? " is-pill" : ""}`} aria-label="Sub Navigation">
<div className="sh4-nav">
{navItems.map((item) => (
<Link
key={item.to}
to={item.to}
className={`sh4-nav-tab${pathname === item.to ? " sh4-nav-tab--active" : ""}`}
>
<Link key={item.to} to={item.to} className={`sh4-nav-tab${pathname === item.to ? " sh4-nav-tab--active" : ""}`}>
{item.label}
</Link>
))}

115
src/css/common.css

@ -32,30 +32,31 @@ body{overflow-x:hidden;}
overflow: hidden;
margin-top: calc(-1 * var(--header-height));
padding: calc(var(--header-height) + 80px) 80px 96px;
background:
radial-gradient(ellipse 80% 60% at 70% 110%, rgba(217, 72, 137, 0.10) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 10% -10%, rgba(123, 63, 160, 0.10) 0%, transparent 55%),
radial-gradient(ellipse 50% 40% at 90% 10%, rgba(25, 141, 199, 0.08) 0%, transparent 50%),
linear-gradient(155deg, #f5f4fb 0%, #faf5fc 35%, #f0f6fb 70%, #f8f4fa 100%);
}
/* .sh4{position:relative;overflow:hidden;margin-top:calc(-1 * var(--header-height));padding:calc(var(--header-height) + 80px) 80px 96px;background:#fff;} */
.sh4-inner{position:relative;z-index:2;max-width:1440px;margin:0 auto;}
.sh4-right{position:relative;height:100%;min-height:320px;}
.sh4-label{display:block;font-size:11px;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:rgba(26,31,94,.38);margin-bottom:20px;opacity:0;transform:translateY(10px);animation:sh4Up .55s cubic-bezier(.16,1,.3,1) forwards;}
.sh4-title{margin:0 0 20px;font-size:clamp(40px,6vw,80px);font-weight:900;line-height:1.05;letter-spacing:-.055em;color:var(--navy);}
.sh4-title em{font-style:normal;background:var(--grad-brand-h);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;}
.sh4-title em{font-size:7rem;font-style:normal;background:var(--grad-brand-h);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;}
.sh4-title-line{display:block;overflow:hidden;padding-bottom:.08em;margin-bottom:-.08em;}
.sh4-title-line-inner{display:block;}
.sh4-desc{font-size:clamp(14px,1.2vw,17px);line-height:1.8;color:rgba(26,31,94,.42);word-break:keep-all;margin:0;opacity:0;transform:translateY(12px);animation:sh4Up .6s .5s cubic-bezier(.16,1,.3,1) forwards;}
.sh4-breadcrumb {display:flex; align-items:center; gap:6px; margin-bottom:16px;}
.sh4-breadcrumb-item {font-size:13px; font-weight:500; color:#888; text-decoration:none; display:flex; align-items:center; transition:color .2s;}
.sh4-breadcrumb-item:hover {color:var(--color-primary);}
.sh4-breadcrumb-item--active {color:#333; pointer-events:none;}
.sh4-breadcrumb-sep {font-size:12px; color:#ccc;}
/*sh4 split*/
.sh4--split .sh4-inner{display:grid;grid-template-columns:1fr 1fr;gap:80px;align-items:center;}
/*sh4 sticky 탭 네비*/
/* .sh4-nav-wrap{position:sticky;top:var(--header-height);z-index:50;background:rgba(255,255,255,.98);backdrop-filter:blur(16px) saturate(180%);border-bottom:1px solid rgba(26,31,94,.08);box-shadow:0 1px 0 rgba(26,31,94,.06);} */
.sh4-nav-wrap{border-bottom:1px solid rgba(26,31,94,.08);box-shadow:0 1px 0 rgba(26,31,94,.06);}
.sh4-nav{max-width:1440px;margin:0 auto;padding:0 80px;display:flex;align-items:center;}
.sh4-nav{max-width:1440px;margin:0 auto;display:flex;align-items:center;}
.sh4-nav-tab{position:relative;display:inline-flex;align-items:center;height:52px;padding:0 20px;font-size:14px;font-weight:600;color:rgba(17,17,17,.38);text-decoration:none;transition:color .25s;white-space:nowrap;letter-spacing:-.01em;}
.sh4-nav-tab:hover{color:var(--navy);}
.sh4-nav-tab--active{color:var(--navy);}
@ -149,3 +150,103 @@ body{overflow-x:hidden;}
.partners-grid--5 .partners-logo-cell:nth-last-child(-n+3) { border-bottom: none; }
.partners-cta { flex-direction: column; align-items: flex-start; gap: 1.5rem; }
}
/*연혁*/
/* ── Inner Wrap (공통 max-width) ── */
.inner-wrap {max-width:1440px; margin:0 auto; padding:0 60px;}
/* ── 상단 타이틀 ── */
.ht-header {text-align:center; padding:80px 0;}
.ht-header-title {font-size:48px; font-weight:500; letter-spacing:-.04em; color:#000; line-height:1.3; margin-bottom:0;}
.ht-header-title em {display:inline-block; font-size:60px; font-style:normal;}
.ht-header-title span {color:var(--color-primary); font-weight:700;}
.ht-header-title b {font-weight:700;}
.ht-header-sub {display:inline-block; margin-top:28px; font-size:16px; letter-spacing:-.03em; color:#666; font-weight:500; line-height:1.7; font-style:normal;}
/* ── 연혁 카드 박스 ── */
.ht-card {background:#f7f8fc; border-radius:20px; padding:52px 48px 80px; margin-bottom:80px;}
/* ── Tabs ── */
.ht-tabs {display:flex; gap:16px; margin-bottom:52px;align-items:center;justify-content:center;}
.ht-tab {flex:1;max-width:400px;font-size:14px; font-weight:600; padding:9px 26px; border-radius:100px; border:1.5px solid var(--color-primary-border); background:transparent; color:#888; cursor:pointer; transition:all .25s; letter-spacing:.01em;}
.ht-tab:hover {border-color:var(--color-primary); color:var(--color-primary);}
.ht-tab.is-active {background:var(--color-primary); border-color:var(--color-primary); color:#fff;}
/* ── Panel ── */
.ht-panel {display:flex; gap:0 40px; align-items:start; width:fit-content; margin:0 auto;min-width:670px}
/* ── Sidebar ── */
.ht-sidebar {padding-top:4px;min-width:155px}
.ht-decade {font-size:52px; font-weight:800; color:#111; line-height:1; letter-spacing:-.03em;}
.ht-since { font-size:11px; font-weight:500; color:#aaa; letter-spacing:.08em; text-transform:uppercase; margin-top:8px;}
/* ── 중앙 1px 라인 ── */
.ht-line-col {position:relative; align-self:stretch; width:1px;}
.ht-line-bg {position:absolute; inset:0; background:var(--color-primary-soft-border); width:1px;}
.ht-line-fill {position:absolute; top:0; left:0; width:1px; height:100%; background:var(--color-primary);}
/* ── Content ── */
.ht-content {padding-top:0;}
/* ── Year Group ── */
.ht-group {position:relative; padding:0 0 72px 0;}
.ht-group:last-child {padding-bottom:0;}
/* ── Dot ── */
.ht-dot {position:absolute; left:-44px; top:12px; width:8px; height:8px; border-radius:50%; background:#f7f8fc; border:1.5px solid var(--color-primary-border); transition:background .35s, border-color .35s, transform .35s; z-index:1;}
.ht-dot.is-active {background:var(--color-primary); border-color:var(--color-primary); transform:scale(1.4);}
/* ── Year ── */
.ht-year {font-size:40px; font-weight:800; letter-spacing:-.03em; line-height:1; margin:0 0 18px 0;}
/* ── List ── */
.ht-list {list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:9px;}
/* ── Item ── */
.ht-item {display:flex; gap:10px; align-items:baseline;}
.ht-bullet {width:3px; height:3px; border-radius:50%; background:var(--color-primary); opacity:.4; flex-shrink:0; position:relative; top:-2px;}
.ht-text {font-size:14px; color:#444; line-height:1.65; word-break:keep-all;}
/* ── Responsive ── */
@media (max-width: 900px) {
.inner-wrap {
padding: 0 20px;
}
.ht-header {
padding: 48px 0 32px;
}
.ht-header-title {
font-size: 32px;
}
.ht-card {
padding: 32px 20px 56px;
border-radius: 12px;
}
.ht-panel {
grid-template-columns: 1fr;
gap: 0;
}
.ht-sidebar {
margin-bottom: 32px;
}
.ht-line-col {
display: none;
}
.ht-content {
padding-left: 20px;
border-left: 1px solid var(--color-primary-soft-border);
}
.ht-dot {
left: -24px;
}
.ht-decade {
font-size: 36px;
}
.ht-year {
font-size: 26px;
}
}

8
src/pages/company/AboutPage.jsx

@ -23,14 +23,12 @@ export default function AboutPage() {
<SubHero
title={
<>
<span style={{ color: "#111" }}>가치를 실천하는</span>
<br />
<em>항공 IT 전문기업</em>
<em>About Us</em>
</>
}
desc="팔네트웍스는 항공 데이터와 통합 관제 기술을 기반으로 안전한 하늘길을 만들어갑니다."
// desc=" ."
navItems={COMPANY_NAV}
rightSlot={heroRight}
// rightSlot={heroRight}
/>
</article>
);

258
src/pages/company/HistoryPage.jsx

@ -1,5 +1,6 @@
import { useRef, useState, useEffect } from "react";
import { motion, useScroll, useTransform } from "framer-motion";
import SubHero from "../../components/SubHero";
import useFadeIn from "../../hooks/useFadeIn";
const COMPANY_NAV = [
{ label: "회사소개", to: "/company/about" },
@ -8,127 +9,172 @@ const COMPANY_NAV = [
{ label: "찾아오시는 길", to: "/company/location" },
];
const HISTORY = [
{
year: "2024",
items: [
{
month: "11월",
text: "UAM 통합 교통관리 시스템(UATM) 1단계 연구개발 착수",
},
{ month: "06월", text: "KT G-cloud 인천 총판 계약 체결" },
{ month: "03월", text: "비행상황관제 시스템 v3.0 고도화 완료" },
],
},
{
year: "2023",
items: [
{ month: "09월", text: "인천광역시 로봇랜드 14층 본사 이전" },
{
month: "07월",
text: "스마트 관광 예약 플랫폼 제주도관광공사 구축 완료",
},
{ month: "04월", text: "기업부설연구소 인정 (한국산업기술진흥협회)" },
{ month: "01월", text: "UTM 드론 비행 승인 시스템 실증 사업 참여" },
],
},
{
year: "2022",
items: [
{ month: "11월", text: "인천 항공산업 선도기업 유망기업 선정" },
{ month: "06월", text: "IBE(인터넷 부킹 엔진) v2 고도화 완료" },
{ month: "03월", text: "국방부 산하 항공 운영 시스템 구축 SI 수주" },
],
},
{
year: "2021",
items: [
{ month: "10월", text: "비행상황관제 시스템 조달청 우수제품 지정" },
{ month: "05월", text: "소프트웨어 품질인증 GS 인증 획득 (TTA)" },
],
},
{
year: "2019",
items: [
{
month: "08월",
text: "인천국제공항공사 항공 운영 시스템 유지보수 계약",
},
{
month: "03월",
text: "드론 관제 및 비행 계획 승인 시스템 프로토타입 개발",
},
],
},
{
year: "2017",
items: [
{ month: "09월", text: "스마트 관광 예약 플랫폼 1.0 출시" },
{ month: "04월", text: "IBE(인터넷 부킹 엔진) 첫 상용화" },
],
},
{
year: "2015",
items: [
{ month: "06월", text: "비행상황관제 시스템 초기 버전 개발 완료" },
{ month: "02월", text: "한국공항공사 MRO 시스템 구축 참여" },
],
},
{
year: "2010",
items: [
{ month: "05월", text: "주식회사 팔네트웍스 설립" },
{
month: "05월",
text: "항공 IT 소프트웨어 개발 전문 기업으로 사업 시작",
},
],
},
];
const TABS = ["2020's", "2010's"];
const HISTORY = {
"2020's": [
{
year: "2025",
items: ["KAC 드론교통관리(UTM) 시스템 구축사업 (한국공항공사)", "항공우주 부트캠프사업단 홈페이지 구축 (청주대학교)", "DIAMOND DA40NG Simulator 구축 (청주대학교)", "UAM 시뮬레이터를 활용한 비행관제 시스템 전시 (부산 벡스코 드론쇼코리아)", "인천 파브(PAV) 기업대상 기업홍보 지원사업 선정 (인천테크노파크)", "파브(PAV) 부품기술개발 지원사업 (인천테크노파크)", "감항인증 전문교육과정 위탁사업 홈페이지 운영 (방위산업청)", "공공용 UTM 시작품 정보공유체계 개발 (한국공항공사)", "IBE 개발 및 안정화 (섬에어)", "기술혁신형 중소기업 이노비즈 취득"],
},
{
year: "2024",
items: ["강화도 화개정원 유지보수 (강화군)", "타이드스퀘어 PRIVIA 항공 개발 업무 (타이드스퀘어)", "항공교통데이터시스템 (아이비리더스)", "24년 군 감항인증 전문교육과정 홈페이지 유지보수 (방위사업청)", "HILS 기반 PAV 부품 성능시험 시스템 (인하공업전문대학)", "PAV 원격 모니터링 시스템 (인천테크노파크)", "드론 지형추적비행 매뉴얼 개발 용역 (소방청 국립소방연구소)", "UAM 팀코리아(UTK) 워킹그룹 참여 (국토교통부)", "KAC 드론교통관리(UTM) 시스템 구축사업 (한국공항공사)", "도심항공교통(UAM, AAM등) 관련 사업 분석 위탁용역 (인천테크노파크)"],
},
{
year: "2023",
items: ["섬에어(주) 항공운항 예약시스템 구축", "남원시 드론 실증도시 구축사업 협약 체결 (국토교통부)", "팔네트웍스와 베셀 MOU 체결", "UAM 운항 공역 비행체 감시 기술개발 사전기획 (인천테크노파크)", "블록체인 기반 드론교통관리(UTM) 시스템 정보공유체계 개발 (한국공항공사)", "UAM 컨설팅 (SKT)", "항공모빌리티(PAV) 부품 기술개발 지원사업 (인천테크노파크)", "LAANC/드론/PAV/UAM 비행상황관리 시스템 전시 (서울 ADEX 2023)", "UAM 비행상황관리 시스템 전시 (2023 K-UAM CONFEX)", "PAV GNSS 시뮬레이터 1세트 등 3종 제작 구매 (인하공업전문대)"],
},
{
year: "2022",
items: ["현대자동차 QR 코드 출입시스템 운영 및 유지보수 (현대자동차)", "군감항인증전문교육과정위탁사업 (방위산업청)", "항공모빌리티(PAV) 부품 기술개발 (인천테크노파크)", "XR 메타버스 인천이음 프로젝트 활용확산 콘텐츠 개발 (인천테크노파크)", "드론교통관리(UTM) 시스템 설계", "남원시 항공산업(드론·UAM) 클러스터 조성 연구용역", "K-UAM CONFEX 전시회 참가 (무인비행장치 관제시스템)", "남원시 항공산업 업무협약"],
},
{
year: "2021",
items: ["현대자동차 QR코드 출입시스템 운영 및 유지보수 (현대자동차)", "파브 기술 및 기업/산업 현황 분석 (인천테크노파크)", "KT 클라우드 인천총판 등록 (KT)", "드론 규제 샌드박스 사업 - 비행 관제시스템 구축 (항공안전기술원)", "KB 국민은행 소프트웨어 경진대회 메타버스 구축 (사피엔스 4.0)", "드론 실증 비즈니스 사업화(공공서비스 기술 고도화) 선정", "캐플릭스 OTA 항공서비스 구축 (제주패스)", "ADEX 2021 전시회 참가", "인천 PAV 컨소시엄 선정", "KMF(Korea Metaverse Festival) 전시회 참가"],
},
{
year: "2020",
items: ["하이에어 통합시스템 유지보수 (하이에어)", "VR·AR 융합콘텐츠 실증 및 개발지원 과제 선정 (인천테크노파크)", "QR코드 활용 방문자 방역관리 모바일웹 개발 (인천관광공사)", "QR 출입명부 및 자동도어제어 시스템 개발 (현대자동차)", "하이에어 웹투어 연동 (하이에어)", "IATA Online Agency Accreditation 인증"],
},
],
"2010's": [
{
year: "2019",
items: ["IBS(PSS, 승무원 스케쥴, MRO) MOU 체결", "하이에어 IBS PSS 개발 및 납품", "하이에어 안전보안시스템(E-SMS) 개발", "하이에어 Weight & Balance System 개발"],
},
{
year: "2018",
items: ["항공사 홈페이지 예약시스템 구축 (에어필립)", "드론을 활용한 항공등화시설 정밀점검 (항공안전기술원)", "PAL&TOUR 국외여행업 등록", "벤처기업 인증", "시험용 UTM 시스템 DB 구축 용역 개발 (KT)", "인천광역시 인천 항공 유망기업 지정"],
},
{
year: "2017",
items: ["경량항공기 내비게이션 및 비행상황관리시스템 개발 (국토교통부)", "A CDM 설계 및 RMS 개선용역 (한국공항공사)", "IBTP MOU 체결", "항공사 정비시스템 구축 (에어필립)", "본사 로봇타워 이전"],
},
{
year: "2016",
items: ["항공인력양성사업 정보화시스템 개선 및 유지보수 (항공협회)"],
},
{
year: "2015",
items: ["주민자치센터 홈페이지 운영시스템 (인천시)", "기업부설연구소 인정", "아우리포탈 ActiveX 제거사업 (인천공항공사)", "㈜팔네트웍스 법인 전환", "중소기업청 창업성장과제 채택 (완료: 성공판정)"],
},
{
year: "2014",
items: ["개인기업 '팔네트웍스' 개업", "항공인력양성사업 정보화시스템 보완 용역 (항공협회)", "미얀마국제항공 웹사이트 구축 (크리스탈에비에이션)", "직접생산등록", "세계 책의 수도 홈페이지 구축용역 (인천시)"],
},
],
};
function YearGroup({ group }) {
const ref = useRef(null);
const [active, setActive] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(([entry]) => setActive(entry.isIntersecting), { rootMargin: "-30% 0px -60% 0px" });
observer.observe(el);
return () => observer.disconnect();
}, []);
return (
<div ref={ref} className="ht-group">
<div className={`ht-dot${active ? " is-active" : ""}`} />
<motion.h3 className="ht-year" animate={{ color: active ? "var(--color-primary)" : "#ccc" }} transition={{ duration: 0.4 }}>
{group.year}
</motion.h3>
<ul className="ht-list">
{group.items.map((text, i) => (
<motion.li key={i} className="ht-item" initial={{ opacity: 0, y: 8 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: "-40px" }} transition={{ delay: i * 0.04, duration: 0.3 }}>
<span className="ht-bullet" />
<span className="ht-text">{text}</span>
</motion.li>
))}
</ul>
</div>
);
}
function TimelinePanel({ tab }) {
const groups = HISTORY[tab];
const containerRef = useRef(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start 15%", "end 85%"],
});
const lineScaleY = useTransform(scrollYProgress, [0, 1], [0, 1]);
return (
<div className="ht-panel" ref={containerRef}>
{/* 왼쪽 연대 */}
<div className="ht-sidebar">
<div className="ht-decade">{tab}</div>
<div className="ht-since">Since 2014</div>
</div>
{/* 중앙 라인 — 1px 얇은 선 */}
<div className="ht-line-col">
<div className="ht-line-bg" />
<motion.div className="ht-line-fill" style={{ scaleY: lineScaleY, transformOrigin: "top" }} />
</div>
{/* 오른쪽 콘텐츠 */}
<div className="ht-content">
{groups.map((group) => (
<YearGroup key={group.year} group={group} />
))}
</div>
</div>
);
}
export default function HistoryPage() {
const ref = useFadeIn();
const [activeTab, setActiveTab] = useState("2020's");
return (
<article ref={ref}>
<article>
<SubHero
label="Company"
title={
<>
<span style={{ color: "#111" }}>하늘길 기술의</span>
<br />
<em>변화와 성장</em>
<em>HISTORY</em>
</>
}
desc="항공 IT의 기초부터 UAM/UATM 미래 기술까지, 팔네트웍스가 걸어온 발자취입니다."
navItems={COMPANY_NAV}
/>
<div className="sub-content">
<section className="sub-section">
<span className="sub-section-eyebrow sub-fade-in">History</span>
<h2 className="sub-section-title sub-fade-in">연혁</h2>
<div className="inner-wrap">
{/* 상단 타이틀 */}
<motion.div className="ht-header" initial={{ opacity: 0, y: 32 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: "-80px" }} transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}>
<p className="ht-header-title">
Leading the Future
<br />
<em>
<span>of</span> <b>Aviation Technology</b>
</em>
</p>
<em className="ht-header-sub">
항공 IT 소프트웨어 개발부터 드론·UAM·UTM 미래 모빌리티까지,
<br />
하늘길을 개척해온 팔네트웍스의 10 발자취를 담았습니다.
</em>
</motion.div>
<div className="history-timeline">
{HISTORY.map((group, gi) => (
<div
key={group.year}
className="history-year-group sub-fade-in"
style={{ transitionDelay: `${gi * 60}ms` }}
>
<div className="history-year">{group.year}</div>
<div className="history-items">
{group.items.map((item, ii) => (
<div key={ii} className="history-item">
<span className="history-item-month">{item.month}</span>
<p className="history-item-text">{item.text}</p>
</div>
))}
</div>
</div>
))}
{/* 연혁 카드 박스 */}
<div className="ht-card">
<div className="ht-tabs">
{TABS.map((tab) => (
<button key={tab} className={`ht-tab${activeTab === tab ? " is-active" : ""}`} onClick={() => setActiveTab(tab)}>
{tab}
</button>
))}
</div>
<TimelinePanel key={activeTab} tab={activeTab} />
</div>
</section>
</div>
</div>
</article>
);

33
src/pages/company/LocationPage.jsx

@ -82,8 +82,7 @@ const TRANSPORT = [
];
// /:
const MAP_SRC =
"https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3161.8!2d126.6854!3d37.5613!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x357b7f6e5a5cfc7f%3A0x0!2z7Iuc7LKc7Jy87Lanb-qzoOq1rOyGjA!5e0!3m2!1sko!2skr!4v1700000000000";
const MAP_SRC = "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3161.8!2d126.6854!3d37.5613!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x357b7f6e5a5cfc7f%3A0x0!2z7Iuc7LKc7Jy87Lanb-qzoOq1rOyGjA!5e0!3m2!1sko!2skr!4v1700000000000";
export default function LocationPage() {
const ref = useFadeIn();
@ -94,12 +93,12 @@ export default function LocationPage() {
label="Company"
title={
<>
<span style={{ color: "#111" }}>방문 안내</span>
<br />
<em>찾아오시는 </em>
{/* <span style={{ color: "#111" }}> </span>
<br /> */}
<em>Location</em>
</>
}
desc="인천광역시 서구 로봇랜드에 위치한 팔네트웍스 본사입니다."
// desc=" ."
navItems={COMPANY_NAV}
/>
@ -109,13 +108,7 @@ export default function LocationPage() {
{/* 지도 */}
<div className="sub-fade-in">
<div className="location-map">
<iframe
src={MAP_SRC}
allowFullScreen
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
title="팔네트웍스 본사 위치"
/>
<iframe src={MAP_SRC} allowFullScreen loading="lazy" referrerPolicy="no-referrer-when-downgrade" title="팔네트웍스 본사 위치" />
</div>
{/* 교통편 */}
@ -124,9 +117,7 @@ export default function LocationPage() {
<ul className="location-transport-list">
{TRANSPORT.map((t) => (
<li key={t.badge} className="location-transport-item">
<span className="location-transport-badge">
{t.badge}
</span>
<span className="location-transport-badge">{t.badge}</span>
<span className="location-transport-text">{t.text}</span>
</li>
))}
@ -135,10 +126,7 @@ export default function LocationPage() {
</div>
{/* 연락처 카드 */}
<div
className="location-info-card sub-fade-in"
style={{ transitionDelay: "100ms" }}
>
<div className="location-info-card sub-fade-in" style={{ transitionDelay: "100ms" }}>
<h3>() PALNETWORKS</h3>
<ul className="location-info-list">
{INFO.map((item) => (
@ -151,10 +139,7 @@ export default function LocationPage() {
{item.value}
</a>
) : (
<p
className="location-info-value"
style={{ margin: 0, whiteSpace: "pre-line" }}
>
<p className="location-info-value" style={{ margin: 0, whiteSpace: "pre-line" }}>
{item.value}
</p>
)}

152
src/pages/company/PartnersPage.jsx

@ -1,6 +1,7 @@
import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { motion } from "framer-motion";
import SubHero from "../../components/SubHero";
import useFadeIn from "../../hooks/useFadeIn";
@ -25,32 +26,12 @@ const CLIENTS = [
{ id: "molit", logo: "24" },
];
const PARTNERS = [
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"10",
"11",
"12",
"13",
"14",
"18",
"19",
"22",
];
const PARTNERS = ["02", "03", "04", "05", "06", "07", "08", "10", "11", "12", "13", "14", "18", "19", "22"];
function LogoCard({ logo, basePath }) {
return (
<div className="partners-logo-cell">
<img
src={`${basePath}images/partner/banner${logo}.png`}
alt=""
loading="lazy"
/>
<img src={`${basePath}images/partner/banner${logo}.png`} alt="" loading="lazy" />
</div>
);
}
@ -87,76 +68,81 @@ export default function PartnersPage() {
label="Company"
title={
<>
<span style={{ color: "#111" }}>함께하는</span>
<br />
<em>고객과 파트너</em>
{/* <span style={{ color: "#111" }}></span>
<br /> */}
<em>Partners</em>
</>
}
desc="공공·항공·국방 분야 주요 기관과의 신뢰를 바탕으로 성장해왔습니다."
// desc="·· ."
navItems={COMPANY_NAV}
/>
<div className="partners-wrap" ref={wrapRef}>
<div className="partners-title-block">
<h2 className="partners-title-main">
오랜 신뢰, 깊은 협력
<br />
<em>함께하는 기관과 파트너</em>
</h2>
<p className="partners-title-desc">
공공·항공·국방 분야의 주요 기관 기술 협력사와 함께 나은 미래를
만들어갑니다.
</p>
</div>
{/* 주요 고객사 */}
<div className="partners-section-row">
<div className="partners-sidebar">
<p className="partners-eyebrow">Clients</p>
<h2 className="partners-heading">
주요
<div className="sub-content">
<div className="inner-wrap">
{/* 상단 타이틀 */}
<motion.div className="ht-header" initial={{ opacity: 0, y: 32 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: "-80px" }} transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}>
<p className="ht-header-title">
Trusted by Many,
<br />
고객사
</h2>
<p className="partners-count">{CLIENTS.length}개사</p>
</div>
<div className="partners-grid partners-grid--4">
{CLIENTS.map((c) => (
<LogoCard key={c.id} logo={c.logo} basePath={basePath} />
))}
</div>
</div>
<em>
<span>Built</span> <b>for the Future</b>
</em>
</p>
<em className="ht-header-sub">오랜 신뢰, 깊은 협력 함께하는 기관과 파트너</em>
</motion.div>
{/* 기술 협력사 */}
<div className="partners-section-row">
<div className="partners-sidebar">
<p className="partners-eyebrow">Partners</p>
<h2 className="partners-heading">
기술
<br />
협력사
</h2>
<p className="partners-count">{PARTNERS.length}개사</p>
</div>
<div className="partners-grid partners-grid--5">
{PARTNERS.map((num) => (
<LogoCard key={num} logo={num} basePath={basePath} />
))}
</div>
</div>
<div className="partners-wrap" ref={wrapRef}>
{/* 주요 고객사 */}
<div className="partners-section-row">
<div className="partners-sidebar">
<p className="partners-eyebrow">Clients</p>
<h2 className="partners-heading">
주요
<br />
고객사
</h2>
<p className="partners-count">{CLIENTS.length}개사</p>
</div>
<div className="partners-grid partners-grid--4">
{CLIENTS.map((c) => (
<LogoCard key={c.id} logo={c.logo} basePath={basePath} />
))}
</div>
</div>
{/* CTA */}
<div className="partners-cta">
<div className="partners-cta-left">
<p className="partners-eyebrow">Become a Partner</p>
<p className="partners-cta-title">
팔네트웍스와 함께 성장할
<br />
파트너를 찾습니다
</p>
{/* 기술 협력사 */}
<div className="partners-section-row">
<div className="partners-sidebar">
<p className="partners-eyebrow">Partners</p>
<h2 className="partners-heading">
기술
<br />
협력사
</h2>
<p className="partners-count">{PARTNERS.length}개사</p>
</div>
<div className="partners-grid partners-grid--5">
{PARTNERS.map((num) => (
<LogoCard key={num} logo={num} basePath={basePath} />
))}
</div>
</div>
{/* CTA */}
<div className="partners-cta">
<div className="partners-cta-left">
<p className="partners-eyebrow">Become a Partner</p>
<p className="partners-cta-title">
팔네트웍스와 함께 성장할
<br />
파트너를 찾습니다
</p>
</div>
<a href="/contact/inquiry" className="partners-cta-btn">
협력 문의하기
</a>
</div>
</div>
<a href="/contact/inquiry" className="partners-cta-btn">
협력 문의하기
</a>
</div>
</div>
</article>

Loading…
Cancel
Save