Compare commits

..

2 Commits

Author SHA1 Message Date
68893236+KINDNICK@users.noreply.github.com
d4a3a9afa5 Update: 서비스 페이지 논리 오류 수정 및 3월 요금제 변경 팝업 추가
- 서비스1 명칭 '모션캡처 녹화 서비스'로 통일
- 가격 카드 설명을 모션 녹화 중심으로 수정 (방송 표현 제거)
- SEO 메타/OG/Twitter 가격 및 용어 오류 수정
- mailto 템플릿 가격·용어 현행화
- 메인 팝업: 3월 요금제 변경 안내로 교체
  - 할인 이벤트 종료, 스트리밍글 4시간 신설, 추천인 제도 오픈
- 팝업 스크롤바 숨김 처리 및 변경사항 카드 스타일 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 20:50:02 +09:00
서버
ba99cbe0ae Update: OptiTrack 카메라 28대 → 30대 반영 (Prime 13 2대 추가)
- index.html: meta, JSON-LD, 히어로 카운터, 장비 소개
- gallery.html: OG/Twitter 메타, 이미지 alt
- services.html: OG/Twitter 메타, 스펙 태그
- qna.html: FAQ 장비 안내

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:25:58 +09:00
41 changed files with 480 additions and 285 deletions

View File

@ -298,7 +298,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

View File

@ -27,7 +27,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

View File

@ -226,7 +226,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

View File

