/* 메인 「보아텍이 함께한 공간」 — 설치사례 페이지와 같은 데이터(js/cases-data.js)로 그림 (2026-10-23)
전체: LED 2건 + 분류별 최신 1건 / 분류 탭: 해당 분류 최신 6건 */
(function () {
'use strict';
var D = window.VOATEK_CASES; if (!D) return;
var tabs = document.getElementById('caseTabs'), ul = document.getElementById('cases');
if (!tabs || !ul) return;
var CAT = {}; D.cats.forEach(function (c) { CAT[c.k] = c.label; });
var esc = function (s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); };
var CODE = {"etc": "c1", "hall": "c2", "public": "c3", "church": "c4", "edu": "c5", "hotel": "c7"};
var PER = 6, pick = {}, order = [];
function add(it, all) {
if (!pick[it.id]) { pick[it.id] = { it: it, all: false }; order.push(it.id); }
if (all) pick[it.id].all = true;
}
// 전체
D.items.filter(function (i) { return i.cat === 'led'; }).slice(0, 2).forEach(function (i) { add(i, true); });
D.cats.forEach(function (c) {
if (c.k === 'all' || c.k === 'led') return;
var inCat = D.items.filter(function (i) { return i.cat === c.k; });
var f = inCat.filter(function (i) { return !/^(OO|대전 OO)/.test(i.name); })[0] || inCat[0]; if (f) add(f, true);
});
// 분류별
D.cats.forEach(function (c) {
if (c.k === 'all') return;
D.items.filter(function (i) { return i.cat === c.k; }).slice(0, PER).forEach(function (i) { add(i, false); });
});
tabs.innerHTML = D.cats.map(function (c, n) {
return '';
}).join('');
var html = order.map(function (id) {
var p = pick[id], it = p.it, led = it.cat === 'led';
var sub = led ? 'LED' : '';
var meta = led ? esc(it.venue || '') + ' · LED전광판' : esc(CAT[it.cat]) + ' · ' + esc(it.type);
return '
' +
'' +
'
' +
'' + sub + '' + esc(it.name) + '' + meta + '
';
}).join('');
ul.insertAdjacentHTML('afterbegin', html);
})();
/* VOATEK main page · preview build (vanilla JS, no dependencies) */
(function () {
'use strict';
var doc = document, root = doc.documentElement, body = doc.body;
var RM = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var mqMobile = window.matchMedia('(max-width: 960px)');
var HOVER = window.matchMedia('(hover: hover)').matches;
var LOW = (navigator.hardwareConcurrency || 8) <= 4;
var $ = function (s, c) { return (c || doc).querySelector(s); };
var $$ = function (s, c) { return Array.prototype.slice.call((c || doc).querySelectorAll(s)); };
var clamp = function (v, a, b) { return v < a ? a : v > b ? b : v; };
var easeOut = function (t) { return 1 - Math.pow(1 - t, 3); };
var vh = function () { return window.innerHeight; };
/* ---------- toast ---------- */
var toastEl = $('#toast'), toastT;
if (!toastEl) { toastEl = doc.createElement('div'); }
function toast(msg) {
toastEl.textContent = msg; toastEl.classList.add('on');
clearTimeout(toastT); toastT = setTimeout(function () { toastEl.classList.remove('on'); }, 2600);
}
$$('[data-toast]').forEach(function (b) { b.addEventListener('click', function () { toast(b.dataset.toast); }); });
$$('a[href="#"]').forEach(function (a) { a.addEventListener('click', function (e) { e.preventDefault(); }); });
/* ---------- preview toggle ---------- */
var pv = $('#pvToggle');
if (pv) pv.addEventListener('change', function () { body.classList.toggle('pv-off', !pv.checked); });
/* ---------- count-up ---------- */
function countUp(el) {
var to = +el.dataset.to, dur = +(el.dataset.dur || 1200), t0 = performance.now();
if (RM) { el.textContent = to.toLocaleString('ko-KR'); return; }
(function step(t) {
var p = Math.min((t - t0) / dur, 1), e = p === 1 ? 1 : 1 - Math.pow(2, -10 * p);
el.textContent = Math.round(to * e).toLocaleString('ko-KR');
if (p < 1) requestAnimationFrame(step);
})(t0);
}
if (!RM) $$('.count').forEach(function (el) { el.textContent = '0'; });
/* ---------- reveal ---------- */
var io = new IntersectionObserver(function (es) {
es.forEach(function (e) {
if (!e.isIntersecting) return;
var el = e.target;
if (el.hasAttribute('data-stagger')) {
Array.prototype.slice.call(el.children).forEach(function (k, i) {
k.style.transitionDelay = (Math.min(i, 8) * 0.08) + 's';
});
}
el.classList.add('is-in');
$$('.count', el).forEach(countUp);
if (el.classList.contains('count')) countUp(el);
io.unobserve(el);
});
}, { threshold: 0.2, rootMargin: '0px 0px -40px 0px' });
$$('.reveal, [data-stagger]:not(.reveal)').forEach(function (el) { io.observe(el); });
/* =========================================================
Pixel-wave field (hero + contact)
========================================================= */
function Field(canvas, opt) {
this.cv = canvas; this.ctx = canvas.getContext('2d');
this.o = opt; this.waves = []; this.mouse = null; this.run = false; this.last = 0; this.lastWave = 0;
this.cx = 0; this.cy = 0;
this.resize(); var self = this;
window.addEventListener('resize', function () { self.resize(); if (!self.run) self.frame(performance.now(), true); });
if (opt.mouse && HOVER) {
var host = canvas.parentElement;
host.addEventListener('mousemove', function (e) { var r = canvas.getBoundingClientRect(); self.mouse = { x: e.clientX - r.left, y: e.clientY - r.top }; });
host.addEventListener('mouseleave', function () { self.mouse = null; });
host.addEventListener('click', function (e) {
if (e.target.closest('a,button')) return;
var r = canvas.getBoundingClientRect(); self.addWave(e.clientX - r.left, e.clientY - r.top, performance.now());
});
}
new IntersectionObserver(function (es) {
es.forEach(function (e) { e.isIntersecting ? self.start() : self.stop(); });
}, { threshold: 0 }).observe(canvas);
doc.addEventListener('visibilitychange', function () { doc.hidden ? self.stop() : self.start(); });
}
Field.prototype.resize = function () {
var cv = this.cv, dpr = Math.min(window.devicePixelRatio || 1, 2);
this.w = cv.clientWidth; this.h = cv.clientHeight; this.dpr = dpr;
cv.width = Math.round(this.w * dpr); cv.height = Math.round(this.h * dpr);
this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
var sp = this.o.spacing * ((mqMobile.matches || LOW) ? 1.5 : 1);
this.sp = sp;
var cols = Math.ceil(this.w / sp) + 1, rows = Math.ceil(this.h / sp) + 1;
this.n = cols * rows; this.px = new Float32Array(this.n); this.py = new Float32Array(this.n); this.b = new Float32Array(this.n);
var ox = (this.w - (cols - 1) * sp) / 2, oy = (this.h - (rows - 1) * sp) / 2, k = 0;
for (var r = 0; r < rows; r++) for (var c = 0; c < cols; c++) { this.px[k] = ox + c * sp; this.py[k] = oy + r * sp; k++; }
this.baseX = this.w * this.o.cx; this.baseY = this.h * this.o.cy;
if (!this.cx) { this.cx = this.baseX; this.cy = this.baseY; }
this.maxR = Math.hypot(Math.max(this.baseX, this.w - this.baseX), Math.max(this.baseY, this.h - this.baseY)) + 60;
// pre-render base dots
var off = doc.createElement('canvas'); off.width = cv.width; off.height = cv.height;
var oc = off.getContext('2d'); oc.setTransform(dpr, 0, 0, dpr, 0, 0); oc.fillStyle = 'rgba(255,255,255,' + this.o.base + ')';
for (var i = 0; i < this.n; i++) oc.fillRect(this.px[i] - 1, this.py[i] - 1, 2, 2);
this.off = off;
};
Field.prototype.addWave = function (x, y, t) {
this.waves.push({ x: x, y: y, t0: t });
if (this.waves.length > 3) this.waves.shift();
};
Field.prototype.start = function () {
if (this.run || RM || this.paused) return; this.run = true; var self = this;
var loop = function (t) { if (!self.run) return; self.frame(t); requestAnimationFrame(loop); };
requestAnimationFrame(loop);
};
Field.prototype.stop = function () { this.run = false; };
Field.prototype.frame = function (t, still) {
var o = this.o, ctx = this.ctx, w = this.w, h = this.h;
if (!this.started) { this.started = t; this.lastWave = t - o.interval + o.first; }
// wave center follows mouse slightly
var tx = this.baseX, ty = this.baseY;
if (this.mouse) { tx += (this.mouse.x - this.baseX) * 0.16; ty += (this.mouse.y - this.baseY) * 0.16; }
this.cx += (tx - this.cx) * 0.08; this.cy += (ty - this.cy) * 0.08;
if (t - this.lastWave > o.interval) { this.addWave(this.cx, this.cy, t); this.lastWave = t; }
var speed = this.maxR / o.travel, band = o.band;
var live = [];
for (var j = 0; j < this.waves.length; j++) {
var wv = this.waves[j], age = (t - wv.t0) / 1000, rad = age * speed;
if (rad - band < this.maxR * 1.3) live.push({ x: wv.x, y: wv.y, r: rad, f: clamp(1 - rad / (this.maxR * 1.25), 0, 1) });
}
this.waves = this.waves.filter(function (wv) { return (t - wv.t0) / 1000 * speed < this.maxR * 1.3; }, this);
if (still && !live.length) live.push({ x: this.cx, y: this.cy, r: this.maxR * 0.35, f: 0.8 });
ctx.clearRect(0, 0, w, h);
ctx.drawImage(this.off, 0, 0, w, h);
// rings
for (var q = 0; q < live.length; q++) {
var L = live[q]; ctx.beginPath(); ctx.arc(L.x, L.y, Math.max(L.r, 0.1), 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(' + o.ring + ',' + (0.22 * L.f) + ')'; ctx.lineWidth = 1; ctx.stroke();
}
// lit pixels
var b = this.b, px = this.px, py = this.py, mx = this.mouse ? this.mouse.x : -9999, my = this.mouse ? this.mouse.y : -9999;
ctx.fillStyle = 'rgb(' + o.lit + ')';
var decay = still ? 0 : 0.955;
for (var i = 0; i < this.n; i++) {
var target = 0, x = px[i], y = py[i];
for (var k = 0; k < live.length; k++) {
var W = live[k], dx = x - W.x, dy = y - W.y, d = Math.abs(Math.sqrt(dx * dx + dy * dy) - W.r);
if (d < band) { var v = (1 - d / band) * W.f; if (v > target) target = v; }
}
if (this.mouse) { var mdx = x - mx, mdy = y - my, md = mdx * mdx + mdy * mdy; if (md < 14400) { var mv = 0.45 * (1 - Math.sqrt(md) / 120); if (mv > target) target = mv; } }
var nb = b[i] * decay; if (target * o.peak > nb) nb = target * o.peak; b[i] = nb;
if (nb > 0.04) { ctx.globalAlpha = nb; ctx.fillRect(x - 1.25, y - 1.25, 2.5, 2.5); }
}
if (o.spark && !still) for (var s = 0; s < o.spark; s++) { var si = (Math.random() * this.n) | 0; if (b[si] < 0.5) b[si] = 0.35 + Math.random() * 0.4; }
ctx.globalAlpha = 1;
};
var heroCv = $('#heroCanvas');
if (heroCv) {
var hf = new Field(heroCv, { spacing: 16, base: 0.11, lit: '47,107,255', ring: '228,114,0', cx: 0.7, cy: 0.5, interval: 3200, first: 200, travel: 3.8, band: 56, peak: 1, mouse: true, spark: 7 });
if (RM) hf.frame(performance.now(), true);
}
var ctCv = $('#ctCanvas');
if (ctCv) {
var cf = new Field(ctCv, { spacing: 22, base: 0.05, lit: '228,114,0', ring: '228,114,0', cx: 0.85, cy: 0.35, interval: 5200, first: 600, travel: 3.4, band: 50, peak: 0.55, mouse: false });
if (RM) cf.frame(performance.now(), true);
}
/* ---------- hero intro + rotator ---------- */
var hero = $('.hero');
requestAnimationFrame(function () { setTimeout(function () { hero.classList.add('go'); }, 60); });
setTimeout(function () { var l = $('.launch'); if (l && !RM) l.classList.add('glow'); }, 1500);
var track = $('.hero-rot-track');
if (track) {
var items = track.children, n = items.length, idx = 0;
track.appendChild(items[0].cloneNode(true));
var all = track.children;
function mark(i) { for (var k = 0; k < all.length; k++) all[k].classList.toggle('cur', k === i); }
mark(0);
if (!RM) setInterval(function () {
if (doc.hidden) return;
idx++; mark(idx); track.style.transition = ''; track.style.transform = 'translateY(' + (-1.16 * idx) + 'em)';
if (idx === n) setTimeout(function () { track.style.transition = 'none'; track.style.transform = 'translateY(0)'; idx = 0; mark(0); }, 650);
}, 2500);
}
/* ---------- hero slider (3 slides) ---------- */
var slides = $$('.hs', hero), pagerBtns = $$('.hero-pager button', hero), cur = 0, DUR = 6500, timer = null, paused = RM;
var pauseBtn = $('.hc-pause', hero);
hero.style.setProperty('--dur', DUR + 'ms');
function show(n) {
n = (n + slides.length) % slides.length; if (n === cur && slides[n].classList.contains('on')) { schedule(); return; }
slides.forEach(function (s, i) {
var on = i === n; s.classList.toggle('on', on); s.setAttribute('aria-hidden', !on);
$$('a,button', s).forEach(function (a) { if (on) a.removeAttribute('tabindex'); else a.setAttribute('tabindex', '-1'); });
});
pagerBtns.forEach(function (b, i) {
b.setAttribute('aria-selected', i === n);
var bar = $('b', b); bar.style.animation = 'none'; void bar.offsetWidth; bar.style.animation = '';
});
if (typeof hf !== 'undefined') { hf.paused = n !== 0; if (n === 0) hf.start(); else hf.stop(); }
cur = n; schedule();
}
function schedule() { clearTimeout(timer); if (!paused) timer = setTimeout(function () { show(cur + 1); }, DUR); }
function setPaused(p) {
paused = p; hero.classList.toggle('paused', p);
pauseBtn.setAttribute('aria-pressed', p); pauseBtn.setAttribute('aria-label', p ? '자동 넘김 재생' : '자동 넘김 멈춤');
if (p) clearTimeout(timer); else show(cur + 1);
}
pagerBtns.forEach(function (b) { b.addEventListener('click', function () { show(+b.dataset.go); }); });
$('.hc-prev', hero).addEventListener('click', function () { show(cur - 1); });
$('.hc-next', hero).addEventListener('click', function () { show(cur + 1); });
pauseBtn.addEventListener('click', function () { setPaused(!paused); });
// swipe (mobile)
var sx = null;
hero.addEventListener('touchstart', function (e) { sx = e.touches[0].clientX; }, { passive: true });
hero.addEventListener('touchend', function (e) {
if (sx === null) return; var dx = e.changedTouches[0].clientX - sx; sx = null;
if (Math.abs(dx) > 50) show(cur + (dx < 0 ? 1 : -1));
});
// stop auto-advance while hero is off screen or tab hidden
new IntersectionObserver(function (es) { es.forEach(function (e) { if (e.isIntersecting) { if (!paused) schedule(); } else clearTimeout(timer); }); }, { threshold: 0.3 }).observe(hero);
doc.addEventListener('visibilitychange', function () { if (doc.hidden) clearTimeout(timer); else if (!paused) schedule(); });
if (RM) setPaused(true); else schedule();
/* ---------- scroll state (카페24 공통 헤더 사용) ---------- */
var mcall = $('#mcall'), dots = $('.dots');
function onHeader() {
var y = window.scrollY, heroH = hero.offsetHeight;
if (mcall) mcall.classList.toggle('on', y > heroH * 0.8 && y < doc.body.scrollHeight - vh() * 1.6);
if (dots) dots.classList.toggle('on', y > heroH * 0.5);
}
/* ---------- section dots ---------- */
var dotLinks = $$('.dots a');
var secIO = new IntersectionObserver(function (es) {
es.forEach(function (e) {
if (!e.isIntersecting) return;
var key = e.target.dataset.sec;
dotLinks.forEach(function (d) { d.classList.toggle('cur', d.dataset.dot === key); });
dots.classList.toggle('on-light', e.target.dataset.theme === 'light');
});
}, { rootMargin: '-50% 0px -50% 0px' });
$$('section[data-sec]').forEach(function (s) { secIO.observe(s); });
/* =========================================================
scroll-scrubbed pieces
========================================================= */
// pixel edges
function hash(a, b) { var s = Math.sin(a * 127.1 + b * 311.7) * 43758.5453; return s - Math.floor(s); }
var edges = $$('.pxedge').map(function (el) {
var cols = mqMobile.matches ? 20 : 40; el.style.setProperty('--cols', cols);
var cells = [];
for (var r = 0; r < 6; r++) for (var c = 0; c < cols; c++) {
var i = doc.createElement('i'); el.appendChild(i);
var fromBottom = 5 - r; cells.push({ el: i, th: (fromBottom / 6) * 0.72 + hash(c, r) * 0.26, on: false });
}
return { el: el, cells: cells };
});
// cabinet wall
var wall = $('#wall'), assy = $('#assy'), cabs = [], cabCount = $('#cabCount'), assyHead = $('.assy-head'), hint = $('.assy-hint');
if (wall) {
for (var i = 0; i < 12; i++) {
var c = i % 4, r = Math.floor(i / 4), d = doc.createElement('div'); d.className = 'cab';
d.style.backgroundPosition = (c / 3 * 100) + '% ' + (r / 2 * 100) + '%';
wall.appendChild(d);
var sx = (c - 1.5), sy = (r - 1);
cabs.push({ el: d, c: c, r: r, dx: sx * 110 + (hash(i, 3) - 0.5) * 220, dy: sy * 120 + (hash(i, 7) - 0.5) * 200 + 40, rot: (hash(i, 11) - 0.5) * 16, delay: hash(i, 5) * 0.35 });
}
}
// B안: 캐비닛 12칸은 제자리에 두고 한 칸씩 점등 → 다 켜지면 이음선 사라짐
var LIT_ORDER = [5, 6, 1, 2, 4, 7, 9, 10, 0, 3, 8, 11];
cabs.forEach(function (C, k) { C.el.style.transform = 'none'; C.el.style.opacity = 1; C.start = 0.04 + LIT_ORDER.indexOf(k) * 0.058; });
function renderAssy(p) {
var seam = clamp((p - 0.78) / 0.12, 0, 1), title = clamp((p - 0.82) / 0.14, 0, 1), done = 0;
for (var k = 0; k < cabs.length; k++) {
var C = cabs[k], tl = clamp((p - C.start) / 0.07, 0, 1);
C.el.style.setProperty('--dark', (1 - tl).toFixed(3));
C.el.style.setProperty('--px', (Math.sin(tl * Math.PI) * 0.9).toFixed(3));
if (tl > 0.98) done++;
}
wall.style.setProperty('--gap', ((1 - seam) * 4).toFixed(1) + 'px');
wall.style.setProperty('--glow', clamp((p - 0.1) / 0.7, 0, 1).toFixed(3));
wall.classList.toggle('lit', seam > 0.95);
assyHead.style.setProperty('--t', title.toFixed(3));
cabCount.textContent = (done < 10 ? '0' : '') + done;
if (hint) hint.style.opacity = clamp(1 - p * 3, 0, 1);
}
var assyAuto = false;
function autoAssy() {
if (assyAuto) return; assyAuto = true;
if (RM) { renderAssy(1); return; }
var t0 = performance.now();
(function step(t) { var p = clamp((t - t0) / 2600, 0, 1); renderAssy(p); if (p < 1) requestAnimationFrame(step); })(t0);
}
if (wall) {
renderAssy(RM ? 1 : 0);
if (mqMobile.matches || RM) {
new IntersectionObserver(function (es) { es.forEach(function (e) { if (e.isIntersecting) autoAssy(); }); }, { threshold: 0.35 }).observe(wall);
}
}
var tl = $('#timeline'), tlItems = $$('#timeline li'), steps = $('.steps'), stepItems = $$('.steps li');
var refresh = $('.refresh'), warrantyEl = $('#warranty'), scrubCases = null;
function onScrub() {
var H = vh();
// timeline
if (tl) {
var r = tl.getBoundingClientRect(), p = RM ? 1 : clamp((H * 0.85 - r.top) / (H * 0.55), 0, 1);
tl.style.setProperty('--p', p.toFixed(3));
tlItems.forEach(function (li) { li.classList.toggle('on', p >= +li.dataset.at); });
}
// edges
edges.forEach(function (E) {
var r = E.el.getBoundingClientRect(), q = RM ? 1 : clamp((H - r.top) / (H * 0.55), 0, 1);
E.cells.forEach(function (C) { var on = q > C.th; if (on !== C.on) { C.on = on; C.el.classList.toggle('on', on); } });
});
// cabinet assembly (desktop scrub)
if (wall && !mqMobile.matches && !RM) {
var ar = assy.getBoundingClientRect(), range = ar.height - H;
renderAssy(clamp(-ar.top / range, 0, 1));
}
// design steps
if (steps) {
var sr = steps.getBoundingClientRect(), sp = RM ? 1 : clamp((H * 0.82 - sr.top) / (H * 0.5), 0, 1);
steps.style.setProperty('--p', sp.toFixed(3));
stepItems.forEach(function (li, i) { li.classList.toggle('on', sp >= (i / Math.max(1, stepItems.length - 1)) * 0.96 + 0.001); });
}
// refresh bands only while visible
if (scrubCases) scrubCases(H);
if (warrantyEl) { var wr = warrantyEl.getBoundingClientRect(); warrantyEl.classList.toggle('on-screen', wr.top < H && wr.bottom > 0); }
if (refresh) { var rr = refresh.getBoundingClientRect(); refresh.classList.toggle('is-play', rr.top < H && rr.bottom > 0); }
}
var ticking = false;
function onScroll() {
if (ticking) return; ticking = true;
requestAnimationFrame(function () { ticking = false; onHeader(); onScrub(); });
}
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
onHeader(); onScrub();
/* ---------- LED type cards: magnifier on the same image ---------- */
var ZOOM = 2.4, LS = 150;
function placeLens(card, x, y) {
var media = $('.tcard-media', card), img = $('img', media), lens = $('.lens', media);
var W = media.clientWidth, Hh = media.clientHeight, nw = img.naturalWidth || W, nh = img.naturalHeight || Hh;
var s = Math.max(W / nw, Hh / nh), rw = nw * s, rh = nh * s, ox = (W - rw) / 2, oy = (Hh - rh) / 2;
x = clamp(x, 0, W); y = clamp(y, 0, Hh);
lens.style.backgroundImage = 'url("' + img.currentSrc + '")';
lens.style.backgroundSize = (rw * ZOOM) + 'px ' + (rh * ZOOM) + 'px';
lens.style.backgroundPosition = (-((x - ox) * ZOOM - LS / 2)) + 'px ' + (-((y - oy) * ZOOM - LS / 2)) + 'px';
lens.style.setProperty('--lx', (x - LS / 2) + 'px'); lens.style.setProperty('--ly', (y - LS / 2) + 'px');
}
$$('.tcard').forEach(function (card) {
var media = $('.tcard-media', card);
card.addEventListener('mousemove', function (e) {
var r = media.getBoundingClientRect(); placeLens(card, e.clientX - r.left, e.clientY - r.top);
});
card.addEventListener('mouseenter', function (e) {
var r = media.getBoundingClientRect(); placeLens(card, e.clientX - r.left, e.clientY - r.top);
});
card.addEventListener('focus', function () { placeLens(card, media.clientWidth * 0.62, media.clientHeight * 0.5); });
if (!HOVER) card.addEventListener('click', function () {
var on = !card.classList.contains('act');
$$('.tcard').forEach(function (c) { c.classList.remove('act'); });
if (on) { placeLens(card, media.clientWidth * 0.62, media.clientHeight * 0.5); card.classList.add('act'); }
});
});
/* ---------- projects: metafacade "WORLD OF" style scroll motion + tabs ---------- */
var casesEl = $('#cases');
if (casesEl) {
var caseItems = $$('.case', casesEl), empty = $('.case-empty', casesEl);
scrubCases = function (H) {
if (RM) return;
var single = mqMobile.matches, cr = casesEl.getBoundingClientRect();
if (cr.bottom < -200 || cr.top > H + 200) return;
var prog = clamp((H - cr.top) / (cr.height + H), 0, 1), col = 0;
caseItems.forEach(function (c) {
if (c.classList.contains('gone')) return;
var r = c.getBoundingClientRect(), mid = r.top + r.height / 2;
// -1 (bottom of screen) .. 0 (center) .. 1 (top of screen)
var d = clamp((H / 2 - mid) / (H * 0.62), -1, 1), ad = Math.abs(d);
var scale = 1 - (single ? 0.06 : 0.14) * Math.pow(ad, 1.4);
var op = 1 - (single ? 0.35 : 0.62) * Math.pow(ad, 1.6);
var shift = (!single && col % 2 === 1) ? (110 - prog * 220) : 0; // 2nd column moves at a different speed
c.style.transform = 'translate3d(0,' + shift.toFixed(1) + 'px,0) scale(' + scale.toFixed(3) + ')';
if (!c.classList.contains('out')) c.style.opacity = op.toFixed(3);
c.style.setProperty('--zoom', (1 + 0.12 * ad).toFixed(3));
col++;
});
};
$$('#caseTabs button').forEach(function (btn) {
btn.addEventListener('click', function () {
$$('#caseTabs button').forEach(function (b) { b.setAttribute('aria-selected', b === btn); });
var f = btn.dataset.f;
caseItems.forEach(function (c) { c.classList.add('out'); });
setTimeout(function () {
var show = caseItems.filter(function (c) { return f === 'all' ? c.dataset.all === '1' : c.dataset.c === f; });
var all = $('#caseAll'), CODE = {"etc": "c1", "hall": "c2", "public": "c3", "church": "c4", "edu": "c5", "hotel": "c7"}; if (all) all.href = '/bizdemo79551/mp2/mp2_sub2.php?sub=02' + (CODE[f] ? '&com_board_category_code=' + CODE[f] : '');
caseItems.forEach(function (c) { c.classList.toggle('gone', show.indexOf(c) < 0); });
empty.hidden = show.length > 0;
scrubCases(vh());
requestAnimationFrame(function () { show.forEach(function (c) { c.classList.remove('out'); }); scrubCases(vh()); });
}, RM ? 0 : 220);
});
});
scrubCases(vh());
}
/* ---------- liquid glass: 마우스를 따라오는 반사광 ---------- */
if (HOVER && !RM) {
$$('.caps li, .tcard, .feat, .lg-plan, .pcard, .team, .certs li, .board, .ct-form, .w3-card, .biz-grid, .hero-pager, .whys > li, .br, .cl-grid > div').forEach(function (el) {
el.classList.add('glare');
el.addEventListener('mousemove', function (e) {
var r = el.getBoundingClientRect();
el.style.setProperty('--gx', (e.clientX - r.left) + 'px'); el.style.setProperty('--gy', (e.clientY - r.top) + 'px');
});
});
} else {
$$('.caps li, .tcard, .feat, .lg-plan, .pcard, .team, .certs li, .board, .ct-form, .w3-card, .whys > li, .br, .cl-grid > div').forEach(function (el) { el.classList.add('glare'); });
}
/* ---------- 팀 섹션 시안 A/B 전환 (검토용) ---------- */
$$('.pp-switch button').forEach(function (b) {
b.addEventListener('click', function () {
$$('.pp-switch button').forEach(function (x) { x.setAttribute('aria-selected', x === b); });
$$('.pp-v').forEach(function (v) {
var on = v.dataset.v === b.dataset.v; v.hidden = !on;
if (on) $$('.reveal, [data-stagger]', v).forEach(function (r) { r.classList.add('is-in'); });
});
});
});
/* ---------- contact form (preview only) ---------- */
var form = $('#ctForm');
if (form) form.addEventListener('submit', function (e) {
e.preventDefault();
var tel = form.tel, agree = form.agree, ok = true;
tel.classList.toggle('err', !tel.value.trim()); if (!tel.value.trim()) ok = false;
if (!agree.checked) ok = false;
if (!ok) { toast('연락처와 개인정보 동의를 확인해 주세요.'); (tel.value.trim() ? agree : tel).focus(); return; }
toast('미리보기 화면이라 실제로 전송되지는 않습니다.');
});
})();
/* 공지사항·자료실 최신글 (카페24 게시판 목록에서 읽어 옴) */
(function () {
['vtNotice', 'vtData'].forEach(function (id) {
var ul = document.getElementById(id); if (!ul || !window.fetch || !window.TextDecoder) return;
fetch(ul.getAttribute('data-src'), { credentials: 'same-origin' }).then(function (r) { return r.arrayBuffer(); }).then(function (buf) {
var html = new TextDecoder('euc-kr').decode(buf), d = new DOMParser().parseFromString(html, 'text/html'), out = [];
Array.prototype.slice.call(d.querySelectorAll('table.board tr[onclick]')).some(function (tr) {
var links = tr.querySelectorAll('td[align="left"] a[href]'), a = links[links.length - 1], dt = tr.querySelector('.bbsetc_dateof_write');
if (!a) return false;
var t = a.textContent.replace(/\s+/g, ' ').trim(); if (!t) return false;
out.push('');
out[out.length - 1] = out[out.length - 1].replace('', '' + t.replace(/[&<>]/g, function (c) { return { '&': '&', '<': '<', '>': '>' }[c]; }) + '');
return out.length >= 4;
});
ul.innerHTML = out.length ? out.join('') : '등록된 글이 없습니다.';
}).catch(function () { ul.innerHTML = '목록을 불러오지 못했습니다.'; });
});
})();