Browse Source

dd

remotes/origin/main
김지은 1 month ago
parent
commit
bba4b358a7
  1. 67
      src/components/FloatingKeywords.jsx
  2. 168
      src/components/SubHero.jsx
  3. 148
      src/css/common.css
  4. 248
      src/pages/company/AboutPage.jsx

67
src/components/FloatingKeywords.jsx

@ -0,0 +1,67 @@
import { useEffect, useRef } from "react";
import { motion } from "framer-motion";
const KEYWORDS = [
{ text: "UTM", size: 18 },
{ text: "UAM", size: 22 },
{ text: "UATM", size: 24 },
{ text: "AI System", size: 15 },
{ text: "항공관제", size: 13 },
{ text: "드론관제", size: 11 },
{ text: "R&D", size: 20 },
{ text: "SI", size: 16 },
{ text: "솔루션", size: 12 },
{ text: "항공 데이터", size: 11 },
{ text: "스마트 공역", size: 14 },
{ text: "Flight Control", size: 10 },
{ text: "PAL Networks", size: 11 },
];
// ( )
const POSITIONS = [
{ x: 12, y: 18 },
{ x: 68, y: 8 },
{ x: 82, y: 32 },
{ x: 55, y: 22 },
{ x: 20, y: 55 },
{ x: 75, y: 58 },
{ x: 40, y: 72 },
{ x: 88, y: 75 },
{ x: 60, y: 45 },
{ x: 30, y: 38 },
{ x: 50, y: 85 },
{ x: 78, y: 88 },
{ x: 15, y: 82 },
];
export default function FloatingKeywords() {
return (
<div className="fk-wrap">
{KEYWORDS.map((kw, i) => (
<motion.span
key={kw.text}
className="fk-item"
style={{
left: `${POSITIONS[i].x}%`,
top: `${POSITIONS[i].y}%`,
fontSize: kw.size,
}}
initial={{ opacity: 0 }}
animate={{
opacity: [0, 0.28, 0.22, 0.32, 0.18],
y: [0, -6, 2, -4, 0],
}}
transition={{
duration: 6 + i * 0.8,
delay: i * 0.18,
repeat: Infinity,
repeatType: "mirror",
ease: "easeInOut",
}}
>
{kw.text}
</motion.span>
))}
</div>
);
}

168
src/components/SubHero.jsx

