function LuxuryImageViewer({imgs,index,onIndexChange,onClose,alt}){
  const MAX_ZOOM=4;
  const [zoom,setZoom]=React.useState(1);
  const [pan,setPan]=React.useState({x:0,y:0});
  const [dragging,setDragging]=React.useState(false);
  const [sliderDragging,setSliderDragging]=React.useState(false);
  const [entered,setEntered]=React.useState(false);
  const stageRef=React.useRef(null);
  const dragStart=React.useRef(null);
  const pinchStart=React.useRef(null);
  const lastTap=React.useRef(0);
  const sliderTrackRef=React.useRef(null);
  const TRACK_H=180;

  React.useEffect(()=>{ const t=requestAnimationFrame(()=>setEntered(true)); return ()=>cancelAnimationFrame(t); },[]);
  React.useEffect(()=>{
    [index-1,index+1].forEach(i=>{ if(i>=0&&i<imgs.length){ const im=new Image(); im.src=photo(imgs[i],1600); } });
  },[index,imgs]);
  React.useEffect(()=>{ setZoom(1); setPan({x:0,y:0}); },[index]);

  function clampPan(z,p){
    const el=stageRef.current;
    if(!el)return p;
    const maxX=(el.clientWidth*(z-1))/2;
    const maxY=(el.clientHeight*(z-1))/2;
    return {x:Math.max(-maxX,Math.min(maxX,p.x)),y:Math.max(-maxY,Math.min(maxY,p.y))};
  }
  function setZoomClamped(z){
    const nz=Math.max(1,Math.min(MAX_ZOOM,z));
    setZoom(nz);
    setPan(p=>clampPan(nz,nz===1?{x:0,y:0}:p));
  }

  React.useEffect(()=>{
    function onKey(e){
      if(e.key==='Escape')onClose();
      else if(e.key==='ArrowLeft')onIndexChange((index-1+imgs.length)%imgs.length);
      else if(e.key==='ArrowRight')onIndexChange((index+1)%imgs.length);
    }
    document.addEventListener('keydown',onKey);
    return ()=>document.removeEventListener('keydown',onKey);
  },[index,imgs.length]);

  function onWheel(e){
    e.preventDefault();
    setZoomClamped(zoom - e.deltaY*0.0025);
  }
  function onPointerDown(e){
    if(zoom<=1)return;
    setDragging(true);
    dragStart.current={x:e.clientX,y:e.clientY,pan};
    e.currentTarget.setPointerCapture&&e.currentTarget.setPointerCapture(e.pointerId);
  }
  function onPointerMove(e){
    if(!dragging||!dragStart.current)return;
    const dx=e.clientX-dragStart.current.x;
    const dy=e.clientY-dragStart.current.y;
    setPan(clampPan(zoom,{x:dragStart.current.pan.x+dx,y:dragStart.current.pan.y+dy}));
  }
  function onPointerUp(){ setDragging(false); dragStart.current=null; }
  function onDoubleClick(e){
    if(zoom>1){setZoomClamped(1);return;}
    const r=stageRef.current.getBoundingClientRect();
    const cx=(e.clientX-r.left-r.width/2);
    const cy=(e.clientY-r.top-r.height/2);
    setZoom(2.5);
    setPan(clampPan(2.5,{x:-cx*1.5,y:-cy*1.5}));
  }
  function onTouchStart(e){
    if(e.touches.length===2){
      const [a,b]=e.touches;
      pinchStart.current={dist:Math.hypot(a.clientX-b.clientX,a.clientY-b.clientY),zoom};
    }else if(e.touches.length===1){
      const now=Date.now();
      if(now-lastTap.current<300){ onDoubleClick({clientX:e.touches[0].clientX,clientY:e.touches[0].clientY,currentTarget:stageRef.current}); }
      lastTap.current=now;
      if(zoom>1){ dragStart.current={x:e.touches[0].clientX,y:e.touches[0].clientY,pan}; setDragging(true); }
    }
  }
  function onTouchMove(e){
    if(e.touches.length===2&&pinchStart.current){
      const [a,b]=e.touches;
      const dist=Math.hypot(a.clientX-b.clientX,a.clientY-b.clientY);
      setZoomClamped(pinchStart.current.zoom*(dist/pinchStart.current.dist));
    }else if(e.touches.length===1&&dragging&&dragStart.current){
      const dx=e.touches[0].clientX-dragStart.current.x;
      const dy=e.touches[0].clientY-dragStart.current.y;
      setPan(clampPan(zoom,{x:dragStart.current.pan.x+dx,y:dragStart.current.pan.y+dy}));
    }
  }
  function onTouchEnd(){ setDragging(false); dragStart.current=null; pinchStart.current=null; }

  function sliderPosFromZoom(z){ return (1-(z-1)/(MAX_ZOOM-1))*TRACK_H; }
  function zoomFromSliderPos(y){ const clamped=Math.max(0,Math.min(TRACK_H,y)); return MAX_ZOOM-(clamped/TRACK_H)*(MAX_ZOOM-1); }
  function onSliderPointerDown(e){
    setSliderDragging(true);
    updateFromSliderEvent(e);
  }
  function updateFromSliderEvent(e){
    const r=sliderTrackRef.current.getBoundingClientRect();
    setZoomClamped(zoomFromSliderPos(e.clientY-r.top));
  }
  React.useEffect(()=>{
    if(!sliderDragging)return;
    function move(e){ updateFromSliderEvent(e); }
    function up(){ setSliderDragging(false); }
    document.addEventListener('pointermove',move);
    document.addEventListener('pointerup',up);
    return ()=>{ document.removeEventListener('pointermove',move); document.removeEventListener('pointerup',up); };
  },[sliderDragging,zoom]);

  return (
    <div onClick={e=>{ if(e.target===e.currentTarget)onClose(); }} style={{position:'fixed',inset:0,zIndex:1000,background:'var(--ivory)',opacity:entered?1:0,transition:'opacity 320ms var(--ease-out)',display:'flex',alignItems:'center',justifyContent:'center'}}>
      <button onClick={onClose} aria-label="Close viewer" style={{position:'absolute',top:28,right:32,background:'none',border:'none',cursor:'pointer',fontSize:30,lineHeight:1,color:'var(--text-primary)',fontWeight:300}}>&times;</button>

      <div ref={sliderTrackRef} style={{position:'absolute',left:36,top:'50%',transform:`translateY(-50%) scale(${entered?1:0.9})`,opacity:entered?1:0,transition:'opacity 380ms var(--ease-out) 80ms, transform 380ms var(--ease-out) 80ms',height:TRACK_H,width:20,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'space-between',cursor:'pointer'}} onPointerDown={onSliderPointerDown}>
        <span style={{fontSize:15,color:'var(--text-muted)',userSelect:'none'}}>+</span>
        <div style={{position:'absolute',top:9,bottom:9,left:'50%',width:1,background:'var(--border-default)',transform:'translateX(-50%)'}}></div>
        <div style={{position:'absolute',left:'50%',top:sliderPosFromZoom(zoom),transform:'translate(-50%,-50%)',width:11,height:11,borderRadius:'50%',background:'var(--gold-deep)',boxShadow:'0 0 0 4px var(--ivory)',cursor:'grab'}}></div>
        <span style={{fontSize:15,color:'var(--text-muted)',userSelect:'none'}}>&minus;</span>
      </div>

      <div ref={stageRef} onWheel={onWheel} onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} onPointerLeave={onPointerUp} onDoubleClick={onDoubleClick} onTouchStart={onTouchStart} onTouchMove={onTouchMove} onTouchEnd={onTouchEnd} style={{width:'min(78vw,1100px)',height:'min(84vh,1100px)',display:'flex',alignItems:'center',justifyContent:'center',overflow:'hidden',touchAction:'none',opacity:entered?1:0,transform:`scale(${entered?1:0.96})`,transition:'opacity 380ms var(--ease-out), transform 380ms var(--ease-out)'}}>
        <img src={photo(imgs[index],1800)} alt={alt||''} draggable={false} style={{maxWidth:'100%',maxHeight:'100%',objectFit:'contain',cursor:zoom>1?(dragging?'grabbing':'grab'):'zoom-in',transform:`translate(${pan.x}px,${pan.y}px) scale(${zoom})`,transition:dragging||sliderDragging?'none':'transform 250ms var(--ease-out)',userSelect:'none'}}/>
      </div>

      {imgs.length>1&&(
        <div style={{position:'absolute',left:76,bottom:32,display:'flex',gap:10,opacity:entered?1:0,transition:'opacity 380ms var(--ease-out) 120ms'}}>
          {imgs.map((im,i)=>(
            <button key={i} onClick={()=>onIndexChange(i)} style={{width:52,height:66,padding:0,borderRadius:'var(--radius-sm)',overflow:'hidden',border:i===index?'2px solid var(--gold-deep)':'1px solid var(--border-soft)',cursor:'pointer',opacity:i===index?1:0.7}}>
              <img src={photo(im,200)} alt="" style={{width:'100%',height:'100%',objectFit:'cover'}}/>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}
window.LuxuryImageViewer=LuxuryImageViewer;
