Browse Source

테스트중

remotes/origin/main
김지은 4 weeks ago
parent
commit
d3d796b4b1
  1. 66
      src/css/common.css
  2. 54
      src/pages/utm/CasePage.jsx

66
src/css/common.css

@ -449,55 +449,47 @@ body{overflow-x:hidden;}
} }
} }
/* ── Case Head ── */ /* ── Head ── */
.cs-head {padding:72px 0 64px;} .cs-head {padding:72px 0 100px;}
.cs-title {font-size:40px; font-weight:800; color:#111; letter-spacing:-.02em; line-height:1.25; margin-bottom:16px;} .cs-title {font-size:40px; font-weight:800; color:#111; letter-spacing:-.02em; line-height:1.25; margin-bottom:16px;}
.cs-title em {font-style:normal; background:var(--grad-brand); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text;} .cs-title em {font-style:normal; background:var(--grad-brand); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text;}
.cs-desc {font-size:15px; color:#666; line-height:1.7;} .cs-desc {font-size:15px; color:#666; line-height:1.7;}
/* ── Case List ── */ /* ── List ── */
.cs-list {display:flex; flex-direction:column;} .cs-list {display:flex; flex-direction:column; gap:0; padding-bottom:120px;}
/* ── Item ── */
.cs-item {position:relative; display:grid; grid-template-columns:1fr 1fr; gap:0; align-items:center; min-height:560px; padding:80px 0; border-top:1px solid var(--color-primary-soft-border);}
.cs-item:last-child {border-bottom:1px solid var(--color-primary-soft-border);}
.cs-item--flip .cs-img-wrap {order:2;}
.cs-item--flip .cs-body {order:1; padding-right:80px; padding-left:0;}
/* ── Case Item ── */ /* ── 배경 번호 ── */
.cs-item {position:relative; width:100%; height:560px; overflow:hidden;} .cs-bg-num {position:absolute; font-size:200px; font-weight:800; color:#111; opacity:.03; line-height:1; letter-spacing:-.05em; top:50%; transform:translateY(-50%); left:0; pointer-events:none; z-index:0;}
/* ── Image ── */ /* ── Image ── */
.cs-img-wrap {position:absolute; inset:0;} .cs-img-wrap {position:relative; border-radius:12px; overflow:hidden; aspect-ratio:3/2; will-change:clip-path; z-index:1;}
.cs-img {width:100%; height:100%; object-fit:cover; display:block;} .cs-img {width:100%; height:100%; object-fit:cover; display:block;}
.cs-img-fallback {position:absolute; inset:0; background:linear-gradient(135deg, #e8eaf6 0%, #d0d4ef 100%);} .cs-img-fallback {position:absolute; inset:0; background:var(--color-primary-soft);}
.cs-img-overlay {position:absolute; inset:0; background:linear-gradient(90deg, rgba(10,12,40,.72) 0%, rgba(10,12,40,.3) 50%, rgba(10,12,40,0) 100%);}
/* ── Body ── */ /* ── Body ── */
.cs-body {position:absolute; left:0; top:0; bottom:0; width:480px; display:flex; flex-direction:column; justify-content:center; gap:14px; padding:56px 64px;} .cs-body {padding-left:80px; display:flex; flex-direction:column; gap:20px; z-index:1;}
.cs-num {font-size:13px; font-weight:700; color:rgba(255,255,255,.4); letter-spacing:.2em;} .cs-meta {display:flex; align-items:center; gap:16px;}
.cs-eyebrow {font-size:13px; font-weight:600; letter-spacing:.1em; text-transform:uppercase; color:rgba(255,255,255,.6);} .cs-year {font-size:12px; font-weight:600; color:#aaa; letter-spacing:.1em;}
.cs-item-title {font-size:34px; font-weight:800; color:#fff; letter-spacing:-.02em; line-height:1.25; margin:0;} .cs-eyebrow {font-size:12px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--color-primary); opacity:.8;}
.cs-item-desc {font-size:14px; color:rgba(255,255,255,.75); line-height:1.75; word-break:keep-all;} .cs-item-title {font-size:30px; font-weight:800; color:#111; letter-spacing:-.02em; line-height:1.3; margin:0;}
.cs-item-desc {font-size:14px; color:#555; line-height:1.8; word-break:keep-all;}
.cs-tags {display:flex; gap:8px; flex-wrap:wrap;} .cs-tags {display:flex; gap:8px; flex-wrap:wrap;}
.cs-tag {font-size:12px; font-weight:600; color:#fff; background:rgba(255,255,255,.15); border:1px solid rgba(255,255,255,.2); padding:5px 12px; border-radius:100px; backdrop-filter:blur(4px);} .cs-tag {font-size:11px; font-weight:600; color:var(--color-primary); background:var(--color-primary-soft); padding:4px 12px; border-radius:100px; letter-spacing:.04em;}
/* ── Responsive ── */ /* ── Responsive ── */
@media (max-width: 900px) { @media (max-width: 900px) {
.cs-head { .cs-head {padding:48px 0 64px;}
padding: 48px 0 40px; .cs-title {font-size:28px;}
} .cs-item {grid-template-columns:1fr; min-height:auto; padding:48px 0; gap:32px;}
.cs-title { .cs-item--flip .cs-img-wrap {order:0;}
font-size: 28px; .cs-item--flip .cs-body {order:0; padding-right:0;}
} .cs-body {padding-left:0;}
.cs-item { .cs-bg-num {font-size:120px;}
height: auto; .cs-item-title {font-size:22px;}
min-height: 400px;
}
.cs-img-overlay {
background: linear-gradient(180deg, rgba(10,12,40,0) 0%, rgba(10,12,40,.8) 60%, rgba(10,12,40,.9) 100%);
}
.cs-body {
position:relative;
width:100%;
padding: 32px 24px;
background: rgba(10,12,40,.85);
}
.cs-item-title {
font-size: 24px;
}
} }

54
src/pages/utm/CasePage.jsx

@ -9,39 +9,46 @@ const UTM_NAV = [
const CASES = [ const CASES = [
{ {
id: 1, id: "01",
eyebrow: "한국공항공사", eyebrow: "한국공항공사",
title: "KAC 드론교통관리\nUTM 시스템 구축", title: "KAC 드론교통관리 UTM 시스템 구축",
desc: "한국공항공사의 공공용 UTM 시스템 정보공유체계 개발 및 드론교통관리 시스템 구축 사업을 수행했습니다. 실시간 비행 승인, 충돌 회피, 관제 데이터 통합 관리 기능을 제공하며 안전한 드론 운항 환경을 구현했습니다.", desc: "공공용 UTM 시스템 정보공유체계 개발 및 드론교통관리 시스템 구축 사업을 수행했습니다. 실시간 비행 승인, 충돌 회피, 관제 데이터 통합 관리 기능을 제공니다.",
tags: ["UTM", "드론관제", "한국공항공사"], tags: ["UTM", "드론관제", "한국공항공사"],
img: "/images/case/case01.jpg", img: "/images/case/case01.jpg",
year: "2024",
}, },
{ {
id: 2, id: "02",
eyebrow: "국토교통부", eyebrow: "국토교통부",
title: "UAM 팀코리아\n워킹그룹 참여", title: "UAM 팀코리아 워킹그룹 참여",
desc: "국토교통부 주관 UAM 팀코리아(UTK) 워킹그룹에 참여하여 도심항공교통 운항 공역 비행체 감시 기술 개발 및 UAM 비행상황관리 시스템 구축에 기여했습니다.", desc: "국토교통부 주관 UAM 팀코리아(UTK) 워킹그룹에 참여하여 도심항공교통 운항 공역 비행체 감시 기술 개발 및 UAM 비행상황관리 시스템 구축에 기여했습니다.",
tags: ["UAM", "UATM", "국토교통부"], tags: ["UAM", "UATM", "국토교통부"],
img: "/images/case/case02.jpg", img: "/images/case/case02.jpg",
year: "2024",
}, },
{ {
id: 3, id: "03",
eyebrow: "남원시 · 항공안전기술원", eyebrow: "남원시 · 항공안전기술원",
title: "드론 실증도시\n구축 및 관제시스템", title: "드론 실증도시 구축 및 관제시스템",
desc: "남원시 드론 실증도시 구축사업 협약 체결 및 드론 규제 샌드박스 사업을 통해 드론 관리 체계에 따른 식별장치 및 비행 관제시스템을 구축했습니다.", desc: "남원시 드론 실증도시 구축사업 협약 체결 및 드론 규제 샌드박스 사업을 통해 드론 관리 체계에 따른 식별장치 및 비행 관제시스템을 구축했습니다.",
tags: ["드론", "실증도시", "관제시스템"], tags: ["드론", "실증도시", "관제시스템"],
img: "/images/case/case03.jpg", img: "/images/case/case03.jpg",
year: "2023",
}, },
]; ];
function CaseItem({ item }) { function CaseItem({ item, index }) {
const ref = useRef(null); const ref = useRef(null);
const inView = useInView(ref, { once: true, margin: "-15%" }); const inView = useInView(ref, { once: true, margin: "-10%" });
const isEven = index % 2 === 0;
return ( return (
<div ref={ref} className="cs-item"> <div ref={ref} className={`cs-item${isEven ? "" : " cs-item--flip"}`}>
{/* 이미지 — clipPath로 왼쪽에서 펼쳐짐 */} {/* 큰 번호 배경 */}
<Motion.div className="cs-img-wrap" initial={{ clipPath: "inset(0 100% 0 0)" }} animate={inView ? { clipPath: "inset(0 0% 0 0)" } : {}} transition={{ duration: 0.9, ease: [0.76, 0, 0.24, 1] }}> <div className="cs-bg-num">{item.id}</div>
{/* 이미지 */}
<Motion.div className="cs-img-wrap" initial={{ clipPath: isEven ? "inset(0 100% 0 0)" : "inset(0 0 0 100%)" }} animate={inView ? { clipPath: "inset(0 0% 0 0)" } : {}} transition={{ duration: 1, ease: [0.76, 0, 0.24, 1] }}>
<img <img
src={item.img} src={item.img}
alt={item.title} alt={item.title}
@ -53,18 +60,13 @@ function CaseItem({ item }) {
<div className="cs-img-fallback" /> <div className="cs-img-fallback" />
</Motion.div> </Motion.div>
{/* 텍스트 — 페이드인 */} {/* 텍스트 */}
<Motion.div className="cs-body" initial={{ opacity: 0, y: 24 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.7, delay: 0.4, ease: [0.4, 0, 0.2, 1] }}> <Motion.div className="cs-body" initial={{ opacity: 0, y: 32 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.7, delay: 0.5, ease: [0.4, 0, 0.2, 1] }}>
<span className="cs-num">0{item.id}</span> <div className="cs-meta">
<span className="cs-eyebrow">{item.eyebrow}</span> <span className="cs-year">{item.year}</span>
<h3 className="cs-item-title"> <span className="cs-eyebrow">{item.eyebrow}</span>
{item.title.split("\n").map((line, j) => ( </div>
<span key={j}> <h3 className="cs-item-title">{item.title}</h3>
{line}
<br />
</span>
))}
</h3>
<p className="cs-item-desc">{item.desc}</p> <p className="cs-item-desc">{item.desc}</p>
<div className="cs-tags"> <div className="cs-tags">
{item.tags.map((tag) => ( {item.tags.map((tag) => (
@ -102,8 +104,8 @@ export default function CasePage() {
</Motion.div> </Motion.div>
<div className="cs-list"> <div className="cs-list">
{CASES.map((item) => ( {CASES.map((item, i) => (
<CaseItem key={item.id} item={item} /> <CaseItem key={item.id} item={item} index={i} />
))} ))}
</div> </div>
</div> </div>

Loading…
Cancel
Save