@charset "utf-8";
/* [index02 전용 사본] 원본 : /df/kwaa/visual/css/visual.css (2026-09-10 복사)
   index02.html 만 이 파일을 쓴다. index.html 쪽 원본을 고쳐도 여기엔 반영되지 않는다(반대도 같다). */
/* ============================================================================
   visual.css  —  메인 비주얼(GLB Dot Form) 컨테이너 스타일
   df/test/bust-dots-demo/style.css 를 .visual_con 안으로 스코프해서 옮긴 것.

   원본은 position:fixed 로 화면 전체를 덮는 랩 페이지였으나,
   여기서는 .visual_con 안에서만 동작하도록 전부 컨테이너 기준으로 바꿨다.
   ( html,body { overflow:hidden } 같은 전역 규칙도 전부 제거 — 페이지가 스크롤돼야 하므로 )

   2026-09-08 변경 — 슬라이드 배경으로 편입
     · .visual_con 이 .visual_slide_con(메인 슬라이드) 안쪽 첫 자식으로 들어갔다.
     · 높이를 100vh 로 따로 잡던 것을 폐기하고 absolute inset:0 으로 슬라이드 박스에 묶었다.
       → 리사이징하면 슬라이드와 완전히 같은 비율로 같이 줄고 는다.
     · 순수 배경으로 쓰기로 해서 pointer-events:none. 스와이프·클릭은 전부 슬라이드가 받는다.
   ============================================================================ */

/* ---- 스태킹 : 배경(비주얼) 아래, 슬라이드 위 ------------------------------
   .visual_slide_con 은 main_style.css 에서 position 지정이 없다.
   배경 레이어의 기준 박스가 되어야 하므로 여기서 relative 를 준다.
   ※ main_style.css 를 건드리지 않기 위해 이 파일에서 잡는다.
      (visual.css 는 header.html 의 CSS 들보다 뒤에 로드되므로 동일 특이도에서 이긴다) */
#main_con > .visual_slide_con {
	position: relative;
	z-index: 0;
}
#main_con > .visual_slide_con > .swiper-container {
	z-index: 1;                 /* 슬라이드 콘텐츠가 배경 위에 온다 */
	background-color: transparent;
}

/* ---- 컨테이너 : 슬라이드 박스에 그대로 묶는다 -----------------------------
   높이를 따로 정하지 않는다. inset:0 이라 .visual_slide_con 의 실측 높이를
   그대로 따라가고, 그 높이는 슬라이드 .contents_con 이 만든다.

     데스크탑  .contents_con  min(1820px,94.79vw) x min(1080px,56.25vw)  = 1820:1080 고정
     모바일    .contents_con  88.54vw x 200vw                            =  850:1920 고정
       (main_style.css · main_common_all.css)

   즉 브레이크포인트마다 값을 따로 적지 않아도 배경이 슬라이드와 같은 비율로
   리사이징된다. 카메라의 세로 화각이 고정이므로 컨테이너 높이에 비례해
   모델 크기도 같이 변한다 — 슬라이드 텍스트가 vw 로 커지고 줄는 것과 정확히 같은 배율이다.
   (모바일은 박스가 세로로 길어 모델이 잘리므로 visual.js 에서 스케일을 보정한다) */
#main_con .visual_con {
	z-index: 0;
	background-color: #000000;
	overflow: hidden;
	/* 순수 배경 — 마우스/터치는 전부 통과시켜 슬라이드(Swiper 스와이프·버튼)가 받는다 */
	pointer-events: none;
	-webkit-user-select: none;
	-ms-user-select: none;
	user-select: none;
}

/* [A] 슬라이드 배경으로 쓰일 때 (index.html) --------------------------------
   .visual_slide_con 안에 들어가 있는 경우에만 슬라이드 박스에 묶는다. */
#main_con > .visual_slide_con > .visual_con {
	position: absolute;
	top: 0; right: 0; bottom: 0; left: 0;
}

/* [B] 단독으로 쓰일 때 (index_tmp.html 등) ----------------------------------
   #main_con 바로 아래에 홀로 놓인 경우다. 이때는 위 [A] 를 쓰면 안 된다 —
   #main_con 이 static 이라 absolute 가 body 기준으로 붙어버리고,
   문서 흐름에서 빠지는 바람에 #container 높이가 0 이 돼 푸터가 위로 올라온다.
   (2026-09-08 실측으로 확인하고 되돌린 부분)
   그래서 단독일 때는 원래대로 흐름 안에서 자기 높이를 갖는다. */
#main_con > .visual_con {
	position: relative;
	top: 0; left: 0;
	width: 100%;
	height: 100vh;
	min-height: 560px;
	max-height: 1080px;
}