@ -1,3 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
@ -9,9 +10,172 @@ const menuMap = {
"/contact": { label: "Contact Us" }, "/contact": { label: "Contact Us" },
}; };
function NetworkGlobe() {
const canvasRef = useRef(null);
const mouseRef = useRef({ x: 0, y: 0 });
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
let raf;
let rotX = 0.3;
let rotY = 0;
const COLORS = ["#d94889", "#7b3fa0", "#593a84", "#1a1f5e", "#198dc7"];
const N_NODES = 60;
const nodes = Array.from({ length: N_NODES }, (_, i) => {
const phi = Math.acos(1 - (2 * (i + 0.5)) / N_NODES);
const theta = Math.PI * (1 + Math.sqrt(5)) * i;
return {
ox: Math.sin(phi) * Math.cos(theta),
oy: Math.cos(phi),
oz: Math.sin(phi) * Math.sin(theta),
color: COLORS[i % COLORS.length],
};
});
function resize() {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
function onMouseMove(e) {
const rect = canvas.getBoundingClientRect();
mouseRef.current = {
x: ((e.clientX - rect.left) / rect.width - 0.5) * 2,
y: ((e.clientY - rect.top) / rect.height - 0.5) * 2,
};
}
function onMouseLeave() {
mouseRef.current = { x: 0, y: 0 };
}
canvas.addEventListener("mousemove", onMouseMove);
canvas.addEventListener("mouseleave", onMouseLeave);
function project(x, y, z, cx, cy, R) {
const cosY = Math.cos(rotY),
sinY = Math.sin(rotY);
const x1 = x * cosY - z * sinY;
const z1 = x * sinY + z * cosY;
const cosX = Math.cos(rotX),
sinX = Math.sin(rotX);
const y2 = y * cosX - z1 * sinX;
const z2 = y * sinX + z1 * cosX;
const scale = 1 / (1.6 + z2 * 0.4);
return { sx: cx + x1 * R * scale, sy: cy + y2 * R * scale, sz: z2, scale };
}
function draw() {
const w = canvas.width;
const h = canvas.height;
const cx = w / 2;
const cy = h / 2;
const R = Math.min(w, h) * 0.4;
ctx.clearRect(0, 0, w, h);
rotY += 0.004 + mouseRef.current.x * 0.002;
rotX = 0.3 + mouseRef.current.y * 0.15;
const projected = nodes
.map((n) => ({
...project(n.ox, n.oy, n.oz, cx, cy, R),
color: n.color,
ox: n.ox,
oy: n.oy,
oz: n.oz,
}))
.sort((a, b) => a.sz - b.sz);
//
for (let i = 0; i < projected.length; i++) {
for (let j = i + 1; j < projected.length; j++) {
const a = projected[i],
b = projected[j];
const dx = a.ox - b.ox,
dy = a.oy - b.oy,
dz = a.oz - b.oz;
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 aHex = Math.round(Math.min(255, alpha * 255))
.toString(16)
.padStart(2, "0");
const grad = ctx.createLinearGradient(a.sx, a.sy, b.sx, b.sy);
grad.addColorStop(0, a.color + aHex);
grad.addColorStop(1, b.color + aHex);
ctx.beginPath();
ctx.moveTo(a.sx, a.sy);
ctx.lineTo(b.sx, b.sy);
ctx.strokeStyle = grad;
ctx.lineWidth = 0.8;
ctx.stroke();
}
}
}
//
projected.forEach((n) => {
const depth = (n.sz + 1) * 0.5;
const r = (1.2 + depth * 2.2) * n.scale;
const alpha = 0.25 + depth * 0.75;
const aHex = Math.round(Math.min(255, alpha * 255))
.toString(16)
.padStart(2, "0");
const gHex = Math.round(Math.min(255, alpha * 0.35 * 255))
.toString(16)
.padStart(2, "0");
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();
ctx.arc(n.sx, n.sy, r * 3.5, 0, Math.PI * 2);
ctx.fillStyle = glow;
ctx.fill();
ctx.beginPath();
ctx.arc(n.sx, n.sy, r, 0, Math.PI * 2);
ctx.fillStyle = n.color + aHex;
ctx.fill();
});
raf = requestAnimationFrame(draw);
}
resize();
draw();
const ro = new ResizeObserver(resize);
ro.observe(canvas);
return () => {
cancelAnimationFrame(raf);
ro.disconnect();
canvas.removeEventListener("mousemove", onMouseMove);
canvas.removeEventListener("mouseleave", onMouseLeave);
};
}, []);
return <canvas ref={canvasRef} className="sh4-globe-canvas" />;
}
export default function SubHero({ title, desc, navItems, rightSlot }) { export default function SubHero({ title, desc, navItems, rightSlot }) {
const { pathname } = useLocation(); const { pathname } = useLocation();
const titleLines = typeof title === "string" ? title.split("\n") : [title]; const titleLines = typeof title === "string" ? title.split("\n") : [title];
const [isPill, setIsPill] = useState(false);
const navRef = useRef(null);
useEffect(() => {
const onScroll = () => {
setIsPill(window.scrollY > 80);
};
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
return ( return (
<> <>
@ -52,7 +216,7 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
</div> </div>
{rightSlot && ( {rightSlot && (
<motion.div className="sh4-right" initial={{ opacity: 0, x: 24 }} animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.8, delay: 0.4, ease: [0.16, 1, 0.3, 1] }}> <motion.div className="sh4-right" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 1, delay: 0.3 }}>
{rightSlot} {rightSlot}
</motion.div> </motion.div>
)} )}
@ -60,7 +224,7 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
</section> </section>
{navItems?.length > 1 && ( {navItems?.length > 1 && (
<nav className="sh4-nav-wrap" aria-label="Sub Navigation"> <nav ref={navRef} className={`sh4-nav-wrap${isPill ? " is-pill" : ""}`} aria-label="Sub Navigation">
<div className="sh4-nav"> <div className="sh4-nav">
{navItems.map((item) => ( {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" : ""}`}>

148
src/css/common.css

@ -29,20 +29,30 @@ body{overflow-x:hidden;}
/*sh4 subhero*/ /*sh4 subhero*/
.sh4{position:relative;overflow:hidden;margin-top:calc(-1 * var(--header-height));padding:calc(var(--header-height) + 80px) 80px 96px;background:#fff;} .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-inner{position:relative;z-index:2;max-width:1440px;margin:0 auto;}
.sh4-left{}
.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-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{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-line{display:block;} .sh4-title-line{display:block;}
.sh4-char{display:inline-block;} .sh4-char{display:inline-block;}
.sh4-desc{font-size:clamp(14px,1.2vw,17px);line-height:1.8;color:rgba(26,31,94,.42);max-width:520px;word-break:keep-all;margin:0;opacity:0;transform:translateY(12px);animation:sh4Up .6s .5s cubic-bezier(.16,1,.3,1) forwards;} .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 sticky 탭 네비 — 언더라인형*/ /*sh4 split*/
.sh4-nav-wrap{position:sticky;top:var(--header-height);z-index:50;background:rgba(255,255,255,.92);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--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;padding:0 80px;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{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:hover{color:var(--navy);}
.sh4-nav-tab--active{color:var(--navy);} .sh4-nav-tab--active{color:var(--navy);}
.sh4-nav-tab--active::after{content:"";position:absolute;bottom:0;left:20px;right:20px;height:2px;border-radius:999px;background:var(--grad-brand-h);} .sh4-nav-tab--active::after{content:"";position:absolute;bottom:0;left:20px;right:20px;height:2px;border-radius:999px;background:var(--grad-brand-h);}
/*floating keywords*/
.fk-wrap{position:absolute;inset:0;pointer-events:none;overflow:hidden;}
.fk-item{position:absolute;font-weight:700;letter-spacing:.04em;color:var(--navy);white-space:nowrap;user-select:none;}
@keyframes sh4Up{ @keyframes sh4Up{
to{opacity:1;transform:translateY(0);} to{opacity:1;transform:translateY(0);}
} }
@ -54,9 +64,141 @@ body{overflow-x:hidden;}
@media (max-width:1024px){ @media (max-width:1024px){
.sh4{padding-left:32px;padding-right:32px;} .sh4{padding-left:32px;padding-right:32px;}
.sh4-nav{padding-left:32px;padding-right:32px;} .sh4-nav{padding-left:32px;padding-right:32px;}
.sh4--split .sh4-inner{grid-template-columns:1fr;gap:0;}
.sh4-right{display:none;}
} }
@media (max-width:768px){ @media (max-width:768px){
.sh4{padding:calc(var(--header-height) + 48px) 20px 64px;} .sh4{padding:calc(var(--header-height) + 48px) 20px 64px;}
.sh4-nav{padding-left:20px;padding-right:20px;} .sh4-nav{padding-left:20px;padding-right:20px;}
.sh4-nav-tab{padding:0 14px;font-size:13px;} .sh4-nav-tab{padding:0 14px;font-size:13px;}
}
/*ab3 stats*/
.ab3-stats{position:relative;padding:120px 80px;background:#fff;overflow:hidden;border-bottom:1px solid rgba(26,31,94,.06);}
.ab3-stats-inner{position:relative;z-index:2;max-width:1440px;margin:0 auto;}
.ab3-label{display:block;font-size:11px;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:rgba(26,31,94,.35);margin-bottom:64px;}
.ab3-label--light{color:rgba(255,255,255,.3);}
.ab3-stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border-top:1px solid rgba(26,31,94,.08);}
.ab3-si{position:relative;padding:52px 40px 40px 0;border-right:1px solid rgba(26,31,94,.06);overflow:hidden;}
.ab3-si:last-child{border-right:0;}
.ab3-si:not(:first-child){padding-left:40px;}
.ab3-si-bg{position:absolute;top:-20px;left:-10px;font-size:clamp(100px,12vw,160px);font-weight:900;letter-spacing:-.08em;color:rgba(26,31,94,.03);line-height:1;pointer-events:none;white-space:nowrap;z-index:0;}
.ab3-si-note{display:block;font-size:10px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;color:var(--pink);margin-bottom:12px;position:relative;z-index:1;}
.ab3-si-num{display:block;font-size:clamp(48px,5.5vw,80px);font-weight:900;letter-spacing:-.06em;line-height:1;color:var(--navy);position:relative;z-index:1;}
.ab3-si-lbl{display:block;margin-top:10px;font-size:14px;font-weight:500;color:rgba(26,31,94,.38);position:relative;z-index:1;}
@media (max-width:1280px){.ab3-stats{padding-left:48px;padding-right:48px;}}
@media (max-width:1024px){
.ab3-stats{padding-left:32px;padding-right:32px;}
.ab3-stats-grid{grid-template-columns:repeat(2,1fr);}
.ab3-si{padding-bottom:40px;}
.ab3-si:nth-child(2){border-right:0;}
.ab3-si:nth-child(3){border-top:1px solid rgba(26,31,94,.06);padding-left:0;}
.ab3-si:nth-child(4){border-top:1px solid rgba(26,31,94,.06);border-right:0;}
}
@media (max-width:768px){
.ab3-stats{padding:80px 20px;}
.ab3-si{padding-right:20px;}
.ab3-si:not(:first-child){padding-left:20px;}
}
/*ab about page*/
.ab-eyebrow{display:block;font-size:11px;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:rgba(26,31,94,.35);margin-bottom:40px;}
.ab-eyebrow--light{color:rgba(255,255,255,.3);}
/*② 소개*/
.ab-intro{background:linear-gradient(135deg,#f8f7ff 0%,#fdf4fb 50%,#f5f7ff 100%);padding:120px 80px;}
.ab-intro-inner{max-width:1440px;margin:0 auto;display:grid;grid-template-columns:1fr 1fr;gap:100px;align-items:start;}
.ab-intro-title{font-size:clamp(40px,5vw,72px);font-weight:900;line-height:1.05;letter-spacing:-.055em;color:var(--navy);margin:0;}
.ab-intro-title em{font-style:normal;background:var(--grad-brand-h);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;}
.ab-intro-badge{display:inline-flex;flex-direction:column;gap:4px;padding:14px 20px;border-radius:12px;border:1px solid rgba(217,72,137,.15);background:rgba(217,72,137,.05);margin-bottom:28px;}
.ab-intro-badge span{font-size:11px;font-weight:600;letter-spacing:.08em;color:rgba(26,31,94,.4);}
.ab-intro-badge strong{font-size:15px;font-weight:800;color:var(--navy);}
.ab-intro-text p{font-size:16px;line-height:1.9;color:rgba(26,31,94,.5);word-break:keep-all;margin:0 0 16px;}
.ab-intro-text p:last-child{margin:0;}
/*③ stats*/
.ab-stats{background:linear-gradient(135deg,#0e1240 0%,#1a1f5e 50%,#141852 100%);padding:120px 80px;position:relative;overflow:hidden;}
.ab-stats::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse 60% 80% at 80% 20%,rgba(123,63,160,.2) 0%,transparent 60%),radial-gradient(ellipse 40% 60% at 10% 80%,rgba(217,72,137,.12) 0%,transparent 55%);pointer-events:none;}
.ab-stats-inner{position:relative;z-index:1;max-width:1440px;margin:0 auto;}
.ab-stats-grid{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid rgba(255,255,255,.08);}
.ab-si{position:relative;padding:52px 40px 40px 0;border-right:1px solid rgba(255,255,255,.06);overflow:hidden;}
.ab-si:last-child{border-right:0;}
.ab-si:not(:first-child){padding-left:40px;}
.ab-si-bg{position:absolute;top:-10px;left:-5px;font-size:clamp(90px,10vw,140px);font-weight:900;letter-spacing:-.08em;color:rgba(255,255,255,.04);line-height:1;pointer-events:none;white-space:nowrap;z-index:0;}
.ab-si-note{display:block;font-size:10px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;color:var(--pink);margin-bottom:12px;position:relative;z-index:1;}
.ab-si-num{display:block;font-size:clamp(48px,5.5vw,80px);font-weight:900;letter-spacing:-.06em;line-height:1;color:#fff;position:relative;z-index:1;}
.ab-si-lbl{display:block;margin-top:10px;font-size:14px;font-weight:500;color:rgba(255,255,255,.35);position:relative;z-index:1;}
/*④ 가치*/
.ab-vals{background:#fff;padding:140px 80px;}
.ab-vals-inner{max-width:1440px;margin:0 auto;}
.ab-vals-head{margin-bottom:72px;}
.ab-vals-title{font-size:clamp(36px,4vw,56px);font-weight:900;letter-spacing:-.055em;color:var(--navy);margin:0;}
.ab-vals-list{display:flex;flex-direction:column;}
.ab-val-row{display:grid;grid-template-columns:120px 1fr 1fr 32px;gap:32px;align-items:center;padding:40px 0;border-top:1px solid rgba(26,31,94,.07);transition:padding-left .3s ease;}
.ab-val-row:last-child{border-bottom:1px solid rgba(26,31,94,.07);}
.ab-val-row:hover{padding-left:12px;}
.ab-val-row:hover .ab-val-arr{color:var(--pink);transform:translateX(6px);}
.ab-val-left{display:flex;flex-direction:column;gap:4px;}
.ab-val-idx{font-size:11px;font-weight:800;letter-spacing:.14em;color:rgba(26,31,94,.18);}
.ab-val-en{font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--pink);}
.ab-val-name{font-size:clamp(18px,2vw,24px);font-weight:800;letter-spacing:-.04em;color:var(--navy);margin:0;}
.ab-val-desc{font-size:14px;line-height:1.85;color:rgba(26,31,94,.45);word-break:keep-all;margin:0;}
.ab-val-arr{font-size:16px;color:rgba(26,31,94,.15);transition:color .3s,transform .3s;}
/*⑤ 수상*/
.ab-awds{background:linear-gradient(135deg,#fff5f9 0%,#fdf4fb 50%,#f8f5ff 100%);padding:140px 80px;}
.ab-awds-inner{max-width:1440px;margin:0 auto;}
.ab-awds-head{margin-bottom:64px;}
.ab-awds-title{font-size:clamp(36px,4vw,56px);font-weight:900;letter-spacing:-.055em;color:var(--navy);margin:0;}
.ab-awds-list{list-style:none;padding:0;margin:0;}
.ab-awd-item{display:grid;grid-template-columns:80px 2px 1fr;gap:32px;align-items:center;padding:36px 0;border-top:1px solid rgba(26,31,94,.08);transition:padding-left .3s ease;}
.ab-awd-item:last-child{border-bottom:1px solid rgba(26,31,94,.08);}
.ab-awd-item:hover{padding-left:12px;}
.ab-awd-yr{font-size:13px;font-weight:800;letter-spacing:.06em;color:rgba(26,31,94,.3);}
.ab-awd-line{width:2px;height:44px;background:linear-gradient(180deg,var(--purple),var(--pink));border-radius:999px;}
.ab-awd-body{display:flex;flex-direction:column;gap:5px;}
.ab-awd-name{font-size:17px;font-weight:700;color:var(--navy);letter-spacing:-.02em;}
.ab-awd-org{font-size:13px;color:rgba(26,31,94,.38);}
/*⑥ CTA*/
.ab-cta{background:linear-gradient(135deg,#0e1240 0%,#1a1f5e 50%,#141852 100%);padding:160px 80px;text-align:center;position:relative;overflow:hidden;}
.ab-cta::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse 65% 60% at 50% 50%,rgba(217,72,137,.18) 0%,transparent 60%),radial-gradient(ellipse 40% 35% at 80% 20%,rgba(123,63,160,.14) 0%,transparent 55%);pointer-events:none;}
.ab-cta-inner{position:relative;z-index:1;max-width:720px;margin:0 auto;display:flex;flex-direction:column;align-items:center;}
.ab-cta-chip{display:inline-block;padding:6px 18px;border-radius:999px;border:1px solid rgba(217,72,137,.35);background:rgba(217,72,137,.12);font-size:10px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;color:var(--pink);margin-bottom:28px;}
.ab-cta-title{font-size:clamp(44px,6vw,80px);font-weight:900;line-height:1.05;letter-spacing:-.06em;color:#fff;margin-bottom:20px;}
.ab-cta-desc{font-size:16px;line-height:1.8;color:rgba(255,255,255,.38);margin-bottom:52px;word-break:keep-all;}
.ab-cta-btns{display:flex;align-items:center;gap:14px;}
.ab-cta-btn{display:inline-flex;align-items:center;justify-content:center;height:56px;padding:0 40px;border-radius:8px;font-size:15px;font-weight:800;text-decoration:none;transition:transform .25s,box-shadow .25s;}
.ab-cta-btn--fill{background:var(--grad-brand-h);color:#fff;}
.ab-cta-btn--fill:hover{transform:translateY(-3px);box-shadow:0 20px 48px rgba(217,72,137,.3);}
.ab-cta-btn--line{background:transparent;color:rgba(255,255,255,.7);border:1px solid rgba(255,255,255,.2);}
.ab-cta-btn--line:hover{border-color:rgba(255,255,255,.4);color:#fff;transform:translateY(-3px);}
@media (max-width:1280px){
.ab-intro,.ab-stats,.ab-vals,.ab-awds,.ab-cta{padding-left:48px;padding-right:48px;}
}
@media (max-width:1024px){
.ab-intro,.ab-stats,.ab-vals,.ab-awds,.ab-cta{padding-left:32px;padding-right:32px;}
.ab-intro-inner{grid-template-columns:1fr;gap:56px;}
.ab-stats-grid{grid-template-columns:repeat(2,1fr);}
.ab-si{padding-bottom:40px;}
.ab-si:nth-child(2){border-right:0;}
.ab-si:nth-child(3){border-top:1px solid rgba(255,255,255,.06);padding-left:0;}
.ab-si:nth-child(4){border-top:1px solid rgba(255,255,255,.06);border-right:0;}
.ab-val-row{grid-template-columns:100px 1fr;grid-template-rows:auto auto;gap:16px 24px;}
.ab-val-desc{grid-column:1/-1;}
.ab-val-arr{display:none;}
}
@media (max-width:768px){
.ab-intro,.ab-vals,.ab-awds{padding-top:80px;padding-bottom:80px;}
.ab-stats{padding-top:80px;padding-bottom:80px;}
.ab-cta{padding-top:100px;padding-bottom:100px;}
.ab-intro,.ab-stats,.ab-vals,.ab-awds,.ab-cta{padding-left:20px;padding-right:20px;}
.ab-si{padding-right:20px;}
.ab-si:not(:first-child){padding-left:20px;}
.ab-awd-item{grid-template-columns:64px 2px 1fr;gap:20px;padding:28px 0;}
.ab-cta-btns{flex-direction:column;width:100%;}
.ab-cta-btn{width:100%;max-width:320px;}
} }

248
src/pages/company/AboutPage.jsx

@ -3,26 +3,11 @@ import { Link } from "react-router-dom";
import { gsap } from "gsap"; import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
import SubHero from "../../components/SubHero"; import SubHero from "../../components/SubHero";
import FloatingKeywords from "../../components/FloatingKeywords";
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
const HERO_STATS = [ const heroRight = <FloatingKeywords />;
{ num: "2010", label: "설립연도" },
{ num: "50+", label: "완료 프로젝트" },
{ num: "15+", label: "주요 고객사" },
{ num: "10+", label: "R&D 전문인력" },
];
const heroRight = (
<div className="ab-hero-stats">
{HERO_STATS.map((s) => (
<div className="ab-hero-stat" key={s.label}>
<strong className="ab-hero-stat-num">{s.num}</strong>
<span className="ab-hero-stat-lbl">{s.label}</span>
</div>
))}
</div>
);
const COMPANY_NAV = [ const COMPANY_NAV = [
{ label: "회사소개", to: "/company/about" }, { label: "회사소개", to: "/company/about" },
@ -39,10 +24,10 @@ const STATS = [
]; ];
const VALUES = [ const VALUES = [
{ idx: "01", title: "기술 혁신", en: "Innovation", desc: "항공 데이터와 UTM 기술의 경계를 지속적으로 확장하며 미래 모빌리티 시대를 선도합니다.", img: "/images/img1.jpg" }, { idx: "01", title: "기술 혁신", en: "Innovation", desc: "항공 데이터와 UTM 기술의 경계를 지속적으로 확장하며 미래 모빌리티 시대를 선도합니다." },
{ idx: "02", title: "신뢰와 책임", en: "Trust", desc: "공공·항공 분야의 핵심 인프라를 다루는 만큼 모든 서비스에 안전과 신뢰를 최우선으로 합니다.", img: "/images/img2.jpg" }, { idx: "02", title: "신뢰와 책임", en: "Trust", desc: "공공·항공 분야의 핵심 인프라를 다루는 만큼 모든 서비스에 안전과 신뢰를 최우선으로 합니다." },
{ idx: "03", title: "파트너십", en: "Partnership", desc: "고객사와 장기 파트너로서 구축부터 운영·유지보수까지 전 과정을 함께합니다.", img: "/images/img3.jpg" }, { idx: "03", title: "파트너십", en: "Partnership", desc: "고객사와 장기 파트너로서 구축부터 운영·유지보수까지 전 과정을 함께합니다." },
{ idx: "04", title: "전문성", en: "Expertise", desc: "항공 IT 분야 10년 이상의 전문 인력이 SI, R&D, 솔루션 개발을 일관되게 수행합니다.", img: "/images/img4.jpg" }, { idx: "04", title: "전문성", en: "Expertise", desc: "항공 IT 분야 10년 이상의 전문 인력이 SI, R&D, 솔루션 개발을 일관되게 수행합니다." },
]; ];
const AWARDS = [ const AWARDS = [
@ -52,7 +37,7 @@ const AWARDS = [
{ year: "2020", title: "조달청 우수제품 지정", org: "비행상황관제 시스템" }, { year: "2020", title: "조달청 우수제품 지정", org: "비행상황관제 시스템" },
]; ];
function animateCount(el, target, suffix, duration = 1800) { function animateCount(el, target, suffix, duration = 1600) {
const from = target > 100 ? target - 4 : 0; const from = target > 100 ? target - 4 : 0;
let start = null; let start = null;
const step = (ts) => { const step = (ts) => {
@ -67,17 +52,15 @@ function animateCount(el, target, suffix, duration = 1800) {
export default function AboutPage() { export default function AboutPage() {
const statRefs = useRef([]); const statRefs = useRef([]);
const statDone = useRef(false);
useEffect(() => { useEffect(() => {
const ctx = gsap.context(() => { const ctx = gsap.context(() => {
/* ── 숫자 섹션 카운터 ── */ /* stats 카운터 */
ScrollTrigger.create({ ScrollTrigger.create({
trigger: ".ab3-stats", trigger: ".ab-stats",
start: "top 75%", start: "top 75%",
toggleActions: "play none none reset",
onEnter: () => { onEnter: () => {
if (statDone.current) return;
statDone.current = true;
statRefs.current.forEach((el, i) => { statRefs.current.forEach((el, i) => {
if (!el) return; if (!el) return;
setTimeout(() => animateCount(el, STATS[i].num, STATS[i].suffix), i * 100); setTimeout(() => animateCount(el, STATS[i].num, STATS[i].suffix), i * 100);
@ -85,84 +68,73 @@ export default function AboutPage() {
}, },
}); });
/* ── 숫자 카드 스태거 ── */ /* stats 스태거 */
gsap.fromTo( gsap.fromTo(
".ab3-si", ".ab-si",
{ opacity: 0, y: 50 }, { opacity: 0, y: 40 },
{ {
opacity: 1, opacity: 1,
y: 0, y: 0,
stagger: 0.12, stagger: 0.12,
duration: 0.9, duration: 0.8,
ease: "power3.out", ease: "power3.out",
scrollTrigger: { trigger: ".ab3-stats", start: "top 78%" }, scrollTrigger: { trigger: ".ab-stats", start: "top 78%" },
}, },
); );
/* ── 철학 섹션 핀 + 텍스트 순차 reveal ── */ /* 소개 */
gsap.fromTo( gsap.fromTo(
".ab3-phil-line", ".ab-intro-text > *",
{ opacity: 0, y: 80 }, { opacity: 0, y: 32 },
{ {
opacity: 1, opacity: 1,
y: 0, y: 0,
stagger: 0.15, stagger: 0.1,
duration: 1.1, duration: 0.9,
ease: "power4.out",
scrollTrigger: { trigger: ".ab3-phil", start: "top 65%" },
},
);
gsap.fromTo(
".ab3-phil-sub",
{ opacity: 0, x: 40 },
{
opacity: 1,
x: 0,
duration: 1,
ease: "power3.out", ease: "power3.out",
scrollTrigger: { trigger: ".ab3-phil", start: "top 55%" }, scrollTrigger: { trigger: ".ab-intro", start: "top 70%" },
}, },
); );
/* ── 가치 섹션 라인 애니 ── */ /* 가치 */
gsap.fromTo( gsap.fromTo(
".ab3-val-row", ".ab-val-row",
{ opacity: 0, x: -40 }, { opacity: 0, x: -32 },
{ {
opacity: 1, opacity: 1,
x: 0, x: 0,
stagger: 0.1, stagger: 0.1,
duration: 0.8, duration: 0.8,
ease: "power3.out", ease: "power3.out",
scrollTrigger: { trigger: ".ab3-vals", start: "top 72%" }, scrollTrigger: { trigger: ".ab-vals", start: "top 72%" },
}, },
); );
/* ── 수상 ── */ /* 수상 */
gsap.fromTo( gsap.fromTo(
".ab3-awd-item", ".ab-awd-item",
{ opacity: 0, y: 30 }, { opacity: 0, y: 24 },
{ {
opacity: 1, opacity: 1,
y: 0, y: 0,
stagger: 0.1, stagger: 0.09,
duration: 0.7, duration: 0.7,
ease: "power2.out", ease: "power2.out",
scrollTrigger: { trigger: ".ab3-awds", start: "top 75%" }, scrollTrigger: { trigger: ".ab-awds", start: "top 75%" },
}, },
); );
/* ── CTA ── */ /* CTA */
gsap.fromTo( gsap.fromTo(
".ab3-cta-inner > *", ".ab-cta-inner > *",
{ opacity: 0, y: 40 }, { opacity: 0, y: 32 },
{ {
opacity: 1, opacity: 1,
y: 0, y: 0,
stagger: 0.12, stagger: 0.12,
duration: 0.9, duration: 0.9,
ease: "power2.out", ease: "power2.out",
scrollTrigger: { trigger: ".ab3-cta", start: "top 75%" }, scrollTrigger: { trigger: ".ab-cta", start: "top 75%" },
}, },
); );
}); });
@ -170,96 +142,95 @@ export default function AboutPage() {
}, []); }, []);
return ( return (
<article className="ab3"> <article>
{/* ① 히어로 */}
<SubHero title={"가치를 실천하는\n항공 IT 전문기업"} desc="팔네트웍스는 항공 데이터와 통합 관제 기술을 기반으로 안전한 하늘길을 만들어갑니다." navItems={COMPANY_NAV} rightSlot={heroRight} /> <SubHero title={"가치를 실천하는\n항공 IT 전문기업"} desc="팔네트웍스는 항공 데이터와 통합 관제 기술을 기반으로 안전한 하늘길을 만들어갑니다." navItems={COMPANY_NAV} rightSlot={heroRight} />
{/* ── STATS ── */} {/* ② 소개 — 흰 배경, 히어로와 자연스럽게 연결 */}
<section className="ab3-stats"> <section className="ab-intro">
<div className="ab3-stats-bg" /> <div className="ab-intro-inner">
<div className="ab3-stats-inner"> <div className="ab-intro-left">
<p className="ab3-label">PAL Networks in Numbers</p> <span className="ab-eyebrow">Our Philosophy</span>
<div className="ab3-stats-grid"> <h2 className="ab-intro-title">
항공산업의
<br />
기술혁신을
<br />
<em>선도합니다.</em>
</h2>
</div>
<div className="ab-intro-text">
<div className="ab-intro-badge">
<span>2010 설립 이후</span>
<strong>항공 IT </strong>
</div>
<p>팔네트웍스는 2010 설립 이후 항공 예약 플랫폼, 비행상황관제 시스템, UTM 솔루션까지 항공 IT 분야의 핵심 기술을 꾸준히 개발해왔습니다.</p>
<p>인천광역시 로봇랜드에서 UAM/UATM 미래 기술을 선행 연구하며 안전한 하늘길을 만들어가고 있습니다.</p>
</div>
</div>
</section>
{/* ③ Stats — 연한 네이비 배경 */}
<section className="ab-stats">
<div className="ab-stats-inner">
<span className="ab-eyebrow ab-eyebrow--light">PAL Networks in Numbers</span>
<div className="ab-stats-grid">
{STATS.map((s, i) => ( {STATS.map((s, i) => (
<div className="ab3-si" key={s.note}> <div className="ab-si" key={s.note}>
<span className="ab3-si-note">{s.note}</span> <span className="ab-si-bg">
<strong className="ab3-si-num" ref={(el) => (statRefs.current[i] = el)}> {s.num}
{s.suffix}
</span>
<span className="ab-si-note">{s.note}</span>
<strong className="ab-si-num" ref={(el) => (statRefs.current[i] = el)}>
{s.num} {s.num}
{s.suffix} {s.suffix}
</strong> </strong>
<span className="ab3-si-lbl">{s.label}</span> <span className="ab-si-lbl">{s.label}</span>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</section> </section>
{/* ── PHILOSOPHY (영상 배경 + 텍스트) ── */} {/* ④ 핵심가치 — 흰 배경 */}
<section className="ab3-phil"> <section className="ab-vals">
<video className="ab3-phil-vid" src="/images/uam.mp4" autoPlay muted loop playsInline /> <div className="ab-vals-inner">
<div className="ab3-phil-overlay" /> <div className="ab-vals-head">
<div className="ab3-phil-inner"> <span className="ab-eyebrow">Core Values</span>
<div className="ab3-phil-left"> <h2 className="ab-vals-title">우리가 지키는 가치</h2>
<p className="ab3-label ab3-label--light">Our Philosophy</p>
<div className="ab3-phil-words">
<span className="ab3-phil-line">항공산업의</span>
<span className="ab3-phil-line">기술혁신을</span>
<span className="ab3-phil-line ab3-phil-line--accent">선도합니다.</span>
</div>
</div>
<div className="ab3-phil-sub">
<div className="ab3-phil-badge">
<span>2010 설립 이후</span>
<strong>항공 IT </strong>
</div>
<p className="ab3-phil-body">팔네트웍스는 2010 설립 이후 항공 예약 플랫폼, 비행상황관제 시스템, UTM 솔루션까지 항공 IT 분야의 핵심 기술을 꾸준히 개발해왔습니다. 인천광역시 로봇랜드에서 UAM/UATM 미래 기술을 선행 연구하고 있습니다.</p>
</div>
</div>
</section>
{/* ── CORE VALUES ── */}
<section className="ab3-vals">
<div className="ab3-vals-inner">
<div className="ab3-vals-head">
<p className="ab3-label">Core Values</p>
<h2 className="ab3-vals-title">
우리가
<br />
지키는 가치
</h2>
</div> </div>
<div className="ab3-vals-list"> <div className="ab-vals-list">
{VALUES.map((v) => ( {VALUES.map((v) => (
<div className="ab3-val-row" key={v.idx} style={{ "--val-img": `url(${v.img})` }}> <div className="ab-val-row" key={v.idx}>
<div className="ab3-val-img" /> <div className="ab-val-left">
<div className="ab3-val-meta"> <span className="ab-val-idx">{v.idx}</span>
<span className="ab3-val-idx">{v.idx}</span> <span className="ab-val-en">{v.en}</span>
<span className="ab3-val-en">{v.en}</span>
</div> </div>
<h3 className="ab3-val-name">{v.title}</h3> <h3 className="ab-val-name">{v.title}</h3>
<p className="ab3-val-desc">{v.desc}</p> <p className="ab-val-desc">{v.desc}</p>
<span className="ab3-val-arr"></span> <span className="ab-val-arr"></span>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</section> </section>
{/* ── AWARDS ── */} {/* ⑤ 수상/인증 — 연한 배경 */}
<section className="ab3-awds"> <section className="ab-awds">
<div className="ab3-awds-bg" /> <div className="ab-awds-inner">
<div className="ab3-awds-inner"> <div className="ab-awds-head">
<div className="ab3-awds-head"> <span className="ab-eyebrow">Certifications & Awards</span>
<p className="ab3-label ab3-label--light">Certifications & Awards</p> <h2 className="ab-awds-title">인증 수상</h2>
<h2 className="ab3-awds-title">인증 수상</h2>
</div> </div>
<ul className="ab3-awds-list"> <ul className="ab-awds-list">
{AWARDS.map((a, i) => ( {AWARDS.map((a, i) => (
<li className="ab3-awd-item" key={i}> <li className="ab-awd-item" key={i}>
<span className="ab3-awd-yr">{a.year}</span> <span className="ab-awd-yr">{a.year}</span>
<div className="ab3-awd-line" /> <div className="ab-awd-line" />
<div className="ab3-awd-body"> <div className="ab-awd-body">
<strong className="ab3-awd-name">{a.title}</strong> <strong className="ab-awd-name">{a.title}</strong>
<span className="ab3-awd-org">{a.org}</span> <span className="ab-awd-org">{a.org}</span>
</div> </div>
</li> </li>
))} ))}
@ -267,22 +238,21 @@ export default function AboutPage() {
</div> </div>
</section> </section>
{/* ── CTA ── */} {/* ⑥ CTA — 네이비 배경 */}
<section className="ab3-cta"> <section className="ab-cta">
<div className="ab3-cta-bg" /> <div className="ab-cta-inner">
<div className="ab3-cta-inner"> <span className="ab-cta-chip">Contact Us</span>
<span className="ab3-cta-chip">Contact Us</span> <h2 className="ab-cta-title">
<h2 className="ab3-cta-title">
팔네트웍스와 팔네트웍스와
<br /> <br />
함께하세요 함께하세요
</h2> </h2>
<p className="ab3-cta-desc">파트너십 문의, 채용, 사업 협력 무엇이든 편하게 연락주세요.</p> <p className="ab-cta-desc">파트너십 문의, 채용, 사업 협력 무엇이든 편하게 연락주세요.</p>
<div className="ab3-cta-btns"> <div className="ab-cta-btns">
<Link to="/contact/inquiry" className="ab3-cta-btn ab3-cta-btn--fill"> <Link to="/contact/inquiry" className="ab-cta-btn ab-cta-btn--fill">
문의하기 문의하기
</Link> </Link>
<Link to="/contact/recruit" className="ab3-cta-btn ab3-cta-btn--line"> <Link to="/contact/recruit" className="ab-cta-btn ab-cta-btn--line">
채용 안내 채용 안내
</Link> </Link>
</div> </div>

Loading…
Cancel
Save