<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.pandoraburo.ru/" xml:lang="ru">
  <id>https://www.pandoraburo.ru/shesterni-kiberklyk</id>
  <link href="https://www.pandoraburo.ru/shesterni-kiberklyk.xml" rel="self"/>
  <title/>
  <subtitle type="html">
&lt;html lang="ru"&gt;&lt;head&gt;&lt;meta charset="UTF-8"&gt;&lt;title&gt;ХРОНОКЛЫК&lt;/title&gt;
&lt;style&gt;html,body{margin:0;height:100%;background:#0a0c10;overflow:hidden}#wrap{display:flex;align-items:center;justify-content:center;height:100%}canvas{background:#0d1117;touch-action:none}&lt;/style&gt;&lt;/head&gt;
&lt;body&gt;&lt;div id="wrap"&gt;&lt;canvas id="game" width="960" height="600"&gt;&lt;/canvas&gt;&lt;/div&gt;
&lt;script&gt;
const cv=document.getElementById('game'),ctx=cv.getContext('2d'),W=cv.width,H=cv.height,TAU=Math.PI*2;
let keys={},press=[],mx=W/2,my=H/2;
addEventListener('keydown',e=&gt;{if(['Space','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','KeyQ','KeyE','KeyF','Enter','KeyC','KeyL','KeyX','KeyV'].includes(e.code))e.preventDefault();if(!e.repeat)press.push(e.code);keys[e.code]=true;initAudio();});
addEventListener('keyup',e=&gt;keys[e.code]=false);
cv.addEventListener('mousedown',e=&gt;{initAudio();if(state==='play'){if(e.button===2)press.push('KeyL');else press.push('Click');}else if(state==='edit'){ed.down=true;paint();}});
addEventListener('mouseup',()=&gt;{if(ed)ed.down=false;});
cv.addEventListener('mousemove',e=&gt;{const r=cv.getBoundingClientRect();mx=(e.clientX-r.left)*(W/r.width);my=(e.clientY-r.top)*(H/r.height);if(state==='edit'&amp;&amp;ed.down)paint();});
cv.addEventListener('contextmenu',e=&gt;{e.preventDefault();if(state==='edit')delete ed.walls[Math.floor(mx/30)+','+Math.floor(my/30)];});

let state='menu',levelIdx=0,page=0,time=0,trophies=0,trophTotal=0,ending=null,customMode=false,CUR=null;
let player,enemies,walls,exitZone,requiredKills,kills,popups,scanR,lastHurt,alerted,parries,shame,spared,endT,projectiles,muzzle=0;
let drones=[],droneCd=0,parts=[],zaps=[],rings=[],hpBonus=0,mile=0,revives=0,killT=[],comboCd=0;
let upg={mask:0,pauld:0,core:0,cable:0,blade:0};
const maxhp=()=&gt;100+25*upg.pauld+hpBonus,gmax=()=&gt;10+2*upg.core,dmul=()=&gt;1+0.2*upg.blade;
function gain(n){trophies+=n;trophTotal+=n;const m=Math.floor(trophTotal/5);if(m&gt;mile){mile=m;if(hpBonus&lt;25){hpBonus+=5;player.hp=Math.min(maxhp(),player.hp+5);pop(player.x,player.y-34,'+5 макс HP');}}}

// ===== ЗВУК =====
let AC=null,master=null,muted=false,nextBar=0,barN=0;
function initAudio(){if(AC)return;try{AC=new (window.AudioContext||window.webkitAudioContext)();master=AC.createGain();master.connect(AC.destination);nextBar=AC.currentTime+0.1;setInterval(schedule,200);}catch(e){}}
function note(f,t,d,type,g){if(!AC)return;const o=AC.createOscillator(),v=AC.createGain();o.type=type;o.frequency.value=f;v.gain.setValueAtTime(g,t);v.gain.exponentialRampToValueAtTime(0.0001,t+d);o.connect(v);v.connect(master);o.start(t);o.stop(t+d);}
function beep(f,d,type,g,slide){if(!AC)return;const t=AC.currentTime,o=AC.createOscillator(),v=AC.createGain();o.type=type||'square';o.frequency.setValueAtTime(f,t);if(slide)o.frequency.exponentialRampToValueAtTime(Math.max(30,f+slide),t+d);v.gain.setValueAtTime(g||0.12,t);v.gain.exponentialRampToValueAtTime(0.0001,t+d);o.connect(v);v.connect(master);o.start(t);o.stop(t+d);}
function noise(d,g){if(!AC)return;const n=AC.sampleRate*d,b=AC.createBuffer(1,n,AC.sampleRate),data=b.getChannelData(0);for(let i=0;i&lt;n;i++)data[i]=(Math.random()*2-1)*(1-i/n);const s=AC.createBufferSource();s.buffer=b;const v=AC.createGain();v.gain=g||0.15;s.connect(v);v.connect(master);s.start();}
function schedule(){while(nextBar&lt;AC.currentTime+0.6){playBar(nextBar,barN);nextBar+=2.4;barN++;}}
function playBar(t,n){const roots=[55,49,65.41,43.65],root=roots[n%4];for(let i=0;i&lt;8;i++)note(root*2,t+i*0.3,0.25,'triangle',0.045);if(n%2===0)note(root*4,t,2.2,'sine',0.03);const pent=[220,261.63,329.63,392,440];for(let i=0;i&lt;4;i++)if(Math.random()&lt;0.55)note(pent[(Math.random()*5)|0]*2,t+i*0.6+Math.random()*0.1,0.2,'square',0.018);}
const sfx={atk:()=&gt;noise(0.07,0.1),hit:()=&gt;beep(200,0.1,'sawtooth',0.12,-80),kill:()=&gt;{beep(110,0.25,'sawtooth',0.18,-60);beep(660,0.2,'sine',0.08);},parry:()=&gt;{beep(1400,0.12,'square',0.1,-500);beep(2100,0.25,'triangle',0.08);},scan:()=&gt;beep(300,0.4,'sine',0.1,600),cloak:()=&gt;beep(800,0.5,'sine',0.08,-500),flame:()=&gt;noise(0.3,0.15),heat:()=&gt;{beep(220,0.3,'square',0.12);beep(180,0.45,'square',0.12);},alert:()=&gt;beep(520,0.15,'square',0.1,200),wind:()=&gt;beep(90,0.5,'sawtooth',0.15,60),boss:()=&gt;{beep(70,0.7,'sawtooth',0.2,-20);noise(0.4,0.2);},spare:()=&gt;{beep(440,0.3,'sine',0.1);beep(550,0.45,'sine',0.1);beep(660,0.7,'sine',0.1);},ui:()=&gt;beep(700,0.06,'square',0.06),shoot:()=&gt;{beep(900,0.1,'sawtooth',0.1,-650);noise(0.05,0.08);},zap:()=&gt;beep(1600,0.08,'sawtooth',0.08,-900),tick:()=&gt;beep(1200,0.03,'square',0.04,-300),drone:()=&gt;beep(600,0.2,'triangle',0.08,300)};

// ===== СЕЙВЫ/ДОСТИЖЕНИЯ =====
const SAVEKEY='chronoklyk_v3';let save=null;try{save=JSON.parse(localStorage.getItem(SAVEKEY));}catch(e){}
save=save||{unlocked:0,best:0,ach:{},endings:{}};
function writeSave(){try{localStorage.setItem(SAVEKEY,JSON.stringify(save));}catch(e){}}
const ACH=[['first','Первый трофей','добыть трофей'],['ghost','Без тени','пройти акт без тревог'],['parry5','Мастер парри','5 парри с Первым'],['honor','Железная честь','15+ трофеев за прохождение'],['eternal','Вечный двигатель','секретная концовка'],['smith','Кузнец','купить 3 апгрейда за прохождение']];
function unlock(id){if(save.ach[id])return;save.ach[id]=1;writeSave();if(player)pop(player.x,player.y-32,'? Достижение!');}

// ===== СЮЖЕТ =====
const STORY=[
[['АКТ I — КОНТРАКТ','Ноктюрн не видит дня. Великая Пружина заводится триста лет.','Ты — КАЙРОС, часовой охотник Гильдии.'],['Контракт: зачистить квартал отступников.','Первые 20 секунд тебя держит защитное поле.','Каждые 4 с от тебя отделяется боевой дрон-прикрытие.']],
[['АКТ II — ЧЕРТЕЖИ','В тайнике отступников — чертежи ТЕБЯ.','Лита: «Пружину заводят души. Твои трофеи — не металл».'],['Гильдия выслала дронов и гончих.','Стражи стреляют издалека. Гончие чуют тепло.']],
[['АКТ III — КОТЕЛЬЩИК','Страж Пружины не спрашивает имён — он давит.','Его удар читается кольцом пара.'],['Отойди из кольца — и после удара он остынет.','Бей в открытый клапан: урон удвоен.']],
[['АКТ IV — МАТЬ СОТНИ ШЕСТЕРЁН','Она рожает шестерёнышей и шепчет на языке трещоток.','Каждый укус возвращает кусок твоей души в Пружину.'],['Она кружит и бросается; после броска — отдыхает.','Не дай выводку обнять тебя.']],
[['АКТ V — ПЕРВЫЙ','Остался Первый — тот, чьё лицо ты носишь.'],['Кольцо вспышки — жми ПРОБЕЛ в последний миг: ПАРРИ.','Когда он преклонит колено — подойди и выбери: клинок или пощада.']]];
const ENDTXT={dark:['Ты опустил клинок на безоружного.','Первый был прав: ты — машина.','','Ноктюрн получил нового заводчика.','КОНЕЦ: «НОВЫЙ ЗАВОДЧИК»'],free:['Ты пощадил Первого и разрушил Пружину душ.','Город погрузился во тьму — но впервые дышит сам.','','КОНЕЦ: «РАССВЕТ В ТЕМНОТЕ»'],secret:['Ты пощадил Первого и выковал новую Пружину.','Её заводят смех, шаги и свет в окнах Ноктюрна.','','СЕКРЕТНЫЙ КОНЕЦ: «ВЕЧНЫЙ ДВИГАТЕЛЬ»']};

// ===== УРОВНИ =====
function en(x,y,type,wp){const hp=type=='elite'?140:type=='boss'?300:type=='boiler'?420:type=='mother'?350:type=='gearling'?20:60;
return{x,y,r:type=='boss'?18:type=='boiler'?26:type=='mother'?20:type=='elite'?17:type=='gearling'?9:13,type,hp,maxhp:hp,wp,wi:0,face:Math.random()*TAU,state:'patrol',det:0,atkCd:0,shCd:1.5,loseT:0,lx:x,ly:y,stunT:0,atkT:1.6,sumT:3,t:0,vx:0,vy:0,hitDone:false,speed:type=='hound'?150:type=='gearling'?170:type=='boiler'?55:type=='mother'?120:type=='elite'?105:85,view:type=='hound'?250:type=='gearling'?300:190,cone:type=='hound'?0.75:type=='gearling'?1.2:0.65};}
const LEVELS=[
{req:3,walls:[{x:180,y:110,w:36,h:190},{x:380,y:300,w:210,h:36},{x:690,y:90,w:36,h:170},{x:500,y:60,w:170,h:28},{x:140,y:430,w:190,h:28},{x:740,y:380,w:150,h:36},{x:420,y:140,w:30,h:110}],en:()=&gt;[en(300,150,'guard',[[300,150],[300,260]]),en(600,200,'guard',[[600,200],[600,330],[470,380]]),en(810,300,'guard',[[810,300],[810,170]]),en(500,480,'guard',[[500,480],[700,480]]),en(240,520,'guard',[[240,520],[340,520]])]},
{req:4,walls:[{x:240,y:90,w:36,h:180},{x:680,y:90,w:36,h:180},{x:240,y:330,w:180,h:36},{x:540,y:330,w:180,h:36},{x:430,y:180,w:100,h:36},{x:140,y:470,w:150,h:30},{x:670,y:470,w:150,h:30}],en:()=&gt;[en(100,140,'guard',[[100,140],[100,300]]),en(480,90,'hound',[[420,90],[560,90]]),en(830,150,'guard',[[830,150],[830,300]]),en(480,420,'hound',[[300,420],[660,420]]),en(120,540,'guard',[[120,540],[300,540]]),en(840,540,'guard',[[840,540],[700,540]])]},
{req:999,walls:[{x:230,y:140,w:50,h:50},{x:680,y:140,w:50,h:50},{x:230,y:410,w:50,h:50},{x:680,y:410,w:50,h:50}],en:()=&gt;[en(480,160,'boiler',[[480,160]]),en(100,300,'guard',[[100,300],[100,450]]),en(860,300,'guard',[[860,300],[860,450]])]},
{req:999,walls:[{x:455,y:120,w:50,h:120},{x:455,y:360,w:50,h:120},{x:200,y:270,w:120,h:50},{x:640,y:270,w:120,h:50}],en:()=&gt;[en(480,150,'mother',[[480,150]])]},
{req:999,walls:[{x:180,y:110,w:36,h:190},{x:690,y:90,w:36,h:170},{x:140,y:430,w:190,h:28},{x:740,y:380,w:150,h:36}],en:()=&gt;[en(480,170,'boss',[[480,170]]),en(200,520,'hound',[[200,520],[420,520]]),en(820,520,'hound',[[820,520],[600,520]])]}];
LEVELS.forEach((L,i)=&gt;L.story=STORY[i]);

// ===== КУЗНЯ =====
const SHOP=[['mask','Маска','−15% радиус обнаружения',[4,6,8]],['pauld','Наплечники','+25 макс. прочности',[4,6,8]],['core','Ядро','+2 макс. ⚙',[5,7,9]],['cable','Кабели','+радиус и стан скана',[3,5,7]],['blade','Клинок','+20% урона и плазмы',[5,7,9]]];
let buys=0;
function buy(i){const it=SHOP[i],lv=upg[it[0]];if(lv&gt;=3)return;const c=it[3][lv];if(trophies&lt;c)return;trophies-=c;upg[it[0]]++;buys++;if(buys&gt;=3)unlock('smith');if(it[0]==='pauld'&amp;&amp;player)player.hp=Math.min(maxhp(),player.hp+25);sfx.ui();}

// ===== ЭФФЕКТЫ =====
function boom(x,y,col,n){for(let i=0;i&lt;n;i++){const a=Math.random()*TAU,s=60+Math.random()*220;parts.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,t:0.4+Math.random()*0.5,col,sz:1+Math.random()*3});}rings.push({x,y,t:0.4,r0:8,r1:60+n,col});}
function zapLine(x1,y1,x2,y2){zaps.push({x1,y1,x2,y2,t:0.15});}
function pop(x,y,t){popups.push({x,y,t:1.2,txt:t});}
function angDiff(a,b){let d=a-b;while(d&gt;Math.PI)d-=TAU;while(d&lt;-Math.PI)d+=TAU;return d;}
function collideWalls(o){for(const w of walls){const cx=Math.max(w.x,Math.min(o.x,w.x+w.w)),cy=Math.max(w.y,Math.min(o.y,w.y+w.h));let dx=o.x-cx,dy=o.y-cy;const d=Math.hypot(dx,dy);if(d&lt;o.r&amp;&amp;d&gt;0){o.x=cx+dx/d*o.r;o.y=cy+dy/d*o.r;}else if(d===0)o.y=w.y-o.r;}}
function los(x1,y1,x2,y2){for(let i=1;i&lt;20;i++){const x=x1+(x2-x1)*i/20,y=y1+(y2-y1)*i/20;for(const w of walls)if(x&gt;w.x&amp;&amp;x&lt;w.x+w.w&amp;&amp;y&gt;w.y&amp;&amp;y&lt;w.y+w.h)return false;}return true;}
function anyBoss(){for(const e of enemies)if((e.type=='boss'||e.type=='boiler'||e.type=='mother')&amp;&amp;!e.dead)return e;return null;}
function segDist(px,py,x1,y1,x2,y2){const dx=x2-x1,dy=y2-y1,l2=dx*dx+dy*dy;let t=l2?((px-x1)*dx+(py-y1)*dy)/l2:0;t=Math.max(0,Math.min(1,t));return Math.hypot(px-(x1+t*dx),py-(y1+t*dy));}

