diff --git a/index.html b/index.html
index 310bd42..25d7ca4 100644
--- a/index.html
+++ b/index.html
@@ -166,8 +166,17 @@
.map-q-box{background:#fff;border-radius:15px;padding:14px 18px;
box-shadow:var(--shadow);margin-bottom:14px;text-align:center;}
#map-svg-container{width:100%;background:#D8EEF8;border-radius:16px;
- overflow:hidden;box-shadow:var(--shadow);}
+ overflow:hidden;box-shadow:var(--shadow);position:relative;}
#map-svg{width:100%;height:auto;display:block;cursor:pointer;}
+ /* Zoom controls overlay */
+ #map-zoom-controls{position:absolute;bottom:10px;right:10px;display:flex;
+ flex-direction:column;gap:4px;z-index:10;}
+ .map-zoom-btn{width:36px;height:36px;border-radius:50%;border:none;
+ background:rgba(255,255,255,.88);box-shadow:0 2px 6px rgba(0,0,0,.22);
+ font-size:1.3rem;line-height:1;cursor:pointer;display:flex;
+ align-items:center;justify-content:center;transition:background .15s,transform .1s;}
+ .map-zoom-btn:hover{background:#fff;transform:scale(1.12);}
+ .map-zoom-btn:active{transform:scale(.95);}
.comarca-bg{fill:#C5DEB0;stroke:#a8c890;stroke-width:.8;transition:fill .2s;}
.comarca-mountain{stroke:#fff;stroke-width:1.4;cursor:pointer;
transition:fill .25s,filter .2s;filter:drop-shadow(0 1px 2px rgba(0,0,0,.18));}
@@ -518,6 +527,11 @@
π« Comarques de muntanya Β· π© Resta de Catalunya
@@ -877,6 +891,9 @@ let sessionStart = null;
let currentLevel=1, currentQ=0, score=0, questions=[];
let fcFlipped=false, fcIndex=0;
let mapMode='name', mapTarget=null, mapAnswered=false, mapBuilt=false, mapBlind=false;
+// Zoom state: viewBox is 820Γ600 at scale 1. Pan centre starts at (410, 300).
+const MAP_W=820, MAP_H=600;
+let mapZoom=1, mapPanX=MAP_W/2, mapPanY=MAP_H/2;
let l3Round=0, l3Sel=null, l3BTotal=0, l3BDone=0;
const L3B=5;
@@ -1513,6 +1530,51 @@ function buildMap(){
addTextSVG(svg,36,310,'ARAGΓ',7,'#999','middle','rotate(-90,36,310)');
addTextSVG(svg,790,430,'Mar Mediterrani',7,'#6BAFD6','middle','rotate(90,790,430)');
addTextSVG(svg,320,575,'COMUNITAT VALENCIANA',7,'#999','middle');
+ // Drag-to-pan (only when zoomed in)
+ let _drag=null;
+ svg.addEventListener('pointerdown',e=>{
+ if(mapZoom<=1)return;
+ // Ignore clicks on comarca paths (handled separately)
+ if(e.target.classList.contains('comarca-mountain'))return;
+ _drag={x:e.clientX,y:e.clientY,px:mapPanX,py:mapPanY};
+ svg.style.cursor='grabbing';
+ e.preventDefault();
+ },{passive:false});
+ svg.addEventListener('pointermove',e=>{
+ if(!_drag)return;
+ // Convert screen delta to SVG coordinate delta
+ const rect=svg.getBoundingClientRect();
+ const scaleX=MAP_W/(mapZoom*rect.width);
+ const scaleY=MAP_H/(mapZoom*rect.height);
+ mapPanX=_drag.px-(e.clientX-_drag.x)*scaleX;
+ mapPanY=_drag.py-(e.clientY-_drag.y)*scaleY;
+ _applyMapViewBox();
+ });
+ const _endDrag=()=>{_drag=null;svg.style.cursor='pointer';};
+ svg.addEventListener('pointerup',_endDrag);
+ svg.addEventListener('pointerleave',_endDrag);
+ // Pinch-to-zoom (touch)
+ let _pinchDist=0;
+ svg.addEventListener('touchstart',e=>{
+ if(e.touches.length===2){
+ const dx=e.touches[0].clientX-e.touches[1].clientX;
+ const dy=e.touches[0].clientY-e.touches[1].clientY;
+ _pinchDist=Math.hypot(dx,dy);
+ }
+ },{passive:true});
+ svg.addEventListener('touchmove',e=>{
+ if(e.touches.length===2){
+ const dx=e.touches[0].clientX-e.touches[1].clientX;
+ const dy=e.touches[0].clientY-e.touches[1].clientY;
+ const d=Math.hypot(dx,dy);
+ if(_pinchDist>0){
+ mapZoom=Math.max(1,Math.min(8,mapZoom*(d/_pinchDist)));
+ _applyMapViewBox();
+ }
+ _pinchDist=d;
+ e.preventDefault();
+ }
+ },{passive:false});
}
function addLabel(svg,x,y,text,small){
const t=document.createElementNS('http://www.w3.org/2000/svg','text');
@@ -1529,6 +1591,7 @@ function resetMapPaths(){
}
function initMap(){
currentQ=0;score=0;mapAnswered=false;
+ mapZoomReset(); // always start at full-view
// Both levels show mode tabs β level 6 just removes labels from the map
const tabs=document.querySelector('.map-mode-tabs');
tabs.style.display='flex';
@@ -1538,6 +1601,28 @@ function initMap(){
nextMapQ();
}
function setMapMode(mode){mapMode=mode;document.getElementById('tab-name').classList.toggle('active',mode==='name');document.getElementById('tab-cap').classList.toggle('active',mode==='capital');initMap();}
+
+/* ββ Zoom helpers βββββββββββββββββββββββββββββββββββββββββββββββββ */
+function _applyMapViewBox(){
+ const svg=document.getElementById('map-svg');
+ const w=MAP_W/mapZoom, h=MAP_H/mapZoom;
+ const x=Math.max(0,Math.min(MAP_W-w, mapPanX-w/2));
+ const y=Math.max(0,Math.min(MAP_H-h, mapPanY-h/2));
+ svg.setAttribute('viewBox',`${x} ${y} ${w} ${h}`);
+}
+function mapZoomIn(){
+ mapZoom=Math.min(mapZoom*1.5, 8); // max 8Γ zoom
+ _applyMapViewBox();
+}
+function mapZoomOut(){
+ mapZoom=Math.max(mapZoom/1.5, 1); // min 1Γ (full view)
+ _applyMapViewBox();
+}
+function mapZoomReset(){
+ mapZoom=1; mapPanX=MAP_W/2; mapPanY=MAP_H/2;
+ const svg=document.getElementById('map-svg');
+ if(svg) svg.setAttribute('viewBox',`0 0 ${MAP_W} ${MAP_H}`);
+}
function nextMapQ(){
if(currentQ>=questions.length){showResult(5);return;}
mapAnswered=false;mapTarget=questions[currentQ];