2025-12-17 12:59:26 +09:00
< ? php require_once 'session.php' ; ?>
<! DOCTYPE html >
< html lang = " ko " >
< head >
< meta charset = " UTF-8 " >
< meta name = " viewport " content = " width=device-width, initial-scale=1.0 " >
< title > CodeBridge - X SAM - 영업관리 </ title >
< script src = " https://cdn.tailwindcss.com " ></ script >
< script src = " https://unpkg.com/lucide@latest " ></ script >
< link href = " https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+KR:wght@300;400;500;600;700&display=swap " rel = " stylesheet " >
< script >
tailwind . config = {
theme : {
extend : {
fontFamily : {
sans : [ 'Inter' , 'Noto Sans KR' , 'sans-serif' ],
},
colors : {
brand : {
50 : '#f0f9ff' ,
100 : '#e0f2fe' ,
200 : '#bae6fd' ,
500 : '#0ea5e9' ,
600 : '#0284c7' ,
700 : '#0369a1' ,
900 : '#0c4a6e' ,
}
},
animation : {
blob : " blob 7s infinite " ,
'fade-in-up' : 'fadeInUp 0.8s ease-out forwards' ,
},
keyframes : {
blob : {
" 0% " : { transform : " translate(0px, 0px) scale(1) " },
" 33% " : { transform : " translate(30px, -50px) scale(1.1) " },
" 66% " : { transform : " translate(-20px, 20px) scale(0.9) " },
" 100% " : { transform : " translate(0px, 0px) scale(1) " },
},
fadeInUp : {
'0%' : { opacity : '0' , transform : 'translateY(20px)' },
'100%' : { opacity : '1' , transform : 'translateY(0)' },
}
},
}
}
}
</ script >
< style >
. delay - 100 { animation - delay : 100 ms ; }
. delay - 200 { animation - delay : 200 ms ; }
. delay - 300 { animation - delay : 300 ms ; }
. animation - delay - 2000 { animation - delay : 2 s ; }
. animation - delay - 4000 { animation - delay : 4 s ; }
/* Hide scrollbar for clean modal */
. no - scroll { overflow : hidden ; }
2025-12-18 10:15:00 +09:00
/* Image Zoom & Float Effect */
. img - zoom - container {
overflow : visible ! important ;
perspective : 1000 px ;
}
. img - zoom - target {
cursor : zoom - in ;
transition : all 0.4 s cubic - bezier ( 0.165 , 0.84 , 0.44 , 1 );
position : relative ;
z - index : 1 ;
/* 인라인 스타일을 클래스로 통합 */
image - rendering : - webkit - optimize - contrast ;
image - rendering : crisp - edges ;
transform : translateZ ( 0 );
backface - visibility : hidden ;
}
. img - zoom - target : hover {
transform : scale ( 1.1 ) translateZ ( 0 ) ! important ;
box - shadow : 0 25 px 50 px - 12 px rgba ( 0 , 0 , 0 , 0.5 );
z - index : 50 ;
}
/* Full Size Viewer */
. img - full - container {
max - height : 70 vh ;
overflow : auto ;
cursor : grab ;
background : #f8fafc;
border - radius : 0.75 rem ;
border : 1 px solid #e2e8f0;
}
. img - full - container : active { cursor : grabbing ; }
. img - natural {
max - width : none ! important ;
width : auto ! important ;
height : auto ! important ;
}
2025-12-18 10:51:01 +09:00
/* Full Screen Lightbox */
#fullscreen-view {
position : fixed ;
inset : 0 ;
background : rgba ( 15 , 23 , 42 , 0.95 );
z - index : 100 ;
overflow - y : auto ;
display : none ;
flex - direction : column ;
align - items : center ;
padding : 2 rem 0 ; /* 상하 여백 */
}
#fullscreen-view img {
width : 100 % ; /* 기기 너비 꽉 채움 */
max - width : 1400 px ; /* PC에서는 너무 커지지 않게 제한 (선택 가능) */
height : auto ;
display : block ;
box - shadow : 0 0 50 px rgba ( 0 , 0 , 0 , 0.5 );
}
. fullscreen - close {
position : fixed ;
top : 1.5 rem ;
right : 1.5 rem ;
z - index : 110 ;
background : white ;
border - radius : 9999 px ;
padding : 0.75 rem ;
cursor : pointer ;
box - shadow : 0 10 px 15 px - 3 px rgba ( 0 , 0 , 0 , 0.3 );
}
2025-12-19 10:59:32 +09:00
/* Mobile touch optimization */
. touch - manipulation {
touch - action : manipulation ;
- webkit - tap - highlight - color : transparent ;
}
2025-12-19 11:03:48 +09:00
/* Mobile title optimization - prevent single character line breaks */
@ media ( max - width : 640 px ) {
. break - keep {
word - break : keep - all ;
overflow - wrap : break - word ;
hyphens : none ;
}
/* 타이틀 폰트 크기 조정으로 한 글자 줄바꿈 방지 */
h1 , h2 , h3 . title - responsive {
font - size : clamp ( 1 rem , 4 vw , 1.5 rem );
line - height : 1.3 ;
}
2025-12-19 11:09:12 +09:00
/* 모바일 버튼 텍스트 줄바꿈 방지 */
button {
word - break : keep - all ;
white - space : nowrap ;
}
2025-12-19 11:03:48 +09:00
}
2025-12-17 12:59:26 +09:00
</ style >
</ head >
< body class = " bg-slate-50 text-slate-900 font-sans selection:bg-brand-200 selection:text-brand-900 " >
<!-- Toast Notification -->
< div id = " toast " class = " fixed top-24 right-4 z-[60] bg-slate-800 text-white px-6 py-4 rounded-xl shadow-2xl flex items-center gap-3 transition-all duration-500 transform translate-x-full opacity-0 " >
< i data - lucide = " check-circle-2 " class = " text-green-400 w-6 h-6 " ></ i >
< div >
< h4 class = " font-bold text-sm " > 다운로드 시작됨 </ h4 >
< p class = " text-slate-400 text-xs " > CodeBridgeX_Proposal_v2 . 4. pdf </ p >
</ div >
</ div >
<!-- Navigation -->
< nav class = " sticky top-0 z-30 bg-white/80 backdrop-blur-md border-b border-slate-200 " >
< div class = " max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 " >
< div class = " flex justify-between items-center h-16 " >
< div class = " flex items-center gap-3 cursor-pointer " onclick = " filterAssets('All') " >
< div class = " w-8 h-8 bg-brand-600 rounded-lg flex items-center justify-center text-white font-bold text-lg shadow-lg shadow-brand-200 " >
S
</ div >
< span class = " text-xl font-bold tracking-tight text-slate-900 " > CodeBridgeX < span class = " text-brand-600 " > SAM </ span ></ span >
</ div >
2025-12-19 10:53:22 +09:00
<!-- Desktop Navigation -->
< div class = " hidden md:flex items-center gap-4 " >
2025-12-17 12:59:26 +09:00
< a href = " sales_scenario/ " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors " > 영업 시나리오 </ a >
< a href = " sales_manager_scenario/ " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors " > 매니저 시나리오 </ a >
< a href = " salesmanagement/ " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors " > 영업관리 </ a >
2025-12-17 15:41:30 +09:00
< a href = " corp/kodata.php " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors " > 기업분석 </ a >
2025-12-17 12:59:26 +09:00
< ? php if ( isset ( $_SESSION [ 'userid' ]) && $_SESSION [ 'userid' ] != '' ) : ?>
2025-12-19 10:53:22 +09:00
< div class = " flex items-center gap-2 ml-2 " >
2025-12-17 12:59:26 +09:00
< div class = " w-8 h-8 rounded-full bg-slate-200 flex items-center justify-center text-slate-500 font-bold border border-slate-300 " >
< ? = mb_substr ( $_SESSION [ 'name' ], 0 , 1 ) ?>
</ div >
< span class = " text-sm font-medium text-slate-700 " >< ? = $_SESSION [ 'name' ] ?> 님</span>
< a href = " login/logout.php " class = " px-3 py-1.5 bg-slate-100 text-slate-600 text-xs font-bold rounded hover:bg-slate-200 transition-colors border border-slate-200 " > 로그아웃 </ a >
</ div >
< ? php else : ?>
< a href = " login/login_form.php " class = " px-4 py-2 bg-brand-600 text-white text-sm font-bold rounded-lg hover:bg-brand-700 transition-colors shadow-lg shadow-brand-200 " >
로그인
</ a >
< ? php endif ; ?>
</ div >
2025-12-19 10:53:22 +09:00
<!-- Mobile Hamburger Button -->
2025-12-19 10:59:32 +09:00
< button id = " mobile-menu-button " class = " md:hidden p-2 rounded-lg text-slate-600 hover:bg-slate-100 active:bg-slate-200 transition-colors touch-manipulation " >
2025-12-19 10:53:22 +09:00
< i data - lucide = " menu " class = " w-6 h-6 " id = " menu-icon " ></ i >
< i data - lucide = " x " class = " w-6 h-6 hidden " id = " close-icon " ></ i >
</ button >
</ div >
<!-- Mobile Menu -->
< div id = " mobile-menu " class = " hidden md:hidden border-t border-slate-200 py-4 " >
< div class = " flex flex-col gap-3 " >
2025-12-19 10:56:04 +09:00
< a href = " sales_scenario/ " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors px-2 py-2 hover:bg-slate-50 rounded-lg " onclick = " closeMobileMenu() " > 영업 시나리오 </ a >
< a href = " sales_manager_scenario/ " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors px-2 py-2 hover:bg-slate-50 rounded-lg " onclick = " closeMobileMenu() " > 매니저 시나리오 </ a >
< a href = " salesmanagement/ " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors px-2 py-2 hover:bg-slate-50 rounded-lg " onclick = " closeMobileMenu() " > 영업관리 </ a >
< a href = " corp/kodata.php " class = " text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors px-2 py-2 hover:bg-slate-50 rounded-lg " onclick = " closeMobileMenu() " > 기업분석 </ a >
2025-12-19 10:53:22 +09:00
< ? php if ( isset ( $_SESSION [ 'userid' ]) && $_SESSION [ 'userid' ] != '' ) : ?>
< div class = " flex items-center gap-2 px-2 py-2 border-t border-slate-200 mt-2 pt-3 " >
< div class = " w-8 h-8 rounded-full bg-slate-200 flex items-center justify-center text-slate-500 font-bold border border-slate-300 " >
< ? = mb_substr ( $_SESSION [ 'name' ], 0 , 1 ) ?>
</ div >
< span class = " text-sm font-medium text-slate-700 flex-1 " >< ? = $_SESSION [ 'name' ] ?> 님</span>
2025-12-19 10:56:04 +09:00
< a href = " login/logout.php " class = " px-3 py-1.5 bg-slate-100 text-slate-600 text-xs font-bold rounded hover:bg-slate-200 transition-colors border border-slate-200 " onclick = " closeMobileMenu() " > 로그아웃 </ a >
2025-12-19 10:53:22 +09:00
</ div >
< ? php else : ?>
2025-12-19 10:56:04 +09:00
< a href = " login/login_form.php " class = " px-4 py-2 bg-brand-600 text-white text-sm font-bold rounded-lg hover:bg-brand-700 transition-colors shadow-lg shadow-brand-200 text-center mt-2 " onclick = " closeMobileMenu() " >
2025-12-19 10:53:22 +09:00
로그인
</ a >
< ? php endif ; ?>
</ div >
2025-12-17 12:59:26 +09:00
</ div >
</ div >
</ nav >
<!-- Hero Section -->
< header class = " relative bg-white pt-16 pb-20 lg:pt-24 lg:pb-28 overflow-hidden " >
< div class = " max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 " >
< div class = " text-center max-w-4xl mx-auto " >
< div class = " inline-flex items-center gap-2 px-3 py-1 rounded-full bg-brand-50 text-brand-700 text-xs font-semibold uppercase tracking-wider mb-6 animate-fade-in-up " >
< i data - lucide = " shield-check " class = " w-4 h-4 " ></ i >
CEO Management Solution
</ div >
2025-12-19 11:03:48 +09:00
< h1 class = " text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-extrabold text-slate-900 tracking-tight mb-6 animate-fade-in-up delay-100 leading-tight " >
< span class = " whitespace-nowrap " > 직원 관리 도구가 아닙니다 .</ span >< br />
< span class = " text-transparent bg-clip-text bg-gradient-to-r from-brand-600 to-indigo-600 whitespace-nowrap " >
2025-12-17 12:59:26 +09:00
대표님 경영 무기입니다 .
</ span >
</ h1 >
< p class = " text-lg sm:text-xl text-slate-500 mb-10 leading-relaxed max-w-2xl mx-auto animate-fade-in-up delay-200 " >
2025-12-18 08:11:13 +09:00
SAM은 현장에 없어도 , 현장을 다 보여줍니다 . < br class = " hidden sm:block " />
대표님 손 안의 스마트 경영 계기판 , SAM < br />
2025-12-17 12:59:26 +09:00
< strong > 오직 CEO를 위한 시크릿 대시보드 </ strong > 를 제안하십시오 .
</ p >
<!-- Main Hero Image -->
< div class = " mt-16 relative z-10 animate-fade-in-up delay-300 " >
< div class = " relative rounded-2xl overflow-hidden shadow-2xl border-4 border-white/50 bg-white " >
< img src = " img/sam_project.jpg " alt = " SAM Project Dashboard " class = " w-full h-auto object-cover " >
< div class = " absolute inset-0 bg-gradient-to-t from-slate-900/10 to-transparent pointer-events-none " ></ div >
</ div >
<!-- Decorative Elements around image -->
< div class = " absolute -top-10 -right-10 w-24 h-24 bg-brand-400 rounded-full mix-blend-multiply filter blur-2xl opacity-40 animate-blob " ></ div >
< div class = " absolute -bottom-10 -left-10 w-24 h-24 bg-indigo-400 rounded-full mix-blend-multiply filter blur-2xl opacity-40 animate-blob animation-delay-2000 " ></ div >
</ div >
</ div >
</ div >
<!-- Background Decorations -->
< div class = " absolute top-0 left-1/2 -translate-x-1/2 w-full h-full z-0 pointer-events-none " >
< div class = " absolute top-20 left-10 w-72 h-72 bg-brand-200 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob " ></ div >
< div class = " absolute top-20 right-10 w-72 h-72 bg-indigo-200 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-2000 " ></ div >
< div class = " absolute -bottom-8 left-1/2 w-72 h-72 bg-pink-200 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-4000 " ></ div >
</ div >
</ header >
<!-- Main Content -->
< main class = " max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 " >
< div class = " flex flex-col sm:flex-row items-center justify-between mb-8 gap-4 " >
< h2 class = " text-2xl font-bold text-slate-900 " > 영업 자료 </ h2 >
< div id = " filter-buttons " class = " flex flex-wrap gap-2 justify-center " >
<!-- Filter buttons injected by JS -->
</ div >
</ div >
<!-- Grid Container -->
< div id = " assets-grid " class = " grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-auto " >
<!-- Cards injected by JS -->
</ div >
< div id = " empty-state " class = " hidden text-center py-20 text-slate-400 col-span-3 " >
해당 카테고리에 자료가 없습니다 .
</ div >
</ main >
<!-- Footer -->
< footer class = " bg-white border-t border-slate-200 py-12 mt-12 " >
< div class = " max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center gap-6 " >
< p class = " text-slate-500 text-sm " > © 2025 CodeBridge - X Corp . 영업 관리 </ p >
< div class = " flex gap-6 " >
< a href = " # " class = " text-slate-400 hover:text-slate-600 " > 사내 전용 ( 대외비 ) </ a >
< a href = " # " class = " text-slate-400 hover:text-slate-600 " > 영업 스크립트 </ a >
< a href = " # " class = " text-slate-400 hover:text-slate-600 " > 고객 지원 </ a >
</ div >
</ div >
</ footer >
<!-- Modal Backdrop & Modal -->
< div id = " modal-backdrop " class = " fixed inset-0 bg-slate-900/50 backdrop-blur-sm z-50 hidden transition-opacity opacity-0 " onclick = " closeModal() " ></ div >
< div id = " modal-container " class = " fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-none hidden " >
<!-- Modal Content injected by JS -->
</ div >
2025-12-18 10:51:01 +09:00
<!-- Full Screen Viewer Overlay -->
< div id = " fullscreen-view " onclick = " closeFullscreen() " >
< div class = " fullscreen-close " >
< i data - lucide = " x " class = " w-6 h-6 text-slate-900 " ></ i >
</ div >
< img id = " fullscreen-img " src = " " alt = " Full size view " onclick = " event.stopPropagation() " >
</ div >
2025-12-19 10:48:59 +09:00
<!-- PDF Viewer Modal -->
< div id = " pdf-modal-backdrop " class = " fixed inset-0 bg-slate-900/50 backdrop-blur-sm z-[70] hidden transition-opacity opacity-0 " onclick = " closePdfViewer() " ></ div >
< div id = " pdf-modal-container " class = " fixed inset-0 z-[70] flex items-center justify-center p-4 pointer-events-none hidden " >
< div class = " bg-white rounded-2xl shadow-2xl w-full max-w-[95vw] h-[95vh] flex flex-col pointer-events-auto scale-95 opacity-0 transition-all duration-300 " id = " pdf-modal-card " >
< div class = " sticky top-0 bg-white/90 backdrop-blur-sm p-4 border-b border-slate-100 flex justify-between items-center z-10 " >
< h3 class = " text-lg font-bold text-slate-900 " id = " pdf-modal-title " > 상세자료 </ h3 >
< button onclick = " closePdfViewer() " class = " w-8 h-8 rounded-full bg-slate-50 hover:bg-slate-100 flex items-center justify-center text-slate-500 transition-colors " >
< i data - lucide = " x " class = " w-5 h-5 " ></ i >
</ button >
</ div >
< div class = " flex-1 overflow-hidden " >
< iframe id = " pdf-viewer-iframe " src = " " class = " w-full h-full border-0 " frameborder = " 0 " ></ iframe >
</ div >
</ div >
</ div >
2025-12-17 12:59:26 +09:00
< script >
2025-12-19 10:17:10 +09:00
// 통일된 영업 자료 데이터 구조
// 모든 카드는 동일한 포맷: 이미지(4:3), 설명, 동영상, 오디오, 영업 스크립트
2025-12-17 12:59:26 +09:00
const ASSETS = [
{
2025-12-19 10:17:10 +09:00
id : '1' ,
title : '대표를 위한 무기' ,
image : 'img/sales1.jpg' ,
2025-12-18 10:15:00 +09:00
description : '기존 ERP는 직원의 관리 도구였지만, SAM은 대표님의 의사결정 무기입니다. 직원의 보고를 기다리지 마십시오.' ,
2025-12-19 10:48:59 +09:00
video : '<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/1147875239?badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share" referrerpolicy="strict-origin-when-cross-origin" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="SAM__CEO를_위한_무기"></iframe></div>' ,
audio : 'm4a/sales1.m4a' ,
pdf : 'pdf/sales1.pdf' ,
2025-12-17 12:59:26 +09:00
script : " 대표님, ERP나 MES 들어보셨죠? 보통 직원들이 입력하고 관리하는 도구입니다. 정작 대표님은 직원한테 보고를 받아야만 회사를 알 수 있죠. SAM은 반대입니다. 직원이 아니라 '대표님을 위한 무기'입니다. 외근 중이든 집이든, 대표님 폰에서 회사의 자금, 인력, 리스크가 한눈에 보입니다. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Pitch' , 'Opener' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 11:30:36 +09:00
id : '2' ,
2025-12-19 10:17:10 +09:00
title : 'CEO 시크릿 대시보드' ,
2025-12-19 11:30:36 +09:00
image : 'img/sales2.jpg' ,
description : 'CEO 시크릿 대시보드는 마치 고성능 전투기의 조종석 대시보드와 같습니다. 수많은 기계적 장치(직원들의 업무)가 뒤에서 복잡하게 돌아가더라도, 조종사(대표님)는 전방의 적기(세무 리스크)와 연료 잔량(자금 흐름) 등 승리를 위해 지금 당장 필요한 정보만 확인하며 최적의 의사결정을 내릴 수 있게 돕기 때문입니다.' ,
2025-12-19 10:17:10 +09:00
video : '<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/1146972515?title=0&byline=0&portrait=0&badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share" referrerpolicy="strict-origin-when-cross-origin" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="CEO의_경영_비서,_SAM"></iframe></div>' ,
2025-12-19 11:30:36 +09:00
audio : 'm4a/sales2.m4a' ,
pdf : 'pdf/sales2.pdf' ,
script : " 특히 대표님들께서 가장 좋아하시는 기능이 있습니다. 외근 중에 회사에 큰 계약이 성사되거나 매출이 입금되면, 대표님 휴대폰에서만 경쾌하게 'SAM~' 하고 시그니처 알림음이 울립니다. 직원의 보고를 기다릴 필요 없이, 돈이 들어오는 순간을 즉시 느끼며 성취감을 맛보실 수 있습니다. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Demo' , 'Dashboard' , 'Finance' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 11:30:36 +09:00
id : '3' ,
2025-12-19 10:17:10 +09:00
title : '20가지 고충 해결' ,
image : 'img/sam_sales2.jpg' ,
2025-12-18 11:02:54 +09:00
description : '사람(근태), 돈(세금), 운영(현장), 대표의 삶(리스크). CEO가 겪는 20가지 고충을 방어합니다.' ,
2025-12-19 10:17:10 +09:00
video : null ,
audio : null ,
2025-12-17 12:59:26 +09:00
script : " 중소기업 대표님의 머릿속을 20가지로 정리해봤습니다. 직원 근태, 자금 압박, 세무 조사... 이 모든 걸 혼자 감당하고 계시지 않습니까? SAM은 단순 프로그램이 아니라, 이 20가지 리스크를 막아주는 방패입니다. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Pain Points' , 'Solution' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 11:30:36 +09:00
id : '4' ,
2025-12-19 10:17:10 +09:00
title : '모바일 & 감성 알림' ,
image : 'img/sam_alert.jpg' ,
2025-12-17 12:59:26 +09:00
description : '수주/입금 시 울리는 "SAM~" 알림음. 외근 중에도 회사가 돌아가는 소리를 들으세요.' ,
2025-12-19 10:17:10 +09:00
video : null ,
audio : null ,
2025-12-17 12:59:26 +09:00
script : " 가장 인기 있는 기능입니다. 외근 나가 계실 때 불안하시죠? 직원이 큰 수주를 따오거나, 거래처에서 돈을 입금하면 대표님 폰에서 'SAM~' 하고 알림이 옵니다. 그 소리만 들으면 '아, 우리 회사 잘 돌아가고 있구나' 안심이 되실 겁니다. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Mobile' , 'UX' , 'Emotion' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '6' ,
title : '영업 전략 해부: CEO에게 파는 법' ,
image : 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80' ,
description : 'SAM 프로젝트의 핵심 영업 전략은 CEO들의 심리적 약점과 욕구(통제 욕구, 복잡한 것 회피, 비용 민감성)를 파고들어 설득력을 극대화하도록 재정비되었습니다.' ,
video : '<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/1146961422?title=0&byline=0&portrait=0&badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share" referrerpolicy="strict-origin-when-cross-origin" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="영업_전략_해부__CEO에게_파는_법"></iframe></div>' ,
audio : null ,
2025-12-17 12:59:26 +09:00
script : " 1. 통제 욕구 및 불안감 자극/해소: 지각/무단결근 등 특이사항과 내일 나갈 돈(예상 부가세)을 미리 보여주어 통제감을 높여줍니다. \n 2. 복잡한 것 회피: 모바일 중심의 '시크릿 모드'와 10초 사용을 강조하여 진입장벽을 낮춥니다. \n 3. 비용 민감성: 월 30~50만 원으로 24시간 AI 경영 비서를 고용하는 가성비를 강조합니다. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Strategy' , 'CEO Psychology' , 'Sales Pitch' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '7' ,
title : '자동화 & 인수인계' ,
image : 'img/sam_time.jpg' ,
2025-12-17 12:59:26 +09:00
description : '직원이 갑자기 퇴사해도 걱정 없습니다. 견적부터 발주, 출고까지 모든 이력이 클릭 한 번으로 자동 인수인계됩니다.' ,
2025-12-19 10:17:10 +09:00
video : null ,
audio : null ,
2025-12-17 12:59:26 +09:00
script : " 직원이 갑자기 그만둔다고 하면 눈앞이 캄캄하시죠? 파일 어디 있냐, 거래처 연락처 뭐냐... SAM을 쓰시면 그럴 일 없습니다. 모든 업무 기록이 서버에 남기 때문에, 후임자는 '클릭' 한 번이면 전임자의 모든 업무를 그대로 이어받습니다. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Automation' , 'Management' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '9' ,
title : '도입 제안 (Closing)' ,
image : null ,
description : '월 구독료로 수천만 원대 맞춤형 ERP 기능과 개인 비서를 고용하는 효과를 누리십시오. 내일부터는 "걱정" 대신 "설렘"으로 출근하십시오.' ,
video : null ,
audio : null ,
2025-12-17 12:59:26 +09:00
script : " 직원 한 명 월급의 1/10도 안 되는 비용입니다. 이 돈으로 24시간 비서, 그리고 완벽한 경영 시스템을 고용하시는 겁니다. 오늘 결정하시고, 내일부터는 가벼운 마음으로 출근하십시오. " ,
2025-12-19 10:17:10 +09:00
tags : [ 'Closing' , 'Pricing' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '10' ,
title : '[Core Features] 대표님이 반할 기능 8선' ,
image : null ,
description : 'AI 경영 비서부터 자금/세무 관리까지, SAM이 제공하는 8가지 핵심 기능을 3분 요약으로 들어보세요.' ,
video : null ,
audio : 'm4a/strategy.m4a' ,
script : " AI 경영 비서: 음성 인식 업무일지, 회의록, 자동 번역 \n 스마트 현장: 사진 토큰 관리, GPS 기반 출퇴근, QR 설비 관리 \n 자금/세무: 예상 부가세, 법인카드 한도 관리, 경리일보 자동화 \n 리스크 방어: 미수금 알림, 재고 부족 경고, 신규 거래처 신용 등급 표시 \n 편의 기능: 용량 걱정 없는 무제한 파일함, 시그니처 매출 알림음 " ,
tags : [ 'Podcast' , 'Audio' , 'Feature' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '11' ,
title : '1. 대표님의 고민 (Pain Points)' ,
image : null ,
description : '매출은 느는데 이익은 제자리? 직원과 현장을 100% 믿을 수 있나? 세금 낼 때마다 자금 계획이 꼬이나? 이는 정보 부재에서 오는 "전략적 리스크"입니다.' ,
video : null ,
audio : null ,
script : " 매출은 계속 오르는데, 왜 통장에 남는 돈은 없을까요? 직원들은 정말 열심히 하고 있는 걸까요? 세금 낼 때마다 목돈 마련하느라 허덕이시진 않나요? 더 많은 보고서가 아니라, 대표님 손안에 '진짜 정보'가 필요할 때입니다. " ,
tags : [ 'Pain Points' , 'Risk' , 'Needs' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '12' ,
title : '2. 새로운 해법 (Solution)' ,
image : null ,
description : 'SAM은 "실무자 관리 도구"가 아닌 "CEO 의사결정 도구"입니다. 복잡한 입력은 AI가, 대표님은 직관적인 신호등과 요약 카드만 확인하십시오.' ,
video : null ,
audio : null ,
script : " 기존 ERP는 관리자용이라 복잡하고 비쌉니다. SAM은 다릅니다. 오직 대표님을 위해 설계되었습니다. 복잡한 표 대신 신호등으로, PC 대신 모바일로, 수천만 원 구축비 대신 합리적 구독료로 제공합니다. 복잡한 건 AI에게 맡기고, 대표님은 흐름만 보십시오. " ,
tags : [ 'Solution' , 'Differentiation' , 'AI' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '13' ,
title : '3. 핵심 기능 (Core Features)' ,
image : null ,
description : '리스크 사전 방지(미수금, 재고), 자금 흐름 관리(예상 부가세, 한도 체크), 실시간 현장 통제(지각, AI 업무일지).' ,
video : null ,
audio : null ,
script : " A업체 미수금 3개월 경과, 접대비 한도 80% 소진, 오늘 지각자 3명... 대표님이 놓치기 쉬운 '리스크'만 골라서 붉은색 알림을 띄워드립니다. 사무실 밖에서도 현장과 자금이 한눈에 들어옵니다. " ,
tags : [ 'Features' , 'Risk' , 'Finance' , 'Ops' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '14' ,
title : '4. 도입 효과 (ROI)' ,
image : null ,
description : '보고 시간 0분(실시간 확인), 단순 업무 80% 자동화, 의사결정 속도 즉시. 눈에 보이는 비용 절감과 경쟁력 강화를 경험하십시오.' ,
video : null ,
audio : null ,
script : " 직원이 보고서 만들어서 결재 올릴 때까지 기다리지 마십시오. SAM을 쓰면 보고 시간이 '0분'이 됩니다. 단순 반복 업무는 80% 자동화됩니다. 이게 바로 돈 버는 시스템입니다. " ,
tags : [ 'ROI' , 'Efficiency' , 'Automation' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '15' ,
title : '5. 제안 (Investment)' ,
image : null ,
description : '연 3,000만 원 경리 직원 채용 vs 월 30만 원 AI 비서. 24시간 잠들지 않는 AI를 신입사원 월급의 1/10도 안 되는 비용으로 고용하십시오.' ,
video : null ,
audio : null ,
script : " 사람 한 명 뽑으려면 3천만 원 듭니다. SAM은 월 30만 원입니다. 1/100 비용으로 24시간 잠들지 않고 실수도 없는 완벽한 AI 비서를 채용하시는 겁니다. 이건 지출이 아니라 가장 확실한 투자입니다. " ,
tags : [ 'Investment' , 'Cost' , 'Value' ],
2025-12-17 12:59:26 +09:00
},
{
2025-12-19 10:17:10 +09:00
id : '16' ,
title : '6. 다음 단계 (Next Steps)' ,
image : null ,
description : '백 번 설명보다 한 번의 경험이 낫습니다. 대표님 업종에 맞춘 "무료 진단 & 시연"과 "3일 무료 체험"을 신청하십시오.' ,
video : null ,
audio : null ,
script : " 우리 회사에 맞을까 고민하지 마세요. 대표님 업종에 딱 맞춘 대시보드를 미리 구성해서 보여드립니다. 3일만 직접 써보시면, 왜 SAM이 필수인지 바로 아실 겁니다. " ,
tags : [ 'Call to Action' , 'Demo' , 'Free Trial' ],
2025-12-17 12:59:26 +09:00
}
];
// Constants
const FILTERS = [ '전체' , 'CEO 설득' , '데모' ];
let activeFilter = '전체' ;
// DOM Elements
const gridEl = document . getElementById ( 'assets-grid' );
const filterContainer = document . getElementById ( 'filter-buttons' );
const emptyState = document . getElementById ( 'empty-state' );
const modalBackdrop = document . getElementById ( 'modal-backdrop' );
const modalContainer = document . getElementById ( 'modal-container' );
const toast = document . getElementById ( 'toast' );
// Initial Render
function init () {
renderFilters ();
renderGrid ();
2025-12-19 10:56:04 +09:00
if ( typeof lucide !== 'undefined' ) {
lucide . createIcons ();
}
2025-12-17 12:59:26 +09:00
}
// Filter Logic
function renderFilters () {
filterContainer . innerHTML = FILTERS . map ( filter => `
< button
onclick = " filterAssets(' ${ filter } ') "
2025-12-19 11:09:12 +09:00
class = " px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors duration-200 whitespace-nowrap $ { activeFilter === filter ? 'bg-slate-900 text-white shadow-md' : 'bg-white text-slate-600 hover:bg-slate-100 border border-transparent hover:border-slate-200'} "
2025-12-17 12:59:26 +09:00
>
$ { filter }
</ button >
` ) . join ( '' );
}
window . filterAssets = ( filter ) => {
activeFilter = filter ;
renderFilters ();
renderGrid ();
lucide . createIcons ();
};
function getFilteredAssets () {
if ( activeFilter === '전체' ) return ASSETS ;
return ASSETS . filter ( asset => {
2025-12-19 10:17:10 +09:00
if ( activeFilter === 'CEO 설득' ) return asset . tags . some ( tag => [ 'Pitch' , 'Opener' , 'Pain Points' , 'Solution' , 'Closing' , 'Risk' , 'Needs' , 'Differentiation' , 'Investment' , 'Cost' , 'Value' ] . includes ( tag ));
if ( activeFilter === '데모' ) return asset . tags . some ( tag => [ 'Demo' , 'Dashboard' , 'Video' , 'Mobile' , 'UX' , 'Infra' , 'Feature' , 'Automation' , 'Management' ] . includes ( tag ));
2025-12-17 12:59:26 +09:00
return true ;
});
}
// Grid Render
function renderGrid () {
const assets = getFilteredAssets ();
if ( assets . length === 0 ) {
emptyState . classList . remove ( 'hidden' );
} else {
emptyState . classList . add ( 'hidden' );
}
gridEl . innerHTML = assets . map ( asset => {
2025-12-19 10:17:10 +09:00
// [1] 이미지 영역 (실제 이미지 비율 계산)
let imageHtml = '' ;
if ( asset . image ) {
imageHtml = `
< div class = " relative bg-slate-50 overflow-hidden group/img cursor-pointer border-b border-slate-100 " onclick = " openModal(' ${ asset.id } ') " data - asset - id = " ${ asset.id } " >
< img src = " ${ asset.image } " alt = " ${ asset.title } "
class = " w-full h-auto object-cover transform group-hover/img:scale-105 transition-transform duration-700 "
style = " image-rendering: -webkit-optimize-contrast; "
onload = " setImageAspectRatio(this) "
data - asset - id = " ${ asset.id } " >
< div class = " absolute inset-0 bg-black/5 opacity-0 group-hover/img:opacity-100 transition-opacity flex items-center justify-center pointer-events-none " >
< div class = " bg-white/90 backdrop-blur p-2 rounded-full shadow-lg " >
< i data - lucide = " zoom-in " class = " w-5 h-5 text-slate-700 " ></ i >
2025-12-17 12:59:26 +09:00
</ div >
</ div >
2025-12-19 10:17:10 +09:00
</ div > ` ;
} else {
// 이미지가 없을 때 아이콘 표시
let iconName = 'file-text' ;
if ( asset . video ) iconName = 'play-circle' ;
else if ( asset . audio ) iconName = 'mic' ;
imageHtml = `
< div class = " relative aspect-[4/3] bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center border-b border-slate-100 " >
< div class = " text-slate-300 " >
< i data - lucide = " ${ iconName } " class = " w-16 h-16 opacity-40 " ></ i >
2025-12-17 12:59:26 +09:00
</ div >
2025-12-19 10:17:10 +09:00
</ div > ` ;
2025-12-17 12:59:26 +09:00
}
2025-12-19 10:17:10 +09:00
// [2] 정보 영역
2025-12-17 12:59:26 +09:00
return `
2025-12-19 10:17:10 +09:00
< div class = " bg-white rounded-2xl border border-slate-200 shadow-sm hover:shadow-xl transition-all duration-500 overflow-hidden flex flex-col group h-full " onclick = " openModal(' ${ asset.id } ') " >
$ { imageHtml }
< div class = " p-6 flex-grow flex flex-col " >
< div class = " flex items-center justify-between mb-3 " >
< div class = " flex gap-2 " >
$ { asset . tags . map ( tag => `<span class="text-slate-400 text-[10px] font-medium">#${tag}</span>` ) . join ( ' ' )}
</ div >
< button class = " text-slate-300 hover:text-brand-500 transition-colors " >
< i data - lucide = " share-2 " class = " w-4 h-4 " ></ i >
</ button >
</ div >
2025-12-19 11:03:48 +09:00
< h3 class = " text-base sm:text-lg font-bold text-slate-900 mb-2 group-hover:text-brand-600 transition-colors break-keep leading-tight " style = " word-break: keep-all; overflow-wrap: break-word; " > $ { asset . title } </ h3 >
2025-12-19 10:17:10 +09:00
< div class = " flex-grow " >
< p class = " text-slate-600 text-sm leading-relaxed mb-4 line-clamp-3 " >
$ { asset . description || '' }
</ p >
2025-12-17 12:59:26 +09:00
</ div >
2025-12-19 10:17:10 +09:00
<!-- 미디어 타입 표시 -->
< div class = " mb-3 flex items-center gap-2 text-xs text-slate-400 " >
$ { asset . image ? '<span class="flex items-center gap-1"><i data-lucide="image" class="w-3 h-3"></i> 이미지</span>' : '' }
$ { asset . video ? '<span class="flex items-center gap-1"><i data-lucide="play-circle" class="w-3 h-3"></i> 동영상</span>' : '' }
$ { asset . audio ? '<span class="flex items-center gap-1"><i data-lucide="mic" class="w-3 h-3"></i> 팟캐스트</span>' : '' }
$ { asset . script ? '<span class="flex items-center gap-1"><i data-lucide="message-square" class="w-3 h-3"></i> 스크립트</span>' : '' }
</ div >
< div class = " mt-auto pt-4 border-t border-slate-50 flex items-center justify-between " >
< button class = " text-brand-600 text-xs font-bold hover:underline flex items-center gap-1 " >
상세 보기 < i data - lucide = " chevron-right " class = " w-3 h-3 " ></ i >
</ button >
< span class = " text-[10px] text-slate-300 font-bold uppercase tracking-widest " > SAM </ span >
</ div >
2025-12-17 12:59:26 +09:00
</ div >
</ div >
` ;
}) . join ( '' );
}
// Modal Logic
window . openModal = ( id ) => {
const asset = ASSETS . find ( a => a . id === id );
if ( ! asset ) return ;
// Generate content
const tagsHtml = asset . tags . map ( tag => `<span class="px-2 py-1 rounded-md bg-slate-100 text-slate-500 text-xs font-medium">#${tag}</span>` ) . join ( '' );
2025-12-19 10:17:10 +09:00
// 통일된 미디어 표시 구조
2025-12-17 12:59:26 +09:00
let mediaHtml = '' ;
2025-12-19 10:17:10 +09:00
// 1. 이미지 (실제 비율 계산)
if ( asset . image ) {
mediaHtml += `
< div class = " mb-6 " >
< div class = " flex items-center gap-2 mb-3 " >
< div class = " w-1 h-5 rounded-full bg-brand-500 " ></ div >
< h4 class = " font-bold text-slate-900 " > 이미지 </ h4 >
</ div >
< div class = " relative group viewer bg-slate-50 rounded-xl overflow-hidden border border-slate-100 " >
< div class = " absolute top-3 right-3 z-[60] flex gap-2 translate-y-2 opacity-0 group-hover:opacity-100 transition-all duration-300 " >
< button onclick = " toggleNaturalSize(this) "
class = " px-4 py-2 bg-white/95 backdrop-blur-md shadow-lg border border-slate-200 rounded-full text-xs font-bold text-brand-600 hover:bg-brand-600 hover:text-white transition-all flex items-center gap-2 " >
< i data - lucide = " maximize " class = " w-3.5 h-3.5 " ></ i >
< span > 원본 크기 보기 </ span >
</ button >
</ div >
< div class = " img-full-container custom-scrollbar scroll-smooth w-full overflow-hidden " >
< img src = " ${ asset.image } "
id = " modal-main-img- ${ asset.id } "
class = " w-full h-auto object-contain rounded-xl img-zoom-target "
style = " image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; "
onload = " setModalImageAspectRatio(this) " >
2025-12-17 12:59:26 +09:00
</ div >
</ div >
</ div > ` ;
2025-12-19 10:17:10 +09:00
}
2025-12-19 10:48:59 +09:00
// 2. 설명 (이미지 밑에 표시)
if ( asset . description ) {
mediaHtml += `
< div class = " mb-6 " >
< div class = " flex items-center gap-2 mb-3 " >
< div class = " w-1 h-5 rounded-full bg-brand-500 " ></ div >
< h4 class = " font-bold text-slate-900 " > 설명 </ h4 >
</ div >
< div class = " p-4 bg-slate-50 rounded-xl border border-slate-100 text-slate-700 leading-relaxed " >
$ { asset . description }
</ div >
</ div > ` ;
}
2025-12-19 10:17:10 +09:00
// 2. 동영상
if ( asset . video ) {
mediaHtml += `
< div class = " mb-6 " >
< div class = " flex items-center gap-2 mb-3 " >
< div class = " w-1 h-5 rounded-full bg-brand-500 " ></ div >
< h4 class = " font-bold text-slate-900 " > 동영상 </ h4 >
</ div >
< div class = " aspect-video bg-slate-100 rounded-xl overflow-hidden relative z-0 " >
$ { asset . video }
</ div >
</ div > ` ;
}
// 3. 오디오 팟캐스트
if ( asset . audio ) {
mediaHtml += `
< div class = " mb-6 " >
< div class = " flex items-center gap-2 mb-3 " >
< div class = " w-1 h-5 rounded-full bg-brand-500 " ></ div >
< h4 class = " font-bold text-slate-900 " > 팟캐스트 </ h4 >
</ div >
< div class = " bg-slate-900 rounded-xl p-8 text-center " >
< div class = " w-24 h-24 rounded-full bg-indigo-500 mx-auto mb-6 flex items-center justify-center shadow-lg " >
2025-12-17 12:59:26 +09:00
< i data - lucide = " mic " class = " w-10 h-10 text-white " ></ i >
</ div >
< h4 class = " text-white font-bold text-xl mb-2 " > $ { asset . title } </ h4 >
2025-12-19 10:17:10 +09:00
< p class = " text-slate-400 text-sm mb-6 " > SAM Strategy Podcast </ p >
2025-12-17 12:59:26 +09:00
< audio controls class = " w-full " >
2025-12-19 10:17:10 +09:00
< source src = " ${ asset.audio } " type = " audio/mp4 " >
< source src = " ${ asset.audio } " type = " audio/mpeg " >
2025-12-17 12:59:26 +09:00
Your browser does not support the audio element .
</ audio >
</ div >
2025-12-19 10:17:10 +09:00
</ div > ` ;
2025-12-17 12:59:26 +09:00
}
const modalContent = `
< div class = " bg-white rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto pointer-events-auto scale-95 opacity-0 transition-all duration-300 " id = " modal-card " >
< div class = " sticky top-0 bg-white/90 backdrop-blur-sm p-4 border-b border-slate-100 flex justify-between items-center z-10 " >
< div class = " flex items-center gap-2 " >
2025-12-19 11:03:48 +09:00
< h3 class = " text-base sm:text-lg font-bold text-slate-900 break-keep leading-tight " style = " word-break: keep-all; overflow-wrap: break-word; " > $ { asset . title } </ h3 >
2025-12-17 12:59:26 +09:00
</ div >
< button onclick = " closeModal() " class = " w-8 h-8 rounded-full bg-slate-50 hover:bg-slate-100 flex items-center justify-center text-slate-500 transition-colors " >
< i data - lucide = " x " class = " w-5 h-5 " ></ i >
</ button >
</ div >
< div class = " p-6 sm:p-8 " >
$ { mediaHtml }
2025-12-19 10:17:10 +09:00
<!-- 영업 스크립트 -->
$ { asset . script ? `
< div class = " mb-8 " >
< div class = " flex items-center gap-2 mb-3 " >
< div class = " w-1 h-5 rounded-full bg-brand-500 " ></ div >
< h4 class = " font-bold text-slate-900 " > 영업 스크립트 </ h4 >
2025-12-17 12:59:26 +09:00
</ div >
2025-12-19 10:17:10 +09:00
< div class = " bg-indigo-50/50 p-5 rounded-xl border border-indigo-100 relative " >
< i data - lucide = " quote " class = " w-5 h-5 text-indigo-200 absolute top-4 left-4 " ></ i >
< p class = " text-slate-800 leading-relaxed pl-6 relative z-10 font-medium whitespace-pre-line " > " ${ asset.script } " </ p >
2025-12-17 12:59:26 +09:00
</ div >
</ div >
` : '' }
< div class = " flex flex-wrap gap-2 pt-6 border-t border-slate-100 " >
$ { tagsHtml }
</ div >
</ div >
< div class = " p-4 border-t border-slate-100 bg-slate-50 rounded-b-2xl flex justify-end gap-3 " >
2025-12-19 11:09:12 +09:00
< button onclick = " closeModal() " class = " px-3 sm:px-4 py-2 rounded-lg bg-white border border-slate-200 text-slate-700 font-medium hover:bg-slate-50 transition-colors whitespace-nowrap text-sm sm:text-base " > 닫기 </ button >
2025-12-19 10:48:59 +09:00
$ { asset . pdf ? `
2025-12-19 11:09:12 +09:00
< button onclick = " openPdfViewer(' ${ asset.id } ') " class = " px-3 sm:px-4 py-2 rounded-lg bg-brand-600 text-white font-medium hover:bg-brand-700 shadow-md transition-colors flex items-center gap-2 whitespace-nowrap text-sm sm:text-base " >
< i data - lucide = " file-text " class = " w-4 h-4 flex-shrink-0 " ></ i >
< span > 상세자료 보기 </ span >
2025-12-19 10:48:59 +09:00
</ button >
` : `
2025-12-19 11:09:12 +09:00
< button onclick = " showDownloadToast(); closeModal() " class = " px-3 sm:px-4 py-2 rounded-lg bg-brand-600 text-white font-medium hover:bg-brand-700 shadow-md transition-colors flex items-center gap-2 whitespace-nowrap text-sm sm:text-base " >
< i data - lucide = " download " class = " w-4 h-4 flex-shrink-0 " ></ i >
< span > 자료 사용하기 </ span >
2025-12-17 12:59:26 +09:00
</ button >
2025-12-19 10:48:59 +09:00
` }
2025-12-17 12:59:26 +09:00
</ div >
</ div >
` ;
modalContainer . innerHTML = modalContent ;
// Animation In
modalBackdrop . classList . remove ( 'hidden' );
modalContainer . classList . remove ( 'hidden' );
document . body . classList . add ( 'no-scroll' );
// Trigger reflow
void modalBackdrop . offsetWidth ;
modalBackdrop . classList . remove ( 'opacity-0' );
const card = document . getElementById ( 'modal-card' );
card . classList . remove ( 'scale-95' , 'opacity-0' );
lucide . createIcons ();
};
window . closeModal = () => {
const card = document . getElementById ( 'modal-card' );
if ( card ) {
card . classList . add ( 'scale-95' , 'opacity-0' );
}
modalBackdrop . classList . add ( 'opacity-0' );
setTimeout (() => {
modalBackdrop . classList . add ( 'hidden' );
modalContainer . classList . add ( 'hidden' );
document . body . classList . remove ( 'no-scroll' );
modalContainer . innerHTML = '' ;
}, 300 );
};
2025-12-18 10:51:01 +09:00
// Toggle Image Natural Size (Now Full Screen Wide View)
2025-12-18 10:15:00 +09:00
window . toggleNaturalSize = ( btn ) => {
2025-12-19 10:17:10 +09:00
// 버튼의 부모 컨테이너에서 이미지 찾기
const container = btn . closest ( '.group.viewer' ) || btn . closest ( '.relative' );
const img = container ? container . querySelector ( 'img.img-zoom-target' ) : null ;
if ( ! img ) {
console . error ( '이미지를 찾을 수 없습니다.' );
return ;
}
2025-12-18 10:51:01 +09:00
const fsView = document . getElementById ( 'fullscreen-view' );
const fsImg = document . getElementById ( 'fullscreen-img' );
2025-12-18 10:15:00 +09:00
2025-12-19 10:17:10 +09:00
if ( ! fsView || ! fsImg ) {
console . error ( '풀스크린 뷰어를 찾을 수 없습니다.' );
return ;
}
2025-12-18 10:51:01 +09:00
fsImg . src = img . src ;
fsView . style . display = 'flex' ;
document . body . classList . add ( 'no-scroll' );
2025-12-18 10:15:00 +09:00
lucide . createIcons ();
2025-12-17 12:59:26 +09:00
};
2025-12-18 10:51:01 +09:00
window . closeFullscreen = () => {
const fsView = document . getElementById ( 'fullscreen-view' );
fsView . style . display = 'none' ;
// 모달이 열려있는 상태면 스크롤 제한 유지, 아니면 해제
if ( document . getElementById ( 'modal-backdrop' ) . classList . contains ( 'hidden' )) {
document . body . classList . remove ( 'no-scroll' );
}
};
2025-12-19 10:48:59 +09:00
// PDF Viewer Functions
window . openPdfViewer = ( id ) => {
const asset = ASSETS . find ( a => a . id === id );
if ( ! asset || ! asset . pdf ) return ;
const pdfBackdrop = document . getElementById ( 'pdf-modal-backdrop' );
const pdfContainer = document . getElementById ( 'pdf-modal-container' );
const pdfCard = document . getElementById ( 'pdf-modal-card' );
const pdfIframe = document . getElementById ( 'pdf-viewer-iframe' );
const pdfTitle = document . getElementById ( 'pdf-modal-title' );
pdfTitle . textContent = asset . title ;
// PDF를 Google Drive Viewer로 표시 (로컬 파일도 가능)
// 또는 직접 PDF 파일 경로 사용 (브라우저 지원 시)
const pdfUrl = asset . pdf ;
pdfIframe . src = pdfUrl ;
// Animation In
pdfBackdrop . classList . remove ( 'hidden' );
pdfContainer . classList . remove ( 'hidden' );
document . body . classList . add ( 'no-scroll' );
// Trigger reflow
void pdfBackdrop . offsetWidth ;
pdfBackdrop . classList . remove ( 'opacity-0' );
pdfCard . classList . remove ( 'scale-95' , 'opacity-0' );
lucide . createIcons ();
};
window . closePdfViewer = () => {
const pdfBackdrop = document . getElementById ( 'pdf-modal-backdrop' );
const pdfContainer = document . getElementById ( 'pdf-modal-container' );
const pdfCard = document . getElementById ( 'pdf-modal-card' );
const pdfIframe = document . getElementById ( 'pdf-viewer-iframe' );
if ( pdfCard ) {
pdfCard . classList . add ( 'scale-95' , 'opacity-0' );
}
pdfBackdrop . classList . add ( 'opacity-0' );
setTimeout (() => {
pdfBackdrop . classList . add ( 'hidden' );
pdfContainer . classList . add ( 'hidden' );
pdfIframe . src = '' ;
// 모달이 열려있는 상태면 스크롤 제한 유지, 아니면 해제
if ( document . getElementById ( 'modal-backdrop' ) . classList . contains ( 'hidden' )) {
document . body . classList . remove ( 'no-scroll' );
}
}, 300 );
};
2025-12-19 10:17:10 +09:00
// 카드 이미지 비율 계산 함수
window . setImageAspectRatio = ( img ) => {
if ( img . naturalWidth && img . naturalHeight ) {
const container = img . closest ( '[data-asset-id]' );
if ( container ) {
container . style . aspectRatio = `${img.naturalWidth} / ${img.naturalHeight}` ;
}
}
};
// 모달 이미지 비율 계산 함수
window . setModalImageAspectRatio = ( img ) => {
if ( img . naturalWidth && img . naturalHeight ) {
const container = img . closest ( '.img-full-container' );
if ( container ) {
// 모달에서는 최대 높이 제한을 두고 비율 유지
const maxHeight = window . innerHeight * 0.6 ;
const aspectRatio = img . naturalWidth / img . naturalHeight ;
const calculatedWidth = maxHeight * aspectRatio ;
if ( calculatedWidth > container . offsetWidth ) {
img . style . width = '100%' ;
img . style . height = 'auto' ;
} else {
img . style . width = `${calculatedWidth}px` ;
img . style . height = `${maxHeight}px` ;
}
}
}
};
2025-12-19 10:56:04 +09:00
// Mobile Menu Functions
2025-12-19 10:59:32 +09:00
window . toggleMobileMenu = ( e ) => {
if ( e ) {
e . preventDefault ();
e . stopPropagation ();
}
2025-12-19 10:53:22 +09:00
const mobileMenu = document . getElementById ( 'mobile-menu' );
const menuIcon = document . getElementById ( 'menu-icon' );
const closeIcon = document . getElementById ( 'close-icon' );
2025-12-19 10:56:04 +09:00
if ( ! mobileMenu || ! menuIcon || ! closeIcon ) {
console . error ( 'Mobile menu elements not found' );
return ;
}
2025-12-19 10:53:22 +09:00
if ( mobileMenu . classList . contains ( 'hidden' )) {
mobileMenu . classList . remove ( 'hidden' );
menuIcon . classList . add ( 'hidden' );
closeIcon . classList . remove ( 'hidden' );
} else {
mobileMenu . classList . add ( 'hidden' );
menuIcon . classList . remove ( 'hidden' );
closeIcon . classList . add ( 'hidden' );
}
2025-12-19 10:56:04 +09:00
// 아이콘 재생성
if ( typeof lucide !== 'undefined' ) {
lucide . createIcons ();
}
};
window . closeMobileMenu = () => {
const mobileMenu = document . getElementById ( 'mobile-menu' );
const menuIcon = document . getElementById ( 'menu-icon' );
const closeIcon = document . getElementById ( 'close-icon' );
if ( mobileMenu && ! mobileMenu . classList . contains ( 'hidden' )) {
mobileMenu . classList . add ( 'hidden' );
if ( menuIcon ) menuIcon . classList . remove ( 'hidden' );
if ( closeIcon ) closeIcon . classList . add ( 'hidden' );
}
2025-12-19 10:53:22 +09:00
};
2025-12-19 10:59:32 +09:00
// Mobile menu button event listeners
// 스크립트가 body 끝에 있으므로 DOM은 이미 로드됨
( function setupMobileMenuButton () {
const mobileMenuButton = document . getElementById ( 'mobile-menu-button' );
if ( mobileMenuButton ) {
// 터치 이벤트와 클릭 이벤트 모두 처리
let touchStartTime = 0 ;
mobileMenuButton . addEventListener ( 'touchstart' , ( e ) => {
touchStartTime = Date . now ();
e . stopPropagation ();
}, { passive : true });
mobileMenuButton . addEventListener ( 'touchend' , ( e ) => {
e . preventDefault ();
e . stopPropagation ();
// 빠른 터치만 처리 (300ms 이내)
if ( Date . now () - touchStartTime < 300 ) {
toggleMobileMenu ( e );
}
}, { passive : false });
mobileMenuButton . addEventListener ( 'click' , ( e ) => {
e . preventDefault ();
e . stopPropagation ();
toggleMobileMenu ( e );
});
}
})();
2025-12-19 10:53:22 +09:00
// Close mobile menu when clicking outside
document . addEventListener ( 'click' , ( e ) => {
const mobileMenu = document . getElementById ( 'mobile-menu' );
const mobileMenuButton = document . getElementById ( 'mobile-menu-button' );
if ( mobileMenu && mobileMenuButton &&
! mobileMenu . classList . contains ( 'hidden' ) &&
! mobileMenu . contains ( e . target ) &&
! mobileMenuButton . contains ( e . target )) {
2025-12-19 10:56:04 +09:00
closeMobileMenu ();
2025-12-19 10:53:22 +09:00
}
});
2025-12-19 10:59:32 +09:00
// Close mobile menu on touch outside (for mobile)
document . addEventListener ( 'touchstart' , ( e ) => {
const mobileMenu = document . getElementById ( 'mobile-menu' );
const mobileMenuButton = document . getElementById ( 'mobile-menu-button' );
if ( mobileMenu && mobileMenuButton &&
! mobileMenu . classList . contains ( 'hidden' ) &&
! mobileMenu . contains ( e . target ) &&
! mobileMenuButton . contains ( e . target )) {
closeMobileMenu ();
}
});
2025-12-17 12:59:26 +09:00
// Initialize
init ();
</ script >
< script src = " https://player.vimeo.com/api/player.js " ></ script >
</ body >
</ html >