// ===== ПРАВИЛА ЖИЗНИ =====
function onKill(){const p=player;p.auraT=7;killT.push(time);killT=killT.filter(t2=&gt;time-t2&lt;8);
if(killT.length&gt;=3&amp;&amp;comboCd&lt;=0){comboCd=8;p.hp=Math.min(maxhp(),p.hp+15);p.speedT=3;pop(p.x,p.y-40,'СЕРИЯ! +15 HP');sfx.spare();}}
function triggerOverheat(){const p=player;p.overT=2.5;p.auraT=0;pop(p.x,p.y-24,'ПЕРЕГРЕВ!');sfx.heat();
for(const e of enemies){if(e.dead)continue;const d=Math.hypot(e.x-p.x,e.y-p.y);if(d&lt;130){const k=90/Math.max(20,d);e.x+=(e.x-p.x)*k;e.y+=(e.y-p.y)*k;e.stunT=Math.max(e.stunT||0,0.6);}}
boom(p.x,p.y,'#cfd8e3',18);}
function hurtP(n,src){const p=player;
if(p.shieldT&gt;0){pop(p.x,p.y-18,'⚡');sfx.zap();return;}
if(p.invT&gt;0)return;
if(p.microT&gt;0){p.microT=0;pop(p.x,p.y-18,'?');sfx.zap();return;}
if(p.auraT&gt;0){if(src=='boss')n=Math.ceil(n/2);else{pop(p.x,p.y-18,'?');sfx.zap();return;}}
p.hp-=n;lastHurt=time;pop(p.x,p.y-18,'-'+n);sfx.hit();
if(p.hp&lt;=0){
 if(p.gears&gt;=3&amp;&amp;p.springCd&lt;=0){p.gears-=3;p.hp=35;p.invT=2;p.springCd=6;pop(p.x,p.y-26,'? Пружина жизни!');sfx.spare();return;}
 if(revives&lt;2&amp;&amp;trophies&gt;=5){trophies-=5;revives++;p.hp=Math.round(maxhp()*0.6);p.shieldT=20;pop(p.x,p.y-26,'Возрождение за 5?');sfx.spare();return;}}}