/* ---- WebGL 캔버스 --------------------------------------------------------
   크기는 JS(ResizeObserver)가 컨테이너 실측값으로 잡는다.
   여기서는 레이아웃상 자리만 채워둔다. */
#main_con .visual_con .visual_canvas {
	display: block;
	position: absolute;
	top: 0; left: 0;
	width: 100%;
	height: 100%;
	z-index: 1;
	/* 배경 전용이라 캔버스는 입력을 받지 않는다. 컨테이너의 pointer-events:none 을
	   그대로 물려받지만, OrbitControls 가 domElement 에 인라인 스타일을 넣던 흔적이
	   남지 않도록 여기서도 명시해 둔다. (visual.js 는 INTERACTIVE=false 로 돈다) */
	pointer-events: none;
	touch-action: auto;
}

/* ---- 로딩 오버레이 -------------------------------------------------------
   GLB 3개(약 45MB) + 대륙 GeoJSON 을 받는 동안만 보인다. 로드가 끝나면 .hidden. */
#main_con .visual_con .visual_overlay {
	position: absolute;
	top: 0; left: 0; right: 0; bottom: 0;
	z-index: 5;
	display: flex;
	align-items: center;
	justify-content: center;
	color: rgba(255,255,255,0.55);
	text-align: center;
	pointer-events: none;
	transition: opacity 0.4s ease;
}
#main_con .visual_con .visual_overlay.hidden {
	opacity: 0;
}
#main_con .visual_con .visual_overlay .box {
	padding: 28px 36px;
	border: 1px dashed rgba(255,255,255,0.25);
	border-radius: 8px;
	font-size: 13px;
	line-height: 1.7;
	letter-spacing: 0.02em;
}

/* ---- 안내 워딩 숨김 -------------------------------------------------------
   원본 데모(df/test/bust-dots-demo/style.css)와 동일하게 처리.
   기능은 그대로 두고 화면에만 안 보이게 한다.
     · .visual_hint        하단 설명 문구
     · .visual_overlay .box 가운데 로딩/드래그 안내 박스
   ※ .visual_filepick 은 아래에서 opacity:0 으로 처리한다(?glbdev=1 일 때만 클릭 유지).
   문구를 다시 띄우려면 이 블록만 지우면 된다. */
#main_con .visual_con .visual_hint,
#main_con .visual_con .visual_overlay .box {
	display: none;
}

/* ---- 하단 안내 문구 ------------------------------------------------------ */
#main_con .visual_con .visual_hint {
	position: absolute;
	bottom: 14px; left: 50%;
	transform: translateX(-50%);
	z-index: 5;
	color: rgba(255,255,255,0.3);
	pointer-events: none;
	white-space: nowrap;
	font-size: 13px;
	letter-spacing: 0.02em;
}

/* ---- GLB 교체 UI ---------------------------------------------------------
   opacity 0 이라 눈에는 안 보이지만 클릭·파일선택은 동작한다.
   (display:none 으로 지우면 클릭이 안 되므로 쓰지 않는다)

   ※ 컨테이너가 pointer-events:none 이므로 클릭을 되살리려면 여기서 auto 로 되돌려야 한다.
     운영 화면에서 이 투명 박스가 슬라이드 클릭을 가로채면 안 되므로
     visual.js 가 ?glbdev=1 일 때만 .is_dev 를 붙이고, 그때만 클릭이 살아난다.
   완전히 없애려면 visual.js 의 GLB_PICKER_ALWAYS_ON 을 false 로 바꾼다. */
#main_con .visual_con .visual_filepick {
	position: absolute;
	/* 데모는 top:16px 였지만, 이 페이지는 #header(고정, 높이 100px / 모바일 102px)가
	   그 자리를 덮어 클릭이 헤더 로고에 먹힌다. 헤더 아래로 내린다. */
	top: 116px; left: 16px;
	z-index: 6;
	color: rgba(255,255,255,0.5);
	font-size: 13px;
	opacity: 0;
	pointer-events: none;
}
#main_con .visual_con .visual_filepick.is_dev {
	pointer-events: auto;
}
#main_con .visual_con .visual_filepick input {
	display: none;
}
#main_con .visual_con .visual_filepick label {
	display: inline-block;
	padding: 6px 12px;
	border: 1px solid rgba(255,255,255,0.25);
	border-radius: 6px;
	cursor: pointer;
}
#main_con .visual_con .visual_filepick label:hover {
	border-color: rgba(255,255,255,0.6);
	color: #ffffff;
}

/* ---- 모바일 -------------------------------------------------------------
   높이를 여기서 다시 잡지 않는다. 슬라이드 박스(.contents_con 88.54vw x 200vw)를
   그대로 따라가야 비율이 어긋나지 않기 때문이다.
   세로로 길어진 만큼 모델이 잘리는 문제는 visual.js 의 스케일 보정이 처리한다. */
