gsap.registerPlugin(Flip); const q=selector=>document.querySelector(selector),qa=selector=>Array.from(document.querySelectorAll(selector)); const studyAbort=new AbortController(),studyCleanup=[]; const listen=(target,type,handler)=>target.addEventListener(type,handler,{signal:studyAbort.signal}); const preference=matchMedia('(prefers-reduced-motion: reduce)'),staticControl=q('#static-mode'); staticControl.checked=preference.matches;staticControl.disabled=preference.matches; const isStatic=()=>staticControl.checked||preference.matches; let applyMode=()=>{}; listen(staticControl,'change',()=>applyMode()); listen(preference,'change',()=>{staticControl.checked=preference.matches;staticControl.disabled=preference.matches;applyMode();}); listen(window,'pagehide',()=>{studyCleanup.forEach(cleanup=>cleanup());studyAbort.abort();}); const reading=text=>q('#reading').textContent=text; const dialog=q('#gallery-dialog'),slot=q('#dialog-slot'),openers=qa('.gallery-opener'); let activeVisual=null,activeOpener=null,originalParent=null,originalNext=null,placeholder=null,modalTween=null,phase='closed',queuedClose=false; function reflect(){reading('상태: '+phase+(queuedClose?' · 닫기 1회 대기':''));} function restoreVisual(){if(!activeVisual)return;activeVisual.classList.remove('expanded');if(originalNext?.parentNode===originalParent)originalParent.insertBefore(activeVisual,originalNext);else originalParent.append(activeVisual);placeholder?.remove();placeholder=null;} function finishClose(){phase='closed';queuedClose=false;const opener=activeOpener;activeVisual=null;activeOpener=null;originalParent=null;originalNext=null;modalTween=null;reflect();opener?.focus({preventScroll:true});} function closeModal(){if(phase==='opening'){queuedClose=true;reflect();return;}if(phase!=='open')return;phase='closing';queuedClose=false;const state=Flip.getState(activeVisual);dialog.close();restoreVisual();reflect();if(isStatic()){gsap.set(activeVisual,{clearProps:'transform,opacity'});finishClose();return;}modalTween=Flip.from(state,{duration:.6,ease:'power2.inOut',onComplete:finishClose});} function finishOpen(){phase='open';modalTween=null;reflect();if(queuedClose)closeModal();} function openModal(opener){if(phase!=='closed')return;activeOpener=opener;activeVisual=opener.querySelector('.gallery-visual');originalParent=activeVisual.parentNode;originalNext=activeVisual.nextSibling; const state=Flip.getState(activeVisual),bounds=activeVisual.getBoundingClientRect();placeholder=document.createElement('span');placeholder.className='gallery-placeholder';placeholder.style.width=bounds.width+'px';placeholder.style.height=bounds.height+'px';placeholder.setAttribute('aria-hidden','true');originalParent.insertBefore(placeholder,activeVisual); phase='opening';queuedClose=false;dialog.showModal();activeVisual.classList.add('expanded');slot.append(activeVisual);q('#dialog-title').textContent=opener.getAttribute('aria-label').replace(' 열기','');q('#close-dialog').focus({preventScroll:true});reflect();if(isStatic()){finishOpen();return;}modalTween=Flip.from(state,{duration:.6,ease:'power2.inOut',onComplete:finishOpen});} openers.forEach(opener=>listen(opener,'click',()=>openModal(opener)));listen(q('#close-dialog'),'click',closeModal);listen(dialog,'cancel',event=>{event.preventDefault();closeModal();}); applyMode=()=>{if(!modalTween)return;const running=modalTween;running.progress(1);running.kill();}; studyCleanup.push(()=>{modalTween?.kill();if(dialog.open)dialog.close();restoreVisual();});reflect();