// ===== БОЙ =====
function damageEnemy(e,dmg,ranged){e.hp-=dmg;e.state='alert';e.lx=player.x;e.ly=player.y;pop(e.x,e.y-16,'-'+dmg);sfx.hit();
if(e.hp&lt;=0){e.dead=true;kills++;gain(ranged?2:(e.type=='gearling'?1:2));pop(e.x,e.y-30,'Трофей!');unlock('first');boom(e.x,e.y,'#ff9a3a',14);onKill();}}
function applyBoss(b,d){b.hp-=d;pop(b.x,b.y-16,'-'+d);sfx.hit();
if(b.hp&lt;=0&amp;&amp;!b.dead){b.dead=true;gain(5);pop(b.x,b.y-28,'Ядро добыто! +5?');sfx.kill();unlock('first');boom(b.x,b.y,'#ffd76a',40);onKill();endT=2;}}
function bossShot(b,dmg){if(b.type=='boss'){if(b.state=='kneel'){b.dead=true;b.state='dead';shame=true;endT=2;pop(b.x,b.y-20,'ПОЗОР…');sfx.heat();return;}
 applyBoss(b,Math.round(dmg*(b.state=='windup'?0.5:b.state=='stagger'?2:1)));return;}
const vuln=(b.state=='tired'||b.state=='rest'||b.state=='stagger')?2:((b.state=='windup'||b.state=='dashT')?0.5:1);
applyBoss(b,Math.round(dmg*vuln));}
function shoot(){const p=player;if(p.shootCd&gt;0||p.overT&gt;0)return;p.shootCd=0.55;
const a=p.aim,sx=p.x+Math.cos(a)*(p.r+10),sy=p.y+Math.sin(a)*(p.r+10);
projectiles.push({x:sx,y:sy,vx:Math.cos(a)*430,vy:Math.sin(a)*430,dmg:Math.round(25*dmul()*(p.laser?2:1)),t:1.4});
p.heat=Math.min(100,p.heat+6);muzzle=0.08;sfx.shoot();
if(p.heat&gt;=100)triggerOverheat();}
function attack(){const p=player;if(p.atkCd&gt;0||p.overT&gt;0)return;p.atkCd=0.45;sfx.atk();
let best=null,bd=56;for(const e of enemies){if(e.dead)continue;const d=Math.hypot(e.x-p.x,e.y-p.y);if(d&lt;bd+e.r){bd=d;best=e;}}
if(!best)return;
if(best.type!='guard'&amp;&amp;best.type!='hound'&amp;&amp;best.type!='elite'&amp;&amp;best.type!='gearling'){bossHit(best);return;}
const behind=Math.abs(angDiff(best.face,Math.atan2(p.y-best.y,p.x-best.x)))&gt;1.9;
if(best.state!=='alert'&amp;&amp;behind){best.dead=true;kills++;gain(3);p.gears=Math.min(gmax(),p.gears+3);p.hp=Math.min(maxhp(),p.hp+10);pop(best.x,best.y-16,'Трофей! +3⚙ +10HP');sfx.kill();unlock('first');boom(best.x,best.y,'#ff9a3a',14);onKill();}
else damageEnemy(best,Math.round((p.flameT&gt;0?60:34)*dmul()),false);
if(p.heat&gt;=100)triggerOverheat();}
function bossHit(b){const p=player;
if(b.type=='boss'){
 if(b.state=='kneel'){b.dead=true;b.state='dead';shame=true;endT=2;pop(b.x,b.y-20,'ПОЗОР…');sfx.heat();return;}
 if(b.state=='windup'&amp;&amp;b.t&lt;0.34){parries++;p.gears=Math.min(gmax(),p.gears+2);p.hp=Math.min(maxhp(),p.hp+5);applyBoss(b,40);b.state='stagger';b.t=1.6;pop(b.x,b.y-20,'ПАРРИ! +5HP');sfx.parry();if(parries&gt;=5)unlock('parry5');return;}
 if(b.state=='windup'){applyBoss(b,5);pop(b.x,b.y-20,'Блок');return;}
 const flame=p.flameT&gt;0;applyBoss(b,Math.round((b.state=='stagger'?68:(flame?60:34))*dmul()));if(flame)p.heat=Math.min(100,p.heat+22);if(b.state=='duel')b.atkT=0.4;
 if(p.heat&gt;=100)triggerOverheat();return;}
bossShot(b,Math.round((p.flameT&gt;0?60:34)*dmul()));if(p.flameT&gt;0)p.heat=Math.min(100,p.heat+22);
if(p.heat&gt;=100)triggerOverheat();}

// ===== ДРОНЫ =====
function spawnDrone(){const a=Math.random()*TAU;drones.push({x:player.x+Math.cos(a)*22,y:player.y+Math.sin(a)*22,t:12,burstCd:1.2,burst:0,burstT:0,laserCd:4+Math.random()*3,charge:0,beam:0,ramCd:6+Math.random()*4,ramT:0,state:'orbit',vx:0,vy:0,interCd:0,dead:false});
rings.push({x:player.x,y:player.y,t:0.3,r0:6,r1:40,col:'#7ec4ff'});sfx.drone();}
function updDrone(d,dt){d.t-=dt;
let best=null,bd=1e9;for(const e of enemies){if(e.dead||(e.type=='boss'&amp;&amp;e.state=='kneel'))continue;const dd=(e.x-d.x)**2+(e.y-d.y)**2;if(dd&lt;bd){bd=dd;best=e;}}
if(!best){const a=time*2+d.x*0.01,tx=player.x+Math.cos(a)*40,ty=player.y+Math.sin(a)*40,dd=Math.hypot(tx-d.x,ty-d.y);if(dd&gt;4){d.x+=(tx-d.x)/dd*150*dt;d.y+=(ty-d.y)/dd*150*dt;}return;}
const dx=best.x-d.x,dy=best.y-d.y,dist=Math.max(1,Math.hypot(dx,dy));
if(d.state=='ram'){d.x+=d.vx*520*dt;d.y+=d.vy*520*dt;d.ramT-=dt;
 if(dist&lt;best.r+10){const isB=best.type!='guard'&amp;&amp;best.type!='hound'&amp;&amp;best.type!='elite'&amp;&amp;best.type!='gearling';
  if(isB)bossShot(best,20);else damageEnemy(best,40,true);
  for(const e of enemies){if(e.dead||e===best)continue;if(Math.hypot(e.x-d.x,e.y-d.y)&lt;80)damageEnemy(e,20,true);}
  boom(d.x,d.y,'#ff9a3a',30);boom(d.x,d.y,'#8a93a6',12);sfx.boss();d.dead=true;}
 else if(d.ramT&lt;=0)d.state='orbit';
 return;}
const ang=Math.atan2(dy,dx)+0.9,tx=best.x-Math.cos(ang)*140,ty=best.y-Math.sin(ang)*140,dd2=Math.hypot(tx-d.x,ty-d.y);
if(dd2&gt;6){d.x+=(tx-d.x)/dd2*170*dt;d.y+=(ty-d.y)/dd2*170*dt;}
d.burstCd-=dt;if(d.burstCd&lt;=0){d.burstCd=3;d.burst=5;d.burstT=0;}
if(d.burst&gt;0){d.burstT-=dt;if(d.burstT&lt;=0){d.burstT=0.09;d.burst--;const a=Math.atan2(dy,dx)+(Math.random()-0.5)*0.14;
 projectiles.push({x:d.x,y:d.y,vx:Math.cos(a)*380,vy:Math.sin(a)*380,dmg:4,t:1.2,drone:true,col:'#ffd24d'});sfx.tick();}}
d.laserCd-=dt;
if(d.charge&gt;0){d.charge-=dt;if(d.charge&lt;=0)d.beam=0.8;}
else if(d.beam&gt;0){d.beam-=dt;const isB=best.type!='guard'&amp;&amp;best.type!='hound'&amp;&amp;best.type!='elite'&amp;&amp;best.type!='gearling';
 best.hp-=(isB?10:20)*dt;if(Math.random()&lt;0.3)parts.push({x:best.x,y:best.y,vx:(Math.random()-0.5)*160,vy:(Math.random()-0.5)*160,t:0.3,col:'#7ec4ff',sz:2});
 for(const pr of projectiles)if(pr.foe&amp;&amp;segDist(pr.x,pr.y,d.x,d.y,best.x,best.y)&lt;22){pr.t=0;zapLine(pr.x,pr.y,d.x,d.y);sfx.zap();}
 if(best.hp&lt;=0&amp;&amp;!best.dead){if(isB)applyBoss(best,1);else damageEnemy(best,1,true);}
 if(d.beam&lt;=0)d.laserCd=7;}
else if(d.laserCd&lt;=0)d.charge=0.6;
d.interCd-=dt;if(d.interCd&lt;=0){for(const pr of projectiles){if(pr.foe&amp;&amp;Math.hypot(pr.x-d.x,pr.y-d.y)&lt;70){pr.t=0;zapLine(d.x,d.y,pr.x,pr.y);sfx.zap();d.interCd=1.2;break;}}}
d.ramCd-=dt;if(d.ramCd&lt;=0&amp;&amp;(best.hp&lt;=30||best.type=='gearling')&amp;&amp;dist&lt;260){d.state='ram';d.vx=dx/dist;d.vy=dy/dist;d.ramT=1;sfx.wind();}}

