Polish company introduction landing page
This commit is contained in:
138
index.html
138
index.html
@@ -8,7 +8,7 @@
|
||||
name="description"
|
||||
content="컴트로피 회사소개 웹페이지. 방송 그래픽, 실시간 데이터, AI 솔루션, 웹/앱 개발 역량 소개."
|
||||
>
|
||||
<title>컴트로피 | Total IT Service</title>
|
||||
<title>컴트로피 | Broadcast Data Graphics & AI Solutions</title>
|
||||
<script>
|
||||
document.documentElement.classList.remove("no-js");
|
||||
document.documentElement.classList.add("js");
|
||||
@@ -48,6 +48,7 @@
|
||||
<a href="#about">회사</a>
|
||||
<a href="#clients">고객사</a>
|
||||
<a href="#services">사업</a>
|
||||
<a href="#cases">사례</a>
|
||||
<a href="#contact">문의</a>
|
||||
</nav>
|
||||
<a class="header__contact" href="mailto:ecoropy@naver.com">
|
||||
@@ -66,7 +67,8 @@
|
||||
<a href="#about"><span>01</span>회사</a>
|
||||
<a href="#clients"><span>02</span>고객사</a>
|
||||
<a href="#services"><span>03</span>사업</a>
|
||||
<a href="#contact"><span>04</span>문의</a>
|
||||
<a href="#cases"><span>04</span>사례</a>
|
||||
<a href="#contact"><span>05</span>문의</a>
|
||||
</nav>
|
||||
<p>서울특별시 금천구 서부샛길 606 대성지식산업센터 B동 1601호</p>
|
||||
</div>
|
||||
@@ -80,14 +82,22 @@
|
||||
<div class="shell hero__inner">
|
||||
<div class="hero__copy reveal">
|
||||
<p class="eyebrow">Company Introduction</p>
|
||||
<h1 id="heroTitle">고객과 함께<br>성장하는<br>Total IT Service</h1>
|
||||
<h1 id="heroTitle">COMTROPY</h1>
|
||||
<p class="hero__subhead">Broadcast Data Graphics & AI Solutions</p>
|
||||
<p class="hero__lead">
|
||||
컴트로피는 실시간 방송 자막 송출 시스템, 방송 TV Graphic 솔루션,
|
||||
웹/앱 구축과 AI 솔루션을 개발하는 IT 솔루션 전문기업입니다.
|
||||
방송 현장의 실시간 데이터를 안정적인 화면 언어로 바꾸는 개발 파트너.
|
||||
컴트로피는 선거, 스포츠, 경제, 홈쇼핑, AI 영상 분석까지 운영 흐름에 맞춘
|
||||
송출 시스템과 그래픽 솔루션을 만듭니다.
|
||||
</p>
|
||||
<div class="hero__proof" aria-label="핵심 검증 분야">
|
||||
<span>선관위 실시간 연동</span>
|
||||
<span>KPGA 공식 기록</span>
|
||||
<span>HD/UHD TV Graphic</span>
|
||||
<span>Unreal / Unity</span>
|
||||
</div>
|
||||
<div class="hero__actions">
|
||||
<a class="button button--hot" href="#services">
|
||||
<span>주요 사업 보기</span>
|
||||
<a class="button button--hot" href="#cases">
|
||||
<span>프로젝트 보기</span>
|
||||
<i data-lucide="arrow-up-right"></i>
|
||||
</a>
|
||||
<a class="button" href="#contact">
|
||||
@@ -97,20 +107,36 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="hero__console reveal" data-delay="1" aria-label="핵심 역량">
|
||||
<aside class="hero__console reveal" data-delay="1" aria-label="컴트로피 핵심 솔루션 화면">
|
||||
<div class="console__top">
|
||||
<span>LIVE SYSTEM</span>
|
||||
<span>COMMAND CENTER</span>
|
||||
<strong>ON AIR</strong>
|
||||
</div>
|
||||
<div class="hero__metrics" aria-label="주요 역량">
|
||||
<div>
|
||||
<strong>LIVE</strong>
|
||||
<span>Data Interface</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>CG</strong>
|
||||
<span>Broadcast Output</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>AI</strong>
|
||||
<span>Vision Solution</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero__screen">
|
||||
<img src="assets/media/election-candidate-screen.webp" alt="선거방송 후보자 득표 그래픽 화면">
|
||||
</div>
|
||||
<div class="signal-grid">
|
||||
<span>Election</span>
|
||||
<span>Home Shopping</span>
|
||||
<span>Sports TVG</span>
|
||||
<span>Finance</span>
|
||||
<span>Sports</span>
|
||||
<span>Shopping</span>
|
||||
<span>Virtual Graphic</span>
|
||||
<span>AI Vision</span>
|
||||
</div>
|
||||
<img src="assets/media/election-candidate-screen.webp" alt="선거방송 후보자 득표 그래픽 화면">
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
@@ -133,13 +159,13 @@
|
||||
<div class="shell about__grid">
|
||||
<div class="section-heading reveal">
|
||||
<p class="eyebrow">01 / About</p>
|
||||
<h2 id="aboutTitle">고객 감동 실현을 기업의 사명으로 삼습니다.</h2>
|
||||
<h2 id="aboutTitle">방송사의 기술 연구소 역할을 수행하는 개발 파트너</h2>
|
||||
</div>
|
||||
<div class="about__body reveal" data-delay="1">
|
||||
<p>
|
||||
최고의 기술력과 서비스를 지향하며 고객 중심의 사업경쟁력을 강화합니다.
|
||||
핵심 서비스 분야의 품질을 보장하고, 고객의 가치 향상을 극대화할 수
|
||||
있도록 끊임없이 개선합니다.
|
||||
컴트로피는 실시간 방송 자막 송출 시스템, 방송 TV Graphic 솔루션,
|
||||
웹/앱 구축과 AI 솔루션을 개발하는 IT 솔루션 전문기업입니다.
|
||||
현장의 속도와 안정성을 이해하고, 운영자가 믿고 다룰 수 있는 시스템을 설계합니다.
|
||||
</p>
|
||||
<div class="value-strip" aria-label="핵심 가치">
|
||||
<strong>Best People</strong>
|
||||
@@ -176,17 +202,20 @@
|
||||
</div>
|
||||
<div class="intro-band__copy reveal" data-delay="1">
|
||||
<p class="eyebrow">Company Profile</p>
|
||||
<h2>방송사의 기술 연구소 역할을 대행하는 개발 파트너</h2>
|
||||
<h2>실시간 데이터와 화면 출력 사이를 책임집니다.</h2>
|
||||
<p>
|
||||
선거방송, 증권, 홈쇼핑, 스포츠와 게임 방송의 자막 송출 솔루션을
|
||||
제공하고, 실시간 데이터 기반 방송 그래픽과 AI 솔루션, 웹 사이트 구축
|
||||
사업역량을 강화하고 있습니다.
|
||||
선거방송, 증권, 홈쇼핑, 스포츠와 게임 방송의 자막 송출 솔루션을 제공하고,
|
||||
데이터 기반 방송 그래픽과 AI 솔루션, 웹 사이트 구축 역량을 강화하고 있습니다.
|
||||
</p>
|
||||
<dl class="profile-list">
|
||||
<div>
|
||||
<dt>사업 분야</dt>
|
||||
<dd>소프트웨어 개발 및 공급, 방송 TV Graphic 솔루션, 웹 콘텐츠 개발</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>핵심 기술</dt>
|
||||
<dd>실시간 데이터 연동, 송출 제어, API 서버, AI 영상 분석, VR/AR 그래픽</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>위치</dt>
|
||||
<dd>서울특별시 금천구 서부샛길 606 대성지식산업센터 B동 1601호</dd>
|
||||
@@ -197,9 +226,15 @@
|
||||
</section>
|
||||
|
||||
<section class="section clients" id="clients" aria-labelledby="clientsTitle">
|
||||
<div class="shell section-heading section-heading--wide reveal">
|
||||
<p class="eyebrow">02 / Clients</p>
|
||||
<h2 id="clientsTitle">방송, 스포츠, 커머스, 게임 산업의 현장과 함께했습니다.</h2>
|
||||
<div class="shell clients__head reveal">
|
||||
<div class="section-heading section-heading--wide">
|
||||
<p class="eyebrow">02 / Clients</p>
|
||||
<h2 id="clientsTitle">방송, 스포츠, 커머스, 게임 산업의 현장과 함께했습니다.</h2>
|
||||
</div>
|
||||
<p class="client-note">
|
||||
공공 방송부터 스포츠 중계, 홈쇼핑, 게임 방송까지 다양한 운영 환경에서
|
||||
안정성과 정확도가 필요한 화면 시스템을 구축했습니다.
|
||||
</p>
|
||||
</div>
|
||||
<div class="shell logo-grid reveal" data-delay="1">
|
||||
<figure><img src="assets/media/client-01-kpga.png" alt="KPGA"></figure>
|
||||
@@ -229,46 +264,70 @@
|
||||
|
||||
<div class="shell service-grid">
|
||||
<article class="service-card reveal">
|
||||
<span>01</span>
|
||||
<div class="service-card__top">
|
||||
<span>01</span>
|
||||
<i data-lucide="captions"></i>
|
||||
</div>
|
||||
<h3>방송 그래픽 자막 송출 솔루션</h3>
|
||||
<p>선거방송, 홈쇼핑, 증권, 스포츠와 게임 데이터를 방송 그래픽으로 연결합니다.</p>
|
||||
<div class="service-card__tags"><span>CG</span><span>Live Data</span><span>Output</span></div>
|
||||
</article>
|
||||
<article class="service-card reveal" data-delay="1">
|
||||
<span>02</span>
|
||||
<div class="service-card__top">
|
||||
<span>02</span>
|
||||
<i data-lucide="satellite-dish"></i>
|
||||
</div>
|
||||
<h3>국제신호 제작</h3>
|
||||
<p>아시안게임, 동남아시안게임, 월드컵 등 국제경기 중계의 IS 제작 경험을 보유합니다.</p>
|
||||
<div class="service-card__tags"><span>IS</span><span>HD/UHD</span><span>Sports</span></div>
|
||||
</article>
|
||||
<article class="service-card reveal" data-delay="2">
|
||||
<span>03</span>
|
||||
<div class="service-card__top">
|
||||
<span>03</span>
|
||||
<i data-lucide="scan-search"></i>
|
||||
</div>
|
||||
<h3>AI 솔루션 연구 개발</h3>
|
||||
<p>딥러닝 기반 영상 분석과 거리 측정 시스템 등 현장형 AI 솔루션을 개발합니다.</p>
|
||||
<div class="service-card__tags"><span>CUDA</span><span>Vision</span><span>Tracking</span></div>
|
||||
</article>
|
||||
<article class="service-card reveal">
|
||||
<span>04</span>
|
||||
<div class="service-card__top">
|
||||
<span>04</span>
|
||||
<i data-lucide="database-zap"></i>
|
||||
</div>
|
||||
<h3>데이터베이스 시스템 구축</h3>
|
||||
<p>실시간 데이터 연계, API, 데이터 송수신 프로그램과 방송 송출 서버를 설계합니다.</p>
|
||||
<div class="service-card__tags"><span>API</span><span>DB</span><span>Scheduler</span></div>
|
||||
</article>
|
||||
<article class="service-card reveal" data-delay="1">
|
||||
<span>05</span>
|
||||
<div class="service-card__top">
|
||||
<span>05</span>
|
||||
<i data-lucide="panel-top"></i>
|
||||
</div>
|
||||
<h3>웹 사이트 / 앱 개발</h3>
|
||||
<p>방송 운영 시스템과 연동되는 웹 페이지, 모바일 앱, 관리 도구를 구현합니다.</p>
|
||||
<div class="service-card__tags"><span>Web</span><span>Mobile</span><span>Admin</span></div>
|
||||
</article>
|
||||
<article class="service-card reveal" data-delay="2">
|
||||
<span>06</span>
|
||||
<div class="service-card__top">
|
||||
<span>06</span>
|
||||
<i data-lucide="box"></i>
|
||||
</div>
|
||||
<h3>버추얼 그래픽 시스템</h3>
|
||||
<p>Unreal Engine, Unity 기반 VR/AR 시스템과 실시간 3D 그래픽 처리를 제공합니다.</p>
|
||||
<div class="service-card__tags"><span>VR/AR</span><span>3D</span><span>Unity</span></div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section case-lab" aria-labelledby="caseTitle">
|
||||
<section class="section case-lab" id="cases" aria-labelledby="caseTitle">
|
||||
<div class="shell case-lab__head reveal">
|
||||
<div>
|
||||
<p class="eyebrow">Project Fields</p>
|
||||
<p class="eyebrow">04 / Project Fields</p>
|
||||
<h2 id="caseTitle">현장에서 검증된 여섯 가지 솔루션</h2>
|
||||
</div>
|
||||
<p>
|
||||
데이터 연동, 그래픽 송출, AI 분석, 운영 UI까지 현장 흐름에 맞춰 설계합니다.
|
||||
데이터 연동, 그래픽 송출, AI 분석, 운영 UI까지 실제 방송 흐름에 맞춰 설계합니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -296,15 +355,28 @@
|
||||
|
||||
<article class="case-stage reveal" data-delay="1" aria-live="polite">
|
||||
<div class="case-stage__media">
|
||||
<div class="case-stage__bar" aria-hidden="true">
|
||||
<span></span><span></span><span></span>
|
||||
<strong id="caseStatus">LIVE READY</strong>
|
||||
</div>
|
||||
<img id="caseImage" src="assets/media/election-candidate-screen.webp" alt="선거방송 후보자 득표 그래픽 화면">
|
||||
<div class="case-stage__thumbs" id="caseThumbs"></div>
|
||||
</div>
|
||||
<div class="case-stage__copy">
|
||||
<p class="case-stage__kicker" id="caseKicker">2.1 선거방송</p>
|
||||
<div class="case-stage__meta" aria-label="프로젝트 정보">
|
||||
<span id="caseClient">Election Broadcast</span>
|
||||
<span id="caseField">Real-time CG</span>
|
||||
</div>
|
||||
<h3 id="caseName">선거방송 데이터 방송 그래픽 솔루션 개발</h3>
|
||||
<p id="caseDescription">
|
||||
선관위 데이터와 방송 그래픽을 실시간으로 연동해 자막 송출 시스템을 구성합니다.
|
||||
</p>
|
||||
<div class="case-stage__chips" id="caseTags" aria-label="핵심 기술"></div>
|
||||
<div class="case-stat" aria-label="대표 성과">
|
||||
<strong id="caseStatValue">Real-time</strong>
|
||||
<span id="caseStatLabel">선관위 데이터와 방송 그래픽 연동</span>
|
||||
</div>
|
||||
<ul id="caseBullets"></ul>
|
||||
</div>
|
||||
</article>
|
||||
@@ -334,7 +406,7 @@
|
||||
<section class="section contact" id="contact" aria-labelledby="contactTitle">
|
||||
<div class="shell contact__grid">
|
||||
<div class="contact__copy reveal">
|
||||
<p class="eyebrow">04 / Contact</p>
|
||||
<p class="eyebrow">05 / Contact</p>
|
||||
<h2 id="contactTitle">방송 현장의 데이터를 안정적인 화면으로 연결합니다.</h2>
|
||||
<p>
|
||||
실시간 송출 시스템, 방송 그래픽, AI 영상 분석, 웹/앱 개발이 필요한 프로젝트를
|
||||
|
||||
157
script.js
157
script.js
@@ -1,8 +1,14 @@
|
||||
const caseData = {
|
||||
election: {
|
||||
kicker: "2.1 선거방송",
|
||||
title: "선거방송 데이터 방송 그래픽 솔루션 개발",
|
||||
kicker: "2.1 Election Broadcast",
|
||||
title: "선거방송 데이터 방송 그래픽 솔루션",
|
||||
client: "Public Broadcast",
|
||||
field: "Real-time CG",
|
||||
status: "LIVE DATA",
|
||||
description: "선관위 데이터와 방송 그래픽을 실시간으로 연동해 자막 송출 시스템을 구성합니다.",
|
||||
tags: ["선관위 연동", "스케줄러", "송출 컨트롤"],
|
||||
statValue: "Real-time",
|
||||
statLabel: "선관위 데이터와 방송 그래픽 실시간 연동",
|
||||
bullets: [
|
||||
"선관위 데이터 실시간 연동 코드 적용",
|
||||
"데이터 스케줄러와 송출 컨트롤러 시스템 개발",
|
||||
@@ -15,9 +21,15 @@ const caseData = {
|
||||
]
|
||||
},
|
||||
virtual: {
|
||||
kicker: "2.2 버추얼 그래픽 시스템",
|
||||
title: "버추얼 그래픽 솔루션 개발",
|
||||
kicker: "2.2 Virtual Graphic",
|
||||
title: "버추얼 그래픽 솔루션",
|
||||
client: "Broadcast Studio",
|
||||
field: "VR / AR System",
|
||||
status: "3D READY",
|
||||
description: "Unreal Engine과 Unity를 활용해 VR, AR 기반의 실시간 3D 그래픽 시스템을 개발합니다.",
|
||||
tags: ["Unreal Engine", "Unity", "3D Data"],
|
||||
statValue: "VR / AR",
|
||||
statLabel: "실시간 데이터 기반 3D 그래픽 처리",
|
||||
bullets: [
|
||||
"Unreal Engine 기반 VR/AR 시스템 개발",
|
||||
"실시간 데이터 3D 그래픽 처리 시스템 적용",
|
||||
@@ -30,9 +42,15 @@ const caseData = {
|
||||
]
|
||||
},
|
||||
finance: {
|
||||
kicker: "2.3 데이터베이스 / 경제 방송",
|
||||
kicker: "2.3 Finance Broadcast",
|
||||
title: "증권 데이터 및 경제 방송 송출 그래픽 시스템",
|
||||
client: "Finance Media",
|
||||
field: "Database / API",
|
||||
status: "MARKET API",
|
||||
description: "코스콤 데이터와 해외 지수 데이터를 연계해 방송 송출 그래픽과 API 서버를 구현합니다.",
|
||||
tags: ["Koscom", "REST API", "DB Server"],
|
||||
statValue: "API",
|
||||
statLabel: "국내외 지수 데이터 연계 및 송출",
|
||||
bullets: [
|
||||
"코스콤 데이터와 해외 지수 연계 데이터베이스 서버 구축",
|
||||
"실시간 그래픽 연계 데이터 송수신 프로그램 개발",
|
||||
@@ -45,9 +63,15 @@ const caseData = {
|
||||
]
|
||||
},
|
||||
ai: {
|
||||
kicker: "2.4 AI 학습 솔루션",
|
||||
kicker: "2.4 AI Vision",
|
||||
title: "AI 기반 거리 측정 시스템",
|
||||
client: "KPGA",
|
||||
field: "AI Learning",
|
||||
status: "AI RESULT",
|
||||
description: "KPGA 공식 기록에 사용되는 그린 홀 컵 간 거리 측정 솔루션을 개발했습니다.",
|
||||
tags: ["CUDA", "Deep Learning", "Coordinate"],
|
||||
statValue: "KPGA",
|
||||
statLabel: "공식 기록 환경에 적용된 거리 측정 솔루션",
|
||||
bullets: [
|
||||
"CUDA 기반 딥러닝 모델 학습",
|
||||
"카메라 영상 신호 수신 후 공 위치와 좌표 변환",
|
||||
@@ -60,9 +84,15 @@ const caseData = {
|
||||
]
|
||||
},
|
||||
shopping: {
|
||||
kicker: "2.5 홈쇼핑 송출 시스템",
|
||||
kicker: "2.5 Commerce Broadcast",
|
||||
title: "홈쇼핑 자동화 송출 시스템 / 라이브 톡",
|
||||
client: "Commerce Channel",
|
||||
field: "Automation",
|
||||
status: "CONTROL",
|
||||
description: "송출 스케줄과 원격 제어, 모바일 연동을 포함한 홈쇼핑 방송 운영 시스템을 개발합니다.",
|
||||
tags: ["Remote Control", "Mobile", "Schedule"],
|
||||
statValue: "Automation",
|
||||
statLabel: "송출 스케줄과 모바일 운영 흐름 연동",
|
||||
bullets: [
|
||||
"송출 스케줄 관리 데이터 송수신 모듈 개발",
|
||||
"홈쇼핑 송출 프로그램 원격제어 로컬/모바일 연동",
|
||||
@@ -75,9 +105,15 @@ const caseData = {
|
||||
]
|
||||
},
|
||||
sports: {
|
||||
kicker: "2.6 스포츠 TV Graphic",
|
||||
title: "스포츠 / 게임 데이터 솔루션 송출 시스템 개발",
|
||||
kicker: "2.6 Sports TV Graphic",
|
||||
title: "스포츠 / 게임 데이터 솔루션 송출 시스템",
|
||||
client: "Sports & Game",
|
||||
field: "TV Graphic",
|
||||
status: "UHD CG",
|
||||
description: "Live Data를 자막시스템에서 사용 가능한 형태로 변환하고 다양한 종목의 TVG 시스템을 제작합니다.",
|
||||
tags: ["HD/UHD", "International Signal", "Scorebug"],
|
||||
statValue: "IS",
|
||||
statLabel: "국제신호 제작과 고해상도 CG 송출 대응",
|
||||
bullets: [
|
||||
"HD/UHD급 CG 제작과 국제신호 제작",
|
||||
"3D/2D Cut, 애니메이션 등 특수 효과 적용",
|
||||
@@ -98,29 +134,44 @@ const menuToggle = document.querySelector(".menu-toggle");
|
||||
const cursor = document.querySelector(".cursor");
|
||||
const loaderBar = document.getElementById("loaderBar");
|
||||
const loaderValue = document.getElementById("loaderValue");
|
||||
const caseStage = document.querySelector(".case-stage");
|
||||
const caseImage = document.getElementById("caseImage");
|
||||
const caseKicker = document.getElementById("caseKicker");
|
||||
const caseName = document.getElementById("caseName");
|
||||
const caseDescription = document.getElementById("caseDescription");
|
||||
const caseBullets = document.getElementById("caseBullets");
|
||||
const caseThumbs = document.getElementById("caseThumbs");
|
||||
const caseClient = document.getElementById("caseClient");
|
||||
const caseField = document.getElementById("caseField");
|
||||
const caseStatus = document.getElementById("caseStatus");
|
||||
const caseTags = document.getElementById("caseTags");
|
||||
const caseStatValue = document.getElementById("caseStatValue");
|
||||
const caseStatLabel = document.getElementById("caseStatLabel");
|
||||
const navLinks = [...document.querySelectorAll(".primary-nav a")];
|
||||
|
||||
let loaderProgress = 0;
|
||||
const loaderTimer = window.setInterval(() => {
|
||||
loaderProgress = Math.min(loaderProgress + Math.ceil(Math.random() * 12), 100);
|
||||
loaderBar.style.width = `${loaderProgress}%`;
|
||||
loaderValue.textContent = String(loaderProgress).padStart(2, "0");
|
||||
if (loaderProgress >= 100) {
|
||||
window.clearInterval(loaderTimer);
|
||||
window.setTimeout(() => {
|
||||
body.classList.remove("is-loading");
|
||||
body.classList.add("is-ready");
|
||||
}, 220);
|
||||
}
|
||||
}, 55);
|
||||
const finishLoading = () => {
|
||||
body.classList.remove("is-loading");
|
||||
body.classList.add("is-ready");
|
||||
};
|
||||
|
||||
if (loaderBar && loaderValue) {
|
||||
const loaderTimer = window.setInterval(() => {
|
||||
loaderProgress = Math.min(loaderProgress + Math.ceil(Math.random() * 12), 100);
|
||||
loaderBar.style.width = `${loaderProgress}%`;
|
||||
loaderValue.textContent = String(loaderProgress).padStart(2, "0");
|
||||
if (loaderProgress >= 100) {
|
||||
window.clearInterval(loaderTimer);
|
||||
window.setTimeout(finishLoading, 220);
|
||||
}
|
||||
}, 55);
|
||||
} else {
|
||||
finishLoading();
|
||||
}
|
||||
|
||||
const updateScrollState = () => {
|
||||
if (!header) return;
|
||||
|
||||
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
|
||||
const progress = scrollable > 0 ? (scrollTop / scrollable) * 100 : 0;
|
||||
@@ -158,6 +209,8 @@ const revealObserver = new IntersectionObserver(
|
||||
document.querySelectorAll(".reveal").forEach(element => revealObserver.observe(element));
|
||||
|
||||
const setMenuOpen = open => {
|
||||
if (!menuToggle || !mobilePanel) return;
|
||||
|
||||
menuToggle.setAttribute("aria-expanded", String(open));
|
||||
menuToggle.setAttribute("aria-label", open ? "메뉴 닫기" : "메뉴 열기");
|
||||
mobilePanel.classList.toggle("is-open", open);
|
||||
@@ -173,32 +226,58 @@ if (menuToggle) {
|
||||
});
|
||||
}
|
||||
|
||||
mobilePanel.querySelectorAll("a").forEach(link => {
|
||||
mobilePanel?.querySelectorAll("a").forEach(link => {
|
||||
link.addEventListener("click", () => setMenuOpen(false));
|
||||
});
|
||||
|
||||
const fillList = (element, items, tagName = "span") => {
|
||||
if (!element) return;
|
||||
const nodes = items.map(item => {
|
||||
const node = document.createElement(tagName);
|
||||
node.textContent = item;
|
||||
return node;
|
||||
});
|
||||
element.replaceChildren(...nodes);
|
||||
};
|
||||
|
||||
const renderCase = (key, imageIndex = 0) => {
|
||||
const data = caseData[key];
|
||||
const image = data.images[imageIndex];
|
||||
if (!data || !caseImage) return;
|
||||
|
||||
const image = data.images[imageIndex] || data.images[0];
|
||||
caseStage?.classList.add("is-switching");
|
||||
window.setTimeout(() => caseStage?.classList.remove("is-switching"), 180);
|
||||
|
||||
caseImage.src = image.src;
|
||||
caseImage.alt = image.alt;
|
||||
caseKicker.textContent = data.kicker;
|
||||
caseName.textContent = data.title;
|
||||
caseDescription.textContent = data.description;
|
||||
caseBullets.innerHTML = data.bullets.map(item => `<li>${item}</li>`).join("");
|
||||
caseThumbs.innerHTML = data.images
|
||||
.map(
|
||||
(item, index) => `
|
||||
<button type="button" class="${index === imageIndex ? "is-active" : ""}" data-case-thumb="${index}" aria-label="${item.alt}">
|
||||
<img src="${item.src}" alt="">
|
||||
</button>
|
||||
`
|
||||
)
|
||||
.join("");
|
||||
if (caseKicker) caseKicker.textContent = data.kicker;
|
||||
if (caseName) caseName.textContent = data.title;
|
||||
if (caseDescription) caseDescription.textContent = data.description;
|
||||
if (caseClient) caseClient.textContent = data.client;
|
||||
if (caseField) caseField.textContent = data.field;
|
||||
if (caseStatus) caseStatus.textContent = data.status;
|
||||
if (caseStatValue) caseStatValue.textContent = data.statValue;
|
||||
if (caseStatLabel) caseStatLabel.textContent = data.statLabel;
|
||||
fillList(caseTags, data.tags);
|
||||
fillList(caseBullets, data.bullets, "li");
|
||||
|
||||
caseThumbs.querySelectorAll("button").forEach(button => {
|
||||
button.addEventListener("click", () => renderCase(key, Number(button.dataset.caseThumb)));
|
||||
});
|
||||
if (caseThumbs) {
|
||||
const buttons = data.images.map((item, index) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = index === imageIndex ? "is-active" : "";
|
||||
button.dataset.caseThumb = String(index);
|
||||
button.setAttribute("aria-label", item.alt);
|
||||
|
||||
const thumb = document.createElement("img");
|
||||
thumb.src = item.src;
|
||||
thumb.alt = "";
|
||||
button.appendChild(thumb);
|
||||
button.addEventListener("click", () => renderCase(key, index));
|
||||
return button;
|
||||
});
|
||||
caseThumbs.replaceChildren(...buttons);
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll(".case-tabs button").forEach(button => {
|
||||
|
||||
341
styles.css
341
styles.css
@@ -260,6 +260,7 @@ html.no-js .loader {
|
||||
}
|
||||
|
||||
body.is-ready .loader {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
@@ -1218,6 +1219,312 @@ body.is-ready .loader {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.primary-nav {
|
||||
gap: 26px;
|
||||
}
|
||||
|
||||
.hero__subhead {
|
||||
max-width: 740px;
|
||||
margin: 14px 0 0;
|
||||
color: var(--signal);
|
||||
font-family: var(--mono);
|
||||
font-size: clamp(0.88rem, 1.2vw, 1.08rem);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero__proof {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
max-width: 730px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.hero__proof span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 34px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid rgba(241, 238, 230, 0.18);
|
||||
border-radius: 999px;
|
||||
background: rgba(7, 7, 6, 0.32);
|
||||
color: rgba(241, 238, 230, 0.82);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hero__metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
}
|
||||
|
||||
.hero__metrics div {
|
||||
min-height: 88px;
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 4px;
|
||||
padding: 18px;
|
||||
border-right: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.hero__metrics div:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.hero__metrics strong {
|
||||
color: var(--paper);
|
||||
font-family: var(--display);
|
||||
font-size: 2.05rem;
|
||||
line-height: 0.9;
|
||||
}
|
||||
|
||||
.hero__metrics span {
|
||||
color: var(--muted);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero__screen {
|
||||
position: relative;
|
||||
background: #050505;
|
||||
}
|
||||
|
||||
.hero__screen::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 12px;
|
||||
pointer-events: none;
|
||||
border: 1px solid rgba(200, 255, 73, 0.28);
|
||||
box-shadow: inset 0 0 0 1px rgba(7, 7, 6, 0.4);
|
||||
}
|
||||
|
||||
.clients__head {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(280px, 430px);
|
||||
gap: 56px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.client-note {
|
||||
margin: 0;
|
||||
color: var(--paper-2);
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
.logo-grid figure {
|
||||
position: relative;
|
||||
background: #e7e1d6;
|
||||
overflow: hidden;
|
||||
transition: background 0.3s var(--ease), transform 0.3s var(--ease);
|
||||
}
|
||||
|
||||
.logo-grid figure::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(7, 7, 6, 0.06) 1px, transparent 1px) 0 0 / 28px 28px,
|
||||
linear-gradient(180deg, rgba(7, 7, 6, 0.06) 1px, transparent 1px) 0 0 / 28px 28px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s var(--ease);
|
||||
}
|
||||
|
||||
.logo-grid figure:hover {
|
||||
background: var(--paper);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.logo-grid figure:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.logo-grid img {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
filter: grayscale(1) saturate(0) contrast(0.92);
|
||||
opacity: 0.72;
|
||||
transition: filter 0.35s var(--ease), opacity 0.35s var(--ease), transform 0.35s var(--ease);
|
||||
}
|
||||
|
||||
.logo-grid figure:hover img {
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.service-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.service-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: auto 24px 0 24px;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--hot), var(--signal));
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 0.34s var(--ease);
|
||||
}
|
||||
|
||||
.service-card:hover::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.service-card__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.service-card__top svg {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
color: rgba(241, 238, 230, 0.78);
|
||||
stroke-width: 1.7;
|
||||
}
|
||||
|
||||
.service-card__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
margin-top: auto;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.service-card__tags span {
|
||||
min-height: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 9px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--paper-2);
|
||||
font-size: 0.68rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.case-stage {
|
||||
box-shadow: 0 28px 90px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.case-stage__bar {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
min-height: 34px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid rgba(241, 238, 230, 0.18);
|
||||
border-radius: 4px;
|
||||
background: rgba(7, 7, 6, 0.68);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.case-stage__bar span {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: var(--muted-dark);
|
||||
}
|
||||
|
||||
.case-stage__bar span:first-child {
|
||||
background: var(--hot-2);
|
||||
}
|
||||
|
||||
.case-stage__bar span:nth-child(2) {
|
||||
background: var(--signal);
|
||||
}
|
||||
|
||||
.case-stage__bar strong {
|
||||
margin-left: auto;
|
||||
color: var(--paper-2);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.case-stage__media > img {
|
||||
transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
|
||||
}
|
||||
|
||||
.case-stage.is-switching .case-stage__media > img {
|
||||
opacity: 0.42;
|
||||
transform: scale(0.992);
|
||||
}
|
||||
|
||||
.case-stage__meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.case-stage__meta span,
|
||||
.case-stage__chips span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 9px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--paper-2);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.case-stage__chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.case-stage__chips span {
|
||||
border-color: rgba(200, 255, 73, 0.24);
|
||||
color: var(--signal);
|
||||
}
|
||||
|
||||
.case-stat {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(96px, 0.36fr) 1fr;
|
||||
gap: 18px;
|
||||
align-items: center;
|
||||
margin-top: 22px;
|
||||
padding: 18px;
|
||||
border: 1px solid var(--line);
|
||||
background: rgba(7, 7, 6, 0.24);
|
||||
}
|
||||
|
||||
.case-stat strong {
|
||||
color: var(--paper);
|
||||
font-family: var(--display);
|
||||
font-size: 1.78rem;
|
||||
line-height: 0.95;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.case-stat span {
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
html.js .reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(34px);
|
||||
@@ -1257,6 +1564,11 @@ html.js .reveal[data-delay="2"] {
|
||||
font-size: 5.2rem;
|
||||
}
|
||||
|
||||
.clients__head {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 26px;
|
||||
}
|
||||
|
||||
.hero__inner,
|
||||
.about__grid,
|
||||
.intro-band__grid,
|
||||
@@ -1346,6 +1658,15 @@ html.js .reveal[data-delay="2"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.hero__proof {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.hero__proof span {
|
||||
min-height: 32px;
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
|
||||
.hero__actions {
|
||||
align-items: stretch;
|
||||
}
|
||||
@@ -1417,6 +1738,17 @@ html.js .reveal[data-delay="2"] {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.case-stage__bar {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.case-stat {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.case-stage h3 {
|
||||
font-size: 1.55rem;
|
||||
}
|
||||
@@ -1452,6 +1784,15 @@ html.js .reveal[data-delay="2"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero__subhead {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.hero__proof {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mission-panel,
|
||||
.service-card {
|
||||
padding: 24px;
|
||||
|
||||
Reference in New Issue
Block a user