@media all and (max-width: 1024px) {
	#main_con .visual_con .visual_hint {
		font-size: 11px;
	}
	/* 단독 사용([B])일 때만 높이를 조정한다. 배경([A])은 슬라이드 박스를 따라가야 하므로 건드리지 않는다. */
	#main_con > .visual_con {
		height: 78vh;
		min-height: 440px;
	}
}

@media all and (max-width: 640px) {
	#main_con > .visual_con {
		height: 68vh;
		min-height: 380px;
	}
}

/* ---- 모션 최소화 설정 ----------------------------------------------------
   JS 쪽에서도 애니메이션 루프를 돌리지 않고 정지 프레임 1장만 그린다.
   (CLAUDE.md 지켜야 할 것 1번) 여기서는 UI 문구의 트랜지션만 죽인다. */
@media (prefers-reduced-motion: reduce) {
	#main_con .visual_con .visual_overlay {
		transition: none;
	}
	#main_con .visual_con .visual_hint {
		display: none;
	}
}

/* ---- 끌어서 돌릴 수 있다는 신호 -------------------------------------------
   [2026-09-09] 마우스로 끌면 배경 오브제가 돌아간다(visual.js 의 DRAG_*).
   캔버스는 pointer-events:none 이라 커서 모양이 안 먹으므로, 이벤트를 실제로 받는
   .visual_slide_con 에 건다. 끄는 동안에는 visual.js 가 .is_dragging 을 붙인다.

   ※ PC 에서만 의미가 있다. 손가락에는 커서가 없고, 모바일에서는 스와이프가
     슬라이드를 넘기는 원래 동작으로 남아 있어야 한다. */
@media all and (min-width: 1025px) {
	#main_con > .visual_slide_con { cursor: grab; }
	#main_con > .visual_slide_con.is_dragging { cursor: grabbing; }
	/* 끄는 동안 글자가 잡혀 끌리지 않게 한다 */
	#main_con > .visual_slide_con.is_dragging * { user-select: none; -webkit-user-select: none; }
	/* 링크·버튼은 원래 커서를 지킨다 */
	#main_con > .visual_slide_con a,
	#main_con > .visual_slide_con .slide_ui button { cursor: pointer; }
	/* 끄는 중이라도 조작 버튼 위에서는 grabbing 으로 바뀌지 않게 */
	#main_con > .visual_slide_con.is_dragging a,
	#main_con > .visual_slide_con.is_dragging .slide_ui button { cursor: pointer; }
}

/* ---- [index03] 유리 심볼 레이어 (glass-symbol03.js 가 만든다) ----------------------------
   [2026-09-14 3차] "심볼이 글자 위로" — .visual_con 밖, 슬라이드 텍스트(.swiper-container) 바로 뒤에 들어간다.
     z-index 1 = 텍스트(.swiper-container, z 1)와 같고 DOM 순서가 뒤라 텍스트 위.
     좌/우 버튼(.slide_ui, z 2)보다는 아래. 클릭은 전부 아래로 통과한다.
   배경은 투명 · 심볼만 불투명이라 합성은 normal (screen 이면 흰 글자가 심볼을 뚫고 보인다).
   투명도 · 줌은 JS 가 인라인으로 움직인다 — 여기서 opacity 트랜지션을 걸지 않는다. */
#main_con > .visual_slide_con > .visual_glass {
	display: block;
	position: absolute;
	top: 0; left: 0;
	width: 100%;
	height: 100%;
	z-index: 1;
	pointer-events: none;
	will-change: opacity;
}


/* ---- [index03] 유리 심볼 — 헤더 영역 가림 (2026-09-14 6차) --------------------------------
   #header 는 fixed · 배경 투명이라 유리가 메뉴 글자 밑으로 비쳤다.
   가림(mask-image)은 glass-symbol03.js 가 캔버스에 인라인으로 쓴다 — 헤더 아랫변까지 투명, 아래로 60px 에 걸쳐 드러남.
   ※ 처음에는 @property --glass-cut + transition 으로 여기서 했으나, 계산값이 한 박자씩 늦고 마스크가 다시 그려지지 않아
     메뉴 바에 유리가 남았다(헤드리스 실측). 그래서 CSS 쪽 규칙은 뺐다. 여기에 mask 를 다시 넣지 말 것(인라인과 겹친다). */