// ===== ВРАГИ =====
function updEnemy(e,dt){const p=player,dx=p.x-e.x,dy=p.y-e.y,d=Math.hypot(dx,dy);
if(e.stunT&gt;0){e.stunT-=dt;return;}
if(e.type=='gearling'&amp;&amp;d&lt;260)e.det=1;
let view=e.view*(1-0.15*Math.min(3,upg.mask)),rate=1;if(p.cloakT&gt;0){view*=0.18;rate*=0.5;}if(p.stealth){view*=0.65;rate*=0.6;}if(p.laser)view*=1.5;
const seen=d&lt;view&amp;&amp;Math.abs(angDiff(e.face,Math.atan2(dy,dx)))&lt;e.cone&amp;&amp;los(e.x,e.y,p.x,p.y);
if(seen)e.det=Math.min(1,e.det+dt*(0.5+(1-d/view))*rate*1.4);else e.det=Math.max(0,e.det-dt*0.6);
if(e.det&gt;=1&amp;&amp;e.state!=='alert'){e.state='alert';if(e.type!='gearling')alerted=true;sfx.alert();}
if(e.state=='alert'){
 if(seen){e.lx=p.x;e.ly=p.y;e.loseT=0;}else{e.loseT+=dt;if(e.loseT&gt;3){e.state='patrol';e.det=0.4;}}
 const tx=e.lx,ty=e.ly,dd=Math.hypot(tx-e.x,ty-e.y);
 if(dd&gt;4){e.face=Math.atan2(ty-e.y,tx-e.x);e.x+=(tx-e.x)/dd*e.speed*1.6*dt;e.y+=(ty-e.y)/dd*e.speed*1.6*dt;}
 if(d&lt;e.r+p.r+8){e.atkCd-=dt;if(e.atkCd&lt;=0){e.atkCd=0.9;hurtP(e.type=='elite'?14:e.type=='gearling'?5:8);}}
 if((e.type=='guard'||e.type=='elite')&amp;&amp;seen&amp;&amp;d&gt;90&amp;&amp;d&lt;330){e.shCd-=dt;
  if(e.shCd&lt;=0){e.shCd=e.type=='elite'?1.3:2.0;const a=Math.atan2(dy,dx)+(Math.random()-0.5)*0.12;
   projectiles.push({x:e.x+Math.cos(a)*e.r,y:e.y+Math.sin(a)*e.r,vx:Math.cos(a)*260,vy:Math.sin(a)*260,dmg:e.type=='elite'?12:8,t:2.2,foe:true});sfx.shoot();}}
}else{const t=e.wp[e.wi],dd=Math.hypot(t[0]-e.x,t[1]-e.y);
 if(dd&lt;6)e.wi=(e.wi+1)%e.wp.length;
 else{e.face=Math.atan2(t[1]-e.y,t[0]-e.x);e.x+=(t[0]-e.x)/dd*e.speed*dt;e.y+=(t[1]-e.y)/dd*e.speed*dt;}
 if(e.det&gt;0.4&amp;&amp;!seen)e.face=Math.atan2(dy,dx);}
collideWalls(e);e.x=Math.max(e.r,Math.min(W-e.r,e.x));e.y=Math.max(e.r,Math.min(H-e.r,e.y));}
function setSt(b,s,t){b.state=s;b.t=t;}
function clampB(b){collideWalls(b);b.x=Math.max(b.r,Math.min(W-b.r,b.x));b.y=Math.max(b.r,Math.min(H-b.r,b.y));}
function updBoss(b,dt){const p=player;if(b.state=='dead'||b.state=='kneel')return;
const dx=p.x-b.x,dy=p.y-b.y,d=Math.hypot(dx,dy);
if(b.hp&lt;=b.maxhp*0.12){setSt(b,'kneel',0);pop(b.x,b.y-26,'Первый преклонил колено…');sfx.boss();return;}
b.t-=dt;b.face=Math.atan2(dy,dx);
if(b.state=='duel'){if(d&gt;46){b.x+=dx/d*95*dt;b.y+=dy/d*95*dt;}b.atkT-=dt;
 if(b.atkT&lt;=0){if(d&lt;150){setSt(b,'windup',0.8);sfx.wind();}else{setSt(b,'dash',0.55);b.vx=dx/d;b.vy=dy/d;noise(0.2,0.12);}}}
else if(b.state=='windup'){if(b.t&lt;=0)setSt(b,'strike',0.25);}
else if(b.state=='strike'){if(!b.hitDone&amp;&amp;d&lt;62){b.hitDone=true;hurtP(15,'boss');}if(b.t&lt;=0){b.hitDone=false;b.atkT=1.4+Math.random()*0.8;setSt(b,'duel',0);}}
else if(b.state=='dash'){b.x+=b.vx*430*dt;b.y+=b.vy*430*dt;if(!b.hitDone&amp;&amp;d&lt;42){b.hitDone=true;hurtP(12,'boss');}if(b.t&lt;=0){b.hitDone=false;b.atkT=1.6;setSt(b,'duel',0);}}
else if(b.state=='stagger'){if(b.t&lt;=0){b.atkT=1.0;setSt(b,'duel',0);}}
clampB(b);}
function updBoiler(b,dt){const p=player,dx=p.x-b.x,dy=p.y-b.y,d=Math.hypot(dx,dy);b.t-=dt;b.face=Math.atan2(dy,dx);if(b.wave&gt;0)b.wave-=dt;
if(b.state=='duel'){if(d&gt;70){b.x+=dx/d*55*dt;b.y+=dy/d*55*dt;}b.atkT-=dt;if(b.atkT&lt;=0){setSt(b,'windup',1.0);sfx.wind();}}
else if(b.state=='windup'){if(b.t&lt;=0){setSt(b,'tired',2.2);b.wave=0.5;if(d&lt;130)hurtP(25,'boss');sfx.boss();}}
else if(b.state=='tired'){if(b.t&lt;=0){setSt(b,'duel',0);b.atkT=2.5;}}
clampB(b);}
function updMother(b,dt){const p=player,dx=p.x-b.x,dy=p.y-b.y,d=Math.hypot(dx,dy);b.t-=dt;b.face=Math.atan2(dy,dx);
if(b.state=='duel'){const ang=Math.atan2(dy,dx)+0.7,tx=p.x-Math.cos(ang)*180,ty=p.y-Math.sin(ang)*180,dd=Math.hypot(tx-b.x,ty-b.y);
 if(dd&gt;8){b.x+=(tx-b.x)/dd*120*dt;b.y+=(ty-b.y)/dd*120*dt;}
 b.atkT-=dt;b.sumT-=dt;
 if(b.sumT&lt;=0){b.sumT=5;setSt(b,'spawn',0.8);sfx.alert();}
 else if(b.atkT&lt;=0){b.atkT=3.5;setSt(b,'dashT',0.5);sfx.wind();}}
else if(b.state=='spawn'){if(b.t&lt;=0){setSt(b,'duel',0);for(let i=0;i&lt;2;i++){const a=Math.random()*TAU;enemies.push(en(b.x+Math.cos(a)*40,b.y+Math.sin(a)*40,'gearling',[[b.x,b.y]]));}}}
else if(b.state=='dashT'){if(b.t&lt;=0){setSt(b,'dash',0.4);b.vx=dx/d;b.vy=dy/d;noise(0.2,0.12);}}
else if(b.state=='dash'){b.x+=b.vx*460*dt;b.y+=b.vy*460*dt;if(d&lt;40&amp;&amp;!b.hitDone){b.hitDone=true;hurtP(12,'boss');}if(b.t&lt;=0){b.hitDone=false;setSt(b,'rest',1.0);}}
else if(b.state=='rest'){if(b.t&lt;=0)setSt(b,'duel',0);}
clampB(b);}

// ===== МОБИЛЬНОЕ =====
const isTouch=('ontouchstart' in window);
let joyId=null,joyOX=0,joyOY=0,joyDX=0,joyDY=0,stealthTouch=false;
const TBTN=[{id:'sh',x:W-64,y:H-70,r:30,l:'?'},{id:'atk',x:W-130,y:H-46,r:26,l:'⚔'},{id:'stl',x:W-186,y:H-70,r:24,l:'?'},{id:'lz',x:W-40,y:H-140,r:22,l:'?'},{id:'sc',x:W-92,y:H-160,r:22,l:'?'},{id:'mk',x:W-144,y:H-140,r:22,l:'?'},{id:'F',x:W-196,y:H-120,r:22,l:'?'}];
function tpos(t){const r=cv.getBoundingClientRect();return[(t.clientX-r.left)*(W/r.width),(t.clientY-r.top)*(H/r.height)];}
cv.addEventListener('touchstart',e=&gt;{e.preventDefault();initAudio();for(let i=0;i&lt;e.changedTouches.length;i++){const t=e.changedTouches[i],xy=tpos(t),x=xy[0],y=xy[1];let hit=null;for(const b of TBTN)if(Math.hypot(x-b.x,y-b.y)&lt;b.r+10)hit=b;
if(hit){if(hit.id=='sh')press.push('Click');else if(hit.id=='atk')press.push('Space');else if(hit.id=='stl')stealthTouch=!stealthTouch;else if(hit.id=='lz')press.push('KeyQ');else if(hit.id=='sc')press.push('KeyV');else if(hit.id=='mk')press.push('KeyX');else press.push('KeyF');}
else if(state!=='play')press.push('Enter');else if(x&lt;W*0.5&amp;&amp;joyId===null){joyId=t.identifier;joyOX=x;joyOY=y;joyDX=0;joyDY=0;}}},{passive:false});
cv.addEventListener('touchmove',e=&gt;{e.preventDefault();for(let i=0;i&lt;e.changedTouches.length;i++){const t=e.changedTouches[i];if(t.identifier===joyId){const xy=tpos(t);joyDX=Math.max(-50,Math.min(50,xy[0]-joyOX));joyDY=Math.max(-50,Math.min(50,xy[1]-joyOY));}}},{passive:false});
cv.addEventListener('touchend',e=&gt;{for(let i=0;i&lt;e.changedTouches.length;i++)if(e.changedTouches[i].identifier===joyId){joyId=null;joyDX=0;joyDY=0;}});

