{"id":535,"date":"2026-05-29T07:38:17","date_gmt":"2026-05-29T07:38:17","guid":{"rendered":"https:\/\/studiohos.com\/?p=535"},"modified":"2026-05-29T10:30:34","modified_gmt":"2026-05-29T10:30:34","slug":"test-post-1","status":"publish","type":"post","link":"https:\/\/studiohos.com\/el\/test-post-1\/","title":{"rendered":"test post 1"},"content":{"rendered":"<div class=\"fusion-fullwidth fullwidth-box fusion-builder-row-1 fusion-flex-container has-pattern-background has-mask-background nonhundred-percent-fullwidth non-hundred-percent-height-scrolling\" style=\"--awb-border-radius-top-left:0px;--awb-border-radius-top-right:0px;--awb-border-radius-bottom-right:0px;--awb-border-radius-bottom-left:0px;--awb-flex-wrap:wrap;\" ><div class=\"fusion-builder-row fusion-row fusion-flex-align-items-flex-start fusion-flex-content-wrap\" style=\"max-width:1248px;margin-left: calc(-4% \/ 2 );margin-right: calc(-4% \/ 2 );\"><div class=\"fusion-layout-column fusion_builder_column fusion-builder-column-0 fusion_builder_column_1_1 1_1 fusion-flex-column\" style=\"--awb-bg-size:cover;--awb-width-large:100%;--awb-margin-top-large:0px;--awb-spacing-right-large:1.92%;--awb-margin-bottom-large:20px;--awb-spacing-left-large:1.92%;--awb-width-medium:100%;--awb-order-medium:0;--awb-spacing-right-medium:1.92%;--awb-spacing-left-medium:1.92%;--awb-width-small:100%;--awb-order-small:0;--awb-spacing-right-small:1.92%;--awb-spacing-left-small:1.92%;\"><div class=\"fusion-column-wrapper fusion-column-has-shadow fusion-flex-justify-content-flex-start fusion-content-layout-column\"><div class=\"fusion-text fusion-text-1\"><p>codebelow<\/p>\n<\/div><style>\r\n#jig-root {\r\n    font-family: 'Segoe UI', sans-serif;\r\n    width: 100%;\r\n    box-sizing: border-box;\r\n}\r\n#jig-topbar {\r\n    display: flex;\r\n    align-items: center;\r\n    gap: 8px;\r\n    padding: 8px 0 10px;\r\n    flex-wrap: wrap;\r\n}\r\n#jig-start-btn {\r\n    background: #d4af37;\r\n    color: #000;\r\n    border: none;\r\n    padding: 7px 20px;\r\n    font-weight: 700;\r\n    font-size: 13px;\r\n    cursor: pointer;\r\n}\r\n#jig-start-btn:hover { background: #b8952e; }\r\n#jig-start-btn:disabled { opacity: 0.5; cursor: default; }\r\n#jig-peek-btn {\r\n    background: none;\r\n    border: 1px solid #555;\r\n    color: #ccc;\r\n    font-size: 18px;\r\n    cursor: pointer;\r\n    padding: 4px 10px;\r\n    line-height: 1;\r\n    border-radius: 4px;\r\n}\r\n#jig-peek-btn:hover { border-color: #d4af37; color: #d4af37; }\r\n#jig-restart-btn {\r\n    background: none;\r\n    border: 1px solid #555;\r\n    color: #aaa;\r\n    font-size: 12px;\r\n    cursor: pointer;\r\n    padding: 5px 12px;\r\n    border-radius: 4px;\r\n    font-weight: 600;\r\n}\r\n#jig-restart-btn:hover { border-color: #d4af37; color: #d4af37; }\r\n#jig-restart-btn:disabled { opacity: 0.3; cursor: default; border-color: #333; color: #555; }\r\n#jig-sort-btn {\r\n    background: none;\r\n    border: 1px solid #555;\r\n    color: #aaa;\r\n    font-size: 12px;\r\n    cursor: pointer;\r\n    padding: 5px 10px;\r\n    border-radius: 4px;\r\n    font-weight: 600;\r\n}\r\n#jig-sort-btn:hover { border-color: #d4af37; color: #d4af37; }\r\n#jig-sort-btn:disabled { opacity: 0.3; cursor: default; border-color: #333; color: #555; }\r\n#jig-sound-btn {\r\n    background: none;\r\n    border: 1px solid #555;\r\n    color: #aaa;\r\n    font-size: 16px;\r\n    cursor: pointer;\r\n    padding: 4px 8px;\r\n    border-radius: 4px;\r\n    line-height: 1;\r\n}\r\n#jig-sound-btn:hover { border-color: #d4af37; color: #d4af37; }\r\n#jig-canvas-wrap {\r\n    width: 100%;\r\n    position: relative;\r\n    background: #13131f;\r\n    border: 2px solid #2a2a3e;\r\n    cursor: default;\r\n}\r\n#jig-canvas {\r\n    display: block;\r\n    width: 100%;\r\n    height: auto;\r\n    touch-action: none;\r\n}\r\n#jig-win-banner {\r\n    display: none;\r\n    position: absolute;\r\n    inset: 0;\r\n    background: rgba(10,10,20,0.93);\r\n    align-items: center;\r\n    justify-content: center;\r\n    flex-direction: column;\r\n    gap: 10px;\r\n}\r\n#jig-win-banner.show { display: flex; }\r\n#jig-win-banner h2 { color: #d4af37; font-size: 24px; margin: 0; }\r\n#jig-win-banner p  { color: #ccc; margin: 0; font-size: 13px; }\r\n#jig-next-btn {\r\n    background: #d4af37; color: #000;\r\n    border: none; padding: 8px 24px;\r\n    font-weight: 700; font-size: 14px; cursor: pointer;\r\n    margin-top: 4px;\r\n}\r\n#jig-hint {\r\n    font-size: 11px; color: #555;\r\n    padding: 5px 0 0;\r\n    font-family: 'Segoe UI', sans-serif;\r\n}\r\n\/* Difficulty modal *\/\r\n#jig-diff-overlay {\r\n    display: none;\r\n    position: absolute;\r\n    inset: 0;\r\n    background: rgba(0,0,0,0.7);\r\n    align-items: center;\r\n    justify-content: center;\r\n    z-index: 10;\r\n}\r\n#jig-diff-overlay.show { display: flex; }\r\n#jig-diff-box {\r\n    background: #1a1a2e;\r\n    border: 2px solid #d4af37;\r\n    border-radius: 8px;\r\n    padding: 24px 32px;\r\n    text-align: center;\r\n}\r\n#jig-diff-box p {\r\n    color: #d4af37;\r\n    font-weight: 700;\r\n    font-size: 15px;\r\n    margin: 0 0 14px;\r\n}\r\n.jig-diff-pick {\r\n    background: #0e0e1a;\r\n    color: #aaa;\r\n    border: 1px solid #333;\r\n    padding: 10px 28px;\r\n    font-size: 14px;\r\n    font-weight: 700;\r\n    cursor: pointer;\r\n    margin: 0 6px;\r\n    border-radius: 4px;\r\n}\r\n.jig-diff-pick:hover { border-color: #d4af37; color: #d4af37; background: #22223a; }\r\n\/* Reference peek overlay (on top of board) *\/\r\n#jig-ref-overlay {\r\n    display: none;\r\n    position: absolute;\r\n    inset: 0;\r\n    background: rgba(0,0,0,0.75);\r\n    align-items: center;\r\n    justify-content: center;\r\n    z-index: 15;\r\n    opacity: 0;\r\n    transition: opacity 0.25s;\r\n}\r\n#jig-ref-overlay.show {\r\n    display: flex;\r\n    opacity: 1;\r\n}\r\n#jig-ref-overlay img {\r\n    max-width: 85%;\r\n    max-height: 85%;\r\n    display: block;\r\n    border: 2px solid #d4af37;\r\n    border-radius: 2px;\r\n    box-shadow: 0 0 30px rgba(0,0,0,0.6);\r\n}\r\n<\/style>\r\n<div id=\"jig-root\">\r\n    <div id=\"jig-topbar\">\r\n        <button id=\"jig-start-btn\">Start Puzzle<\/button>\r\n        <button id=\"jig-restart-btn\" disabled>\u21bb Restart<\/button>\r\n        <button id=\"jig-sort-btn\" disabled>\u21c5 Sort<\/button>\r\n        <button id=\"jig-peek-btn\">\ud83d\udc41<\/button>\r\n        <button id=\"jig-sound-btn\">\ud83d\udd0a<\/button>\r\n        <span id=\"jig-hint\"> Tap a piece to rotate \u00b7 Drag to place<\/span>\r\n    <\/div>\r\n    <div id=\"jig-canvas-wrap\">\r\n        <canvas id=\"jig-canvas\"><\/canvas>\r\n        <div id=\"jig-win-banner\">\r\n            <h2> Puzzle Complete!<\/h2>\r\n            <p id=\"jig-win-msg\">Well done!<\/p>\r\n            <button id=\"jig-next-btn\">Play Again \u2192<\/button>\r\n        <\/div>\r\n        <div id=\"jig-diff-overlay\">\r\n            <div id=\"jig-diff-box\">\r\n                <p>Choose difficulty<\/p>\r\n                <button class=\"jig-diff-pick\" data-pieces=\"9\">9 Pieces<\/button>\r\n                <button class=\"jig-diff-pick\" data-pieces=\"16\">16 Pieces<\/button>\r\n                <button class=\"jig-diff-pick\" data-pieces=\"22\">22 Pieces<\/button>\r\n            <\/div>\r\n        <\/div>\r\n        <div id=\"jig-ref-overlay\"><\/div>\r\n    <\/div>\r\n<\/div>\r\n<script>\r\nvar PUZZLE_IMAGE_SRC = \"https:\/\/studiohos.com\/wp-content\/uploads\/2026\/01\/li-zhang-LRFR-AMjQc-unsplash.jpg\";\r\n(function(){\r\n    const canvas = document.getElementById('jig-canvas');\r\n    const ctx    = canvas.getContext('2d');\r\n    const wrap   = document.getElementById('jig-canvas-wrap');\r\n    const img = new Image();\r\n    let imgLoaded = false;\r\n    let peekTimer = null;\r\n    let CW, CH;\r\n    let TX, TY, TW, TH;     \/\/ pieces tray (top)\r\n    let BX, BY, BW, BH;     \/\/ board (bottom)\r\n    let PW, PH;             \/\/ piece size on board\r\n    let ROWS, COLS;\r\n    let totalPieces = 9;\r\n    let gameStarted = false;\r\n    const TAB  = 0.26;\r\n    const SNAP = 0.22;\r\n    let pieces   = [];\r\n    let dragging = null;\r\n    let selected = null;\r\n    let dragOffX = 0, dragOffY = 0;\r\n    let didDrag  = false, downTime = 0, downPos = null;\r\n    let edgeGlowEnd = 0;\r\n    let snapFlash = null;\r\n    let muted = false;\r\n    let audioCtx = null;\r\n    const peekBtn = document.getElementById('jig-peek-btn');\r\n    const refOverlay = document.getElementById('jig-ref-overlay');\r\n    peekBtn.addEventListener('click', () => {\r\n        if (!PUZZLE_IMAGE_SRC) return;\r\n        if (peekTimer) { clearTimeout(peekTimer); peekTimer = null; }\r\n        refOverlay.innerHTML = '<img decoding=\"async\" src=\"' + PUZZLE_IMAGE_SRC + '\" alt=\"Reference\">';\r\n        refOverlay.classList.add('show');\r\n        peekTimer = setTimeout(() => {\r\n            refOverlay.classList.remove('show');\r\n            peekTimer = null;\r\n        }, 4000);\r\n    });\r\n    refOverlay.addEventListener('click', () => {\r\n        if (peekTimer) { clearTimeout(peekTimer); peekTimer = null; }\r\n        refOverlay.classList.remove('show');\r\n    });\r\n    function playSound(type){\r\n        if(muted) return;\r\n        try {\r\n            if(!audioCtx) audioCtx = new (window.AudioContext||window.webkitAudioContext)();\r\n            const osc = audioCtx.createOscillator();\r\n            const gain = audioCtx.createGain();\r\n            osc.connect(gain); gain.connect(audioCtx.destination);\r\n            const t = audioCtx.currentTime;\r\n            if(type==='click'){\r\n                osc.frequency.setValueAtTime(500,t); osc.type='square';\r\n                gain.gain.setValueAtTime(0.06,t);\r\n                gain.gain.exponentialRampToValueAtTime(0.001,t+0.04);\r\n                osc.start(t); osc.stop(t+0.04);\r\n            } else if(type==='snap'){\r\n                osc.frequency.setValueAtTime(660,t);\r\n                osc.frequency.exponentialRampToValueAtTime(880,t+0.12);\r\n                osc.type='sine';\r\n                gain.gain.setValueAtTime(0.12,t);\r\n                gain.gain.exponentialRampToValueAtTime(0.001,t+0.15);\r\n                osc.start(t); osc.stop(t+0.15);\r\n            } else if(type==='win'){\r\n                [523,659,784,1047].forEach((f,i)=>{\r\n                    const o=audioCtx.createOscillator();\r\n                    const g=audioCtx.createGain();\r\n                    o.connect(g); g.connect(audioCtx.destination);\r\n                    o.frequency.value=f; o.type='sine';\r\n                    const st=t+i*0.12;\r\n                    g.gain.setValueAtTime(0.08,st);\r\n                    g.gain.exponentialRampToValueAtTime(0.001,st+0.25);\r\n                    o.start(st); o.stop(st+0.25);\r\n                });\r\n            }\r\n        } catch(e){}\r\n    }\r\n    function setDifficulty(n){\r\n        totalPieces = n;\r\n        if(n <= 9){ ROWS=3; COLS=3; }\r\n        else if(n <= 16){ ROWS=4; COLS=4; }\r\n        else { ROWS=4; COLS=6; }\r\n    }\r\n    function initLayout(){\r\n        const containerW = wrap.clientWidth || 800;\r\n        CW = containerW;\r\n        const imgRatio = (imgLoaded && img.naturalWidth>0)\r\n            ? img.naturalHeight \/ img.naturalWidth : 0.6;\r\n        \/\/ Board: full width at bottom\r\n        BW = CW;\r\n        BH = Math.round(BW * imgRatio);\r\n        BX = 0;\r\n        BY = 0; \/\/ set after tray height is known\r\n        PW = BW \/ COLS;\r\n        PH = BH \/ ROWS;\r\n        \/\/ Tray: full width at top, compact\r\n        TX = 0; TY = 0;\r\n        TW = CW;\r\n        const pieceScale = 0.35;\r\n        const tGap = 3;\r\n        const tMargin = 5;\r\n        const tPW = PW * pieceScale;\r\n        const tPH = PH * pieceScale;\r\n        const tPerRow = Math.max(1, Math.floor((TW - tMargin * 2 + tGap) \/ (tPW + tGap)));\r\n        const tRows = Math.ceil(totalPieces \/ tPerRow);\r\n        TH = Math.max(tRows * tPH + tMargin * 2 + 4, 55);\r\n        \/\/ Board starts below tray\r\n        BY = TH + 1;\r\n        \/\/ Total canvas height\r\n        CH = TH + 1 + BH;\r\n        canvas.width  = CW;\r\n        canvas.height = CH;\r\n    }\r\n    let hConn = [], vConn = [];\r\n    function buildConnectors(){\r\n        hConn=[]; vConn=[];\r\n        for(let r=0;r<ROWS-1;r++){\r\n            hConn[r]=[];\r\n            for(let c=0;c<COLS;c++) hConn[r][c]=Math.random()<0.5?1:-1;\r\n        }\r\n        for(let r=0;r<ROWS;r++){\r\n            vConn[r]=[];\r\n            for(let c=0;c<COLS-1;c++) vConn[r][c]=Math.random()<0.5?1:-1;\r\n        }\r\n    }\r\n    function getSides(r,c){\r\n        return [\r\n            r===0?0:-hConn[r-1][c],\r\n            c===COLS-1?0:vConn[r][c],\r\n            r===ROWS-1?0:hConn[r][c],\r\n            c===0?0:-vConn[r][c-1]\r\n        ];\r\n    }\r\n    function makePath(sides, pw, ph){\r\n        const [top,right,bottom,left]=sides;\r\n        const tx=pw*TAB, ty=ph*TAB;\r\n        const p=new Path2D();\r\n        p.moveTo(0,0);\r\n        if(!top){ p.lineTo(pw,0); }\r\n        else {\r\n            p.lineTo(pw*0.3,0);\r\n            p.bezierCurveTo(pw*0.3,-top*ty*0.65,pw*0.17,-top*ty,pw*0.5,-top*ty);\r\n            p.bezierCurveTo(pw*0.83,-top*ty,pw*0.7,-top*ty*0.65,pw*0.7,0);\r\n            p.lineTo(pw,0);\r\n        }\r\n        if(!right){ p.lineTo(pw,ph); }\r\n        else {\r\n            p.lineTo(pw,ph*0.3);\r\n            p.bezierCurveTo(pw+right*tx*0.65,ph*0.3,pw+right*tx,ph*0.17,pw+right*tx,ph*0.5);\r\n            p.bezierCurveTo(pw+right*tx,ph*0.83,pw+right*tx*0.65,ph*0.7,pw,ph*0.7);\r\n            p.lineTo(pw,ph);\r\n        }\r\n        if(!bottom){ p.lineTo(0,ph); }\r\n        else {\r\n            p.lineTo(pw*0.7,ph);\r\n            p.bezierCurveTo(pw*0.7,ph+bottom*ty*0.65,pw*0.83,ph+bottom*ty,pw*0.5,ph+bottom*ty);\r\n            p.bezierCurveTo(pw*0.17,ph+bottom*ty,pw*0.3,ph+bottom*ty*0.65,pw*0.3,ph);\r\n            p.lineTo(0,ph);\r\n        }\r\n        if(!left){ p.lineTo(0,0); }\r\n        else {\r\n            p.lineTo(0,ph*0.7);\r\n            p.bezierCurveTo(-left*tx*0.65,ph*0.7,-left*tx,ph*0.83,-left*tx,ph*0.5);\r\n            p.bezierCurveTo(-left*tx,ph*0.17,-left*tx*0.65,ph*0.3,0,ph*0.3);\r\n            p.lineTo(0,0);\r\n        }\r\n        p.closePath();\r\n        return p;\r\n    }\r\n    function scatterPieces(){\r\n        const gap = 3;\r\n        const margin = 6;\r\n        const availW = TW - margin * 2;\r\n        const availH = TH - margin * 2;\r\n        const unplaced = pieces.filter(p=>!p.onBoard);\r\n        if(!unplaced.length) return;\r\n        const perRow = Math.max(1, Math.min(\r\n            unplaced.length,\r\n            Math.floor((availW + gap) \/ (PW * 0.4 + gap))\r\n        ));\r\n        const rows = Math.ceil(unplaced.length \/ perRow);\r\n        const scaleF = Math.min(\r\n            (availW - (perRow-1) * gap) \/ (perRow * PW),\r\n            (availH - (rows-1)   * gap) \/ (rows   * PH)\r\n        ) * 0.9;\r\n        const sPW = PW * scaleF;\r\n        const sPH = PH * scaleF;\r\n        const totalW = perRow * sPW + (perRow-1) * gap;\r\n        const totalH = rows   * sPH + (rows-1)   * gap;\r\n        const startX = TX + margin + (availW - totalW) \/ 2;\r\n        const startY = TY + margin + (availH - totalH) \/ 2;\r\n        unplaced.forEach((p,i)=>{\r\n            const col = i % perRow;\r\n            const row = Math.floor(i \/ perRow);\r\n            p.px = startX + col * (sPW + gap);\r\n            p.py = startY + row * (sPH + gap);\r\n            p.ps = scaleF;\r\n        });\r\n    }\r\n    function createPieces(){\r\n        buildConnectors();\r\n        pieces=[];\r\n        let idx=0;\r\n        for(let r=0;r<ROWS&&idx<totalPieces;r++){\r\n            for(let c=0;c<COLS&&idx<totalPieces;c++){\r\n                pieces.push({\r\n                    col:c, row:r,\r\n                    sides: getSides(r,c),\r\n                    bx:0, by:0,\r\n                    px:0, py:0, ps:1,\r\n                    rotation: [0,90,180,270][Math.floor(Math.random()*4)],\r\n                    onBoard: false,\r\n                    placed:  false,\r\n                    id: idx\r\n                });\r\n                idx++;\r\n            }\r\n        }\r\n        pieces.sort(()=>Math.random()-0.5);\r\n        scatterPieces();\r\n    }\r\n    function drawPiece(p, wx, wy, pw, ph, highlight){\r\n        ctx.save();\r\n        ctx.translate(wx+pw\/2, wy+ph\/2);\r\n        ctx.rotate(p.rotation*Math.PI\/180);\r\n        ctx.translate(-pw\/2, -ph\/2);\r\n        const path = makePath(p.sides, pw, ph);\r\n        if(highlight && !p.placed){\r\n            ctx.shadowColor='rgba(0,0,0,0.55)';\r\n            ctx.shadowBlur=16;\r\n            ctx.shadowOffsetY=6;\r\n        }\r\n        ctx.save();\r\n        ctx.clip(path);\r\n        if(imgLoaded){\r\n            const pad = TAB;\r\n            const sw = img.naturalWidth \/ COLS;\r\n            const sh = img.naturalHeight \/ ROWS;\r\n            const sx = p.col * sw - sw * pad;\r\n            const sy = p.row * sh - sh * pad;\r\n            const srcW = sw * (1 + pad * 2);\r\n            const srcH = sh * (1 + pad * 2);\r\n            const dstX = -pw * pad;\r\n            const dstY = -ph * pad;\r\n            const dstW = pw * (1 + pad * 2);\r\n            const dstH = ph * (1 + pad * 2);\r\n            ctx.drawImage(img, sx, sy, srcW, srcH, dstX, dstY, dstW, dstH);\r\n            const g=ctx.createLinearGradient(0,0,pw,ph);\r\n            g.addColorStop(0,'rgba(255,255,255,0.06)');\r\n            g.addColorStop(1,'rgba(0,0,0,0.18)');\r\n            ctx.fillStyle=g; ctx.fill(path);\r\n        } else {\r\n            ctx.fillStyle='#1a1a2e'; ctx.fill(path);\r\n            ctx.fillStyle='#d4af37';\r\n            ctx.font=`bold ${Math.min(pw,ph)*0.28}px sans-serif`;\r\n            ctx.textAlign='center'; ctx.textBaseline='middle';\r\n            ctx.fillText(p.id+1, pw\/2, ph\/2);\r\n        }\r\n        ctx.restore();\r\n        ctx.shadowColor='transparent'; ctx.shadowBlur=0; ctx.shadowOffsetY=0;\r\n        if(p.placed){\r\n            ctx.strokeStyle='rgba(80,220,120,0.6)'; ctx.lineWidth=2;\r\n        } else if(highlight){\r\n            ctx.strokeStyle='#d4af37'; ctx.lineWidth=2.5;\r\n        } else {\r\n            ctx.strokeStyle='rgba(200,200,255,0.22)'; ctx.lineWidth=1;\r\n        }\r\n        ctx.stroke(path);\r\n        ctx.restore();\r\n    }\r\n    function draw(){\r\n        ctx.clearRect(0,0,CW,CH);\r\n        \/\/ \u2500\u2500 Tray (top section) \u2500\u2500\r\n        ctx.fillStyle='#11111e';\r\n        ctx.fillRect(TX,TY,TW,TH);\r\n        ctx.strokeStyle='#2a2a3e';\r\n        ctx.lineWidth=1;\r\n        ctx.strokeRect(TX,TY,TW,TH);\r\n        if(gameStarted){\r\n            pieces.filter(p=>!p.onBoard&&p!==dragging).forEach(p=>{\r\n                const pw=PW*p.ps, ph=PH*p.ps;\r\n                drawPiece(p, p.px, p.py, pw, ph, p===selected);\r\n                if(edgeGlowEnd && Date.now()<edgeGlowEnd){\r\n                    const isEdge=p.row===0||p.row===ROWS-1||p.col===0||p.col===COLS-1;\r\n                    if(isEdge){\r\n                        const path=makePath(p.sides,pw,ph);\r\n                        ctx.save();\r\n                        ctx.translate(p.px+pw\/2,p.py+ph\/2);\r\n                        ctx.rotate(p.rotation*Math.PI\/180);\r\n                        ctx.translate(-pw\/2,-ph\/2);\r\n                        ctx.shadowColor='#d4af37';\r\n                        ctx.shadowBlur=22;\r\n                        ctx.strokeStyle='rgba(212,175,55,0.5)';\r\n                        ctx.lineWidth=3;\r\n                        ctx.stroke(path);\r\n                        ctx.shadowBlur=0;\r\n                        ctx.restore();\r\n                    }\r\n                }\r\n            });\r\n        } else {\r\n            ctx.fillStyle='#555';\r\n            ctx.font='13px sans-serif';\r\n            ctx.textAlign='center';\r\n            ctx.fillText('Pieces will appear here after you start', TX+TW\/2, TY+TH\/2);\r\n        }\r\n        \/\/ \u2500\u2500 Divider \u2500\u2500\r\n        ctx.strokeStyle='#d4af37';\r\n        ctx.lineWidth=1;\r\n        ctx.beginPath(); ctx.moveTo(TX, TY+TH);\r\n        ctx.lineTo(TX+TW, TY+TH);\r\n        ctx.stroke();\r\n        \/\/ \u2500\u2500 Board (bottom section) \u2500\u2500\r\n        ctx.fillStyle='#0e0e1a';\r\n        ctx.fillRect(BX,BY,BW,BH);\r\n        if(!gameStarted && imgLoaded){\r\n            ctx.globalAlpha=1;\r\n            ctx.drawImage(img, BX, BY, BW, BH);\r\n            ctx.globalAlpha=0.5;\r\n            ctx.fillStyle='#000';\r\n            ctx.fillRect(BX,BY,BW,BH);\r\n            ctx.globalAlpha=1;\r\n            ctx.fillStyle='#d4af37';\r\n            ctx.font=`bold ${Math.round(BW*0.038)}px sans-serif`;\r\n            ctx.textAlign='center'; ctx.textBaseline='middle';\r\n            ctx.fillText('Press Start Puzzle to begin', BX+BW\/2, BY+BH\/2);\r\n        } else if(gameStarted){\r\n            for(let r=0;r<ROWS;r++){\r\n                for(let c=0;c<COLS;c++){\r\n                    const p0=pieces.find(q=>q.col===c&&q.row===r);\r\n                    const sides=p0?p0.sides:getSides(r,c);\r\n                    const path=makePath(sides,PW,PH);\r\n                    ctx.save();\r\n                    ctx.translate(BX+c*PW, BY+r*PH);\r\n                    ctx.fillStyle='rgba(255,255,255,0.025)';\r\n                    ctx.fill(path);\r\n                    ctx.strokeStyle='rgba(255,255,255,0.09)';\r\n                    ctx.lineWidth=1;\r\n                    ctx.stroke(path);\r\n                    ctx.restore();\r\n                }\r\n            }\r\n        }\r\n        ctx.strokeStyle='#2e2e4a';\r\n        ctx.lineWidth=2;\r\n        ctx.strokeRect(BX,BY,BW,BH);\r\n        \/\/ \u2500\u2500 Board pieces \u2500\u2500\r\n        if(gameStarted){\r\n            const boardPieces=pieces.filter(p=>p.onBoard&&p!==dragging);\r\n            boardPieces.sort((a,b)=>a.placed?-1:1);\r\n            boardPieces.forEach(p=>{\r\n                const isFlash = snapFlash && p===snapFlash.piece;\r\n                if(isFlash){\r\n                    const progress = Math.min(snapFlash.frame \/ 4, 1);\r\n                    const glow = 1 - progress;\r\n                    ctx.save();\r\n                    ctx.shadowColor='rgba(212,175,55,'+(glow*0.5)+')';\r\n                    ctx.shadowBlur=30*glow;\r\n                    drawPiece(p, BX+p.bx, BY+p.by, PW, PH, true);\r\n                    ctx.shadowBlur=0;\r\n                    ctx.restore();\r\n                } else {\r\n                    drawPiece(p, BX+p.bx, BY+p.by, PW, PH, p===selected);\r\n                }\r\n            });\r\n        }\r\n        \/\/ \u2500\u2500 Dragging piece \u2500\u2500\r\n        if(dragging){\r\n            const pw=dragging.onBoard?PW:PW*dragging.ps;\r\n            const ph=dragging.onBoard?PH:PH*dragging.ps;\r\n            drawPiece(dragging, dragging.dx, dragging.dy, pw, ph, true);\r\n        }\r\n    }\r\n    function pieceAt(mx, my){\r\n        const candidates=[...pieces].reverse().filter(p=>p!==dragging);\r\n        for(const p of candidates){\r\n            let wx, wy, pw, ph;\r\n            if(p.onBoard){ wx=BX+p.bx; wy=BY+p.by; pw=PW; ph=PH; }\r\n            else          { wx=p.px;   wy=p.py;    pw=PW*p.ps; ph=PH*p.ps; }\r\n            const dx=mx-(wx+pw\/2), dy=my-(wy+ph\/2);\r\n            const rad=-p.rotation*Math.PI\/180;\r\n            const lx=dx*Math.cos(rad)-dy*Math.sin(rad)+pw\/2;\r\n            const ly=dx*Math.sin(rad)+dy*Math.cos(rad)+ph\/2;\r\n            if(lx>-pw*TAB&&lx<pw*(1+TAB)&&ly>-ph*TAB&&ly<ph*(1+TAB)) return p;\r\n        }\r\n        return null;\r\n    }\r\n    function snapCheck(p){\r\n        const tx=p.col*PW, ty=p.row*PH;\r\n        const rot=(p.rotation%360+360)%360;\r\n        const snapDist=Math.min(PW,PH)*SNAP;\r\n        if(Math.abs(p.bx-tx)<snapDist&&Math.abs(p.by-ty)<snapDist&&rot===0){\r\n            p.bx=tx; p.by=ty; p.placed=true; p.onBoard=true;\r\n            pieces.splice(pieces.indexOf(p),1);\r\n            pieces.unshift(p);\r\n            playSound('snap');\r\n            snapFlash = { piece: p, frame: 0 };\r\n            requestAnimationFrame(function animFlash(){\r\n                if(!snapFlash) return;\r\n                snapFlash.frame++;\r\n                draw();\r\n                if(snapFlash.frame < 5) requestAnimationFrame(animFlash);\r\n                else { snapFlash = null; draw(); }\r\n            });\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n    function checkWin(){\r\n        if(pieces.every(p=>p.placed)){\r\n            gameStarted=false;\r\n            document.getElementById('jig-start-btn').disabled=false;\r\n            document.getElementById('jig-restart-btn').disabled=true;\r\n            document.getElementById('jig-sort-btn').disabled=true;\r\n            document.getElementById('jig-win-banner').classList.add('show');\r\n            playSound('win');\r\n        }\r\n    }\r\n    function getPos(e){\r\n        const rect=canvas.getBoundingClientRect();\r\n        const scaleX=CW\/rect.width, scaleY=CH\/rect.height;\r\n        const src=e.touches?e.touches[0]:e;\r\n        return { x:(src.clientX-rect.left)*scaleX, y:(src.clientY-rect.top)*scaleY };\r\n    }\r\n    canvas.addEventListener('mousedown', onDown);\r\n    canvas.addEventListener('touchstart', onDown, {passive:false});\r\n    window.addEventListener('mousemove', onMove);\r\n    window.addEventListener('touchmove', onMove, {passive:false});\r\n    window.addEventListener('mouseup',   onUp);\r\n    window.addEventListener('touchend',  onUp);\r\n    function onDown(e){\r\n        e.preventDefault();\r\n        if(!gameStarted) return;\r\n        const {x,y}=getPos(e);\r\n        downTime=Date.now(); downPos={x,y}; didDrag=false;\r\n        dragging=pieceAt(x,y);\r\n        if(!dragging) return;\r\n        playSound('click');\r\n        pieces.splice(pieces.indexOf(dragging),1);\r\n        pieces.push(dragging);\r\n        if(dragging.onBoard){\r\n            dragOffX=x-(BX+dragging.bx); dragOffY=y-(BY+dragging.by);\r\n        } else {\r\n            dragOffX=x-dragging.px; dragOffY=y-dragging.py;\r\n        }\r\n        dragging.dx=x-dragOffX; dragging.dy=y-dragOffY;\r\n        draw();\r\n    }\r\n    function onMove(e){\r\n        if(!dragging) return;\r\n        e.preventDefault();\r\n        const {x,y}=getPos(e);\r\n        if(Math.hypot(x-downPos.x,y-downPos.y)>5) didDrag=true;\r\n        dragging.dx=x-dragOffX;\r\n        dragging.dy=y-dragOffY;\r\n        const overBoard = x>BX&&x<BX+BW&&y>BY&&y<BY+BH;\r\n        dragging.onBoard=overBoard;\r\n        if(overBoard){\r\n            dragging.bx=x-dragOffX-BX;\r\n            dragging.by=y-dragOffY-BY;\r\n        }\r\n        draw();\r\n    }\r\n    function onUp(e){\r\n        if(!dragging){ return; }\r\n        const elapsed=Date.now()-downTime;\r\n        if(!didDrag&&elapsed<300){\r\n            dragging.rotation=(dragging.rotation+90)%360;\r\n            selected=dragging;\r\n            if(!dragging.onBoard){ dragging.dx=dragging.px; dragging.dy=dragging.py; }\r\n        } else {\r\n            const src=e.changedTouches?e.changedTouches[0]:e;\r\n            const rect=canvas.getBoundingClientRect();\r\n            const scaleX=CW\/rect.width, scaleY=CH\/rect.height;\r\n            const fx=(src.clientX-rect.left)*scaleX;\r\n            const fy=(src.clientY-rect.top)*scaleY;\r\n            const overBoard=fx>BX&&fx<BX+BW&&fy>BY&&fy<BY+BH;\r\n            if(overBoard){\r\n                dragging.onBoard=true;\r\n                dragging.bx=fx-dragOffX-BX;\r\n                dragging.by=fy-dragOffY-BY;\r\n                snapCheck(dragging);\r\n                checkWin();\r\n            } else {\r\n                dragging.onBoard=false;\r\n                dragging.px=fx-dragOffX;\r\n                dragging.py=fy-dragOffY;\r\n                dragging.px=Math.max(TX, Math.min(TX+TW-PW*dragging.ps, dragging.px));\r\n                dragging.py=Math.max(TY, Math.min(TY+TH-PH*dragging.ps, dragging.py));\r\n            }\r\n            selected=null;\r\n        }\r\n        dragging=null;\r\n        draw();\r\n    }\r\n    function startGame(n){\r\n        document.getElementById('jig-diff-overlay').classList.remove('show');\r\n        document.getElementById('jig-win-banner').classList.remove('show');\r\n        document.getElementById('jig-start-btn').disabled=true;\r\n        document.getElementById('jig-restart-btn').disabled=false;\r\n        document.getElementById('jig-sort-btn').disabled=false;\r\n        snapFlash = null;\r\n        edgeGlowEnd = Date.now() + 3000;\r\n        setDifficulty(n);\r\n        initLayout();\r\n        createPieces();\r\n        selected=null; dragging=null;\r\n        gameStarted=true;\r\n        draw();\r\n    }\r\n    function restartPuzzle(){\r\n        document.getElementById('jig-win-banner').classList.remove('show');\r\n        startGame(totalPieces);\r\n    }\r\n    document.querySelectorAll('.jig-diff-pick').forEach(btn => {\r\n        btn.addEventListener('click', () => {\r\n            startGame(parseInt(btn.dataset.pieces));\r\n        });\r\n    });\r\n    document.getElementById('jig-start-btn').addEventListener('click', () => {\r\n        if (gameStarted || document.getElementById('jig-start-btn').disabled) return;\r\n        document.getElementById('jig-diff-overlay').classList.add('show');\r\n    });\r\n    document.getElementById('jig-restart-btn').addEventListener('click', restartPuzzle);\r\n    document.getElementById('jig-sort-btn').addEventListener('click', () => {\r\n        if(!gameStarted) return;\r\n        scatterPieces();\r\n        draw();\r\n    });\r\n    document.getElementById('jig-sound-btn').addEventListener('click', () => {\r\n        muted = !muted;\r\n        document.getElementById('jig-sound-btn').textContent = muted ? '\ud83d\udd07' : '\ud83d\udd0a';\r\n    });\r\n    document.getElementById('jig-next-btn').addEventListener('click', () => {\r\n        document.getElementById('jig-start-btn').disabled=false;\r\n        document.getElementById('jig-start-btn').click();\r\n    });\r\n    function onImageReady(){\r\n        imgLoaded=true;\r\n        initLayout();\r\n        scatterPieces();\r\n        draw();\r\n    }\r\n    function initialRender(){\r\n        setDifficulty(9);\r\n        initLayout();\r\n        createPieces();\r\n        gameStarted=false;\r\n        if(PUZZLE_IMAGE_SRC){\r\n            img.onload=onImageReady;\r\n            img.onerror=()=>{ imgLoaded=false; draw(); };\r\n            if(img.complete&&img.naturalWidth>0) onImageReady();\r\n            else img.src=PUZZLE_IMAGE_SRC+'?v='+Date.now();\r\n        } else draw();\r\n    }\r\n    initialRender();\r\n    window.addEventListener('resize',()=>{ initLayout(); scatterPieces(); draw(); });\r\n})();\r\n<\/script><\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":166,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-535","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/posts\/535","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/comments?post=535"}],"version-history":[{"count":21,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/posts\/535\/revisions"}],"predecessor-version":[{"id":560,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/posts\/535\/revisions\/560"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/media\/166"}],"wp:attachment":[{"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/media?parent=535"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/categories?post=535"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/studiohos.com\/el\/wp-json\/wp\/v2\/tags?post=535"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}