Files
yilsemandClaude Fable 5 ff59171296 Daisy duzeltmeleri: kullanici ID'leri (33,59,63,75,77,99) + 2-seed akustik tarama
- ID 33,63=Daisy; 59,77=Manong; 75=Kuya1; 99=Daisy (kullanici kesin)
- 33 oncesi Daisy etiketleri -> Marissa (kullanici kurali)
- 2-seed Daisy parmak-iziyle 14 blok Marissa->Daisy (metin paneli onayli)
- Ihtilafli 5 blok (48,93,121,147,263) Marissa'da birakildi
- Degisen bloklar Lily/dogru seslerle yeniden dublaj, timeline yeniden kuruldu

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:24:04 +03:00

701 lines
39 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Toplantı Odası — İnteraktif 3D Kayıt</title>
<style>
:root{ --bg:#0a0e14; --panel:#12161fdd; --border:#2b3240; --txt:#e8edf4; --mut:#8b95a6;
--accent:#2f81f7; --gold:#e0b400; --glow:0 0 18px rgba(47,129,247,.35); }
*{box-sizing:border-box}
html,body{margin:0;height:100%;background:var(--bg);color:var(--txt);
font-family:"Segoe UI",system-ui,sans-serif;overflow:hidden;user-select:none}
#scene{position:fixed;inset:0}
/* ---- ÜST BAR ---- */
#topbar{position:fixed;top:0;left:0;right:0;height:54px;display:flex;align-items:center;gap:12px;
padding:0 16px;background:linear-gradient(#0a0e14f0,#0a0e1400);z-index:10;pointer-events:none}
#topbar h1{font-size:15px;margin:0;font-weight:700;letter-spacing:.4px;pointer-events:auto;
text-shadow:0 0 14px rgba(47,129,247,.5)}
#topbar .sub{font-size:11px;color:var(--mut);font-weight:400;letter-spacing:0}
.spacer{flex:1}
.toggle{pointer-events:auto;display:flex;background:#161b26;border:1px solid var(--border);
border-radius:20px;overflow:hidden;box-shadow:var(--glow)}
.toggle button{background:none;border:0;color:var(--mut);padding:7px 15px;font-size:13px;cursor:pointer;font-weight:700}
.toggle button.on{background:var(--accent);color:#fff}
.toggle button.on.trm{background:#8957e5}
.iconbtn{pointer-events:auto;background:#161b26;border:1px solid var(--border);color:var(--mut);
border-radius:20px;padding:7px 13px;font-size:12.5px;cursor:pointer;font-weight:700}
.iconbtn.on{color:#fff;border-color:var(--accent);box-shadow:var(--glow)}
/* ---- SOL PANEL: taraflar ---- */
#legend{position:fixed;top:62px;left:12px;z-index:10;background:var(--panel);border:1px solid var(--border);
border-radius:12px;padding:10px 12px;font-size:11.5px;backdrop-filter:blur(8px);max-width:228px}
#legend>b{font-size:12px;letter-spacing:.4px}
#legend .row{display:flex;align-items:center;gap:7px;margin-top:5px;cursor:pointer}
#legend .row:hover{color:#fff}
#legend .dot{width:11px;height:11px;border-radius:50%;flex:none;box-shadow:0 0 6px currentColor}
/* ---- SAĞ PANEL: skor ---- */
#stats{position:fixed;top:62px;right:12px;z-index:10;background:var(--panel);border:1px solid var(--border);
border-radius:12px;padding:10px 12px;font-size:11px;backdrop-filter:blur(8px);width:216px}
#stats>b{font-size:12px;letter-spacing:.4px}
.srow{margin-top:6px;transition:transform .15s}
.srow.on{transform:translateX(-3px)}
.srow .nm{display:flex;justify-content:space-between;font-size:11px;margin-bottom:2px}
.srow.on .nm{color:#fff;font-weight:700;text-shadow:0 0 8px rgba(255,255,255,.4)}
.srow .bar{height:5px;border-radius:3px;background:#1c2230;overflow:hidden}
.srow .fill{height:100%;border-radius:3px;opacity:.85}
.srow.on .fill{opacity:1;box-shadow:0 0 8px currentColor}
/* ---- RPG DİYALOG KUTUSU ---- */
#dlg{position:fixed;left:50%;transform:translateX(-50%);bottom:92px;width:min(820px,94vw);z-index:10;
background:linear-gradient(#141a26ee,#0e131de8);border:1px solid #34405a;border-radius:14px;
padding:10px 14px 11px;backdrop-filter:blur(10px);display:flex;gap:12px;align-items:flex-start;
box-shadow:0 10px 40px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.05);transition:opacity .2s}
#dlgAva{width:46px;height:46px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;
font-weight:800;font-size:19px;color:#0a0e14;margin-top:3px;box-shadow:0 0 14px currentColor}
#dlgBody{flex:1;min-width:0;text-align:left}
#dlgName{font-weight:800;font-size:13px;letter-spacing:.4px;margin-bottom:3px}
#dlgName .arrow{color:var(--mut);font-weight:400;margin:0 5px}
#dlgName .target{color:#cdd6e4}
#dlgMain{font-size:13.5px;line-height:1.38;min-height:19px}
#dlgMain .cursor{display:inline-block;width:7px;background:#e8edf4;height:13px;margin-left:2px;
animation:blink .8s steps(1) infinite;vertical-align:middle}
@keyframes blink{50%{opacity:0}}
#dlgSub{font-size:11.5px;line-height:1.3;color:var(--mut);margin-top:3px}
/* ---- ALT KONTROLLER ---- */
#controls{position:fixed;left:0;right:0;bottom:0;z-index:10;
background:linear-gradient(#0a0e1400,#0a0e14f5 45%);padding:12px 18px 13px}
#tl{width:100%;height:34px;display:block;cursor:pointer;border-radius:6px}
#bar{display:flex;align-items:center;gap:12px;margin-top:8px}
.btn{background:#161b26;border:1px solid var(--border);color:var(--txt);width:40px;height:40px;border-radius:50%;
font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s}
.btn:hover{border-color:var(--accent);box-shadow:var(--glow)}
.btn.play{width:48px;height:48px;font-size:17px;background:linear-gradient(135deg,#2f81f7,#1a5fd0);
border-color:#2f81f7;box-shadow:0 0 20px rgba(47,129,247,.45)}
#time{font-variant-numeric:tabular-nums;font-size:13px;color:var(--mut);min-width:120px}
#hint{font-size:11px;color:var(--mut)}
/* ---- GİRİŞ EKRANI ---- */
#intro{position:fixed;inset:0;z-index:50;background:radial-gradient(ellipse at 50% 38%,#131a28 0%,#0a0e14 70%);
display:flex;align-items:center;justify-content:center;text-align:center}
#intro .in{max-width:520px;padding:24px}
#intro h2{font-size:30px;margin:0 0 4px;letter-spacing:1.5px;font-weight:800;
background:linear-gradient(180deg,#fff,#7fb0f5);-webkit-background-clip:text;background-clip:text;color:transparent;
text-shadow:0 0 30px rgba(47,129,247,.35)}
#intro .sm{color:var(--mut);font-size:13px;margin-bottom:22px}
#intro .feats{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-bottom:26px;font-size:11.5px;color:#aab6c8}
#intro .feats span{background:#141a26;border:1px solid var(--border);border-radius:8px;padding:6px 10px}
#startBtn{background:linear-gradient(135deg,#2f81f7,#1a5fd0);color:#fff;border:0;border-radius:30px;
padding:14px 46px;font-size:17px;font-weight:800;letter-spacing:1px;cursor:pointer;
box-shadow:0 0 34px rgba(47,129,247,.55);animation:pulse 1.8s ease-in-out infinite}
@keyframes pulse{50%{box-shadow:0 0 50px rgba(47,129,247,.85);transform:scale(1.03)}}
#intro .keys{margin-top:22px;font-size:11px;color:#66707f}
.badge{display:inline-block;font-size:10px;background:#3d2a00;color:var(--gold);border:1px solid #6b4a00;
border-radius:6px;padding:1px 6px;margin-left:6px;vertical-align:middle}
@media(max-width:760px){
#stats{display:none}
#legend{max-width:150px;font-size:10px;padding:7px 9px;top:58px}
#legend .desc{display:none}
#dlg{bottom:132px;padding:8px 10px}
#dlgAva{width:36px;height:36px;font-size:15px}
#dlgMain{font-size:12px}
#dlgSub{font-size:10.5px}
#hint{display:none}
#bar{flex-wrap:wrap}
#topbar h1{font-size:12.5px}
#topbar .sub{display:none}
.toggle button{padding:6px 10px;font-size:12px}
.iconbtn{padding:6px 9px;font-size:11px}
#intro h2{font-size:22px}
}
</style>
</head>
<body>
<div id="scene"></div>
<div id="topbar">
<h1>⚖️ TOPLANTI ODASI <span class="sub">· interaktif 3D kayıt · kim ne dedi, kime dedi</span></h1>
<div class="spacer"></div>
<button class="iconbtn on" id="camBtn" title="Oto kamera: konuşana odaklan">🎥 Oto</button>
<div class="toggle" id="langToggle">
<button data-m="orig" class="on">🔊 Orijinal</button>
<button data-m="tr">🇹🇷 Türkçe</button>
</div>
</div>
<div id="legend"><b>KİŞİLER & TARAFLAR</b><div id="legendRows"></div></div>
<div id="stats"><b>📊 KONUŞMA İSTATİSTİĞİ</b><div id="statRows"></div></div>
<div id="dlg">
<div id="dlgAva">?</div>
<div id="dlgBody">
<div id="dlgName"></div>
<div id="dlgMain">Başlamak için ▶</div>
<div id="dlgSub"></div>
</div>
</div>
<div id="controls">
<canvas id="tl"></canvas>
<div id="bar">
<button class="btn play" id="play"></button>
<button class="btn" id="prev" title="Önceki konuşma"></button>
<button class="btn" id="next" title="Sonraki konuşma"></button>
<span id="time">00:00 / 00:00</span>
<div class="spacer"></div>
<span id="hint">🖱️ sürükle: kamera · kişiye tıkla: sıradaki sözü · 📄 faturalar · boşluk: oynat/durdur · ←→: 5sn</span>
</div>
</div>
<div id="intro">
<div class="in">
<h2>TOPLANTI ODASI</h2>
<div class="sm">İnteraktif 3D uzlaşma kaydı — dolandırıcılık görüşmesi · 22 dk</div>
<div class="feats">
<span>🎧 Orijinal + Türkçe dublaj</span><span>💡 Konuşan kişi ışıkla</span>
<span>🎯 Kime söylediği okla</span><span>🎥 Oto kamera</span><span>📊 Canlı istatistik</span>
</div>
<button id="startBtn">&nbsp;BAŞLAT</button>
<div class="keys">boşluk = oynat/durdur &nbsp;·&nbsp; ← → = 5 sn &nbsp;·&nbsp; fare = kamera &nbsp;·&nbsp; kişiye tıkla = sözleri</div>
</div>
</div>
<div id="loadErr" style="display:none;position:fixed;inset:0;z-index:999;background:#0a0e14;color:#e8edf4;
font-family:'Segoe UI',system-ui,sans-serif;align-items:center;justify-content:center;text-align:center;padding:24px">
<div>
<div style="font-size:15px;font-weight:600;margin-bottom:8px">⚠️ Bir dosya yüklenemedi</div>
<div id="loadErrMsg" style="font-size:13px;color:#8b95a6"></div>
<div style="font-size:12px;color:#8b95a6;margin-top:10px">Sayfayı yeniden yüklemeyi deneyin.</div>
</div>
</div>
<script>
function showLoadErr(msg){
const box=document.getElementById('loadErr'), m=document.getElementById('loadErrMsg');
if(m)m.textContent=msg; if(box){box.style.display='flex';}
}
</script>
<audio id="ao" src="toplanti_orijinal.mp3?v=20260707a" preload="metadata" onerror="showLoadErr('toplanti_orijinal.mp3 yüklenemedi')"></audio>
<audio id="at" preload="metadata" onerror="window.TR_AUDIO_FAILED=true;try{this.pause();}catch(e){}"></audio>
<script src="three.min.js?v=20260704f" onerror="showLoadErr('three.min.js (3D motoru) yüklenemedi')"></script>
<script src="OrbitControls.js?v=20260704f" onerror="showLoadErr('OrbitControls.js (kamera kontrolü) yüklenemedi')"></script>
<script src="data.js?v=20260707b" onerror="showLoadErr('data.js (toplantı verisi) yüklenemedi')"></script>
<script>
if(typeof MEET==='undefined'){ showLoadErr('data.js yüklenemedi veya bozuk'); throw new Error('MEET tanımsız'); }
const ROLES=MEET.roles, TL=MEET.TL;
const POS3D={
"Kaan":{x:-1.6,z:2.3,stand:false}, "Marissa":{x:1.6,z:2.3,stand:false},
"Dolandırıcı (D-)":{x:-1.6,z:-2.3,stand:false}, "Uzlaşmacı (U)":{x:0.4,z:-2.3,stand:false},
"Yaşlı adam (V)":{x:-3.7,z:0,stand:false}, "Lider Uzlaşmacı (L.U)":{x:3.7,z:0,stand:false},
"Kayınvalide":{x:3.3,z:-2.6,stand:true}
};
const SHORT={"Kaan":"Kaan","Marissa":"Marissa","Dolandırıcı (D-)":"Scammer Litoy","Uzlaşmacı (U)":"Barangay Kuya 2",
"Yaşlı adam (V)":"Manong","Lider Uzlaşmacı (L.U)":"Barangay Kuya 1","Kayınvalide":"Nanay Daisy"};
const AVA={"Kaan":"K","Marissa":"M","Dolandırıcı (D-)":"L","Uzlaşmacı (U)":"K2",
"Yaşlı adam (V)":"Mn","Lider Uzlaşmacı (L.U)":"K1","Kayınvalide":"ND"};
const H2L={"Kaan":"Kaan","Marissa":"Marissa","Daisy":"Kayınvalide","Litoy":"Dolandırıcı (D-)",
"Kuya1":"Lider Uzlaşmacı (L.U)","Kuya2":"Uzlaşmacı (U)","Manong":"Yaşlı adam (V)"};
function roundRect(g,x,y,w,h,r){g.beginPath();g.moveTo(x+r,y);g.arcTo(x+w,y,x+w,y+h,r);g.arcTo(x+w,y+h,x,y+h,r);g.arcTo(x,y+h,x,y,r);g.arcTo(x,y,x+w,y,r);g.closePath();}
function makeLabel(text,color,scale){ scale=scale||1;
const c=document.createElement('canvas'); c.width=256; c.height=64; const g=c.getContext('2d');
g.fillStyle="rgba(10,14,20,0.88)"; roundRect(g,4,10,248,44,10); g.fill();
g.strokeStyle=color; g.lineWidth=2; roundRect(g,4,10,248,44,10); g.stroke();
g.fillStyle=color; g.font="bold 24px Segoe UI"; g.textAlign="center"; g.textBaseline="middle"; g.fillText(text,128,33);
const spr=new THREE.Sprite(new THREE.SpriteMaterial({map:new THREE.CanvasTexture(c),transparent:true,depthTest:false,sizeAttenuation:false}));
spr.scale.set(0.115*scale,0.029*scale,1); return spr;
}
function woodTexture(base,stripe){
const c=document.createElement('canvas'); c.width=256; c.height=256; const g=c.getContext('2d');
g.fillStyle=base; g.fillRect(0,0,256,256);
for(let i=0;i<40;i++){ g.strokeStyle='rgba(0,0,0,'+(0.04+Math.random()*0.08)+')'; g.lineWidth=1+Math.random()*3;
g.beginPath(); const y=Math.random()*256; g.moveTo(0,y);
g.bezierCurveTo(64,y+(Math.random()*8-4),192,y+(Math.random()*8-4),256,y); g.stroke(); }
g.strokeStyle=stripe; g.lineWidth=1;
for(let y=0;y<256;y+=32){ g.strokeRect(0,y,256,32); }
const t=new THREE.CanvasTexture(c); t.wrapS=t.wrapT=THREE.RepeatWrapping; return t;
}
function windowTexture(){
const c=document.createElement('canvas'); c.width=256; c.height=192; const g=c.getContext('2d');
const gr=g.createLinearGradient(0,0,0,192); gr.addColorStop(0,'#0b1530'); gr.addColorStop(1,'#1a2a50');
g.fillStyle=gr; g.fillRect(0,0,256,192);
g.fillStyle='#dfe8ff'; g.beginPath(); g.arc(200,42,17,0,7); g.fill();
g.fillStyle='rgba(223,232,255,0.25)'; g.beginPath(); g.arc(200,42,24,0,7); g.fill();
for(let i=0;i<60;i++){ g.fillStyle='rgba(255,255,255,'+(0.3+Math.random()*0.7)+')';
g.fillRect(Math.random()*256,Math.random()*120,1.4,1.4); }
for(let i=0;i<14;i++){ const bx=10+i*18, bh=30+Math.random()*46;
g.fillStyle='#0a1020'; g.fillRect(bx,192-bh,14,bh);
g.fillStyle='#e8c56a'; for(let w=0;w<5;w++){ if(Math.random()<0.5) g.fillRect(bx+3+(w%2)*6,196-bh+4+Math.floor(w/2)*8,3,4); } }
return new THREE.CanvasTexture(c);
}
/* ================= SAHNE ================= */
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x0a0e14);
scene.fog=new THREE.Fog(0x0a0e14,22,44);
const camera=new THREE.PerspectiveCamera(48,innerWidth/innerHeight,0.1,100);
function baseCamPos(){ const n=innerWidth<700; return new THREE.Vector3(0, n?8.6:6.2, n?14.5:9.6); }
camera.position.copy(baseCamPos());
const renderer=new THREE.WebGLRenderer({antialias:true});
renderer.setSize(innerWidth,innerHeight); renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.shadowMap.enabled=true; renderer.shadowMap.type=THREE.PCFSoftShadowMap;
document.getElementById('scene').appendChild(renderer.domElement);
const controls=new THREE.OrbitControls(camera,renderer.domElement);
controls.enableDamping=true; controls.dampingFactor=0.08; controls.target.set(0,1,0);
controls.maxPolarAngle=Math.PI*0.49; controls.minDistance=4.5; controls.maxDistance=24;
scene.add(new THREE.AmbientLight(0x8899bb,0.38));
const key=new THREE.DirectionalLight(0xbfd4ff,0.35); key.position.set(6,12,8); scene.add(key);
// tavan lambası + spot
const lampSpot=new THREE.SpotLight(0xffe2b0,1.25,26,Math.PI/3.4,0.5,1.2);
lampSpot.position.set(0,7.2,0); lampSpot.target.position.set(0,1,0);
lampSpot.castShadow=true; lampSpot.shadow.mapSize.set(1024,1024); lampSpot.shadow.bias=-0.0015;
scene.add(lampSpot); scene.add(lampSpot.target);
const cord=new THREE.Mesh(new THREE.CylinderGeometry(0.02,0.02,1.6,6),new THREE.MeshBasicMaterial({color:0x222831}));
cord.position.set(0,7.0,0); scene.add(cord);
const shade=new THREE.Mesh(new THREE.ConeGeometry(0.75,0.55,24,1,true),
new THREE.MeshStandardMaterial({color:0x2a3242,side:THREE.DoubleSide,emissive:0x110c02}));
shade.position.set(0,6.2,0); scene.add(shade);
const bulbL=new THREE.Mesh(new THREE.SphereGeometry(0.14,12,12),
new THREE.MeshStandardMaterial({color:0xfff3cf,emissive:0xffd88a,emissiveIntensity:1.8}));
bulbL.position.set(0,6.05,0); scene.add(bulbL);
// zemin + halı
const floor=new THREE.Mesh(new THREE.PlaneGeometry(40,40),
new THREE.MeshStandardMaterial({map:woodTexture('#20242e','rgba(255,255,255,0.02)'),roughness:0.9}));
floor.material.map.repeat.set(8,8);
floor.rotation.x=-Math.PI/2; floor.receiveShadow=true; scene.add(floor);
const rug=new THREE.Mesh(new THREE.CircleGeometry(7.2,48),new THREE.MeshStandardMaterial({color:0x232c40,roughness:1}));
rug.rotation.x=-Math.PI/2; rug.position.y=0.012; rug.receiveShadow=true; scene.add(rug);
// duvarlar + pencere + tablo + bitki
const wallMat=new THREE.MeshStandardMaterial({color:0x151b28,roughness:0.95});
const wallB=new THREE.Mesh(new THREE.PlaneGeometry(24,5.2),wallMat); wallB.position.set(0,2.6,-7.5); scene.add(wallB);
const wallL=new THREE.Mesh(new THREE.PlaneGeometry(20,5.2),wallMat); wallL.rotation.y=Math.PI/2; wallL.position.set(-9.5,2.6,0); scene.add(wallL);
const wallR=new THREE.Mesh(new THREE.PlaneGeometry(20,5.2),wallMat); wallR.rotation.y=-Math.PI/2; wallR.position.set(9.5,2.6,0); scene.add(wallR);
const winFrame=new THREE.Mesh(new THREE.BoxGeometry(4.6,2.6,0.12),new THREE.MeshStandardMaterial({color:0x39424f}));
winFrame.position.set(-2.2,2.7,-7.44); scene.add(winFrame);
const winGlass=new THREE.Mesh(new THREE.PlaneGeometry(4.2,2.2),
new THREE.MeshBasicMaterial({map:windowTexture()}));
winGlass.position.set(-2.2,2.7,-7.36); scene.add(winGlass);
const winBar=new THREE.Mesh(new THREE.BoxGeometry(0.08,2.2,0.05),new THREE.MeshStandardMaterial({color:0x39424f}));
winBar.position.set(-2.2,2.7,-7.34); scene.add(winBar);
const pic=new THREE.Mesh(new THREE.PlaneGeometry(1.6,1.1),
new THREE.MeshStandardMaterial({color:0x24405e,emissive:0x0a1522,roughness:0.6}));
pic.rotation.y=-Math.PI/2; pic.position.set(9.44,2.8,-1); scene.add(pic);
const picF=new THREE.Mesh(new THREE.BoxGeometry(0.06,1.3,1.8),new THREE.MeshStandardMaterial({color:0x4d4234}));
picF.position.set(9.47,2.8,-1); scene.add(picF);
const potM=new THREE.Mesh(new THREE.CylinderGeometry(0.32,0.24,0.5,12),new THREE.MeshStandardMaterial({color:0x6b4a2b}));
potM.position.set(-7.6,0.25,-6.2); potM.castShadow=true; scene.add(potM);
const plant=new THREE.Mesh(new THREE.ConeGeometry(0.75,1.9,10),new THREE.MeshStandardMaterial({color:0x2e5c37,roughness:0.9}));
plant.position.set(-7.6,1.4,-6.2); plant.castShadow=true; scene.add(plant);
// masa
const TABLE_H=1.0;
const tableTop=new THREE.Mesh(new THREE.BoxGeometry(6.4,0.16,3.2),
new THREE.MeshStandardMaterial({map:woodTexture('#6b4a2b','rgba(0,0,0,0.06)'),roughness:0.55}));
tableTop.position.y=TABLE_H; tableTop.castShadow=true; tableTop.receiveShadow=true; scene.add(tableTop);
[[2.9,1.4],[-2.9,1.4],[2.9,-1.4],[-2.9,-1.4]].forEach(function(p){
const leg=new THREE.Mesh(new THREE.CylinderGeometry(0.11,0.11,TABLE_H,12),new THREE.MeshStandardMaterial({color:0x4a3220}));
leg.position.set(p[0],TABLE_H/2,p[1]); leg.castShadow=true; scene.add(leg);
});
// kayıt cihazı (diyagramdaki yerinde: Kaan önü, masada)
const rec=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.12,0.28),
new THREE.MeshStandardMaterial({color:0xd21f1f,emissive:0x550000,roughness:0.4}));
rec.position.set(-1.3,TABLE_H+0.14,0.55); rec.castShadow=true; scene.add(rec);
const recDot=new THREE.Mesh(new THREE.SphereGeometry(0.035,8,8),
new THREE.MeshStandardMaterial({color:0xff3333,emissive:0xff0000,emissiveIntensity:2}));
recDot.position.set(-1.45,TABLE_H+0.21,0.55); scene.add(recDot);
// kupalar
[[0.8,-0.8],[-2.5,-0.4],[2.6,0.7]].forEach(function(p){
const mug=new THREE.Mesh(new THREE.CylinderGeometry(0.09,0.075,0.14,10),
new THREE.MeshStandardMaterial({color:0xcfd6e2,roughness:0.4}));
mug.position.set(p[0],TABLE_H+0.16,p[1]); mug.castShadow=true; scene.add(mug);
});
// sahte faturalar (tıklanabilir)
const paperGroup=new THREE.Group();
const paperMat=new THREE.MeshStandardMaterial({color:0xf2ede1,roughness:0.85});
const paperLineMat=new THREE.MeshStandardMaterial({color:0xcfc8b8,roughness:0.9});
const A5_W=0.42, A5_H=0.6, A5_T=0.006;
for(let i=0;i<5;i++){
const sheet=new THREE.Mesh(new THREE.BoxGeometry(A5_W,A5_T,A5_H),paperMat);
sheet.position.set((Math.random()-0.5)*0.03,TABLE_H+0.08+i*(A5_T+0.001),(Math.random()-0.5)*0.03);
sheet.rotation.y=(Math.random()-0.5)*0.28; sheet.castShadow=true;
paperGroup.add(sheet);
if(i===4){
for(let l=0;l<4;l++){
const line=new THREE.Mesh(new THREE.BoxGeometry(A5_W*0.72,0.001,0.012),paperLineMat);
line.position.set(sheet.position.x,sheet.position.y+A5_T/2+0.001,sheet.position.z-A5_H*0.28+l*0.12);
line.rotation.y=sheet.rotation.y;
paperGroup.add(line);
}
}
}
paperGroup.position.set(-0.5,0,1.15);
scene.add(paperGroup);
const paperLbl=makeLabel("📄 Faturalar","#e0b400",0.42); paperLbl.position.set(-0.5,TABLE_H+0.42,1.15); scene.add(paperLbl);
/* ---- karakterler ---- */
const SKIN=0xe8b98c, chars={};
function makeChar(role){
const p=POS3D[role], col=new THREE.Color(ROLES[role].renk);
const grp=new THREE.Group();
const cloth=new THREE.MeshStandardMaterial({color:col,roughness:0.7});
const skin=new THREE.MeshStandardMaterial({color:SKIN,roughness:0.6});
const hair=new THREE.MeshStandardMaterial({color:0x2a2018,roughness:0.9});
let headY;
if(p.stand){
const legL=new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,0.85,10),cloth); legL.position.set(-0.13,0.42,0);
const legR=legL.clone(); legR.position.x=0.13; grp.add(legL,legR);
const torso=new THREE.Mesh(new THREE.CylinderGeometry(0.22,0.32,0.85,14),cloth); torso.position.y=1.28; grp.add(torso);
const head=new THREE.Mesh(new THREE.SphereGeometry(0.19,20,20),skin); head.position.y=1.86; grp.add(head);
const h=new THREE.Mesh(new THREE.SphereGeometry(0.205,18,18,0,Math.PI*2,0,Math.PI*0.62),hair); h.position.y=1.9; grp.add(h);
headY=1.86;
} else {
const seat=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.1,0.56),new THREE.MeshStandardMaterial({color:0x353e50,roughness:0.8}));
seat.position.set(0,0.5,-0.08); grp.add(seat);
const back=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.62,0.08),new THREE.MeshStandardMaterial({color:0x353e50,roughness:0.8}));
back.position.set(0,0.8,-0.34); grp.add(back);
const thigh=new THREE.Mesh(new THREE.BoxGeometry(0.34,0.14,0.42),cloth); thigh.position.set(0,0.6,0.14); grp.add(thigh);
const shinL=new THREE.Mesh(new THREE.CylinderGeometry(0.08,0.08,0.5,10),cloth); shinL.position.set(-0.12,0.3,0.32); grp.add(shinL);
const shinR=shinL.clone(); shinR.position.x=0.12; grp.add(shinR);
const torso=new THREE.Mesh(new THREE.CylinderGeometry(0.2,0.3,0.62,14),cloth); torso.position.y=0.98; grp.add(torso);
const head=new THREE.Mesh(new THREE.SphereGeometry(0.185,20,20),skin); head.position.y=1.44; grp.add(head);
const h=new THREE.Mesh(new THREE.SphereGeometry(0.2,18,18,0,Math.PI*2,0,Math.PI*0.6),hair); h.position.y=1.48; grp.add(h);
headY=1.44;
}
grp.traverse(function(o){ if(o.isMesh) o.castShadow=true; });
const ring=new THREE.Mesh(new THREE.TorusGeometry(0.55,0.06,10,32),
new THREE.MeshStandardMaterial({color:col,emissive:col,emissiveIntensity:1.4}));
ring.rotation.x=-Math.PI/2; ring.position.y=0.04; ring.visible=false; grp.add(ring);
const tring=new THREE.Mesh(new THREE.TorusGeometry(0.5,0.035,8,32),
new THREE.MeshBasicMaterial({color:0xffffff,transparent:true,opacity:0.7}));
tring.rotation.x=-Math.PI/2; tring.position.y=0.05; tring.visible=false; grp.add(tring);
const bulb=new THREE.Mesh(new THREE.SphereGeometry(0.1,16,16),
new THREE.MeshStandardMaterial({color:0xfff6c0,emissive:0xffca28,emissiveIntensity:2.2}));
bulb.position.y=headY+1.05; bulb.visible=false; grp.add(bulb);
const halo=new THREE.Mesh(new THREE.SphereGeometry(0.17,16,16),
new THREE.MeshBasicMaterial({color:0xffd23a,transparent:true,opacity:0.25}));
halo.position.y=headY+1.05; halo.visible=false; grp.add(halo);
const blight=new THREE.PointLight(0xffd23a,0,4,2); blight.position.set(0,headY+1.0,0); grp.add(blight);
const lbl=makeLabel(SHORT[role],"#"+col.getHexString()); lbl.position.y=headY+0.55; grp.add(lbl);
grp.position.set(p.x,0,p.z);
const baseY=Math.atan2(-p.x,-p.z);
grp.rotation.y=baseY;
scene.add(grp);
chars[role]={grp:grp,ring:ring,tring:tring,bulb:bulb,halo:halo,blight:blight,active:false,
headY:headY,baseY:baseY,phase:Math.random()*6};
}
Object.keys(POS3D).forEach(makeChar);
/* ---- konuşmacı -> hedef ışını ---- */
let beam=null, beamDot=null, beamPair='';
function clearBeam(){
if(beam){ scene.remove(beam); beam.geometry.dispose(); beam.material.dispose(); beam=null; }
if(beamDot){ scene.remove(beamDot); beamDot.geometry.dispose(); beamDot.material.dispose(); beamDot=null; }
for(const r in chars) chars[r].tring.visible=false;
beamPair='';
}
function setBeam(fromRole,toRole){
const pairKey=fromRole+'>'+(toRole||'');
if(pairKey===beamPair) return;
clearBeam(); beamPair=pairKey;
if(!fromRole||!toRole||!chars[toRole]||fromRole===toRole) return;
const f=chars[fromRole], t=chars[toRole];
const p1=new THREE.Vector3(POS3D[fromRole].x,f.headY+0.25,POS3D[fromRole].z);
const p2=new THREE.Vector3(POS3D[toRole].x,t.headY+0.25,POS3D[toRole].z);
const mid=p1.clone().add(p2).multiplyScalar(0.5); mid.y+=1.15+p1.distanceTo(p2)*0.12;
const curve=new THREE.QuadraticBezierCurve3(p1,mid,p2);
const col=new THREE.Color(ROLES[fromRole].renk);
beam=new THREE.Mesh(new THREE.TubeGeometry(curve,32,0.022,6,false),
new THREE.MeshBasicMaterial({color:col,transparent:true,opacity:0.4}));
beam.userData.curve=curve; scene.add(beam);
beamDot=new THREE.Mesh(new THREE.SphereGeometry(0.07,10,10),
new THREE.MeshBasicMaterial({color:0xffffff}));
scene.add(beamDot);
t.tring.visible=true;
}
/* ================= SES + ZAMAN ================= */
const ao=document.getElementById('ao'), at=document.getElementById('at');
let mode='orig';
const TL_TR=MEET.TL_tr||null;
if(MEET.trAudio){ at.src=MEET.trAudio+'?v=20260707a'; }
function TR_READY(){ return !!MEET.trAudio && !!TL_TR && !window.TR_AUDIO_FAILED; }
function A(){ return (mode==='tr'&&TR_READY())?at:ao; }
function curTL(){ return (mode==='tr'&&TR_READY())?TL_TR:TL; }
/* ---- sol panel ---- */
const GROUPS=[
{ad:'🔵 Kaan tarafı', uyeler:[
['Kaan','şikâyetçi'],['Marissa','eşi; fiyat & makbuz takibi'],['Kayınvalide','baş müzakereci']]},
{ad:'⚖️ Arabulucular', uyeler:[
['Lider Uzlaşmacı (L.U)','lider arabulucu'],['Uzlaşmacı (U)','sorgulayan']]},
{ad:'🟡 Karşı taraf', uyeler:[
['Dolandırıcı (D-)','suçlanan'],['Yaşlı adam (V)','usta; Litoy ile çalıştı']]}
];
const lr=document.getElementById('legendRows');
GROUPS.forEach(function(g){
const h=document.createElement('div');
h.style.cssText='margin-top:7px;font-weight:700;font-size:10.5px;color:#8b95a6;letter-spacing:.3px';
h.textContent=g.ad; lr.appendChild(h);
g.uyeler.forEach(function(u){
const r=u[0], d=document.createElement('div'); d.className='row'; d.title=u[1]+' — tıkla: ilk sözüne git';
d.innerHTML='<span class="dot" style="background:'+ROLES[r].renk+';color:'+ROLES[r].renk+'"></span>'+SHORT[r]
+(POS3D[r].stand?' <span style="color:#8b95a6">(ayakta)</span>':'')
+' <span class="desc" style="color:#8b95a6;font-size:10px">— '+u[1]+'</span>';
d.onclick=function(){ const T=curTL(), b=T.find(function(x){return x.rol===r;});
if(b){ A().currentTime=b.t; update(); } };
lr.appendChild(d);
});
});
/* ---- skor paneli ---- */
const statTotals={};
TL.forEach(function(b){ const s=statTotals[b.rol]||(statTotals[b.rol]={sec:0,n:0});
s.sec+=(b.e-b.t); s.n++; });
const maxSec=Math.max.apply(null,Object.keys(statTotals).map(function(r){return statTotals[r].sec;}));
const statEls={};
const sr=document.getElementById('statRows');
Object.keys(statTotals).sort(function(a,b){return statTotals[b].sec-statTotals[a].sec;}).forEach(function(r){
const s=statTotals[r], d=document.createElement('div'); d.className='srow';
const tot=Math.round(s.sec), mins=Math.floor(tot/60), secs=tot%60;
d.innerHTML='<div class="nm"><span>'+SHORT[r]+'</span><span style="color:#8b95a6">'
+mins+':'+String(secs).padStart(2,'0')+' · '+s.n+' söz</span></div>'
+'<div class="bar"><div class="fill" style="width:'+(s.sec/maxSec*100).toFixed(0)+'%;background:'
+ROLES[r].renk+';color:'+ROLES[r].renk+'"></div></div>';
sr.appendChild(d); statEls[r]=d;
});
function statHighlight(role){ for(const r in statEls) statEls[r].classList.toggle('on',r===role); }
/* ---- zaman çizelgesi ---- */
const tl=document.getElementById('tl'), tg=tl.getContext('2d');
function curDur(){ const d=A().duration, T=curTL(); return (Number.isFinite(d)&&d>0)?d:T[T.length-1].e; }
function fitTL(){ tl.width=tl.clientWidth*devicePixelRatio; tl.height=34*devicePixelRatio; tg.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0); drawTL(); }
function drawTL(){
const T=curTL(), dur=curDur(), W=tl.clientWidth;
tg.clearRect(0,0,W,34);
tg.fillStyle="#161b26"; roundRect(tg,0,11,W,12,6); tg.fill();
tg.globalAlpha=0.85;
T.forEach(function(b){ const x=b.t/dur*W, w=Math.max(1,(b.e-b.t)/dur*W);
tg.fillStyle=(ROLES[b.rol]&&ROLES[b.rol].renk)||"#888"; tg.fillRect(x,11,w,12); });
tg.globalAlpha=1;
const ph=(A().currentTime/dur)*W;
tg.fillStyle="#fff"; tg.shadowColor="#fff"; tg.shadowBlur=6;
tg.fillRect(ph-1,6,2,22); tg.beginPath(); tg.arc(ph,6,4,0,7); tg.fill(); tg.shadowBlur=0;
}
function seekAtClientX(clientX){ const r=tl.getBoundingClientRect(); const x=(clientX-r.left)/r.width;
const a=A(); a.currentTime=Math.max(0,Math.min(1,x))*curDur(); update(); }
let drag=false;
tl.addEventListener('mousedown',function(e){drag=true;seekAtClientX(e.clientX);});
addEventListener('mousemove',function(e){if(drag)seekAtClientX(e.clientX);});
addEventListener('mouseup',function(){drag=false;});
tl.addEventListener('touchstart',function(e){drag=true;seekAtClientX(e.touches[0].clientX);e.preventDefault();},{passive:false});
addEventListener('touchmove',function(e){if(drag){seekAtClientX(e.touches[0].clientX);e.preventDefault();}},{passive:false});
addEventListener('touchend',function(){drag=false;});
addEventListener('touchcancel',function(){drag=false;});
function curIndex(){
const t=A().currentTime, T=curTL();
if(t<T[0].t) return -1;
let ans=0;
for(let i=0;i<T.length;i++){ if(T[i].t<=t) ans=i; else break; }
return ans;
}
/* ---- vurgulama + diyalog ---- */
let lastRole=null, speakerRole=null, targetRole=null;
function highlight(role,target){
speakerRole=role; targetRole=target||null;
if(role) setBeam(role,targetRole); else clearBeam();
statHighlight(role);
if(role===lastRole) return; lastRole=role;
for(const r in chars){ const on=(r===role); chars[r].active=on;
chars[r].ring.visible=on; chars[r].bulb.visible=on; chars[r].halo.visible=on;
if(!on)chars[r].blight.intensity=0; }
}
const fmt=function(s){s=Math.max(0,s|0);return String(s/60|0).padStart(2,'0')+':'+String(s%60).padStart(2,'0');};
const dlg=document.getElementById('dlg'), dAva=document.getElementById('dlgAva'),
dName=document.getElementById('dlgName'), dMain=document.getElementById('dlgMain'), dSub=document.getElementById('dlgSub');
let twText='', twPos=0, twBlock=-1;
function setDialog(b,i){
const col=(ROLES[b.rol]&&ROLES[b.rol].renk)||'#fff';
dAva.textContent=AVA[b.rol]||SHORT[b.rol].charAt(0);
dAva.style.background=col; dAva.style.color='#0a0e14'; dAva.style.boxShadow='0 0 14px '+col;
const hl=b.hedef&&H2L[b.hedef], hshort=hl?SHORT[hl]:(b.hedef==='Herkes'?'Herkese':null);
dName.innerHTML='<span style="background:#21262d;border:1px solid '+col+';border-radius:6px;padding:1px 7px;margin-right:9px;color:#e6edf3;font-size:12px;font-weight:700">ID '+i+'</span>'
+'<span style="color:'+col+'">'+SHORT[b.rol].toUpperCase()+'</span>'
+(hshort?'<span class="arrow">➜</span><span class="target">'+hshort.toUpperCase()+'</span>':'')
+((mode==='tr'&&!TR_READY())?' <span class="badge">ses hazırlanıyor</span>':'');
const main=(mode==='tr')?b.tr:b.orig, sub=(mode==='tr')?b.orig:b.tr;
if(i!==twBlock){ twBlock=i; twText=main; twPos=0; }
dSub.textContent=sub;
}
function twTick(){
if(twPos<twText.length){
twPos=Math.min(twText.length,twPos+2);
dMain.innerHTML=twText.slice(0,twPos).replace(/</g,'&lt;')+(twPos<twText.length?'<span class="cursor"></span>':'');
}
}
function update(){
const a=A(), T=curTL(), dur=curDur();
document.getElementById('time').textContent=fmt(a.currentTime)+' / '+fmt(dur);
const i=curIndex();
if(i>=0){ const b=T[i];
highlight(b.rol, b.hedef&&H2L[b.hedef]?H2L[b.hedef]:null);
setDialog(b,i);
dlg.style.opacity=1;
} else { highlight(null,null); dlg.style.opacity=.6; }
drawTL();
}
ao.addEventListener('timeupdate',function(){if(mode==='orig'||!TR_READY())update();});
at.addEventListener('timeupdate',function(){if(mode==='tr'&&TR_READY())update();});
ao.addEventListener('loadedmetadata',function(){fitTL();update();});
at.addEventListener('loadedmetadata',function(){if(mode==='tr')fitTL();});
/* ---- kontroller ---- */
const playBtn=document.getElementById('play');
playBtn.onclick=function(){ const a=A(); if(a.paused){a.play();playBtn.textContent='⏸';}else{a.pause();playBtn.textContent='▶';} };
document.getElementById('prev').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A();
if(i>0&&a.currentTime-T[i].t<0.4)i--; a.currentTime=T[Math.max(0,i)].t; update(); };
document.getElementById('next').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A();
a.currentTime=T[Math.min(T.length-1,i+1)].t; update(); };
let started=false;
document.addEventListener('keydown',function(e){
if(!started) return;
if(e.code==='Space'){e.preventDefault();playBtn.onclick();}
if(e.code==='ArrowRight')A().currentTime+=5;
if(e.code==='ArrowLeft')A().currentTime-=5; });
[ao,at].forEach(function(a){ a.addEventListener('ended',function(){ playBtn.textContent='▶'; }); });
document.getElementById('langToggle').addEventListener('click',function(e){
const btn=e.target.closest('button'); if(!btn)return; const m=btn.dataset.m; if(m===mode)return;
[].forEach.call(e.currentTarget.children,function(b){ b.classList.toggle('on',b===btn); b.classList.toggle('trm',b===btn&&m==='tr'); });
const wasPlaying=!A().paused; const idx=curIndex(); A().pause();
mode=m;
const T=curTL(); const b=T[Math.max(0,idx)]; if(b)A().currentTime=b.t;
twBlock=-1; fitTL(); update(); if(wasPlaying){A().play();playBtn.textContent='⏸';}
});
/* ---- oto kamera ---- */
let autoCam=true, autoPauseUntil=0, userInteracting=false;
const camBtn=document.getElementById('camBtn');
camBtn.onclick=function(){ autoCam=!autoCam; camBtn.classList.toggle('on',autoCam); };
controls.addEventListener('start',function(){ userInteracting=true; autoPauseUntil=performance.now()+7000; });
controls.addEventListener('end',function(){ userInteracting=false; autoPauseUntil=performance.now()+7000; });
const camTargetPos=new THREE.Vector3();
function autoCamTick(){
if(!autoCam||userInteracting||performance.now()<autoPauseUntil) return;
if(innerWidth<700) return;
let want;
if(speakerRole&&POS3D[speakerRole]){
const p=POS3D[speakerRole];
const d=Math.max(0.35,Math.hypot(p.x,p.z));
const nx=-p.x/d, nz=-p.z/d;
want=new THREE.Vector3(nx*8.8, 5.4, nz*8.8);
if(Math.hypot(want.x-POS3D[speakerRole].x,want.z-POS3D[speakerRole].z)<5){ want.multiplyScalar(1.25); want.y=5.4; }
} else want=baseCamPos();
camTargetPos.copy(want);
camera.position.lerp(camTargetPos,0.022);
}
/* ---- tıklamalar: kişi -> sıradaki sözü, faturalar -> sayfa ---- */
const roleNextIdx={};
function linesForRole(role){ return curTL().filter(function(x){return x.rol===role;}); }
function openFakeInvoicePage(){
const w=window.open('','_blank');
if(!w)return;
w.document.title='Sahte Faturalar';
w.document.body.style.cssText='background:#0d1117;color:#e6edf3;font-family:"Segoe UI",system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;text-align:center;font-size:22px;font-weight:600';
w.document.body.textContent='Alper Tarafından Duzenlenen Sahte faturalar';
}
let downXY=null;
renderer.domElement.addEventListener('pointerdown',function(ev){
downXY=(ev.button===0&&ev.isPrimary)?[ev.clientX,ev.clientY]:null; });
renderer.domElement.addEventListener('pointerup',function(ev){
if(ev.button!==0||!ev.isPrimary||!downXY) return;
const moved=Math.hypot(ev.clientX-downXY[0],ev.clientY-downXY[1]); downXY=null;
if(moved>6) return; // sürükleme = kamera, tık değil
const rc=new THREE.Raycaster(), m=new THREE.Vector2((ev.clientX/innerWidth)*2-1,-(ev.clientY/innerHeight)*2+1);
rc.setFromCamera(m,camera);
const clickable=Object.keys(chars).map(function(r){return chars[r].grp;}).concat([paperGroup]);
const hit=rc.intersectObjects(clickable,true)[0]; if(!hit)return;
let g=hit.object; while(g.parent&&clickable.indexOf(g)<0)g=g.parent;
if(g===paperGroup){ openFakeInvoicePage(); return; }
const role=Object.keys(chars).find(function(r){return chars[r].grp===g;}); if(!role)return;
const lines=linesForRole(role); if(!lines.length)return;
let idx=roleNextIdx[role]||0;
if(idx>=lines.length)idx=0;
const b=lines[idx]; const a=A();
a.currentTime=Math.max(0,b.t-0.1);
roleNextIdx[role]=idx+1;
update(); a.play(); playBtn.textContent='⏸';
});
/* ---- giriş ekranı ---- */
document.getElementById('startBtn').onclick=function(){
started=true;
document.getElementById('intro').style.display='none';
fitTL(); update();
A().play(); playBtn.textContent='⏸';
};
/* ---- animasyon döngüsü ---- */
let frame=0;
function loop(){
requestAnimationFrame(loop);
const t=performance.now()/1000; frame++;
for(const r in chars){ const c=chars[r];
// nefes alma
const breathe=Math.sin(t*1.4+c.phase)*0.008;
if(c.active){
c.grp.position.y=Math.sin(t*4)*0.04;
c.ring.rotation.z+=0.02; c.ring.material.emissiveIntensity=1.1+Math.sin(t*6)*0.5;
c.bulb.material.emissiveIntensity=1.6+Math.sin(t*6)*0.9; c.blight.intensity=1.4+Math.sin(t*6)*0.9;
c.halo.material.opacity=0.22+Math.sin(t*6)*0.12; c.halo.scale.setScalar(1+Math.sin(t*6)*0.12);
} else {
c.grp.position.y=c.grp.position.y*0.85+breathe*0.15;
}
// baş çevirme: konuşana (veya konuşmacı hedefe) bak
let lookAt=null;
if(speakerRole&&r===speakerRole&&targetRole&&POS3D[targetRole]) lookAt=POS3D[targetRole];
else if(speakerRole&&r!==speakerRole&&POS3D[speakerRole]) lookAt=POS3D[speakerRole];
let desired=c.baseY;
if(lookAt){
const dx=lookAt.x-POS3D[r].x, dz=lookAt.z-POS3D[r].z;
let want=Math.atan2(dx,dz);
let diff=want-c.baseY;
while(diff>Math.PI)diff-=2*Math.PI; while(diff<-Math.PI)diff+=2*Math.PI;
desired=c.baseY+Math.max(-0.45,Math.min(0.45,diff));
}
let dy=desired-c.grp.rotation.y;
while(dy>Math.PI)dy-=2*Math.PI; while(dy<-Math.PI)dy+=2*Math.PI;
c.grp.rotation.y+=dy*0.06;
}
if(beam&&beamDot){
const tt=(t*0.55)%1;
beamDot.position.copy(beam.userData.curve.getPointAt(tt));
beam.material.opacity=0.3+Math.sin(t*5)*0.12;
}
recDot.material.emissiveIntensity=1.2+Math.sin(t*3.5)*0.9;
if(frame%2===0) twTick();
autoCamTick();
controls.update(); renderer.render(scene,camera);
}
loop();
addEventListener('resize',function(){ camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.setSize(innerWidth,innerHeight);
if(innerWidth<700) camera.position.copy(baseCamPos());
fitTL(); });
fitTL();
</script>
</body>
</html>