// ===== РЕДАКТОР =====
let ed={walls:{},en:[],player:{x:W/2,y:H/2},exit:{x:W-70,y:70},brush:1,down:false,msg:''};
function paint(){const cx=Math.floor(mx/30),cy=Math.floor(my/30),k=cx+','+cy,b=ed.brush;
if(b==1)ed.walls[k]=1;else if(b==2)delete ed.walls[k];
else if(b&gt;=3&amp;&amp;b&lt;=6){const tp=['guard','hound','elite','gearling'][b-3];ed.en=ed.en.filter(e=&gt;Math.hypot(e.x-mx,e.y-my)&gt;24);ed.en.push({x:cx*30+15,y:cy*30+15,type:tp});}
else if(b==7)ed.player={x:cx*30+15,y:cy*30+15};else if(b==8)ed.exit={x:cx*30+15,y:cy*30+15};}
function edTest(){const walls=[];for(const k in ed.walls){const c=k.split(',');walls.push({x:c[0]*30,y:c[1]*30,w:30,h:30});}
const enL=ed.en.map(e=&gt;en(e.x,e.y,e.type,[[e.x,e.y]]));
const cnt=Math.max(1,enL.filter(e=&gt;e.type!='gearling').length);
CUR=[{req:Math.floor(cnt*0.7)||1,walls,en:()=&gt;enL,player:ed.player,story:[['СВОЙ КОНТРАКТ','Пользовательский контракт Ноктюрна.','Удачи, охотник.']] }];
customMode=true;startRunVars();levelIdx=0;page=0;state='story';}
function edExport(){const walls=[];for(const k in ed.walls){const c=k.split(',');walls.push({x:+c[0]*30,y:+c[1]*30,w:30,h:30});}
const json=JSON.stringify({req:1,walls,en:ed.en.map(e=&gt;({x:e.x,y:e.y,type:e.type,wp:[[e.x,e.y]]})),player:ed.player,exit:ed.exit});
try{navigator.clipboard.writeText(json);ed.msg='JSON скопирован!';}catch(e){ed.msg='JSON в консоли';console.log(json);}}

// ===== СТАРТ/ПЕРЕХОДЫ =====
function safeCenter(){let x=W/2,y=H/2;
for(let i=0;i&lt;14;i++){let bad=false;
 for(const w of walls)if(x&gt;w.x-16&amp;&amp;x&lt;w.x+w.w+16&amp;&amp;y&gt;w.y-16&amp;&amp;y&lt;w.y+w.h+16)bad=true;
 if(!bad)break;y+=34;if(y&gt;H-40){y=60;x+=50;}}
return{x,y};}
function beginLevel(){const L=CUR[levelIdx];walls=L.walls.map(w=&gt;({...w}));requiredKills=L.req;enemies=L.en();exitZone={x:W-70,y:70,r:34};kills=0;popups=[];scanR=-1;lastHurt=-99;alerted=false;parries=0;endT=0;projectiles=[];muzzle=0;
drones=[];droneCd=1.5;parts=[];zaps=[];rings=[];hpBonus=0;mile=Math.floor(trophTotal/5);revives=0;killT=[];comboCd=0;
const sp=L.player?{x:L.player.x,y:L.player.y}:safeCenter();
player={x:sp.x,y:sp.y,r:13,hp:maxhp(),gears:player?player.gears:4,heat:0,face:0,aim:0,laser:false,shootCd:0,cloakT:0,flameT:0,overT:0,atkCd:0,stealth:false,shieldT:20,auraT:0,invT:0,microT:0,springCd:0,speedT:0,adren:false};state='play';}
function startRunVars(){trophies=0;trophTotal=0;shame=false;spared=false;ending=null;player=null;upg={mask:0,pauld:0,core:0,cable:0,blade:0};buys=0;}
function startRun(cont){startRunVars();CUR=LEVELS;customMode=false;levelIdx=cont?Math.min(save.unlocked||0,4):0;page=0;state='story';sfx.ui();}
function nextLevel(){if(customMode){state='edit';customMode=false;return;}if(!alerted)unlock('ghost');save.unlocked=Math.max(save.unlocked||0,levelIdx+1);save.best=Math.max(save.best||0,trophTotal);writeSave();levelIdx++;if(levelIdx&gt;=LEVELS.length){computeEnding();state='victory';}else state='shop';}
function computeEnding(){ending=shame?'dark':(spared&amp;&amp;trophTotal&gt;=15?'secret':'free');save.endings[ending]=1;if(ending==='secret')unlock('eternal');if(trophTotal&gt;=15)unlock('honor');writeSave();}

// ===== ОБНОВЛЕНИЕ =====
function updatePlay(dt){const p=player;
for(const c of press){
 if(c==='KeyX'&amp;&amp;p.gears&gt;=2&amp;&amp;p.cloakT&lt;=0&amp;&amp;p.overT&lt;=0){p.gears-=2;p.cloakT=6;pop(p.x,p.y-20,'Мираж');sfx.cloak();}
 if(c==='KeyV'){const b=anyBoss();
  if(b&amp;&amp;b.type=='boss'&amp;&amp;b.state=='kneel'&amp;&amp;Math.hypot(p.x-b.x,p.y-b.y)&lt;70){spared=true;b.state='dead';endT=2;pop(b.x,b.y-24,'Честь соблюдена');sfx.spare();}
  else if(p.gears&gt;=1){p.gears-=1;scanR=0;p.microT=2;pop(p.x,p.y-20,'Скан +?');sfx.scan();const st=1+0.5*upg.cable,rad=420+80*upg.cable;
   for(const e of enemies)if(!e.dead&amp;&amp;Math.hypot(e.x-p.x,e.y-p.y)&lt;rad){e.det=0;if(e.state=='patrol')e.stunT=st;}}}
 if(c==='KeyF'&amp;&amp;p.flameT&lt;=0&amp;&amp;p.overT&lt;=0){p.flameT=5;pop(p.x,p.y-20,'Зажигание!');sfx.flame();}
 if(c==='KeyQ'||c==='KeyL'){p.laser=!p.laser;sfx.ui();}
 if(c==='KeyE'||c==='Click')shoot();
 if(c==='Space')attack();}
if(p.shieldT&gt;0){p.shieldT-=dt;if(p.shieldT&lt;=0){pop(p.x,p.y-26,'Защитное поле снято!');sfx.heat();}}
p.auraT-=dt;p.invT-=dt;p.microT-=dt;p.springCd-=dt;p.speedT-=dt;comboCd-=dt;
if(!p.adren&amp;&amp;p.hp&lt;25)p.adren=true;if(p.adren&amp;&amp;p.hp&gt;40)p.adren=false;
droneCd-=dt;if(droneCd&lt;=0){droneCd=4;if(drones.length&lt;3)spawnDrone();}
if(p.overT&gt;0){p.overT-=dt;if(p.overT&lt;=0)p.heat=40;}
else{const st=keys.ShiftLeft||keys.ShiftRight||stealthTouch;
 const rot=(keys.KeyD?1:0)-(keys.KeyA?1:0);
 if(rot)p.face+=rot*3.2*dt*(st?0.6:1);
 let vx=0,vy=0;const fw=(keys.KeyW?1:0)-(keys.KeyS?1:0);
 if(fw){const s=fw&gt;0?1:0.6;vx+=Math.cos(p.face)*fw*s;vy+=Math.sin(p.face)*fw*s;}
 let ax=(keys.ArrowRight?1:0)-(keys.ArrowLeft?1:0),ay=(keys.ArrowDown?1:0)-(keys.ArrowUp?1:0);
 if(ax||ay){const l=Math.hypot(ax,ay);ax/=l;ay/=l;p.face=Math.atan2(ay,ax);vx+=ax;vy+=ay;}
 vx+=joyDX/50;vy+=joyDY/50;
 const l2=Math.hypot(vx,vy);if(l2&gt;1){vx/=l2;vy/=l2;}
 if(l2&gt;0.15){if(isTouch)p.face=Math.atan2(vy,vx);const sp=(st?105:210)*(p.speedT&gt;0?1.4:1)*(p.adren?1.3:1);p.x+=vx*sp*dt;p.y+=vy*sp*dt;}
 p.stealth=st;}
p.x=Math.max(p.r,Math.min(W-p.r,p.x));p.y=Math.max(p.r,Math.min(H-p.r,p.y));collideWalls(p);
p.aim=isTouch?p.face:Math.atan2(my-p.y,mx-p.x);
p.atkCd-=dt;p.shootCd-=dt;p.cloakT-=dt;p.flameT-=dt;muzzle-=dt;p.heat=Math.max(0,p.heat-(p.flameT&gt;0?6:14)*dt);
if(time-lastHurt&gt;4)p.hp=Math.min(maxhp(),p.hp+6*dt);
if(scanR&gt;=0){scanR+=600*dt;if(scanR&gt;500+80*upg.cable)scanR=-1;}
projectiles=projectiles.filter(pr=&gt;{pr.t-=dt;pr.x+=pr.vx*dt;pr.y+=pr.vy*dt;if(pr.t&lt;=0)return false;
 for(const w of walls)if(pr.x&gt;w.x&amp;&amp;pr.x&lt;w.x+w.w&amp;&amp;pr.y&gt;w.y&amp;&amp;pr.y&lt;w.y+w.h)return false;
 if(pr.foe){if(Math.hypot(player.x-pr.x,player.y-pr.y)&lt;player.r+3){
  if(p.adren&amp;&amp;Math.random()&lt;0.4){pop(player.x,player.y-18,'мимо');parts.push({x:pr.x,y:pr.y,vx:0,vy:-60,t:0.3,col:'#7ec4ff',sz:2});return false;}
  hurtP(pr.dmg);return false;}return true;}
 for(const e of enemies){if(e.dead)continue;if(Math.hypot(e.x-pr.x,e.y-pr.y)&lt;e.r+4){
  if(e.type=='guard'||e.type=='hound'||e.type=='elite'||e.type=='gearling')damageEnemy(e,pr.dmg,true);else bossShot(e,pr.drone?Math.round(pr.dmg*0.5):pr.dmg);
  return false;}}
 return true;});
for(const d of drones)if(!d.dead)updDrone(d,dt);
drones=drones.filter(d=&gt;!d.dead&amp;&amp;d.t&gt;0);
for(const e of enemies){if(e.dead)continue;if(e.type=='boss')updBoss(e,dt);else if(e.type=='boiler')updBoiler(e,dt);else if(e.type=='mother')updMother(e,dt);else updEnemy(e,dt);}
parts=parts.filter(o=&gt;(o.t-=dt)&gt;0);for(const o of parts){o.x+=o.vx*dt;o.y+=o.vy*dt;o.vx*=0.92;o.vy*=0.92;}
zaps=zaps.filter(o=&gt;(o.t-=dt)&gt;0);rings=rings.filter(o=&gt;(o.t-=dt)&gt;0);
if(kills&gt;=requiredKills&amp;&amp;Math.hypot(p.x-exitZone.x,p.y-exitZone.y)&lt;exitZone.r)nextLevel();
if(endT&gt;0){endT-=dt;if(endT&lt;=0)nextLevel();}
if(p.hp&lt;=0)state='gameover';
popups=popups.filter(o=&gt;(o.t-=dt)&gt;0);for(const o of popups)o.y-=30*dt;}