/* ---- [index03] 1번 슬라이드 문구 "KOREA / DIGITAL AGENCY / ASSOCIATION" — 모바일 보정 (2026-09-14) ----
   모바일(≤1024) 타이틀 기본값은 13.85vw (main_common_all02.css).
   ① 글자 크기 : 그 크기면 "ASSOCIATION" 이 103.5vw 라 슬라이드 폭(88.54vw)을 넘어 오른쪽이 잘린다
      (실측 : 430px 에서 화면 밖 36px · 768px 에서 65px. 원문 "CONNECTING" 은 430px 에서 17px).
      1번 슬라이드만 11.7vw 로 줄인다 → "ASSOCIATION" 약 87.4vw 로 폭 안에 들어간다. 자간도 같은 비율(-0.42 → -0.355vw).
   ② 줄 수 : "DIGITAL AGENCY" 는 줄인 크기에서도 폭을 넘어(약 104.8vw) 두 줄로 감긴다 → 모바일은 항상 4줄.
      (DIGITAL 과 AGENCY 사이에 <br> 을 넣지 않는다 — index03.html 주석 참고)
   ③ 옵션 문구 : 타이틀 높이가 3줄 × 16.62vw(49.86vw) → 4줄 × 14.04vw(56.16vw), 6.3vw 늘어난 만큼만 내린다 : 95vw → 101.3vw.
   PC 는 한 줄에 들어가 3줄 · 원래 크기 그대로라 건드리지 않는다. */
@media all and (max-width: 1024px) {
	#main_con > .visual_slide_con .swiper-container .swiper-wrapper .swiper-slide.text01 > .contents_con > .text_con > .title_con span {font-size:11.7vw; letter-spacing:-0.355vw;}
	#main_con > .visual_slide_con .swiper-container .swiper-wrapper .swiper-slide.text01 > .contents_con > .option_con {top:101.3vw;}
}

/* ---- [index03] 우측 하단 아이콘 되살림 + 슬라이드 좌/우 버튼을 그 옆으로 · 1.3배 (2026-09-14 · 1.5배에서 조금 줄임) ----------
   아이콘(.type_img_con)은 이 파일에서 숨기던 규칙을 뺐다 (index.html · index02.html 은 각 페이지에서 계속 숨긴다).

   버튼 자리 : 우측 하단 정보 칼럼(.info_con · 폭 min(335px,17.45vw) · 오른쪽 정렬)의 "왼쪽 끝", 아이콘과 같은 줄.
     아이콘은 칼럼 오른쪽 끝에 붙어 있고 장마다 폭이 달라서(도트 패턴 · 두 얼굴 · 세계지도), 아이콘 바로 옆에 붙이면
     장이 넘어갈 때 버튼이 좌우로 흔들린다. 칼럼 왼쪽 끝은 장과 무관하게 고정이다.
     아랫변을 아이콘 아랫변에 맞춘다 — 가운데를 맞추면 커진 버튼이 아래 협회명 글자에 닿는다.
       PC   (1920 기준) 정보 칼럼 윗변 877px · 아이콘 877-44 ~ 877-44+32 → 아이콘 아랫변은 바닥에서 215px
            슬라이드 틀 전체가 1920 비율로 줄므로(min(px, vw)) 215/19.2 = 11.2vw
       모바일(≤1024) 정보 칼럼 윗변 = 바닥에서 29.26vw · 아이콘 top -9.38vw · 높이 7.5vw → 아랫변 31.14vw
   크기 1.3배 : 38px → 49px (1.98vw → 2.57vw) · 아이콘 13px → 17px · 간격 8px → 10px / 모바일 9.5vw → 12.35vw
   (처음 1.5배(57px)로 했다가 "조금만 줄여줘" 요청으로 1.3배. index.html · index02.html 도 같은 값)
   ※ slide_ui02.css 가 이 파일보다 뒤에 로드된다 → 같은 선택자로는 지므로 #container 를 앞에 붙여 이긴다. */
#container #main_con > .visual_slide_con > .slide_ui {
	left: auto;
	right: calc((100% - min(1820px, 94.79vw)) / 2);
	width: min(335px, 17.45vw);
	max-width: none;
	margin: 0;
	bottom: min(215px, 11.2vw);
	justify-content: flex-start;
}
#container #main_con > .visual_slide_con > .slide_ui .btns { gap: min(10px, 0.52vw); }
#container #main_con > .visual_slide_con > .slide_ui button { width: min(49px, 2.57vw); height: min(49px, 2.57vw); }
#container #main_con > .visual_slide_con > .slide_ui button svg { width: min(17px, 0.88vw); height: min(17px, 0.88vw); }
@media all and (max-width: 1024px) {
	#container #main_con > .visual_slide_con > .slide_ui {
		left: 0; right: 0;
		width: 100%;
		max-width: 88.54vw;
		margin: 0 auto;
		top: auto;
		bottom: 31.14vw;
	}
	#container #main_con > .visual_slide_con > .slide_ui .btns { gap: 2.6vw; }
	#container #main_con > .visual_slide_con > .slide_ui button { width: 12.35vw; height: 12.35vw; }
	#container #main_con > .visual_slide_con > .slide_ui button svg { width: 4.42vw; height: 4.42vw; }
}
