{"connections":[{"sourceLocalID":2,"sourcePortID":"outlet_output","targetLocalID":0,"targetPortID":"inlet_input"}],"hideGrid":false,"name":"Tread Lightly","nodes":[{"config":{"chrome":"none","closeIsDismiss":true,"height":520,"html":"<!doctype html>\n<html lang='en'>\n  <head>\n    <meta charset='utf-8'>\n    <meta name='viewport' content='width=device-width,initial-scale=1'>\n    <title>Tread Lightly</title>\n    <style>\n:root{color-scheme:dark;font-family:system-ui,-apple-system,sans-serif;background:#0b1020;color:#e8edf8}*{box-sizing:border-box}body{margin:0;min-height:100vh;min-height:100dvh;display:grid;place-items:center;background:radial-gradient(ellipse at top,#20314b,#0b1020 70%)}main{text-align:center;padding:20px;max-width:100vw}header{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-bottom:16px}h1{font-size:28px;margin:0;letter-spacing:-1px}.eyebrow{font-size:11px;letter-spacing:3px;color:#72dfbd;margin-bottom:5px;text-align:left}button,select{font:inherit;color:inherit;border:1px solid #42516b;background:#22314a;border-radius:9px;padding:9px 13px;cursor:pointer}button:hover,select:hover{background:#304361}button:focus-visible,select:focus-visible{outline:3px solid #72dfbd;outline-offset:2px}.controls,.stats{display:flex;justify-content:center;align-items:center;gap:10px;flex-wrap:wrap}.stats{margin:14px 0;font-size:14px;color:#bac8df}.stats strong{color:#fff;font-variant-numeric:tabular-nums}.stats span{background:#111b2e;border:1px solid #293b57;border-radius:8px;padding:7px 12px}#flag[aria-pressed='true']{background:#19684f;border-color:#72dfbd}#board{--cols:9;--rows:9;--cell:min(38px,calc((100vw - 64px)/var(--cols)),calc((100dvh - 285px)/var(--rows)));display:grid;grid-template-columns:repeat(var(--cols),var(--cell));gap:3px;width:max-content;margin:auto;padding:8px;border-radius:12px;background:#080e1a;border:1px solid #36445d;box-shadow:0 20px 70px #0007}#board .cell{width:var(--cell);height:var(--cell);padding:0;border-radius:5px;border:1px solid #425571;background:linear-gradient(#344a69,#253951);font-weight:800;font-size:clamp(11px,2vw,18px);display:grid;place-items:center;user-select:none;-webkit-user-select:none}#board .cell:hover{filter:brightness(1.25)}#board .open{background:#131f31;border-color:#202d41}#board .hit{background:#9a3546}#board .flagged{color:#ffe1a6}#board [data-number='1']{color:#77b9ff}#board [data-number='2']{color:#72dfbd}#board [data-number='3']{color:#ff8b94}#board [data-number='4']{color:#c9a4ff}#board [data-number='5']{color:#ffc37d}#board [data-number='6']{color:#6ee7ee}#board [data-number='7']{color:#e7c9f5}#board [data-number='8']{color:#e8edf8}#status{min-height:24px;margin:14px 0 6px;font-weight:600;color:#72dfbd}.hint{font-size:12px;color:#94a5be;margin:0}#exit{background:transparent;color:#bdc9dd;font-size:13px} @media(max-height:550px){main{padding:8px}header{margin-bottom:6px}h1{font-size:20px}.eyebrow,.hint{display:none}.stats{margin:6px 0}#status{margin:6px 0}#board{--cell:min(32px,calc((100vw - 64px)/var(--cols)),calc((100dvh - 210px)/var(--rows)))}}\n</style>\n  </head>\n  <body>\n    <main>\n      <header>\n        <div>\n          <div class='eyebrow'>ONE SQUARE AT A TIME</div>\n          <h1>Tread Lightly</h1>\n        </div>\n        <button id='exit'>Exit · Esc</button>\n      </header>\n      <div class='controls'>\n        <select id='difficulty' aria-label='Difficulty'>\n          <option value='easy'>Beginner · 9 × 9</option>\n          <option value='medium'>Intermediate · 16 × 16</option>\n          <option value='hard'>Expert · 30 × 16</option>\n        </select>\n        <button id='restart'>New Game</button><button id='flag' aria-pressed='false'>⚑ Flag Mode</button>\n      </div>\n      <div class='stats'>\n        <span>Mines − flags <strong id='remaining'>10</strong></span><span>Time <strong id='time'>0:00</strong></span><span>Cleared <strong id='cleared'>0 / 71</strong></span>\n      </div>\n      <div id='board' role='group' aria-label='Tread Lightly board'></div>\n      <p id='status' role='status' aria-live='polite'>Choose a square. Your first click is safe.</p>\n      <p class='hint'>Click to reveal · Right-click to flag · Arrow keys to move · F to flag · Escape to exit</p>\n    </main>\n    <script>\n(()=>{\nconst $=id=>document.getElementById(id);\nconst board=$('board'),status=$('status');\nconst levels={easy:[9,9,10],medium:[16,16,40],hard:[30,16,99]};\nlet cols,rows,mines,cells,started=false,ended=false,flagMode=false,startedAt=0,ticker=null,exploded=-1;\nfunction around(i){const x=i%cols,y=Math.floor(i/cols),out=[];for(let dy=-1;dy<=1;dy++){for(let dx=-1;dx<=1;dx++){const nx=x+dx,ny=y+dy;if((dx||dy)&&nx>=0&&nx<cols&&ny>=0&&ny<rows)out.push(ny*cols+nx);}}return out;}\nfunction time(){const seconds=started?Math.floor((Date.now()-startedAt)/1000):0;$('time').textContent=Math.floor(seconds/60)+':'+String(seconds%60).padStart(2,'0');}\nfunction seed(first){const safe=new Set([first,...around(first)]),pool=[];for(let i=0;i<cells.length;i++)if(!safe.has(i))pool.push(i);for(let i=pool.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[pool[i],pool[j]]=[pool[j],pool[i]];}for(let i=0;i<mines;i++)cells[pool[i]].mine=true;cells.forEach((cell,i)=>cell.number=around(i).filter(j=>cells[j].mine).length);started=true;startedAt=Date.now();ticker=setInterval(time,250);status.textContent='Find every safe square.';}\nfunction draw(){const active=document.activeElement;const focused=board.contains(active)?Number(active.dataset.index):-1;board.replaceChildren();let flags=0,cleared=0;cells.forEach((cell,i)=>{if(cell.flag)flags++;if(cell.open&&!cell.mine)cleared++;const button=document.createElement('button');button.type='button';button.className='cell';button.dataset.index=i;let label='Covered';if(cell.open){button.classList.add('open');button.dataset.number=cell.number;button.textContent=cell.number||'';label=cell.number?cell.number+' adjacent mines':'Empty';}if(cell.flag){button.classList.add('flagged');button.textContent='⚑';label='Flagged';}if(ended&&cell.mine){button.textContent=cell.flag?'⚑':'✹';label='Mine';}if(ended&&cell.flag&&!cell.mine){button.textContent='×';label='Incorrect flag';}if(i===exploded)button.classList.add('hit');button.setAttribute('aria-label','Row '+(Math.floor(i/cols)+1)+', column '+(i%cols+1)+': '+label);button.setAttribute('aria-disabled',String(ended));button.addEventListener('click',()=>flagMode?toggleFlag(i):reveal(i));button.addEventListener('contextmenu',event=>{event.preventDefault();toggleFlag(i);});button.addEventListener('keydown',event=>{if(event.key.toLowerCase()==='f'){event.preventDefault();toggleFlag(i);return;}const x=i%cols,y=Math.floor(i/cols);let target=i;if(event.key==='ArrowLeft')target=y*cols+Math.max(0,x-1);else if(event.key==='ArrowRight')target=y*cols+Math.min(cols-1,x+1);else if(event.key==='ArrowUp')target=Math.max(0,y-1)*cols+x;else if(event.key==='ArrowDown')target=Math.min(rows-1,y+1)*cols+x;else return;event.preventDefault();board.children[target].focus();});board.appendChild(button);});$('remaining').textContent=mines-flags;$('cleared').textContent=cleared+' / '+(cells.length-mines);if(focused>=0&&board.children[focused])board.children[focused].focus({preventScroll:true});}\nfunction toggleFlag(i){if(ended||cells[i].open)return;cells[i].flag=!cells[i].flag;draw();}\nfunction finish(won){time();ended=true;clearInterval(ticker);if(won){cells.forEach(cell=>{if(cell.mine)cell.flag=true;});status.textContent='You cleared the field! Play again?';}else status.textContent='Mine hit. Start a new game and try again.';}\nfunction reveal(i){if(ended||cells[i].flag||cells[i].open)return;if(!started)seed(i);if(cells[i].mine){exploded=i;finish(false);draw();return;}const stack=[i];while(stack.length){const j=stack.pop(),cell=cells[j];if(cell.open||cell.flag||cell.mine)continue;cell.open=true;if(cell.number===0)for(const neighbor of around(j))if(!cells[neighbor].open)stack.push(neighbor);}if(cells.filter(cell=>cell.open&&!cell.mine).length===cells.length-mines)finish(true);draw();}\nfunction reset(){clearInterval(ticker);[cols,rows,mines]=levels[$('difficulty').value];cells=Array.from({length:cols*rows},()=>({mine:false,number:0,open:false,flag:false}));started=false;ended=false;exploded=-1;flagMode=false;$('flag').setAttribute('aria-pressed','false');board.style.setProperty('--cols',cols);board.style.setProperty('--rows',rows);status.textContent='Choose a square. Your first click is safe.';time();draw();}\n$('restart').addEventListener('click',reset);$('difficulty').addEventListener('change',reset);$('flag').addEventListener('click',()=>{flagMode=!flagMode;$('flag').setAttribute('aria-pressed',String(flagMode));});function exit(){clearInterval(ticker);watchflows.dismiss();}$('exit').addEventListener('click',exit);document.addEventListener('keydown',event=>{if(event.key==='Escape'){event.preventDefault();exit();}});reset();\n})();\n</script>\n  </body>\n</html>","timeoutMinutes":0,"title":"Tread Lightly","width":720,"windowMode":"fullscreen"},"customName":"Tread Lightly","isDisabled":false,"localID":0,"typeID":"action.html_view","x":460,"y":260},{"config":{"_height":380,"_width":460,"text":"**Tread Lightly is a full-screen mine-clearing puzzle that starts when you run this flow.**\n\n**What it does**\n- Clear every safe square without opening a mine. Your first click is always safe.\n- Three board sizes: 9 by 9, 16 by 16 and 30 by 16.\n- Works offline and waits until you close it.\n\n**Set it up**\n- Nothing to connect. Run the flow to play.\n- Click to reveal a square. Right-click or press F to flag one. Arrow keys move around the board.\n- Press Escape or click Exit to close the game.\n\n**Make it yours**\n- Set Window to Window on **Tread Lightly** to play in a normal window instead of full screen."},"isDisabled":false,"localID":1,"typeID":"note","x":100,"y":-220},{"config":{"inputType":"text","textInput":"Play"},"customName":"Launch Game","isDisabled":false,"localID":2,"typeID":"trigger.input","x":100,"y":200}],"version":1}