function step(dt){
if(state=='menu'){if(press.includes('Enter'))startRun(false);if(press.includes('KeyC')&amp;&amp;(save.unlocked||0)&gt;0)startRun(true);if(press.includes('KeyR'))state='edit';}
else if(state=='story'){if(press.includes('Enter')){page++;sfx.ui();if(page&gt;=CUR[levelIdx].story.length)beginLevel();}}
else if(state=='shop'){for(const c of press)if(c.startsWith('Digit')&amp;&amp;+c.slice(5)&gt;=1&amp;&amp;+c.slice(5)&lt;=5)buy(+c.slice(5)-1);if(press.includes('Enter')){state='story';page=0;}}
else if(state=='play')updatePlay(dt);
else if(state=='gameover'){if(press.includes('Enter'))beginLevel();}
else if(state=='victory'){if(press.includes('Enter'))state='menu';}
else if(state=='edit'){for(const c of press){if(c.startsWith('Digit')&amp;&amp;+c.slice(5)&gt;=1&amp;&amp;+c.slice(5)&lt;=8)ed.brush=+c.slice(5);
 if(c=='KeyT')edTest();if(c=='KeyJ')edExport();if(c=='Escape')state='menu';}}
if(press.includes('KeyM')&amp;&amp;AC){muted=!muted;master.gain.value=muted?0:1;}
press=[];}

// ===== ОТРИСОВКА =====
function center(t,x,y,c,f){ctx.textAlign='center';ctx.fillStyle=c;ctx.font=f;ctx.fillText(t,x,y);ctx.textAlign='left';}
function drawZag(x1,y1,x2,y2,col,wd){ctx.strokeStyle=col;ctx.lineWidth=wd;ctx.beginPath();ctx.moveTo(x1,y1);
for(let i=1;i&lt;6;i++){const t=i/6,px=x1+(x2-x1)*t+(Math.random()-0.5)*10,py=y1+(y2-y1)*t+(Math.random()-0.5)*10;ctx.lineTo(px,py);}
ctx.lineTo(x2,y2);ctx.stroke();ctx.lineWidth=1;}
function drawDreads(x,y,face,r,n){for(let i=0;i&lt;n;i++){const a=face+Math.PI+(i-(n-1)/2)*0.22;const lx=x+Math.cos(a)*(r+11),ly=y+Math.sin(a)*(r+11);
 ctx.strokeStyle='#151515';ctx.lineWidth=3;ctx.beginPath();ctx.moveTo(x+Math.cos(a)*4,y+Math.sin(a)*4);ctx.lineTo(lx,ly);ctx.stroke();
 ctx.strokeStyle='#9aa3ad';ctx.lineWidth=1;ctx.beginPath();ctx.arc((x+lx)/2,(y+ly)/2,1.6,0,TAU);ctx.stroke();}ctx.lineWidth=1;}
function drawShield(p){const R=p.r+14;
ctx.fillStyle='rgba(90,220,255,0.08)';ctx.beginPath();ctx.arc(p.x,p.y,R,0,TAU);ctx.fill();
ctx.strokeStyle='rgba(90,220,255,0.75)';ctx.lineWidth=2;ctx.beginPath();ctx.arc(p.x,p.y,R,0,TAU);ctx.stroke();
ctx.strokeStyle='rgba(180,245,255,0.9)';ctx.lineWidth=1;
for(let i=0;i&lt;6;i++){const a=time*2.6+i*TAU/6;ctx.beginPath();ctx.moveTo(p.x+Math.cos(a)*R,p.y+Math.sin(a)*R);
 for(let s2=1;s2&lt;=3;s2++){const rr=R-6+Math.random()*14;const aa=a+(Math.random()*0.36-0.18);ctx.lineTo(p.x+Math.cos(aa)*rr,p.y+Math.sin(aa)*rr);}
 ctx.stroke();}}
function drawAura(p){const k=p.auraT/7,col=p.auraT&gt;4?'77,255,136':p.auraT&gt;2?'255,210,77':'255,85,68';
ctx.strokeStyle='rgba('+col+','+(0.5+0.3*Math.sin(time*10))+')';ctx.lineWidth=2.5;ctx.beginPath();ctx.arc(p.x,p.y,p.r+10,0,TAU);ctx.stroke();ctx.lineWidth=1;
ctx.fillStyle='rgba('+col+',0.07)';ctx.beginPath();ctx.arc(p.x,p.y,p.r+10,0,TAU);ctx.fill();}
function drawPlayer(){const p=player;ctx.save();if(p.cloakT&gt;0)ctx.globalAlpha=0.3;
if(p.shieldT&gt;0)drawShield(p);
if(p.auraT&gt;0)drawAura(p);
drawDreads(p.x,p.y,p.face,p.r,5);
const pa=p.face+2.4;ctx.beginPath();ctx.arc(p.x+Math.cos(pa)*p.r*0.85,p.y+Math.sin(pa)*p.r*0.85,7,0,TAU);ctx.fillStyle='#d4af37';ctx.fill();ctx.strokeStyle='#8a6d1f';ctx.stroke();
const g=ctx.createRadialGradient(p.x-4,p.y-4,2,p.x,p.y,p.r);g.addColorStop(0,'#f5f7fa');g.addColorStop(1,'#8b93a1');
ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,TAU);ctx.fillStyle=g;ctx.fill();
ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.face);
ctx.fillStyle='#e8ecf2';ctx.beginPath();ctx.ellipse(2,0,p.r*0.95,p.r*0.6,0,0,TAU);ctx.fill();
ctx.fillStyle='#0a0a0a';ctx.beginPath();ctx.ellipse(p.r*0.45,-3.2,2.6,1.8,0,0,TAU);ctx.fill();ctx.beginPath();ctx.ellipse(p.r*0.45,3.2,2.6,1.8,0,0,TAU);ctx.fill();
ctx.strokeStyle='#c9d2e0';ctx.lineWidth=2;ctx.beginPath();ctx.arc(p.r*0.5,-2,4,0.6,2.4);ctx.stroke();ctx.beginPath();ctx.arc(p.r*0.5,2,4,-2.4,-0.6);ctx.stroke();
ctx.restore();
const ca=p.face-2.4,sx=p.x+Math.cos(ca)*p.r*0.85,sy=p.y+Math.sin(ca)*p.r*0.85;
ctx.strokeStyle='#3a3f4a';ctx.lineWidth=4;ctx.beginPath();ctx.moveTo(sx,sy);ctx.lineTo(sx+Math.cos(p.aim)*15,sy+Math.sin(p.aim)*15);ctx.stroke();ctx.lineWidth=1;
if(muzzle&gt;0){ctx.beginPath();ctx.arc(sx+Math.cos(p.aim)*17,sy+Math.sin(p.aim)*17,4.5,0,TAU);ctx.fillStyle='#ffcf6a';ctx.fill();}
if(p.laser){let ex=p.x+Math.cos(p.aim)*460,ey=p.y+Math.sin(p.aim)*460;
 for(let d2=20;d2&lt;460;d2+=10){const qx=p.x+Math.cos(p.aim)*d2,qy=p.y+Math.sin(p.aim)*d2;let hitW=false;
  for(const w of walls)if(qx&gt;w.x&amp;&amp;qx&lt;w.x+w.w&amp;&amp;qy&gt;w.y&amp;&amp;qy&lt;w.y+w.h)hitW=true;
  if(hitW){ex=qx;ey=qy;break;}}
 ctx.strokeStyle='rgba(255,40,40,0.55)';ctx.beginPath();ctx.moveTo(sx+Math.cos(p.aim)*15,sy+Math.sin(p.aim)*15);ctx.lineTo(ex,ey);ctx.stroke();
 const L=Math.hypot(ex-p.x,ey-p.y);ctx.fillStyle='#ff3030';
 [0.45,0.6,0.75].forEach(k=&gt;{if(L*k&lt;L){ctx.beginPath();ctx.arc(p.x+Math.cos(p.aim)*L*k,p.y+Math.sin(p.aim)*L*k,2,0,TAU);ctx.fill();}});}