@ -326,6 +326,10 @@
/* ========================================
다크모드
======================================== */
[data-theme="dark"] .page-header {
background: var(--dark-header-gradient);
}
[data-theme="dark"] .info-grid {
background: var(--dark-surface);
border: 1px solid var(--glass-border);

View File

@ -412,6 +412,13 @@ body {
gap: var(--spacing-md);
}
.footer-bottom {
text-align: center;
padding-top: var(--spacing-xl);
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.6);
}
/* 백업/기본 푸터 */
.site-footer {
background: var(--text-primary);
@ -1494,7 +1501,7 @@ body {
.streamer-card {
background: var(--bg-white);
border-radius: var(--border-radius-xl);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-md);
padding: var(--spacing-2xl);
text-align: center;
@ -1570,7 +1577,7 @@ body {
align-items: center;
gap: 6px;
padding: 8px 16px;
border-radius: var(--border-radius-lg);
border-radius: var(--radius-lg);
font-size: var(--font-sm);
font-weight: 500;
text-decoration: none;
@ -1679,7 +1686,7 @@ body {
flex-shrink: 0;
display: flex;
flex-direction: column;
border-radius: var(--border-radius-lg);
border-radius: var(--radius-lg);
overflow: hidden;
background: var(--bg-light);
box-shadow: var(--shadow-sm);

View File

@ -507,6 +507,10 @@
/* ========================================
다크모드
======================================== */
[data-theme="dark"] .page-header {
background: var(--dark-header-gradient);
}
[data-theme="dark"] .contact-card {
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(20px);

View File

@ -929,6 +929,10 @@
/* ========================================
다크모드
======================================== */
[data-theme="dark"] .page-header {
background: var(--dark-header-gradient);
}
[data-theme="dark"] .gallery-grid {
background: transparent;
}

View File

@ -46,6 +46,11 @@
overflow-x: hidden;
animation: popupSlideIn 0.5s cubic-bezier(0.16, 1, 0.3, 1);
z-index: 9999;
scrollbar-width: none;
}
.popup-container::-webkit-scrollbar {
display: none;
}
@keyframes popupSlideIn {
@ -195,6 +200,96 @@
margin-top: 4px;
}
/* 변경사항 카드 */
.change-card {
background: linear-gradient(135deg, #fffbf5 0%, #fff8f0 100%);
border-radius: 14px;
padding: 1rem 1.25rem;
margin-bottom: 0.75rem;
border: 1px solid rgba(255, 136, 0, 0.12);
transition: all 0.3s ease;
}
.change-card:hover {
border-color: rgba(255, 136, 0, 0.3);
box-shadow: 0 4px 16px rgba(255, 136, 0, 0.08);
transform: translateY(-2px);
}
.change-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.change-card-title {
font-size: 0.95rem;
font-weight: 700;
color: #1a1a2e;
display: flex;
align-items: center;
gap: 6px;
}
.change-card-title i {
color: var(--primary-color, #ff8800);
font-size: 0.85rem;
}
.change-badge {
padding: 3px 10px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 700;
color: white;
letter-spacing: 0.02em;
flex-shrink: 0;
}
.badge-end {
background: linear-gradient(135deg, #6b7280, #4b5563);
}
.badge-new {
background: linear-gradient(135deg, #22c55e, #16a34a);
}
.badge-change {
background: linear-gradient(135deg, #f59e0b, #d97706);
}
.change-description {
font-size: 0.85rem;
color: #555;
line-height: 1.6;
margin: 0;
}
.change-description strong {
color: var(--primary-color, #ff8800);
font-weight: 700;
}
.change-price {
display: flex;
align-items: baseline;
gap: 6px;
margin-bottom: 0.5rem;
}
.change-price-amount {
font-size: 1.15rem;
font-weight: 800;
color: #ff6600;
}
.change-price-unit {
font-size: 0.8rem;
color: #888;
font-weight: 500;
}
/* 공지사항 */
.popup-notice {
background: rgba(255, 136, 0, 0.06);
@ -445,6 +540,32 @@
color: rgba(255, 255, 255, 0.8);
}
[data-theme="dark"] .change-card {
background: rgba(255, 255, 255, 0.04);
border-color: rgba(255, 255, 255, 0.08);
}
[data-theme="dark"] .change-card:hover {
border-color: rgba(255, 136, 0, 0.3);
box-shadow: 0 4px 16px rgba(255, 136, 0, 0.1);
}
[data-theme="dark"] .change-card-title {
color: rgba(255, 255, 255, 0.9);
}
[data-theme="dark"] .change-description {
color: rgba(255, 255, 255, 0.55);
}
[data-theme="dark"] .change-price-amount {
color: #ff9933;
}
[data-theme="dark"] .change-price-unit {
color: rgba(255, 255, 255, 0.4);
}
/* ========================================
모바일 반응형
======================================== */

View File

@ -817,6 +817,10 @@
/* ========================================
다크모드
======================================== */
[data-theme="dark"] .page-header {
background: var(--dark-header-gradient);
}
[data-theme="dark"] .tab-btn {
color: var(--dark-text-secondary);
border-color: var(--glass-border);

View File

@ -419,6 +419,10 @@
/* ========================================
다크모드
======================================== */
[data-theme="dark"] .page-header {
background: var(--dark-header-gradient);
}
[data-theme="dark"] .faq-search {
background: var(--dark-surface);
border: 1px solid var(--glass-border);

View File

@ -2127,6 +2127,10 @@
/* ========================================
다크모드
======================================== */
[data-theme="dark"] .page-header {
background: var(--dark-header-gradient);
}
[data-theme="dark"] .service-package {
background-color: var(--dark-surface);
border: 1px solid var(--glass-border);

View File

@ -38,7 +38,7 @@
<!-- Open Graph -->
<meta property="og:title" content="스튜디오 갤러리 - 밍글 스튜디오">
<meta property="og:description" content="28대 OptiTrack 카메라 시스템과 8×7m 대형 촬영 공간. 인천 유일 모션캡처 스튜디오의 실제 시설과 장비를 확인해보세요">
<meta property="og:description" content="30대 OptiTrack 카메라 시스템과 8×7m 대형 촬영 공간. 인천 유일 모션캡처 스튜디오의 실제 시설과 장비를 확인해보세요">
<meta property="og:url" content="https://minglestudio.co.kr/gallery.html">
<meta property="og:type" content="website">
<meta property="og:image" content="https://minglestudio.co.kr/images/logo/mingle-OG.png">
@ -51,7 +51,7 @@
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="스튜디오 갤러리 - 밍글 스튜디오">
<meta name="twitter:description" content="28대 OptiTrack 카메라 시스템과 8×7m 대형 촬영 공간. 인천 유일 모션캡처 스튜디오의 실제 시설과 장비를 확인해보세요">
<meta name="twitter:description" content="30대 OptiTrack 카메라 시스템과 8×7m 대형 촬영 공간. 인천 유일 모션캡처 스튜디오의 실제 시설과 장비를 확인해보세요">
<meta name="twitter:image" content="https://minglestudio.co.kr/images/logo/mingle-OG.png">
<meta name="twitter:site" content="@mingle_studio">
<meta name="twitter:creator" content="@mingle_studio">
@ -87,7 +87,7 @@
<div class="container">
<div class="gallery-grid">
<div class="gallery-item">
<img src="/images/studio/넓은 모션 캡쳐 공간 002.webp" alt="밍글 스튜디오 메인 모션캡쳐 공간 - OptiTrack 카메라 28대와 넓은 8x7m 캡쳐 공간" class="gallery-img" loading="lazy">
<img src="/images/studio/넓은 모션 캡쳐 공간 002.webp" alt="밍글 스튜디오 메인 모션캡쳐 공간 - OptiTrack 카메라 30대와 넓은 8x7m 캡쳐 공간" class="gallery-img" loading="lazy">
<div class="gallery-caption">외부전경 커튼 열림</div>
</div>
<div class="gallery-item">
@ -215,7 +215,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 889 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 KiB

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 503 KiB

View File

@ -49,7 +49,7 @@
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
<!-- SEO 메타 태그 -->
<meta name="description" content="밍글 스튜디오 - 모션캡쳐 스튜디오 대관 서비스. OptiTrack 기반 28대 카메라 시스템으로 전신/페이셜 모션캡쳐 가능. 시간당 20만원(VAT 포함). 인천테크노밸리 위치.">
<meta name="description" content="밍글 스튜디오 - 모션캡쳐 스튜디오 대관 서비스. OptiTrack 기반 30대 카메라 시스템으로 전신/페이셜 모션캡쳐 가능. 시간당 20만원(VAT 포함). 인천테크노밸리 위치.">
<meta name="keywords" content="모션캡쳐, 스튜디오 대관, OptiTrack, 모션캡처, 3D 애니메이션, 버추얼 콘텐츠, 인천 스튜디오, 모션캡쳐 대관, 3D 캐릭터 애니메이션, 버튜버, VTuber">
<meta name="author" content="밍글 스튜디오">
<meta name="robots" content="index, follow">
@ -99,7 +99,7 @@
"@type": "LocalBusiness",
"name": "밍글 스튜디오",
"alternateName": ["MINGLE STUDIO", "밍글스튜디오"],
"description": "인천 모션캡쳐 전문 스튜디오 - OptiTrack 28대 카메라 시스템, 전신/페이셜 모션캡쳐 서비스",
"description": "인천 모션캡쳐 전문 스튜디오 - OptiTrack 30대 카메라 시스템, 전신/페이셜 모션캡쳐 서비스",
"url": "https://minglestudio.co.kr",
"telephone": "+82-10-9288-9190",
"email": "help@minglestudio.co.kr",
@ -135,7 +135,7 @@
"itemOffered": {
"@type": "Service",
"name": "모션캡쳐 스튜디오 대관",
"description": "OptiTrack 28대 카메라 시스템, 8m x 7m x 2.6m 공간"
"description": "OptiTrack 30대 카메라 시스템, 8m x 7m x 2.6m 공간"
},
"price": "200000",
"priceCurrency": "KRW",
@ -190,54 +190,59 @@
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<!-- 할인 이벤트 팝업 -->
<div class="popup-overlay" id="mainPopup" role="dialog" aria-label="이벤트 팝업">
<!-- 요금제 변경 안내 팝업 -->
<div class="popup-overlay" id="mainPopup" role="dialog" aria-label="요금제 변경 안내 팝업">
<div class="popup-container">
<button class="popup-close-x" id="popupCloseX" aria-label="팝업 닫기">×</button>
<div class="popup-header">
<h2 class="popup-title"><i class="fa-solid fa-gift" aria-hidden="true"></i> 밍글 스튜디오 오픈 기념 <i class="fa-solid fa-gift" aria-hidden="true"></i></h2>
<p class="popup-subtitle">특별 할인 이벤트</p>
<div class="popup-badge">최대 20% 할인</div>
<h2 class="popup-title"><i class="fa-solid fa-bullhorn" aria-hidden="true"></i> 요금제 변경 안내</h2>
<p class="popup-subtitle">2026년 3월부터 적용됩니다</p>
<div class="popup-badge">3월 시행</div>
</div>
<div class="popup-body">
<div class="popup-section">
<h3 class="popup-section-title">할인 서비스</h3>
<h3 class="popup-section-title">주요 변경사항</h3>
<div class="discount-card">
<div class="discount-card-header">
<span class="discount-card-title">모션캡쳐 스튜디오 대관</span>
<span class="discount-badge">20% 할인</span>
<div class="change-card">
<div class="change-card-header">
<span class="change-card-title"><i class="fa-solid fa-clock-rotate-left" aria-hidden="true"></i> 할인 이벤트 종료</span>
<span class="change-badge badge-end">종료</span>
</div>
<div class="discount-price">
<span class="original-price">200,000원/시간</span>
<span class="sale-price">160,000원/시간</span>
</div>
<p class="price-note">VAT 별도</p>
<p class="change-description">오픈 기념 20% 할인 이벤트가 <strong>2월 28일</strong>로 종료되며, 3월부터 정상가가 적용됩니다.</p>
</div>
<div class="discount-card">
<div class="discount-card-header">
<span class="discount-card-title">스트리밍글 서비스</span>
<span class="discount-badge">20% 할인</span>
<div class="change-card">
<div class="change-card-header">
<span class="change-card-title"><i class="fa-solid fa-plus-circle" aria-hidden="true"></i> 스트리밍글 4시간 서비스 신설</span>
<span class="change-badge badge-new">NEW</span>
</div>
<div class="discount-price">
<span class="original-price">2,000,000원</span>
<span class="sale-price">1,600,000원</span>
<div class="change-price">
<span class="change-price-amount">1,400,000원</span>
<span class="change-price-unit">/ 4시간 패키지</span>
</div>
<p class="price-note">1~4인 사용 / 6시간 패키지 / VAT 별도</p>
<p class="change-description">기존 스트리밍글 서비스(6시간)와 동일한 혜택을 4시간 패키지로 이용할 수 있습니다.</p>
</div>
<div class="change-card">
<div class="change-card-header">
<span class="change-card-title"><i class="fa-solid fa-user-plus" aria-hidden="true"></i> 추천인 제도 오픈</span>
<span class="change-badge badge-new">NEW</span>
</div>
<p class="change-description">친구를 소개하면 소개한 분, 소개받은 분 모두에게 <strong>20% 할인권</strong>을 드립니다. (1회 사용, 중복 적용 불가)</p>
</div>
</div>
<div class="popup-notice">
<p>※ 뮤직비디오 제작 서비스는 할인이 미적용 됩니다</p>
<p>변경된 요금은 3월 초 서비스 페이지에 반영 예정입니다</p>
</div>
<div class="popup-info">
<div class="info-item">
<div class="info-label">이벤트 기간</div>
<div class="info-value">~ 2026/2/28</div>
<div class="info-label">적용 시기</div>
<div class="info-value">2026년 3월~</div>
</div>
<div class="info-item">
<div class="info-label">문의 메일</div>
@ -246,7 +251,7 @@
</div>
<button class="popup-cta" id="popupCtaBtn">
지금 바로 예약하기 →
현재 서비스 요금 보기 →
</button>
</div>
@ -312,7 +317,7 @@
<!-- 스펙 카운터 -->
<div class="hero-specs">
<div class="spec-item">
<span class="spec-number"><span class="counter" data-target="28">0</span><span class="spec-unit"></span></span>
<span class="spec-number"><span class="counter" data-target="30">0</span><span class="spec-unit"></span></span>
<span class="spec-label">OptiTrack 카메라</span>
</div>
<div class="spec-item">
@ -387,7 +392,7 @@
<div class="showcase-feature-item">
<span class="showcase-feature-icon"><i class="fa-solid fa-clapperboard" aria-hidden="true"></i></span>
<div>
<strong>OptiTrack 카메라 28</strong>
<strong>OptiTrack 카메라 30</strong>
<span>서브밀리미터 수준 광학식 모션 트래킹</span>
</div>
</div>
@ -686,45 +691,39 @@
<div class="signs-marquee">
<div class="signs-marquee-track">
<!-- 원본 12개 -->
<div class="sign-item"><img loading="lazy" src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/얌하.webp" alt="얌하 사인"><span class="sign-name">얌하</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/구슬요.webp" alt="구슬요 사인"><span class="sign-name">구슬요</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
<!-- 원본 10개 -->
<div class="sign-item"><img src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
<div class="sign-item"><img src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
<div class="sign-item"><img src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
<div class="sign-item"><img src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
<div class="sign-item"><img src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
<div class="sign-item"><img src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
<div class="sign-item"><img src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
<div class="sign-item"><img src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
<div class="sign-item"><img src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
<div class="sign-item"><img src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
<!-- 복제 1 -->
<div class="sign-item"><img loading="lazy" src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/얌하.webp" alt="얌하 사인"><span class="sign-name">얌하</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/구슬요.webp" alt="구슬요 사인"><span class="sign-name">구슬요</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
<div class="sign-item"><img src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
<div class="sign-item"><img src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
<div class="sign-item"><img src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
<div class="sign-item"><img src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
<div class="sign-item"><img src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
<div class="sign-item"><img src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
<div class="sign-item"><img src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
<div class="sign-item"><img src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
<div class="sign-item"><img src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
<div class="sign-item"><img src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
<!-- 복제 2 -->
<div class="sign-item"><img loading="lazy" src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/얌하.webp" alt="얌하 사인"><span class="sign-name">얌하</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/구슬요.webp" alt="구슬요 사인"><span class="sign-name">구슬요</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
<div class="sign-item"><img loading="lazy" src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
<div class="sign-item"><img src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
<div class="sign-item"><img src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
<div class="sign-item"><img src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
<div class="sign-item"><img src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
<div class="sign-item"><img src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
<div class="sign-item"><img src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
<div class="sign-item"><img src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
<div class="sign-item"><img src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
<div class="sign-item"><img src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
<div class="sign-item"><img src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
</div>
</div>
</div>
@ -741,13 +740,13 @@
<!-- 클라이언트 로고 마키 -->
<div class="partners-marquee">
<div class="partners-marquee-track">
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/메가메타.jpg" alt="메가메타 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
<div class="partner-logo-item"><img src="/images/partners/메가메타.jpg" alt="메가메타 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
<div class="partner-logo-item"><img src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
<!-- 무한 루프용 복제 -->
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
<div class="partner-logo-item"><img src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
<div class="partner-logo-item"><img src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
<div class="partner-logo-item"><img src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
<div class="partner-logo-item"><img src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
</div>
</div>
</div>
@ -845,7 +844,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

View File

@ -107,8 +107,6 @@
* 이벤트 리스너 설정
*/
function setupEventListeners() {
if (!elements.searchInput || !elements.imageModal || !elements.filterTags) return;
// 검색
elements.searchInput.addEventListener('input', debounce((e) => {
searchQuery = e.target.value.toLowerCase();

View File

@ -35,14 +35,12 @@ async function loadComponents() {
if (headerPlaceholder) {
try {
const response = await fetch('components/header.html');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const html = await response.text();
headerPlaceholder.innerHTML = html;
initializeNavigation(); // 헤더 로드 후 네비게이션 초기화
initThemeToggle(); // 테마 토글 초기화
} catch (error) {
console.error('Error loading header:', error);
headerPlaceholder.innerHTML = '<nav class="navbar"><div class="nav-container"><a href="/" class="nav-logo"><span>밍글 스튜디오</span></a></div></nav>';
}
}
@ -51,18 +49,11 @@ async function loadComponents() {
if (footerPlaceholder) {
try {
const response = await fetch('components/footer.html');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const html = await response.text();
footerPlaceholder.innerHTML = html;
// 동적 푸터의 저작권 연도 자동 업데이트
const footerYear = footerPlaceholder.querySelector('.footer-bottom p');
if (footerYear) {
footerYear.innerHTML = `&copy; ${new Date().getFullYear()} 밍글 스튜디오. All rights reserved.`;
}
// 동적 푸터 로드 성공 시 백업 푸터 숨기기
const backupFooter = document.querySelector('footer.site-footer#backupFooter');
const backupFooter = document.querySelector('footer.site-footer');
if (backupFooter) {
backupFooter.style.display = 'none';
}
@ -76,15 +67,11 @@ async function loadComponents() {
// ========================================
// 네비게이션 기능
// ========================================
let _navInitialized = false;
let _scrollListenerAdded = false;
function initializeNavigation() {
const hamburger = document.querySelector('.hamburger');
const navMenu = document.querySelector('.nav-menu');
if (hamburger && navMenu && !hamburger._listenerAdded) {
hamburger._listenerAdded = true;
if (hamburger && navMenu) {
hamburger.addEventListener('click', function() {
const isActive = hamburger.classList.toggle('active');
navMenu.classList.toggle('active');
@ -103,14 +90,12 @@ function initializeNavigation() {
});
}
// 스크롤 시 네비게이션 바 스타일 변경 (RAF 최적화) - 한 번만 등록
if (!_scrollListenerAdded) {
_scrollListenerAdded = true;
// 스크롤 시 네비게이션 바 스타일 변경 (RAF 최적화)
let scrollTicking = false;
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', () => {
if (!scrollTicking) {
requestAnimationFrame(() => {
const navbar = document.querySelector('.navbar');
if (navbar) {
navbar.classList.toggle('scrolled', window.pageYOffset > 20);
}
@ -118,8 +103,7 @@ function initializeNavigation() {
});
scrollTicking = true;
}
}, { passive: true });
}
});
}
// ========================================
@ -308,6 +292,9 @@ function initLazyLoading() {
}
}
// ========================================
// Export 함수들 (다른 스크립트에서 사용 가능)
// ========================================
// ========================================
// 로딩 상태 관리
// ========================================
@ -340,17 +327,12 @@ function hidePageLoading() {
}
function showComponentLoading(element, text = '로딩 중...') {
const wrapper = document.createElement('div');
wrapper.className = 'component-loading';
const spinner = document.createElement('div');
spinner.className = 'loading-spinner';
const label = document.createElement('div');
label.className = 'loading-text';
label.textContent = text;
wrapper.appendChild(spinner);
wrapper.appendChild(label);
element.innerHTML = '';
element.appendChild(wrapper);
element.innerHTML = `
<div class="component-loading">
<div class="loading-spinner"></div>
<div class="loading-text">${text}</div>
</div>
`;
}
function hideComponentLoading(element, content) {

View File

@ -79,18 +79,16 @@ async function handleFormSubmit(e) {
// 서버 전송 (mailto 기반 폴백)
async function submitContactForm(data) {
// 입력값에서 줄바꿈/캐리지리턴 제거 (이메일 헤더 인젝션 방지)
const sanitize = (str) => (str || '').replace(/[\r\n]/g, ' ').trim();
const subject = encodeURIComponent(`[밍글 스튜디오 문의] ${sanitize(data.name) || '웹사이트 문의'}`);
// mailto 링크로 이메일 클라이언트 열기
const subject = encodeURIComponent(`[밍글 스튜디오 문의] ${data.name || '웹사이트 문의'}`);
const body = encodeURIComponent(
`이름: ${sanitize(data.name)}\n` +
`이메일: ${sanitize(data.email)}\n` +
`전화번호: ${sanitize(data.phone)}\n` +
`문의 유형: ${sanitize(data.service)}\n` +
`\n문의 내용:\n${(data.message || '').trim()}`
`이름: ${data.name || ''}\n` +
`이메일: ${data.email || ''}\n` +
`전화번호: ${data.phone || ''}\n` +
`문의 유형: ${data.service || ''}\n` +
`\n문의 내용:\n${data.message || ''}`
);
window.location.href = `mailto:help@minglestudio.co.kr?subject=${subject}&body=${body}`;
window.location.href = `mailto:mingle_studio@naver.com?subject=${subject}&body=${body}`;
return { success: true, message: '이메일 클라이언트가 열렸습니다.' };
}
@ -209,11 +207,6 @@ function isValidPhone(phone) {
function formatPhoneNumber(e) {
let value = e.target.value.replace(/[^0-9]/g, '');
// 최대 11자리 제한 (한국 휴대폰 번호)
if (value.length > 11) {
value = value.slice(0, 11);
}
if (value.length >= 11) {
// 01X-XXXX-XXXX 형태
value = value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');

View File

@ -13,24 +13,8 @@ document.addEventListener('DOMContentLoaded', function() {
function initGallery() {
const galleryItems = document.querySelectorAll('.gallery-item');
// 레이지 로딩용 옵저버를 하나만 생성 (이미지마다 생성하지 않음)
let imageObserver = null;
if ('IntersectionObserver' in window) {
imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src || img.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
}
galleryItems.forEach((item, index) => {
const img = item.querySelector('.gallery-img');
if (!img) return;
// 이미지 클릭 시 라이트박스 열기
img.addEventListener('click', () => openLightbox(index));
@ -42,20 +26,30 @@ function initGallery() {
});
// 레이지 로딩 구현
if (imageObserver && img.dataset.src) {
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src || img.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
if (img.dataset.src) {
imageObserver.observe(img);
}
}
});
}
// 라이트박스 기능
let currentImageIndex = 0;
let galleryImages = [];
const galleryImages = document.querySelectorAll('.gallery-img');
function initLightbox() {
// DOM 준비 후 갤러리 이미지 수집
galleryImages = document.querySelectorAll('.gallery-img');
// 라이트박스 HTML 생성
const lightboxHTML = `
<div id="lightbox" class="lightbox" role="dialog" aria-label="이미지 뷰어">
@ -71,10 +65,8 @@ function initLightbox() {
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
// ESC 키로 라이트박스 닫기 (라이트박스가 열려 있을 때만)
// ESC 키로 라이트박스 닫기
document.addEventListener('keydown', function(e) {
const lightbox = document.getElementById('lightbox');
if (!lightbox || !lightbox.classList.contains('active')) return;
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowLeft') previousImage();
if (e.key === 'ArrowRight') nextImage();
@ -166,27 +158,113 @@ function initGalleryAnimations() {
});
}
// 터치 이벤트 지원 (모바일) - 라이트박스에만 스코프 제한
// 리스너는 한 번만 등록하고, 라이트박스 활성 상태에서만 동작
let lightboxTouchStartX = 0;
let lightboxTouchListenersAdded = false;
// 갤러리 필터 기능 (향후 확장용)
function initGalleryFilters() {
const filterButtons = document.querySelectorAll('.filter-btn');
const galleryItems = document.querySelectorAll('.gallery-item');
function setupLightboxTouchListeners() {
if (lightboxTouchListenersAdded) return;
const lightbox = document.getElementById('lightbox');
if (!lightbox) return;
lightboxTouchListenersAdded = true;
filterButtons.forEach(btn => {
btn.addEventListener('click', function() {
// 활성 버튼 업데이트
filterButtons.forEach(b => b.classList.remove('active'));
this.classList.add('active');
lightbox.addEventListener('touchstart', function(e) {
if (!lightbox.classList.contains('active')) return;
lightboxTouchStartX = e.changedTouches[0].screenX;
const filter = this.dataset.filter;
galleryItems.forEach(item => {
if (filter === 'all' || item.dataset.category === filter) {
item.style.display = 'block';
setTimeout(() => {
item.style.opacity = '1';
item.style.transform = 'scale(1)';
}, 10);
} else {
item.style.opacity = '0';
item.style.transform = 'scale(0.8)';
setTimeout(() => {
item.style.display = 'none';
}, 300);
}
});
});
});
}
// 이미지 프리로딩
function preloadImages() {
galleryImages.forEach(img => {
const imagePreload = new Image();
imagePreload.src = img.src;
});
}
// 갤러리 검색 기능 (향후 확장용)
function initGallerySearch() {
const searchInput = document.getElementById('gallery-search');
const galleryItems = document.querySelectorAll('.gallery-item');
if (searchInput) {
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
galleryItems.forEach(item => {
const caption = item.querySelector('.gallery-caption');
const alt = item.querySelector('.gallery-img').alt;
const text = (caption ? caption.textContent : '') + ' ' + alt;
if (text.toLowerCase().includes(searchTerm)) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
}
}
// 이미지 로딩 상태 표시
function showGalleryLoading() {
const loading = document.querySelector('.gallery-loading');
if (loading) {
loading.style.display = 'block';
}
}
function hideGalleryLoading() {
const loading = document.querySelector('.gallery-loading');
if (loading) {
loading.style.display = 'none';
}
}
// 갤러리 그리드 리사이즈 최적화
let resizeTimeout;
window.addEventListener('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
// 갤러리 그리드 재조정 로직
const galleryGrid = document.querySelector('.gallery-grid');
if (galleryGrid) {
galleryGrid.style.opacity = '0.8';
setTimeout(() => {
galleryGrid.style.opacity = '1';
}, 100);
}
}, 250);
});
lightbox.addEventListener('touchend', function(e) {
if (!lightbox.classList.contains('active')) return;
const touchEndX = e.changedTouches[0].screenX;
// 터치 이벤트 지원 (모바일) - 라이트박스에만 스코프 제한
let touchStartX = 0;
let touchEndX = 0;
function handleLightboxTouchStart(e) {
touchStartX = e.changedTouches[0].screenX;
}
function handleLightboxTouchEnd(e) {
touchEndX = e.changedTouches[0].screenX;
const swipeThreshold = 50;
const diff = lightboxTouchStartX - touchEndX;
const diff = touchStartX - touchEndX;
if (Math.abs(diff) > swipeThreshold) {
if (diff > 0) {
@ -195,16 +273,28 @@ function setupLightboxTouchListeners() {
previousImage();
}
}
});
}
// openLightbox 래핑: 터치 리스너 초기화 보장
// openLightbox/closeLightbox에서 터치 리스너 관리를 위해 원본 함수 래핑
const _originalOpenLightbox = openLightbox;
openLightbox = function(index) {
_originalOpenLightbox(index);
setupLightboxTouchListeners();
const lightbox = document.getElementById('lightbox');
if (lightbox) {
lightbox.addEventListener('touchstart', handleLightboxTouchStart);
lightbox.addEventListener('touchend', handleLightboxTouchEnd);
}
};
const _originalCloseLightbox = closeLightbox;
closeLightbox = function() {
const lightbox = document.getElementById('lightbox');
if (lightbox) {
lightbox.removeEventListener('touchstart', handleLightboxTouchStart);
lightbox.removeEventListener('touchend', handleLightboxTouchEnd);
}
_originalCloseLightbox();
};
// ========================================
// 간단한 360도 파노라마 뷰어 - 좌우 스크롤 방식
@ -288,7 +378,8 @@ class Easy360Viewer {
user-select: none;
pointer-events: none;
display: block;
image-rendering: auto;
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
filter: contrast(1.05) saturate(1.1);
`;
this.image.draggable = false;
@ -317,23 +408,18 @@ class Easy360Viewer {
// 컨테이너 크기 가져오기
const containerHeight = this.container.clientHeight;
const containerWidth = this.container.clientWidth;
const naturalW = this.image.naturalWidth;
const naturalH = this.image.naturalHeight;
const imageAspectRatio = naturalW / naturalH;
const imageAspectRatio = this.image.naturalWidth / this.image.naturalHeight;
// 컨테이너 높이에 맞추되, 원본 해상도를 초과하지 않도록 제한
let imageHeight = Math.min(containerHeight * this.zoom, naturalH);
// 모바일에서 줌 시 경계 문제 해결을 위한 더 정확한 크기 계산
let imageHeight = containerHeight * this.zoom;
let imageWidth = imageHeight * imageAspectRatio;
// 최소 너비 보장: 컨테이너 1.5배 (3배까지 늘리면 흐릿해짐)
// 단, 원본 너비를 초과하지 않도록 제한
const minWidth = Math.min(containerWidth * 1.5, naturalW);
if (imageWidth < minWidth) {
// 360도 이미지는 매우 가로가 길므로 최소 크기 보장
const minWidth = Math.max(containerWidth * 3, imageWidth);
imageWidth = minWidth;
imageHeight = imageWidth / imageAspectRatio;
}
// 줌 레벨에 따른 크기 조정
// 줌 레벨에 따른 크기 조정 (모바일 확대 시 경계 문제 해결)
if (this.zoom > 1) {
imageHeight = Math.max(containerHeight, imageHeight);
imageWidth = imageHeight * imageAspectRatio;

View File

@ -81,7 +81,7 @@ function initParallaxImages() {
});
ticking = true;
}
}, { passive: true });
});
}
// ========================================
@ -107,7 +107,7 @@ function initHeroScrollFade() {
});
ticking = true;
}
}, { passive: true });
});
}
// ========================================
@ -315,9 +315,8 @@ function initPortfolioTabs() {
activePanel.classList.add('active');
// 탭 전환 시 lazy loading 트리거
activePanel.querySelectorAll('iframe[data-src]').forEach(iframe => {
if (iframe.dataset.src) {
if (!iframe.src) {
iframe.src = iframe.dataset.src;
iframe.removeAttribute('data-src');
}
});
}

View File

@ -41,7 +41,7 @@ function closePopup() {
// "하루동안 보지 않기" 체크된 경우
if (dontShowToday && dontShowToday.checked) {
setCookie('hideMainPopup', 'true', 1); // 1일간 쿠키 저장
setCookie('hidePriceChangePopup', 'true', 1); // 1일간 쿠키 저장
}
}
}
@ -49,7 +49,7 @@ function closePopup() {
// 페이지 로드 시 실행
document.addEventListener('DOMContentLoaded', function() {
// 쿠키 확인
const hidePopup = getCookie('hideMainPopup');
const hidePopup = getCookie('hidePriceChangePopup');
// 쿠키가 없으면 팝업 표시 (1초 후)
if (!hidePopup) {
@ -80,8 +80,7 @@ document.addEventListener('DOMContentLoaded', function() {
// ESC 키로 닫기
document.addEventListener('keydown', function(e) {
const popup = document.getElementById('mainPopup');
if (e.key === 'Escape' && popup && popup.classList.contains('active')) {
if (e.key === 'Escape') {
closePopup();
}
});
@ -91,7 +90,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (ctaBtn) {
ctaBtn.addEventListener('click', function() {
closePopup();
window.location.href = '/contact';
window.location.href = '/services';
});
}
});

View File

@ -323,8 +323,6 @@ function initYouTubePlayers() {
}
});
// pauseOtherVideos에서 사용할 수 있도록 전역에 저장
window.youtubePlayers = players;
return players;
}
@ -337,8 +335,7 @@ function extractVideoId(url) {
// 플레이어 준비 완료
function onPlayerReady(event) {
// 플레이어 컨테이너에 로딩 완료 클래스 추가
const playerElement = event.target.getIframe?.();
if (!playerElement) return;
const playerElement = event.target.getIframe();
const wrapper = playerElement.closest('.video-wrapper');
if (wrapper) {
wrapper.classList.add('loaded');
@ -351,8 +348,7 @@ function onPlayerReady(event) {
// 플레이어 상태 변경
function onPlayerStateChange(event) {
const playerElement = event.target.getIframe?.();
if (!playerElement) return;
const playerElement = event.target.getIframe();
const videoCard = playerElement.closest('.video-card');
if (event.data === YT.PlayerState.PLAYING) {
@ -378,19 +374,13 @@ function onPlayerStateChange(event) {
// 플레이어 오류 처리
function onPlayerError(event) {
console.error('YouTube player error:', event.data);
const playerElement = event.target.getIframe?.();
if (!playerElement) return;
const playerElement = event.target.getIframe();
const wrapper = playerElement.closest('.video-wrapper');
if (wrapper) {
const errorDiv = document.createElement('div');
errorDiv.className = 'video-error';
const mainText = document.createElement('div');
mainText.textContent = '비디오를 로드할 수 없습니다';
const subText = document.createElement('small');
subText.textContent = '네트워크 연결을 확인해주세요';
errorDiv.appendChild(mainText);
errorDiv.appendChild(subText);
errorDiv.innerHTML = '비디오를 로드할 수 없습니다<br><small>네트워크 연결을 확인해주세요</small>';
errorDiv.style.cssText = `
position: absolute;
top: 50%;

View File

@ -74,8 +74,6 @@
* 이벤트 리스너 설정
*/
function setupEventListeners() {
if (!elements.searchInput || !elements.imageModal) return;
// 검색
elements.searchInput.addEventListener('input', debounce((e) => {
searchQuery = e.target.value.toLowerCase();

View File

@ -143,9 +143,7 @@ function handleSearch(query) {
return;
}
// 검색어 이스케이프 및 g 플래그 없이 생성 (.test()의 lastIndex 문제 방지)
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const searchRegex = new RegExp(escapedQuery, 'i');
const searchRegex = new RegExp(query, 'gi');
faqItems.forEach(item => {
const question = item.querySelector('.faq-question h3');
@ -164,8 +162,8 @@ function handleSearch(query) {
// 검색어 하이라이트
highlightSearchTerm(item, query);
// 답변에 매칭되는 경우 자동으로 열기 (이미 열려 있으면 무시)
if (answerMatch && !questionMatch && !item.classList.contains('active')) {
// 답변에 매칭되는 경우 자동으로 열기
if (answerMatch && !questionMatch) {
toggleFAQ(item);
}
} else {
@ -305,6 +303,7 @@ function hideSuggestions() {
// 검색 결과 없음 표시
function showNoResults(query) {
const safeQuery = query.replace(/[<>&"]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'})[c]);
let noResults = document.querySelector('.no-results');
if (!noResults) {
noResults = document.createElement('div');
@ -312,13 +311,13 @@ function showNoResults(query) {
noResults.innerHTML = `
<div class="no-results-icon">🔍</div>
<h3>검색 결과가 없습니다</h3>
<p><strong></strong> .</p>
<p><strong>"${safeQuery}"</strong> .</p>
<p>다른 키워드로 검색해보시거나 <a href="contact.html">직접 문의</a> .</p>
`;
document.querySelector('.faq-list').appendChild(noResults);
}
// textContent로 안전하게 삽입 (XSS 방지)
} else {
noResults.querySelector('p strong').textContent = `"${query}"`;
}
noResults.classList.add('active');
}

View File

@ -659,7 +659,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

View File

@ -256,7 +256,7 @@
<div class="faq-answer">
<p>밍글 스튜디오는 다음과 같은 전문 장비를 보유하고 있습니다:</p>
<ul>
<li><strong>OptiTrack 카메라:</strong> 28</li>
<li><strong>OptiTrack 카메라:</strong> 30</li>
<li><strong>핸드 트래킹:</strong> 로코코 스마트 글러브 사용중</li>
</ul>
</div>
@ -446,7 +446,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>

View File

@ -32,13 +32,13 @@
<meta name="theme-color" content="#ff8800">
<!-- SEO 메타 태그 -->
<meta name="description" content="밍글 스튜디오 서비스 소개 - OptiTrack 모션캡쳐 스튜디오 대관, 시간당 22만원, 전신/페이셜 캡쳐, 실시간 스트리밍 지원">
<meta name="keywords" content="모션캡쳐 대관, OptiTrack, 스튜디오 요금, 버튜버 방송, 실시간 스트리밍">
<meta name="description" content="밍글 스튜디오 서비스 소개 - OptiTrack 모션캡쳐 스튜디오 대관, 1인 120,000원~/시간, 전신/페이셜 캡쳐, 모션 녹화 전문">
<meta name="keywords" content="모션캡쳐 대관, OptiTrack, 스튜디오 요금, 모션 녹화, 모션캡쳐 스튜디오">
<meta name="author" content="밍글 스튜디오">
<!-- Open Graph -->
<meta property="og:title" content="서비스 소개 - 밍글 스튜디오">
<meta property="og:description" content="28대 OptiTrack 카메라로 전신/페이셜 모션캡처, 1인 150,000원, 2인 200,000원. 스트리밍글 서비스 2,000,000원으로 6시간 풀패키지 제공">
<meta property="og:description" content="30대 OptiTrack 카메라로 전신/페이셜 모션캡처, 1인 120,000원~/시간, 2인 160,000원~/시간. 스트리밍글 서비스 1,600,000원으로 6시간 풀패키지 제공">
<meta property="og:url" content="https://minglestudio.co.kr/services.html">
<meta property="og:type" content="website">
<meta property="og:image" content="https://minglestudio.co.kr/images/logo/mingle-OG.png">
@ -46,12 +46,12 @@
<meta property="og:image:height" content="630">
<meta property="og:locale" content="ko_KR">
<meta property="og:site_name" content="밍글 스튜디오">
<meta property="og:image:alt" content="밍글 스튜디오 - 모션캡처 서비스 요금안내">
<meta property="og:image:alt" content="밍글 스튜디오 - 모션캡처 녹화 서비스 요금안내">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="서비스 소개 - 밍글 스튜디오">
<meta name="twitter:description" content="28대 OptiTrack 카메라로 전신/페이셜 모션캡처, 1인 150,000원, 2인 200,000원. 스트리밍글 서비스 2,000,000원으로 6시간 풀패키지 제공">
<meta name="twitter:description" content="30대 OptiTrack 카메라로 전신/페이셜 모션캡처, 1인 120,000원~/시간, 2인 160,000원~/시간. 스트리밍글 서비스 1,600,000원으로 6시간 풀패키지 제공">
<meta name="twitter:image" content="https://minglestudio.co.kr/images/logo/mingle-OG.png">
<meta name="twitter:site" content="@mingle_studio">
<meta name="twitter:creator" content="@mingle_studio">
@ -90,15 +90,15 @@
<div class="container">
<div class="section-header">
<h2>서비스 패키지</h2>
<p>용도와 규모에 맞는 최적의 모션캡처 서비스를 제공합니다</p>
<p>용도와 규모에 맞는 최적의 모션캡처 녹화 서비스를 제공합니다</p>
<p class="vat-notice-inline">※ 모든 가격은 부가세 별도입니다</p>
</div>
<!-- 서비스 1: 모션캡처 서비스 -->
<!-- 서비스 1: 모션캡처 녹화 서비스 -->
<div class="service-package">
<div class="package-header">
<span class="package-icon"><i class="fa-solid fa-bullseye" aria-hidden="true"></i></span>
<h3>서비스 1: 모션캡처 서비스</h3>
<h3>서비스 1: 모션캡처 녹화 서비스</h3>
<span class="package-badge">기본형</span>
</div>
@ -118,9 +118,9 @@
</div>
<div class="card-features">
<ul>
<li>1인 모션 녹화</li>
<li>개인 프로젝트 최적화</li>
<li>VTuber 방송 준비</li>
<li>1인 콘텐츠 제작</li>
<li>단독 캐릭터 모션 수록</li>
</ul>
</div>
</div>
@ -139,9 +139,9 @@
</div>
<div class="card-features">
<ul>
<li>합방 콘텐츠 제작</li>
<li>상호작용 연출</li>
<li>팀 프로젝트</li>
<li>2인 동시 모션 녹화</li>
<li>캐릭터 간 인터랙션 수록</li>
<li>팀 프로젝트 협업</li>
</ul>
</div>
</div>
@ -159,9 +159,9 @@
</div>
<div class="card-features">
<ul>
<li>최대 4인까지</li>
<li>대규모 콘텐츠</li>
<li>그룹 프로젝트</li>
<li>최대 4인 동시 녹화</li>
<li>다인원 모션 캡쳐</li>
<li>그룹 안무·연기 수록</li>
</ul>
</div>
</div>
@ -183,7 +183,7 @@
<div class="tech-specs-compact">
<h5>제공 기술 및 서비스</h5>
<div class="specs-list">
<div class="spec-tag"><span><i class="fa-solid fa-video" aria-hidden="true"></i></span> OptiTrack 28대 카메라</div>
<div class="spec-tag"><span><i class="fa-solid fa-video" aria-hidden="true"></i></span> OptiTrack 30대 카메라</div>
<div class="spec-tag"><span><i class="fa-solid fa-robot" aria-hidden="true"></i></span> 실시간 아바타 녹화</div>
<div class="spec-tag"><span><i class="fa-solid fa-user" aria-hidden="true"></i></span> 전신/페이셜 캡처</div>
<div class="spec-tag"><span><i class="fa-solid fa-chart-bar" aria-hidden="true"></i></span> 실시간 모니터링</div>
@ -276,7 +276,7 @@
<li>1인당 1벌 캐릭터 무료 세팅</li>
<li>기존 보유 배경 중 2개 무료 세팅</li>
<li>프랍 무료 세팅 (신규 프랍 최대 6개, 보유 프랍 무제한)</li>
<li>모션캡처 서비스</li>
<li>모션캡처 녹화 서비스</li>
<li>실시간 영상 촬영</li>
<li><strong>라이브 방송 서비스</strong></li>
</ul>
@ -653,7 +653,7 @@
</div>
<div class="email-send">
<a href="mailto:help@minglestudio.co.kr?subject=[스튜디오 예약 문의] 밍글 스튜디오&body=안녕하세요. 밍글 스튜디오 대관 예약 문의 드립니다.%0A%0A■ 기본 정보%0A- 이용 예정일: (예: 2024년 12월 25일)%0A- 이용 시간: (예: 오후 2시 ~ 4시, 2시간)%0A- 참여 인원: 명%0A- 연락처: %0A- 이메일: %0A%0A■ 촬영 정보%0A- 촬영 목적: (예: 버튜버 방송, 게임 제작, 영상 콘텐츠 등)%0A- 예상 촬영 내용: %0A- 필요한 장비: (전신 모션캡쳐 / 페이셜 캡쳐 / 기타)%0A%0A■ 추가 요청사항%0A- 특별한 요구사항: %0A- 문의사항: %0A%0A■ 참고사항%0A- 최소 대관: 2시간 (220,000원, VAT 포함)%0A- 연장: 1시간당 110,000원%0A- 결제방법: 계좌이체 또는 현금 (카드결제 불가)%0A- 현금영수증/세금계산서 발행 가능%0A%0A빠른 시일 내에 답변 드리겠습니다.%0A감사합니다." class="btn btn-primary">
<a href="mailto:help@minglestudio.co.kr?subject=[스튜디오 예약 문의] 밍글 스튜디오&body=안녕하세요. 밍글 스튜디오 대관 예약 문의 드립니다.%0A%0A■ 기본 정보%0A- 이용 예정일: (예: 2024년 12월 25일)%0A- 이용 시간: (예: 오후 2시 ~ 4시, 2시간)%0A- 참여 인원: 명%0A- 연락처: %0A- 이메일: %0A%0A■ 촬영 정보%0A- 촬영 목적: (예: 모션캡처 녹화, 게임 제작, 뮤직비디오 제작 등)%0A- 예상 촬영 내용: %0A- 필요한 장비: (전신 모션캡쳐 / 페이셜 캡쳐 / 기타)%0A%0A■ 추가 요청사항%0A- 특별한 요구사항: %0A- 문의사항: %0A%0A■ 참고사항%0A- 최소 대관: 2시간 (1인 120,000원~/시간, VAT 별도)%0A- 인원 추가: 2인 160,000원~/시간, 추가 인원 +80,000원~/명/시간%0A- 결제방법: 계좌이체 또는 현금 (카드결제 불가)%0A- 현금영수증/세금계산서 발행 가능%0A%0A빠른 시일 내에 답변 드리겠습니다.%0A감사합니다." class="btn btn-primary">
<i class="fa-solid fa-envelope" aria-hidden="true"></i> 이메일 바로 보내기
</a>
</div>
@ -760,7 +760,7 @@
</div>
</div>
<div class="footer-bottom">
<p>&copy; <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
<p>&copy; 2025 밍글 스튜디오. All rights reserved.</p>
</div>
</div>
</footer>