if(p.flameT&gt;0){ctx.beginPath();ctx.arc(p.x+Math.cos(p.face)*p.r,p.y+Math.sin(p.face)*p.r,6,0,TAU);ctx.fillStyle='#ff8c3a';ctx.fill();}
ctx.restore();}
function drawDrone(d){ctx.save();
ctx.beginPath();ctx.arc(d.x,d.y,7,0,TAU);ctx.fillStyle='#2b3648';ctx.fill();ctx.strokeStyle='#7ec4ff';ctx.lineWidth=2;ctx.stroke();ctx.lineWidth=1;
ctx.beginPath();ctx.arc(d.x,d.y,3,0,TAU);ctx.fillStyle='#bfe9ff';ctx.fill();
if(d.charge&gt;0){let best=null,bd=1e9;for(const e of enemies){if(e.dead)continue;const dd=(e.x-d.x)**2+(e.y-d.y)**2;if(dd&lt;bd){bd=dd;best=e;}}
 if(best){ctx.strokeStyle='rgba(126,196,255,'+(0.3+0.4*Math.sin(time*30))+')';ctx.beginPath();ctx.moveTo(d.x,d.y);ctx.lineTo(best.x,best.y);ctx.stroke();}}
if(d.beam&gt;0){let best=null,bd=1e9;for(const e of enemies){if(e.dead)continue;const dd=(e.x-d.x)**2+(e.y-d.y)**2;if(dd&lt;bd){bd=dd;best=e;}}
 if(best){drawZag(d.x,d.y,best.x,best.y,'rgba(160,240,255,0.9)',3);drawZag(d.x,d.y,best.x,best.y,'rgba(255,255,255,0.7)',1);}}
ctx.restore();}
function drawWorld(){ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);
ctx.strokeStyle='rgba(255,255,255,0.03)';for(let x=0;x&lt;W;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}
for(const w of walls){ctx.fillStyle='#242c3a';ctx.fillRect(w.x,w.y,w.w,w.h);ctx.strokeStyle='#3d4a5f';ctx.strokeRect(w.x,w.y,w.w,w.h);}
const act=kills&gt;=requiredKills;
ctx.beginPath();ctx.arc(exitZone.x,exitZone.y,exitZone.r,0,TAU);ctx.fillStyle=act?'rgba(80,220,140,'+(0.25+0.15*Math.sin(time*5))+')':'rgba(120,120,120,0.12)';ctx.fill();ctx.strokeStyle=act?'#50dc8c':'#555';ctx.stroke();
for(const e of enemies){if(e.dead||e.type=='boss'||e.type=='boiler'||e.type=='mother')continue;
 ctx.fillStyle=e.state=='alert'?'rgba(224,85,85,0.14)':'rgba(230,200,110,0.10)';ctx.beginPath();ctx.moveTo(e.x,e.y);ctx.arc(e.x,e.y,e.view,e.face-e.cone,e.face+e.cone);ctx.closePath();ctx.fill();}
if(scanR&gt;=0){ctx.beginPath();ctx.arc(player.x,player.y,scanR,0,TAU);ctx.strokeStyle='rgba(120,200,255,0.5)';ctx.stroke();}
for(const r of rings){const k=1-r.t/0.4;ctx.strokeStyle=r.col;ctx.globalAlpha=r.t/0.4;ctx.beginPath();ctx.arc(r.x,r.y,r.r0+(r.r1-r.r0)*k,0,TAU);ctx.stroke();ctx.globalAlpha=1;}
for(const pr of projectiles){ctx.strokeStyle=pr.col||(pr.foe?'#ff5544':'#ffd76a');ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(pr.x-pr.vx*0.02,pr.y-pr.vy*0.02);ctx.lineTo(pr.x,pr.y);ctx.stroke();ctx.lineWidth=1;ctx.beginPath();ctx.arc(pr.x,pr.y,3,0,TAU);ctx.fillStyle=pr.foe?'#ffc0b0':'#fff2c0';ctx.fill();}
for(const z of zaps)drawZag(z.x1,z.y1,z.x2,z.y2,'rgba(160,240,255,'+(z.t/0.15)+')',2);
for(const o of parts){ctx.fillStyle=o.col;ctx.globalAlpha=Math.min(1,o.t*2);ctx.fillRect(o.x-o.sz/2,o.y-o.sz/2,o.sz,o.sz);ctx.globalAlpha=1;}
for(const d of drones)drawDrone(d);
for(const e of enemies){if(e.dead)continue;
 if(e.type=='boss'||e.type=='boiler'||e.type=='mother'){drawBig(e);continue;}
 ctx.beginPath();ctx.arc(e.x,e.y,e.r,0,TAU);ctx.fillStyle=e.state=='alert'?'#e05555':(e.det&gt;0.25?'#e6c86e':e.type=='gearling'?'#66e0e0':'#8a93a6');ctx.fill();
 if(e.type=='elite'){ctx.strokeStyle='#d4af37';ctx.lineWidth=3;ctx.stroke();ctx.lineWidth=1;drawDreads(e.x,e.y,e.face,e.r,3);}
 if(e.type=='hound'){ctx.strokeStyle='#66e0e0';ctx.stroke();}
 ctx.beginPath();ctx.moveTo(e.x,e.y);ctx.lineTo(e.x+Math.cos(e.face)*e.r*1.4,e.y+Math.sin(e.face)*e.r*1.4);ctx.strokeStyle='#fff';ctx.stroke();
 if(e.det&gt;0&amp;&amp;e.det&lt;1){ctx.fillStyle='#e6c86e';ctx.fillRect(e.x-15,e.y-e.r-10,30*e.det,4);}}
drawPlayer();
ctx.font='bold 14px monospace';
for(const o of popups){ctx.fillStyle='rgba(255,255,255,'+Math.min(1,o.t)+')';ctx.fillText(o.txt,o.x-20,o.y);}
if(joyId!==null){ctx.strokeStyle='rgba(255,255,255,0.3)';ctx.beginPath();ctx.arc(joyOX,joyOY,40,0,TAU);ctx.stroke();ctx.beginPath();ctx.arc(joyOX+joyDX,joyOY+joyDY,14,0,TAU);ctx.stroke();}
if(isTouch)for(const b of TBTN){ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,TAU);ctx.fillStyle=(b.id=='stl'&amp;&amp;stealthTouch)?'rgba(212,175,55,0.5)':'rgba(255,255,255,0.12)';ctx.fill();ctx.strokeStyle='#666';ctx.stroke();center(b.l,b.x,b.y+6,'#fff','15px monospace');}}
function drawBig(b){
if(b.type=='boiler'){ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,TAU);ctx.fillStyle=b.state=='tired'?'#7ec4ff':'#7a4a3a';ctx.fill();ctx.strokeStyle='#3a2a1a';ctx.lineWidth=4;ctx.stroke();ctx.lineWidth=1;
 if(b.state=='windup'){const k=1-b.t;ctx.beginPath();ctx.arc(b.x,b.y,20+k*110,0,TAU);ctx.strokeStyle='rgba(255,120,60,0.8)';ctx.lineWidth=3;ctx.stroke();ctx.lineWidth=1;}
 if(b.wave&gt;0){ctx.beginPath();ctx.arc(b.x,b.y,(0.5-b.wave)/0.5*130,0,TAU);ctx.strokeStyle='rgba(255,200,120,0.6)';ctx.stroke();}}
else if(b.type=='mother'){for(let i=0;i&lt;8;i++){const a=time*0.8+i*TAU/8;ctx.beginPath();ctx.moveTo(b.x,b.y);ctx.lineTo(b.x+Math.cos(a)*(b.r+12),b.y+Math.sin(a)*(b.r+12));ctx.strokeStyle='#d4af37';ctx.stroke();}
 ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,TAU);ctx.fillStyle=b.state=='rest'?'#7ec4ff':'#b8860b';ctx.fill();ctx.strokeStyle='#d4af37';ctx.lineWidth=2;ctx.stroke();ctx.lineWidth=1;
 if(b.state=='dashT'){ctx.beginPath();ctx.moveTo(b.x,b.y);ctx.lineTo(b.x+Math.cos(b.face)*220,b.y+Math.sin(b.face)*220);ctx.strokeStyle='rgba(255,90,70,0.7)';ctx.stroke();}}
else{drawDreads(b.x,b.y,b.face,b.r,5);
 const col=b.state=='windup'?(Math.sin(time*30)&gt;0?'#ff6655':'#c9d2e0'):b.state=='stagger'?'#7ec4ff':b.state=='kneel'?'#8a93a6':'#c9d2e0';
 ctx.beginPath();ctx.arc(b.x,b.y,b.r*(b.state=='kneel'?0.8:1),0,TAU);ctx.fillStyle=col;ctx.fill();ctx.strokeStyle='#d4af37';ctx.lineWidth=3;ctx.stroke();ctx.lineWidth=1;
 ctx.save();ctx.translate(b.x,b.y);ctx.rotate(b.face);ctx.fillStyle='#0a0a0a';ctx.beginPath();ctx.ellipse(b.r*0.45,-3.5,2.6,1.8,0,0,TAU);ctx.fill();ctx.beginPath();ctx.ellipse(b.r*0.45,3.5,2.6,1.8,0,0,TAU);ctx.fill();ctx.restore();
 if(b.state=='windup'){const k=(0.8-b.t)/0.8;ctx.beginPath();ctx.arc(b.x,b.y,20+k*45,0,TAU);ctx.strokeStyle='rgba(255,90,70,0.8)';ctx.lineWidth=2;ctx.stroke();ctx.lineWidth=1;}}
ctx.beginPath();ctx.moveTo(b.x,b.y);ctx.lineTo(b.x+Math.cos(b.face)*b.r*1.5,b.y+Math.sin(b.face)*b.r*1.5);ctx.strokeStyle='#fff';ctx.stroke();}
function drawHUD(){const p=player;ctx.fillStyle='rgba(10,14,20,0.85)';ctx.fillRect(0,0,W,36);
ctx.fillStyle='#333';ctx.fillRect(10,8,180,10);ctx.fillStyle='#c0392b';ctx.fillRect(10,8,180*Math.max(0,p.hp)/maxhp(),10);
ctx.fillStyle='#333';ctx.fillRect(10,22,180,6);ctx.fillStyle=p.heat&gt;70?'#ff5533':'#ff8c3a';ctx.fillRect(10,22,180*p.heat/100,6);
ctx.fillStyle='#d4af37';ctx.font='16px monospace';ctx.fillText('⚙'.repeat(p.gears),210,22);
if(p.shieldT&gt;0){ctx.fillStyle='#5adcff';ctx.fillText('⚡'+Math.ceil(p.shieldT)+'с',300,22);}
ctx.fillStyle='#7ec4ff';ctx.fillText('?'+drones.length,370,22);
ctx.fillStyle='#fff';ctx.fillText('?'+trophies+(p.laser?'  ?':''),430,22);
const b=anyBoss();if(b){ctx.fillStyle='#333';ctx.fillRect(W/2-150,8,300,8);ctx.fillStyle='#d4af37';ctx.fillRect(W/2-150,8,300*Math.max(0,b.hp)/b.maxhp,8);ctx.fillStyle='#d4af37';ctx.font='10px monospace';ctx.textAlign='center';ctx.fillText(b.type=='boss'?'ПЕРВЫЙ':b.type=='boiler'?'КОТЕЛЬЩИК':'МАТЬ',W/2,28);ctx.textAlign='left';}
ctx.textAlign='right';ctx.fillStyle=kills&gt;=requiredKills?'#50dc8c':'#cfd8e3';
ctx.fillText((requiredKills&gt;900?'Охота':(kills&gt;=requiredKills?'→ К ВЫХОДУ!':'Цель: '+kills+'/'+requiredKills))+'   Акт '+(levelIdx+1),W-10,22);ctx.textAlign='left';
if(!isTouch){ctx.fillStyle='rgba(255,255,255,0.45)';ctx.font='12px monospace';ctx.fillText('W/S·A/D·стрелки—движение · E/ЛКМ—плазма · Q/ПКМ—лазер · ПРОБЕЛ—клинок/парри · X—мираж · V—скан/пощада · F—пламя · SHIFT—скрытно',10,H-10);}}
function drawShop(){ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);
center('КУЗНЯ ГИЛЬДИИ',W/2,90,'#d4af37','bold 34px monospace');
center('Баланс: '+trophies+' ?',W/2,125,'#cfd8e3','14px monospace');
SHOP.forEach((s,i)=&gt;{const lv=upg[s[0]],cost=lv&lt;3?s[3][lv]:'МАКС';
ctx.fillStyle=lv&lt;3&amp;&amp;trophies&gt;=s[3][lv]?'#cfd8e3':'#666';ctx.font='16px monospace';
ctx.fillText('['+(i+1)+'] '+s[1]+' · ур.'+lv+' · '+(lv&lt;3?cost+'?':'МАКС')+' · '+s[2],240,180+i*42);});
center('ENTER — к сюжету',W/2,H-50,'#50dc8c','16px monospace');}
function drawMenu(){ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);
center('ХРОНОКЛЫК',W/2,100,'#d4af37','bold 50px monospace');
center('стелс-экшен: 5 актов · 3 босса · кузня · дроны · плазма и лазер',W/2,132,'#8a93a6','14px monospace');
['W/S — вперёд/назад · A/D — вращение · стрелки — стрейф','E — плазма · Q — лазер · ПРОБЕЛ — клинок/парри · SHIFT — скрытно','X — мираж · V — скан/пощада · F — пламя · M — звук','⚡ 20 с поле · ? аура 7 с за убийство · ? дроны каждые 4 с']
.forEach((s,i)=&gt;center(s,W/2,164+i*22,'#fff','13px monospace'));
center('ENTER — новая охота'+((save.unlocked||0)&gt;0?'  ·  C — продолжить':'')+'  ·  R — редактор',W/2,272,'#50dc8c','15px monospace');
center('Рекорд: '+(save.best||0)+' ? · Концовки: '+Object.keys(save.endings).length+'/3',W/2,298,'#8a93a6','13px monospace');
let ay=330;ctx.font='12px monospace';for(const a of ACH){ctx.fillStyle=save.ach[a[0]]?'#d4af37':'#555';ctx.fillText((save.ach[a[0]]?'✓ ':'· ')+a[1]+' — '+a[2],20,ay);ay+=17;}}
function drawStory(){ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);const L=CUR[levelIdx].story[page];
center(L[0],W/2,200,'#d4af37','bold 30px monospace');
for(let i=1;i&lt;L.length;i++)center(L[i],W/2,260+(i-1)*30,'#cfd8e3','16px monospace');
center('ENTER — далее',W/2,H-40,'#666','14px monospace');}
function drawVictory(){ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);const L=ENDTXT[ending||'free'];
center('ВЕЛИКАЯ ПРУЖИНА',W/2,150,'#d4af37','bold 38px monospace');
L.forEach((s,i)=&gt;center(s,W/2,220+i*32,i==L.length-1?'#d4af37':'#cfd8e3',i==L.length-1?'bold 20px monospace':'16px monospace'));
center('Трофеи: '+trophTotal+' · Парри: '+parries,W/2,430,'#8a93a6','14px monospace');
center('ENTER — в меню',W/2,H-40,'#666','14px monospace');}
function drawEdit(){ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);
ctx.strokeStyle='rgba(255,255,255,0.06)';for(let x=0;x&lt;W;x+=30){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}for(let y=0;y&lt;H;y+=30){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}
ctx.fillStyle='#242c3a';for(const k in ed.walls){const c=k.split(',');ctx.fillRect(c[0]*30,c[1]*30,30,30);}
for(const e of ed.en){ctx.beginPath();ctx.arc(e.x,e.y,12,0,TAU);ctx.fillStyle=e.type=='hound'?'#66e0e0':e.type=='elite'?'#d4af37':e.type=='gearling'?'#4aa':'#8a93a6';ctx.fill();}
ctx.beginPath();ctx.arc(ed.exit.x,ed.exit.y,20,0,TAU);ctx.strokeStyle='#50dc8c';ctx.stroke();
ctx.beginPath();ctx.arc(ed.player.x,ed.player.y,12,0,TAU);ctx.fillStyle='#f5f7fa';ctx.fill();
center('РЕДАКТОР · кисти 1-8: стена/ластик/страж/гончая/элит/шестерёныш/старт/выход · ПКМ—ластик · T—тест · J—JSON · ESC—меню',W/2,20,'#cfd8e3','12px monospace');
center('Кисть: '+ed.brush+(ed.msg?'  ·  '+ed.msg:''),W/2,H-16,'#d4af37','13px monospace');}
function draw(){if(state=='menu')drawMenu();else if(state=='story')drawStory();else if(state=='victory')drawVictory();else if(state=='shop')drawShop();else if(state=='edit')drawEdit();
else{drawWorld();drawHUD();if(state=='gameover'){ctx.fillStyle='rgba(0,0,0,0.6)';ctx.fillRect(0,0,W,H);center('ТЫ РАЗОБРАН',W/2,H/2-10,'#e05555','bold 40px monospace');center('ENTER — повторить',W/2,H/2+24,'#fff','16px monospace');}}}
let last=0;function loop(t){const dt=Math.min(0.05,(t-last)/1000||0);last=t;time+=dt;step(dt);draw();requestAnimationFrame(loop);}
requestAnimationFrame(loop);
&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</subtitle>
  <updated>2026-08-16T06:54:36Z</updated>
  <logo>https://pandoraburo.ru/ssl/u/pic/e2/6850fe98a211f18279a3cef0e847cc/-/logo.png</logo>
  <author>
    <name>1592962</name>
    <uri>https://www.pandoraburo.ru/</uri>
  </author>
</feed>
