{"id":987,"date":"2026-09-04T00:10:33","date_gmt":"2026-09-03T21:10:33","guid":{"rendered":"http:\/\/digitaldesignonline.ru\/?page_id=987"},"modified":"2026-10-28T21:07:55","modified_gmt":"2026-10-28T18:07:55","slug":"3d-design","status":"publish","type":"page","link":"https:\/\/digitaldesignonline.ru\/ru\/3d-design\/","title":{"rendered":"3D-\u0434\u0438\u0437\u0430\u0439\u043d"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"987\" class=\"elementor elementor-987\">\n\t\t\t\t<div class=\"elementor-element elementor-element-8ace8a4 e-con-full e-flex e-con e-parent\" data-id=\"8ace8a4\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-f8bbe29 e-con-full e-flex e-con e-child\" data-id=\"f8bbe29\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e32c125 elementor-widget elementor-widget-html\" data-id=\"e32c125\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- 3D CHESS for Elementor (Sketchfab-style render): paste the whole file into an \"HTML\" widget -->\r\n<div id=\"c3\">\r\n  <div class=\"c3-load\">LOADING <b>0%<\/b><\/div>\r\n  <div class=\"c3-fg\"><\/div>\r\n  <div class=\"c3-ui\">\r\n    <span class=\"c3-st\">Your move (White)<\/span>\r\n    <div class=\"c3-row\">\r\n      <label class=\"c3-sel\"><select class=\"c3-side\" aria-label=\"Side\"><option value=\"w\" selected>Play White<\/option><option value=\"b\">Play Black<\/option><\/select><\/label>\r\n      <label class=\"c3-sel\"><select class=\"c3-lv\" aria-label=\"Level\"><option value=\"0\">Easy<\/option><option value=\"1\">Medium<\/option><option value=\"2\" selected>Hard<\/option><option value=\"3\">ZeroChance<\/option><\/select><\/label>\r\n      <button class=\"c3-new\">New game<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n<style>\r\n#c3{position:relative;width:100%;height:100vh;height:100dvh;min-height:320px;background:#02050a;overflow:hidden;font-family:system-ui,sans-serif;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}\r\n#c3 canvas{display:block;width:100%;height:100%;touch-action:none;cursor:grab}\r\n.c3-fg{position:absolute;inset:0;z-index:6;pointer-events:none;background:center\/cover no-repeat}\r\n.c3-load{position:absolute;inset:0;display:flex;gap:8px;align-items:center;justify-content:center;color:#7fe6ff;letter-spacing:.2em;background:#02050a center\/cover no-repeat;z-index:7;transition:opacity .8s;text-shadow:0 2px 12px rgba(0,0,0,.8)}\r\n.c3-ui{position:absolute;left:50%;bottom:18px;bottom:max(18px,env(safe-area-inset-bottom));transform:translateX(-50%);display:flex;flex-direction:column;gap:10px;align-items:center;padding:12px 14px;border-radius:22px;color:#eaf8ff;white-space:nowrap;max-width:94%;z-index:8;\r\n  background:linear-gradient(135deg,rgba(255,255,255,.18),rgba(255,255,255,.05));border:1px solid rgba(255,255,255,.24);\r\n  -webkit-backdrop-filter:blur(22px) saturate(170%);backdrop-filter:blur(22px) saturate(170%);\r\n  box-shadow:0 10px 40px rgba(0,0,0,.45),0 0 28px rgba(42,212,255,.16),inset 0 1px 0 rgba(255,255,255,.4),inset 0 -1px 0 rgba(255,255,255,.06)}\r\n.c3-st{font-size:14px;letter-spacing:.03em;padding:5px 14px;border-radius:999px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);text-shadow:0 1px 8px rgba(0,0,0,.4)}\r\n.c3-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}\r\n.c3-sel{position:relative;display:block}\r\n.c3-sel:after{content:\"\";position:absolute;right:12px;top:50%;width:6px;height:6px;margin-top:-5px;border-right:2px solid #bfefff;border-bottom:2px solid #bfefff;transform:rotate(45deg);pointer-events:none}\r\n.c3-ui select,.c3-ui button{-webkit-appearance:none;appearance:none;color:#eaf8ff;font:inherit;font-size:14px;min-height:38px;padding:7px 30px 7px 14px;cursor:pointer;border-radius:12px;outline:none;\r\n  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.22);box-shadow:inset 0 1px 0 rgba(255,255,255,.28);transition:background .2s,box-shadow .2s,transform .15s}\r\n.c3-ui button{padding:7px 16px;background:linear-gradient(135deg,rgba(56,232,255,.32),rgba(56,232,255,.10));border-color:rgba(120,236,255,.5)}\r\n.c3-ui select option{background:#0b1822;color:#eaf8ff}\r\n.c3-ui select:hover,.c3-ui button:hover{background:rgba(255,255,255,.18);box-shadow:inset 0 1px 0 rgba(255,255,255,.4),0 0 16px rgba(56,232,255,.4)}\r\n.c3-ui select:active,.c3-ui button:active{transform:scale(.97)}\r\n.c3-ui select:focus-visible,.c3-ui button:focus-visible{box-shadow:0 0 0 2px rgba(120,236,255,.7)}\r\n@media (max-width:600px){\r\n  .c3-ui{gap:8px;padding:10px;border-radius:20px;bottom:max(10px,env(safe-area-inset-bottom));width:94%;box-sizing:border-box}\r\n  .c3-st{font-size:13px;text-align:center;max-width:100%;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis}\r\n  .c3-row{width:100%}.c3-sel{flex:1}.c3-ui select{width:100%;min-height:42px}.c3-ui button{flex:1;min-height:42px}\r\n}\r\n@media (max-height:420px){ .c3-ui{padding:6px 10px;gap:6px;bottom:6px;flex-direction:row;flex-wrap:wrap} .c3-ui select,.c3-ui button{min-height:30px;padding-top:2px;padding-bottom:2px} }\r\n<\/style>\r\n<script>\r\n(async function () {\r\n\/* ================= SETTINGS ================= *\/\r\nconst W = 'https:\/\/digitaldesignonline.ru\/wp-content\/uploads\/2026\/10\/';\r\nconst CFG = {\r\n  board: W + 'Board1.glb',\r\n  pieces:      { p:W+'P-Pawn2W.glb', n:W+'N-Knight1W.glb', b:W+'B-Bishop1W.glb', r:W+'R-Rook1.glb',  q:W+'Q-Queen1W.glb', k:W+'K-King1W.glb' },\r\n  blackPieces: { p:W+'P-Pawn1B.glb', n:W+'N-Knight1B.glb', b:W+'B-Bishop1B.glb', r:W+'R-Rook2B.glb', q:W+'Q-Queen1B.glb', k:W+'K-King1B.glb' },\r\n  tint: false,\r\n  boardSize: 8.8,\r\n  boardY: 0,\r\n  pieceScale: 1,\r\n  pieceRotY: 0,\r\n  hover: 0.32,\r\n  springBack: false,\r\n  background: W + '4b20e577-faca-428f-8572-11169b31ff5f.jpg',\r\n  foreground: '',\r\n  foregroundFit: 'cover',\r\n  preview: W + 'https:\/\/digitaldesignonline.ru\/wp-content\/uploads\/2026\/10\/4b20e577-faca-428f-8572-11169b31ff5f.jpg',  \/\/ loading-screen image ('' = plain dark background)\r\n  previewDim: 0.35,            \/\/ preview darkening 0-1 so the \"LOADING\" text stays readable\r\n  graveScale: 0.5,\r\n  graveHeight: 1.5,\r\n  pushDist: 1.1,\r\n  pushTilt: 1.3,\r\n  \/* --- Sketchfab-style render --- *\/\r\n  envIntensity: 1.0,   \/\/ strength of image-based lighting (IBL)\r\n  exposure: 0.75,      \/\/ tone mapping exposure (ACES)\r\n  bloom: 0.32,         \/\/ HD only: bloom strength\r\n  vignette: 0.28,      \/\/ HD only\r\n  shadowSoft: 1.0,     \/\/ shadow softness multiplier\r\n  zeroSkill: 20,       \/\/ ZeroChance: Stockfish Skill Level 0-20 (~12 = strong club \/ candidate-master range)\r\n  zeroTime: 1500       \/\/ ZeroChance: think time per move, ms\r\n};\r\nconst LV = [ {maxD:2,time:700}, {maxD:3,time:2500}, {maxD:5,time:6000}, {sf:true} ];\r\nconst SF_URL = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/stockfish.js\/10.0.2\/stockfish.js';\r\nconst CDN = 'https:\/\/cdn.jsdelivr.net\/npm\/';\r\nconst isMobile = matchMedia('(pointer:coarse)').matches || Math.min(innerWidth, innerHeight) < 600;\r\nconst U = u => (u && location.protocol === 'https:') ? u.replace(\/^http:\/, 'https:') : u;\r\n\/* ============================================== *\/\r\n\r\nconst $ = s => document.querySelector('#c3 ' + s);\r\n\/\/ loading-screen preview: applied immediately, before any await\r\nif (CFG.preview) {\r\n  const d = CFG.previewDim;\r\n  $('.c3-load').style.backgroundImage = `linear-gradient(rgba(2,5,10,${d}),rgba(2,5,10,${d})),url('${U(CFG.preview)}')`;\r\n}\r\nconst loadJs = u => new Promise((ok, no) => { const s = document.createElement('script'); s.src = u; s.onload = ok; s.onerror = no; document.head.appendChild(s); });\r\nawait loadJs(CDN + 'three@0.128.0\/build\/three.min.js');\r\nawait loadJs(CDN + 'three@0.128.0\/examples\/js\/loaders\/GLTFLoader.js');\r\nawait loadJs(CDN + 'chess.js@0.10.3\/chess.js');\r\n\/\/ post-processing is optional: if anything fails to load, the scene still renders directly\r\nlet FX = false;\r\ntry {\r\n  const E = CDN + 'three@0.128.0\/examples\/js\/';\r\n  await Promise.all([loadJs(E + 'shaders\/CopyShader.js'), loadJs(E + 'shaders\/LuminosityHighPassShader.js'), loadJs(E + 'postprocessing\/EffectComposer.js')]);\r\n  await Promise.all([loadJs(E + 'postprocessing\/ShaderPass.js'), loadJs(E + 'postprocessing\/RenderPass.js'), loadJs(E + 'postprocessing\/UnrealBloomPass.js')]);\r\n  FX = true;\r\n} catch (e) { console.warn('Post-processing unavailable', e); }\r\nconst T = THREE, root = $('canvas') || document.getElementById('c3');\r\nconst lvSel = $('.c3-lv'), sideSel = $('.c3-side');\r\nif (isMobile) lvSel.value = '1';\r\n\r\n\/* ---------- renderer \/ scene ---------- *\/\r\nconst renderer = new T.WebGLRenderer({ antialias: true, alpha: false, powerPreference: 'high-performance' });\r\nrenderer.outputEncoding = T.sRGBEncoding; renderer.toneMapping = T.ACESFilmicToneMapping; renderer.toneMappingExposure = CFG.exposure;\r\nrenderer.shadowMap.enabled = true; renderer.shadowMap.type = T.PCFSoftShadowMap;\r\nroot.prepend(renderer.domElement);\r\nconst cv = renderer.domElement;\r\ncv.addEventListener('webglcontextlost', e => e.preventDefault());\r\nconst MAXA = Math.min(renderer.capabilities.getMaxAnisotropy(), isMobile ? 4 : 16);\r\nconst scene = new T.Scene(); scene.background = new T.Color(0x02050a);\r\nif (!CFG.background) scene.fog = new T.FogExp2(0x02050a, .028);\r\n\/\/ background image is drawn inside the scene (so post-processing sees it) and emulates CSS \"cover\"\r\nlet bgTex = null;\r\nif (CFG.background) new T.TextureLoader().load(U(CFG.background), t => {\r\n  t.encoding = T.sRGBEncoding; t.minFilter = T.LinearFilter; t.generateMipmaps = false; bgTex = t; scene.background = t; resize(); });\r\nfunction coverBg() {\r\n  if (!bgTex || !bgTex.image) return; const ia = bgTex.image.width \/ bgTex.image.height, va = cam.aspect;\r\n  if (va > ia) { bgTex.repeat.set(1, ia \/ va); bgTex.offset.set(0, (1 - ia \/ va) \/ 2); } else { bgTex.repeat.set(va \/ ia, 1); bgTex.offset.set((1 - va \/ ia) \/ 2, 0); }\r\n}\r\nif (CFG.foreground) { const fg = $('.c3-fg'); fg.style.backgroundImage = `url('${U(CFG.foreground)}')`; fg.style.backgroundSize = CFG.foregroundFit; }\r\nconst cam = new T.PerspectiveCamera(40, 1, .1, 100);\r\nconst boardG = new T.Group(); scene.add(boardG);\r\n\r\n\/* ---------- IBL: procedural studio (softboxes + gradient dome) -> PMREM ---------- *\/\r\n{ const pm = new T.PMREMGenerator(renderer), es = new T.Scene();\r\n  es.add(new T.Mesh(new T.SphereGeometry(20, 24, 24), new T.MeshBasicMaterial({ color: 0x03070c, side: T.BackSide })));\r\n  [[0x38e8ff, -10, 4, 6, 9, 5], [0xffffff, 8, 10, 4, 10, 6], [0x38e8ff, 6, 3, -10, 9, 5], [0xbfefff, 0, 14, 0, 12, 12], [0x6fb8d8, 0, 1, 12, 14, 3]].forEach(([c, x, y, z, w, h]) => {\r\n    const m = new T.Mesh(new T.PlaneGeometry(w, h), new T.MeshBasicMaterial({ color: c, side: T.DoubleSide })); m.position.set(x, y, z); m.lookAt(0, 0, 0); es.add(m); });\r\n  scene.environment = pm.fromScene(es, 0.02).texture; pm.dispose(); }\r\nscene.add(new T.HemisphereLight(0x9fdfff, 0x02050a, .5));\r\nconst sun = new T.DirectionalLight(0xdff4ff, 1.1); sun.position.set(6, 12, 8); sun.castShadow = true; scene.add(sun);\r\n{ const s = sun.shadow, c = s.camera; c.left = c.bottom = -7.5; c.right = c.top = 7.5; c.near = 1; c.far = 40; c.updateProjectionMatrix();\r\n  s.bias = -0.0004; s.normalBias = 0.03; s.radius = 3 * CFG.shadowSoft; }\r\nconst rim = new T.DirectionalLight(0x38e8ff, .55); rim.position.set(-6, 6, -9); scene.add(rim);   \/\/ cyan rim light, no shadow\r\n(isMobile ? [[-9, 2, 6]] : [[-9, 2, 6], [9, 3, -7]]).forEach(p => { const l = new T.PointLight(0x2ad4ff, isMobile ? 2.2 : 1.6, 32); l.position.set(...p); scene.add(l); });\r\n\r\n\/* ---------- material prep (Sketchfab-like: anisotropic filtering, shadows, IBL strength) ---------- *\/\r\nfunction prep(o, cast) {\r\n  o.traverse(n => { if (!n.isMesh) return; n.castShadow = cast; n.receiveShadow = true;\r\n    (Array.isArray(n.material) ? n.material : [n.material]).forEach(m => {\r\n      ['map', 'normalMap', 'roughnessMap', 'metalnessMap', 'emissiveMap', 'aoMap'].forEach(k => { if (m[k]) m[k].anisotropy = MAXA; });\r\n      if ('envMapIntensity' in m) m.envMapIntensity = CFG.envIntensity; }); });\r\n}\r\n\r\n\/* ---------- models ---------- *\/\r\nconst H = { p:.85, n:1.05, b:1.2, r:.95, q:1.4, k:1.6 };\r\nconst MAT = {\r\n  w: new T.MeshPhysicalMaterial({ color: 0xe4f2fa, metalness: .25, roughness: .16, emissive: 0x0a2230, clearcoat: .6, clearcoatRoughness: .12, envMapIntensity: CFG.envIntensity }),\r\n  b: new T.MeshPhysicalMaterial({ color: 0x080b0f, metalness: .7, roughness: .18, emissive: 0x00131c, clearcoat: .5, clearcoatRoughness: .15, envMapIntensity: CFG.envIntensity })\r\n};\r\nconst gl = new T.GLTFLoader(), cache = {}; let done = 0, total = 13;\r\nconst bump = () => { $('.c3-load b').textContent = Math.round(++done \/ total * 100) + '%'; };\r\nconst loadGlb = u => cache[u] || (cache[u] = new Promise(r => gl.load(U(u), g => r(g.scene), undefined, () => { console.warn('GLB failed to load:', u); r(null); })));\r\nfunction fit(o, h) {\r\n  const s = new T.Vector3(); new T.Box3().setFromObject(o).getSize(s); o.scale.multiplyScalar(h \/ s.y); o.updateMatrixWorld(true);\r\n  const b = new T.Box3().setFromObject(o), c = b.getCenter(new T.Vector3()); o.position.x -= c.x; o.position.z -= c.z; o.position.y -= b.min.y;\r\n  const g = new T.Group(); g.add(o); return g;\r\n}\r\nfunction fallback(t, col) {\r\n  const g = new T.Group(), m = MAT[col], h = H[t] * CFG.pieceScale, add = (geo, y) => { const x = new T.Mesh(geo, m); x.position.y = y; g.add(x); return x; };\r\n  add(new T.CylinderGeometry(.3, .34, .12, 24), .06);\r\n  add(new T.CylinderGeometry(t === 'r' ? .24 : .1 + h * .05, .27, h * .6, 24), h * .35);\r\n  if (t === 'p') add(new T.SphereGeometry(.17, 20, 16), h * .72);\r\n  if (t === 'r') add(new T.CylinderGeometry(.28, .22, .2, 8), h * .78);\r\n  if (t === 'n') { const x = add(new T.BoxGeometry(.26, .5, .5), h * .75); x.rotation.x = .5; }\r\n  if (t === 'b') { const x = add(new T.SphereGeometry(.17, 20, 16), h * .75); x.scale.y = 1.5; }\r\n  if (t === 'q') { add(new T.ConeGeometry(.25, .35, 20), h * .78); add(new T.SphereGeometry(.09, 12, 12), h * .98); }\r\n  if (t === 'k') { add(new T.BoxGeometry(.1, .4, .1), h * .85); add(new T.BoxGeometry(.28, .09, .1), h * .88); }\r\n  return g;\r\n}\r\nconst TPL = {};\r\nasync function tpl(t, col) {\r\n  const u = (col === 'b' && CFG.blackPieces[t]) || CFG.pieces[t], sc = u && await loadGlb(u); let g;\r\n  if (sc) { const o = sc.clone(true); if (CFG.tint && !(col === 'b' && CFG.blackPieces[t])) o.traverse(n => { if (n.isMesh) n.material = MAT[col]; }); g = fit(o, H[t] * CFG.pieceScale); }\r\n  else g = fallback(t, col);\r\n  prep(g, true);\r\n  g.rotation.y = CFG.pieceRotY + (col === 'b' ? Math.PI : 0);\r\n  const w = new T.Group(); w.add(g); TPL[col + t] = w; bump();\r\n}\r\nawait Promise.all([...'pnbrqk'].flatMap(t => ['w', 'b'].map(c => tpl(t, c))));\r\n\r\n\/* ---------- board ---------- *\/\r\nconst sqPos = s => new T.Vector3(s.charCodeAt(0) - 100.5, 0, 3.5 - (+s[1] - 1));\r\nconst tiles = [], boardGlb = CFG.board && await loadGlb(CFG.board); bump();\r\nfor (let f = 0; f < 8; f++) for (let r = 0; r < 8; r++) {\r\n  const light = (f + r) % 2 === 1, mat = new T.MeshStandardMaterial(boardGlb ? { transparent: true, opacity: 0, depthWrite: false } :\r\n    { color: light ? 0xdce8ef : 0x05070a, roughness: .22, metalness: light ? .05 : .4, emissive: light ? 0x0b1c26 : 0x000000 });\r\n  const t = new T.Mesh(new T.BoxGeometry(1, .1, 1), mat); const s = String.fromCharCode(97 + f) + (r + 1);\r\n  t.position.copy(sqPos(s)).setY(-.05); t.userData.sq = s; t.receiveShadow = !boardGlb; boardG.add(t); tiles.push(t);\r\n}\r\nif (boardGlb) { const o = boardGlb.clone(); prep(o, false); const s = new T.Vector3(); new T.Box3().setFromObject(o).getSize(s); o.scale.multiplyScalar(CFG.boardSize \/ Math.max(s.x, s.z)); o.updateMatrixWorld(true);\r\n  const b = new T.Box3().setFromObject(o), c = b.getCenter(new T.Vector3()); o.position.set(o.position.x - c.x, o.position.y - b.max.y + CFG.boardY, o.position.z - c.z); boardG.add(o); }\r\nelse {\r\n  const fr = new T.Mesh(new T.BoxGeometry(8.7, .3, 8.7), new T.MeshStandardMaterial({ color: 0x070b10, metalness: .8, roughness: .3 })); fr.position.y = -.27; fr.receiveShadow = true; boardG.add(fr);\r\n  const ed = new T.LineSegments(new T.EdgesGeometry(new T.BoxGeometry(8.7, .3, 8.7)), new T.LineBasicMaterial({ color: 0x38e8ff })); ed.position.y = -.27; boardG.add(ed);\r\n  if (!isMobile) { const gl2 = new T.PointLight(0x2ad4ff, 1.4, 9); gl2.position.y = -1.2; boardG.add(gl2); }\r\n}\r\n\r\n\/* ---------- animation ---------- *\/\r\nconst ease = t => t < .5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) \/ 2, tw = [];\r\nconst tween = (d, f, e = ease) => new Promise(r => tw.push({ d, f, e, t: 0, r }));\r\nconst wait = ms => new Promise(r => setTimeout(r, ms));\r\n\r\n\/* ---------- pieces ---------- *\/\r\nconst P = {}, D = [], gy = { w: 0, b: 0 };\r\nconst glowTex = (() => { const c = document.createElement('canvas'); c.width = c.height = 64; const x = c.getContext('2d'), g = x.createRadialGradient(32, 32, 0, 32, 32, 32);\r\n  g.addColorStop(0, 'rgba(80,230,255,.8)'); g.addColorStop(1, 'rgba(80,230,255,0)'); x.fillStyle = g; x.fillRect(0, 0, 64, 64); return new T.CanvasTexture(c); })();\r\nfunction mk(t, col, sq) {\r\n  const r = new T.Group(), inner = TPL[col + t].clone(true);\r\n  const gp = new T.Mesh(new T.PlaneGeometry(1.2, 1.2), new T.MeshBasicMaterial({ map: glowTex, transparent: true, blending: T.AdditiveBlending, depthWrite: false, opacity: .35 }));\r\n  gp.rotation.x = -Math.PI \/ 2; gp.position.y = .02; r.add(gp, inner);\r\n  r.userData = { sq, t, col, inner, lift: 0, sy: 1, ph: Math.random() * 6 }; r.position.copy(sqPos(sq)); boardG.add(r); return r;\r\n}\r\nconst hl = new T.Group(); boardG.add(hl);\r\nconst hlMat = (o) => new T.MeshBasicMaterial({ color: 0x38e8ff, transparent: true, opacity: o, blending: T.AdditiveBlending, depthWrite: false, side: T.DoubleSide });\r\nfunction showHl(sel, ms) {\r\n  hl.clear();\r\n  const add = (g, s, o, pulse) => { const m = new T.Mesh(g, hlMat(o)); m.rotation.x = -Math.PI \/ 2; m.position.copy(sqPos(s)).setY(.03); m.userData = { o, pulse }; hl.add(m); };\r\n  const sq = new T.PlaneGeometry(.94, .94);\r\n  add(sq, sel, .4, 0);\r\n  ms.forEach(m => { add(sq, m.to, .22, 1); add(m.flags.match(\/[ce]\/) ? new T.RingGeometry(.3, .42, 32) : new T.CircleGeometry(.17, 24), m.to, .9, 1); });\r\n}\r\n\r\n\/* ---------- capture animation ---------- *\/\r\nconst AX = new T.Vector3();\r\nconst lean = (p, d, a) => { AX.set(d.z, 0, -d.x); p.quaternion.setFromAxisAngle(AX, a); };\r\nasync function eat(m, c, a, b, cs) {\r\n  const A = sqPos(a), B = sqPos(b), u = m.userData, cu = c.userData, id = gid;\r\n  const dir = B.clone().sub(A).setY(0); if (dir.lengthSq() < 1e-6) dir.set(0, 0, -1); dir.normalize();\r\n  const pre = A.clone().lerp(B, .55); u.sq = b; c.children[0].visible = false;\r\n  await tween(.4, t => { m.position.lerpVectors(A, pre, t); u.lift = Math.sin(Math.PI * .5 * t) * .8; lean(m, dir, -.25 * t); });\r\n  await tween(.18, t => { m.position.lerpVectors(pre, B, t); u.lift = .8 - .4 * t; lean(m, dir, -.25 + .65 * t); }, t => t * t);\r\n  const V0 = c.position.clone();\r\n  await tween(.7, t => {\r\n    c.position.x = V0.x + dir.x * CFG.pushDist * t;\r\n    c.position.z = V0.z + dir.z * CFG.pushDist * t;\r\n    c.position.y = Math.sin(Math.PI * t) * .5;\r\n    lean(c, dir, CFG.pushTilt * t);\r\n    lean(m, dir, .4 * (1 - t)); u.lift = .4 * (1 - t);\r\n  }, t => 1 - (1 - t) * (1 - t));\r\n  m.quaternion.identity(); u.sy = 1; u.lift = 0;\r\n  if (id !== gid) { boardG.remove(c); return; }\r\n  const col = cu.col, n = gy[col]++, S = c.position.clone(),\r\n        sg = me === 'b' ? -1 : 1, E = new T.Vector3(sg * (-3.5 + Math.min(n, 14) * .5), CFG.graveHeight, sg * (col === 'b' ? -5 : -5.7));\r\n  c.quaternion.identity(); c.rotation.y = 0; cu.sq = null; cu.spin = 0; D.push(c);\r\n  tween(1.3, t => { c.position.lerpVectors(S, E, t); c.position.y += Math.sin(Math.PI * t) * 1.4; c.scale.setScalar(1 + (CFG.graveScale - 1) * t); });\r\n}\r\n\r\n\/* ---------- game logic ---------- *\/\r\nlet game = new Chess(), busy = true, sel = null, targets = [], gid = 0, me = 'w', sideAng = 0;\r\nconst status = s => { $('.c3-st').textContent = s; };\r\nfunction refresh() {\r\n  if (game.in_checkmate()) status(game.turn() === me ? 'Checkmate. You lost' : 'Checkmate! You win \ud83c\udf89');\r\n  else if (game.in_draw()) status('Draw');\r\n  else status((game.turn() === me ? 'Your move' : 'Bot\u2019s move') + (game.in_check() ? ' \u2014 check!' : ''));\r\n}\r\nfunction setup() {\r\n  Object.values(P).forEach(p => boardG.remove(p)); for (const k in P) delete P[k];\r\n  D.forEach(p => boardG.remove(p)); D.length = 0; gy.w = gy.b = 0;\r\n  game.board().forEach((row, r) => row.forEach((p, c) => { if (!p) return; const s = String.fromCharCode(97 + c) + (8 - r), m = mk(p.type, p.color, s); P[s] = m;\r\n    m.userData.lift = 4; m.scale.setScalar(.01); const d = Math.random() * .6; tween(.9 + d, t => { m.userData.lift = 4 * (1 - t); m.scale.setScalar(Math.max(.01, t)); }); }));\r\n}\r\nconst mover = (p, a, b, lift) => { const A = sqPos(a), B = sqPos(b); p.userData.sq = b; return tween(.75, t => { p.position.lerpVectors(A, B, t); p.userData.lift = Math.sin(Math.PI * t) * lift; }); };\r\nasync function play(mv) {\r\n  const m = P[mv.from]; delete P[mv.from]; const jobs = []; let c = null, cs = null;\r\n  if (mv.captured) { cs = mv.flags.includes('e') ? mv.to[0] + mv.from[1] : mv.to; c = P[cs]; delete P[cs]; }\r\n  P[mv.to] = m;\r\n  if (c) await eat(m, c, mv.from, mv.to, cs); else jobs.push(mover(m, mv.from, mv.to, 1.1));\r\n  if (\/[kq]\/.test(mv.flags)) { const rk = mv.color === 'w' ? '1' : '8', [a, b] = mv.flags.includes('k') ? ['h', 'f'] : ['a', 'd'], R = P[a + rk]; delete P[a + rk]; P[b + rk] = R; jobs.push(mover(R, a + rk, b + rk, .6)); }\r\n  await Promise.all(jobs);\r\n  if (mv.promotion) { boardG.remove(m); const q = mk(mv.promotion, mv.color, mv.to); P[mv.to] = q; q.scale.setScalar(.01); await tween(.5, t => q.scale.setScalar(Math.max(.01, t))); }\r\n}\r\nconst worker = () => new Worker(URL.createObjectURL(new Blob([`\r\nimportScripts('${CDN}chess.js@0.10.3\/chess.js');\r\nconst V={p:100,n:320,b:330,r:500,q:900,k:0};let g,end;\r\nfunction ev(){let s=0;g.board().forEach((row,r)=>row.forEach((p,c)=>{if(!p)return;const cen=3.5-Math.max(Math.abs(c-3.5),Math.abs(r-3.5));let v=V[p.type]+(p.type==='n'||p.type==='b'?cen*12:p.type==='p'?cen*6:p.type==='q'?cen*3:0);\r\nif(p.type==='p')v+=(p.color==='w'?6-r:r-1)*7;s+=p.color==='w'?v:-v}));return s}\r\nfunction nm(d,a,b,col){if(Date.now()>end)throw 0;const ms=g.moves({verbose:true});if(!ms.length)return g.in_check()?-99999-d:0;if(d===0)return col*ev();\r\nms.forEach(m=>m.o=(m.captured?V[m.captured]*10-V[m.piece]:0)+(m.promotion?800:0));ms.sort((x,y)=>y.o-x.o);\r\nlet best=-Infinity;for(const m of ms){g.move({from:m.from,to:m.to,promotion:m.promotion});const s=-nm(d-1,-b,-a,-col);g.undo();if(s>best)best=s;if(s>a)a=s;if(a>=b)break}return best}\r\nonmessage=e=>{g=new Chess(e.data.fen);const col=g.turn()==='w'?1:-1;end=Date.now()+e.data.time;let best=null;\r\ntry{for(let d=1;d<=e.data.maxD;d++){const ms=g.moves({verbose:true});ms.sort(()=>Math.random()-.5);if(best)ms.sort((x,y)=>(y.from+y.to===best.from+best.to)-(x.from+x.to===best.from+best.to));\r\nlet bs=-Infinity,bm=null,a=-Infinity;for(const m of ms){g.move({from:m.from,to:m.to,promotion:m.promotion});const s=-nm(d-1,-Infinity,-a,-col);g.undo();if(s>bs){bs=s;bm=m}if(s>a)a=s}best=bm}}catch(x){}\r\nif(!best)best=g.moves({verbose:true})[0];postMessage({from:best.from,to:best.to,promotion:best.promotion})}`]))); let wk = worker();\r\nlet sf = null, sfP = null;\r\nconst sfStop = () => { if (sf) sf.terminate(); sf = sfP = null; };\r\nfunction initSF() {   \/\/ real Stockfish (asm.js) in a blob worker; resolves null if it can't load\r\n  return sfP || (sfP = new Promise(res => { try {\r\n    const w = new Worker(URL.createObjectURL(new Blob([`importScripts('${SF_URL}')`]))); let st = 0;\r\n    const to = setTimeout(() => { w.terminate(); res(null); }, 25000);\r\n    w.onerror = () => { clearTimeout(to); res(null); };\r\n    w.onmessage = e => { const l = String(e.data);\r\n      if (st === 0 && l === 'uciok') { w.postMessage('setoption name Skill Level value ' + CFG.zeroSkill); w.postMessage('isready'); st = 1; }\r\n      else if (st === 1 && l === 'readyok') { clearTimeout(to); w.onmessage = null; sf = w; res(w); } };\r\n    w.postMessage('uci'); } catch (e) { res(null); } }));\r\n}\r\nconst sfMove = (w, fen) => new Promise(r => {\r\n  w.onmessage = e => { const l = String(e.data); if (l.indexOf('bestmove') === 0) { w.onmessage = null; const m = l.split(' ')[1]; r({ from: m.slice(0, 2), to: m.slice(2, 4), promotion: m[4] }); } };\r\n  w.postMessage('position fen ' + fen); w.postMessage('go movetime ' + CFG.zeroTime);\r\n});\r\nconst ownMove = (opt) => new Promise(r => { wk.onmessage = e => r(e.data); wk.postMessage({ fen: game.fen(), ...opt }); });\r\nlvSel.addEventListener('change', () => { if (LV[lvSel.value].sf) initSF(); });\r\nasync function botMove() {\r\n  const id = gid, t0 = performance.now(); let n = 0; const iv = setInterval(() => status('Bot is thinking' + '.'.repeat(++n % 4)), 400);\r\n  const lv = LV[lvSel.value]; let mv = null;\r\n  if (lv.sf) { const w = await initSF(); if (id !== gid) { clearInterval(iv); return; } if (w) mv = await sfMove(w, game.fen()); }\r\n  if (!mv) mv = await ownMove(lv.sf ? { maxD: 6, time: 8000 } : lv);   \/\/ fallback if Stockfish failed to load\r\n  clearInterval(iv); if (id !== gid) return; await wait(Math.max(0, 600 - (performance.now() - t0)));\r\n  const m = game.move(mv); await play(m); refresh(); busy = game.game_over();\r\n}\r\nasync function humanMove(mv) {\r\n  busy = true; sel = null; hl.clear(); const m = game.move({ from: mv.from, to: mv.to, promotion: 'q' }); await play(m); refresh();\r\n  if (game.game_over()) return; await botMove();\r\n}\r\nasync function newGame() {\r\n  gid++; wk.terminate(); wk = worker(); if (sf) { sfStop(); } if (LV[lvSel.value].sf) initSF();\r\n  me = sideSel.value; sideAng = me === 'b' ? Math.PI : 0;   \/\/ board turns so the player's side is always at the bottom\r\n  game = new Chess(); sel = null; targets = []; hl.clear(); setup(); busy = true; refresh(); const id = gid; await wait(1500);\r\n  if (id !== gid) return; if (me === 'b') await botMove(); else busy = false;\r\n}\r\nsideSel.onchange = newGame;\r\n$('.c3-new').onclick = newGame;\r\n\r\n\/* ---------- input: tap, rotate (1 finger), pinch zoom (2 fingers) ---------- *\/\r\nconst ray = new T.Raycaster(), ptr = new T.Vector2(), tilt = { x: 0, y: 0 }, pts = new Map();\r\nlet down = null, drag = false, multi = false, zoom = 1, pd = 0;\r\nconst TH = isMobile ? 10 : 5, pinchDist = () => { const [a, b] = [...pts.values()]; return Math.hypot(a.x - b.x, a.y - b.y) || 1; };\r\nfunction pick(e) {\r\n  const r = cv.getBoundingClientRect(); ptr.set((e.clientX - r.left) \/ r.width * 2 - 1, -(e.clientY - r.top) \/ r.height * 2 + 1); ray.setFromCamera(ptr, cam);\r\n  for (const h of ray.intersectObjects([...Object.values(P), ...tiles], true)) { let o = h.object; while (o && !o.userData.sq) o = o.parent; if (o) return o.userData.sq; }\r\n}\r\ncv.addEventListener('pointerdown', e => {\r\n  pts.set(e.pointerId, { x: e.clientX, y: e.clientY }); cv.setPointerCapture(e.pointerId);\r\n  if (pts.size > 1) { multi = true; down = null; pd = pinchDist(); }\r\n  else { down = { x: e.clientX, y: e.clientY }; drag = false; multi = false; }\r\n});\r\ncv.addEventListener('pointermove', e => {\r\n  if (pts.has(e.pointerId)) pts.set(e.pointerId, { x: e.clientX, y: e.clientY });\r\n  if (pts.size === 2) { const d = pinchDist(); zoom = Math.max(.55, Math.min(1.3, zoom * pd \/ d)); pd = d; return; }\r\n  if (!down) return; const dx = e.clientX - down.x, dy = e.clientY - down.y;\r\n  if (!drag && Math.hypot(dx, dy) < TH) return; drag = true; cv.style.cursor = 'grabbing';\r\n  tilt.y += (e.clientX - (down.lx ?? down.x)) * .005;\r\n  tilt.x = Math.max(-.6, Math.min(.6, tilt.x + (e.clientY - (down.ly ?? down.y)) * .003)); down.lx = e.clientX; down.ly = e.clientY;\r\n});\r\nconst endPtr = e => {\r\n  pts.delete(e.pointerId); if (pts.size > 0) return;\r\n  const was = drag || multi, d = down; down = null; drag = false; multi = false; cv.style.cursor = 'grab';\r\n  if (e.type === 'pointercancel' || was || !d || busy || game.turn() !== me) return;\r\n  const s = pick(e); if (!s) return; const mv = targets.find(m => m.to === s);\r\n  if (sel && mv) { targets = []; return humanMove(mv); }\r\n  const p = P[s]; if (p && p.userData.col === me) { sel = s; targets = game.moves({ square: s, verbose: true }); showHl(s, targets); } else { sel = null; targets = []; hl.clear(); }\r\n};\r\ncv.addEventListener('pointerup', endPtr); cv.addEventListener('pointercancel', endPtr);\r\n\r\n\/* ---------- post-processing (HD): MSAA half-float target -> bloom -> sRGB + vignette + dither ---------- *\/\r\nconst FinalShader = { uniforms: { tDiffuse: { value: null }, time: { value: 0 }, vig: { value: CFG.vignette } },\r\n  vertexShader: 'varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}',\r\n  fragmentShader: `uniform sampler2D tDiffuse;uniform float time,vig;varying vec2 vUv;\r\nfloat h(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233))+time)*43758.5453);}\r\nvoid main(){vec3 l=texture2D(tDiffuse,vUv).rgb;\r\nvec3 s=mix(l*12.92,1.055*pow(max(l,vec3(1e-4)),vec3(1.\/2.4))-.055,step(.0031308,l));\r\nvec2 d=vUv-.5;s*=1.-vig*dot(d,d)*2.2;\r\ns+=(h(gl_FragCoord.xy)-.5)\/255.;gl_FragColor=vec4(s,1.);}` };\r\nlet composer = null, finalPass = null, useFx = false;\r\nfunction makeComposer() {\r\n  try {\r\n    const ex = renderer.extensions;\r\n    if (!FX || !renderer.capabilities.isWebGL2 || !(ex.has('EXT_color_buffer_float') || ex.has('EXT_color_buffer_half_float'))) return null;\r\n    const rt = new T.WebGLMultisampleRenderTarget(4, 4, { type: T.HalfFloatType, format: T.RGBAFormat }); rt.samples = 4;\r\n    const c = new T.EffectComposer(renderer, rt);\r\n    c.addPass(new T.RenderPass(scene, cam));\r\n    c.addPass(new T.UnrealBloomPass(new T.Vector2(4, 4), CFG.bloom, .6, .85));\r\n    finalPass = new T.ShaderPass(FinalShader); c.addPass(finalPass);\r\n    return c;\r\n  } catch (e) { console.warn('Composer disabled', e); return null; }\r\n}\r\n\r\n\/* ---------- quality: LD (fast) \/ SD (shadows + MSAA) \/ HD (+ soft shadows 2K, bloom, vignette) ---------- *\/\r\nfunction resize() {\r\n  const w = root.clientWidth, h = root.clientHeight, pr = renderer.getPixelRatio();\r\n  renderer.setSize(w, h); cam.aspect = w \/ h; camK = Math.max(1, 1.2 \/ cam.aspect); cam.updateProjectionMatrix();\r\n  if (composer) composer.setSize(w * pr, h * pr); coverBg();\r\n}\r\nfunction setQuality(v) {\r\n  const dpr = devicePixelRatio; renderer.setPixelRatio(Math.min(dpr, [1, isMobile ? 1.5 : 1.75, isMobile ? 1.5 : 2][v]));\r\n  const sm = [0, 1024, 2048][v]; sun.castShadow = v > 0;\r\n  if (sm && sun.shadow.mapSize.x !== sm) { sun.shadow.mapSize.set(sm, sm); if (sun.shadow.map) { sun.shadow.map.dispose(); sun.shadow.map = null; } }\r\n  if (v === 2 && !composer) composer = makeComposer();\r\n  useFx = v === 2 && !!composer; resize();\r\n}\r\nconst clock = new T.Clock(); let camK = 1, intro = 0, vis = true;\r\nnew IntersectionObserver(en => { vis = en[0].isIntersecting; }).observe(root);\r\nnew ResizeObserver(resize).observe(root);\r\nsetQuality(isMobile ? 1 : 2);   \/\/ fixed by device: phones = SD, desktop = HD\r\n\r\n\/* ---------- loop ---------- *\/\r\nrenderer.setAnimationLoop(() => {\r\n  const dt = Math.min(clock.getDelta(), .05), t = clock.elapsedTime;\r\n  if (!vis) return;\r\n  for (let i = tw.length - 1; i >= 0; i--) { const o = tw[i]; o.t += dt \/ o.d; const k = Math.min(1, o.t); o.f(o.e(k), k); if (k >= 1) { tw.splice(i, 1); o.r(); } }\r\n  intro = Math.min(1, intro + dt \/ 2.6); const e = ease(intro), z = camK * zoom;\r\n  cam.position.set(0, (16 + (9.5 - 16) * e) * z, (22 + (10.5 - 22) * e) * z); cam.lookAt(0, 0, -.8);\r\n  if (!down && CFG.springBack) { tilt.x *= 1 - dt * 1.2; tilt.y *= 1 - dt * 1.2; }\r\n  boardG.rotation.x += (tilt.x - boardG.rotation.x) * dt * 6; boardG.rotation.y += (tilt.y + sideAng - boardG.rotation.y) * dt * 6; boardG.rotation.z = Math.sin(t * .35) * .015;\r\n  Object.values(P).forEach(p => { const u = p.userData, s = u.sy, k = 1 \/ Math.sqrt(s);\r\n    u.inner.position.y = CFG.hover + Math.sin(t * 1.4 + u.ph) * .05 + u.lift; u.inner.rotation.y = Math.sin(t * .6 + u.ph) * .05; u.inner.scale.set(k, s, k); });\r\n  D.forEach(p => { const u = p.userData; u.spin += dt * .8;\r\n    u.inner.position.y = CFG.hover + Math.sin(t * 1.2 + u.ph) * .12; u.inner.rotation.y = u.spin; u.inner.rotation.z = Math.sin(t * .9 + u.ph) * .12; });\r\n  hl.children.forEach(m => { if (m.userData.pulse) m.material.opacity = m.userData.o * (.65 + .35 * Math.sin(t * 4)); });\r\n  if (useFx) { try { finalPass.uniforms.time.value = t % 10; composer.render(dt); } catch (err) { console.warn('Composer failed, falling back', err); useFx = false; composer = null; renderer.setRenderTarget(null); } }\r\n  else renderer.render(scene, cam);\r\n});\r\n\r\n$('.c3-load').style.opacity = 0; setTimeout(() => $('.c3-load').remove(), 900);\r\nawait newGame();\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-db27a55 e-con-full e-flex e-con e-child\" data-id=\"db27a55\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-15b6cfd elementor-widget elementor-widget-html\" data-id=\"15b6cfd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- \u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0432 \u0432\u0438\u0434\u0436\u0435\u0442 Elementor \"HTML\" -->\r\n<style>\r\n  .scene3d {\r\n    --scene-h: 60vh;\r\n    --left-w: 40%;\r\n    display: flex;\r\n    width: 100%;\r\n    height: var(--scene-h);\r\n    min-height: 380px;\r\n    box-sizing: border-box;\r\n    \/* \u0435\u0434\u0438\u043d\u044b\u0439 \u0444\u043e\u043d \u043d\u0430 \u0432\u0441\u044e \u0441\u0446\u0435\u043d\u0443 \u2014 \u0431\u0435\u0437 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f; \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430 transparent *\/\r\n    background: radial-gradient(ellipse at 50% 45%, #3a3e47 0%, #25282f 55%, #16181d 100%);\r\n    overflow: hidden;\r\n  }\r\n  .scene3d *, .scene3d *::before, .scene3d *::after { box-sizing: border-box; }\r\n\r\n  \/* 3D \u043c\u043e\u0434\u0435\u043b\u044c *\/\r\n  .scene3d__model { flex: 0 0 var(--left-w); position: relative; }\r\n  .scene3d model-viewer {\r\n    width: 100%; height: 100%;\r\n    background: transparent;\r\n    --poster-color: transparent;\r\n    --progress-bar-color: #1caad9;\r\n    --progress-bar-height: 3px;\r\n    outline: none;\r\n  }\r\n\r\n  \/* \u041a\u0430\u0440\u0443\u0441\u0435\u043b\u044c *\/\r\n  .scene3d__carousel { flex: 1 1 auto; position: relative; user-select: none; overflow: hidden; }\r\n  .scene3d__slide {\r\n    position: absolute; top: 50%; left: 50%;\r\n    width: 52%; height: 78%;\r\n    transform: translate(-50%, -50%);\r\n    transition: transform .6s cubic-bezier(.65,.05,.36,1), opacity .6s, filter .6s;\r\n    opacity: 0; pointer-events: none; cursor: pointer;\r\n    border-radius: 6px; overflow: hidden;\r\n    box-shadow: 0 12px 40px rgba(0,0,0,.45);\r\n  }\r\n  .scene3d__slide img {\r\n    width: 100%; height: 100%; object-fit: cover; display: block;\r\n    pointer-events: none; -webkit-user-drag: none;\r\n  }\r\n  .scene3d__slide[data-pos=\"0\"]  { opacity: 1; z-index: 3; pointer-events: auto; cursor: default; }\r\n  .scene3d__slide[data-pos=\"-1\"] { opacity: .65; z-index: 2; pointer-events: auto; filter: brightness(.7);\r\n    transform: translate(calc(-50% - 72%), -50%) scale(.74); }\r\n  .scene3d__slide[data-pos=\"1\"]  { opacity: .65; z-index: 2; pointer-events: auto; filter: brightness(.7);\r\n    transform: translate(calc(-50% + 72%), -50%) scale(.74); }\r\n\r\n  .scene3d__btn {\r\n    position: absolute; bottom: 10px; z-index: 5;\r\n    width: 40px; height: 40px; border: 0; border-radius: 0; padding: 0;\r\n    background: rgba(255,255,255,.12); cursor: pointer;\r\n    display: flex; align-items: center; justify-content: center;\r\n    transition: background .2s;\r\n  }\r\n  .scene3d__btn:hover { background: rgba(255,255,255,.28); }\r\n  .scene3d__btn svg { width: 18px; height: 18px; stroke: #fff; fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }\r\n  .scene3d__prev { left: calc(50% - 50px); }\r\n  .scene3d__next { left: calc(50% + 10px); }\r\n\r\n  @media (max-width: 900px) {\r\n    .scene3d { flex-direction: column; height: auto; }\r\n    .scene3d__model { flex: none; width: 100%; height: 50vh; min-height: 300px; }\r\n    .scene3d__carousel { flex: none; width: 100%; height: 45vh; min-height: 260px; }\r\n  }\r\n<\/style>\r\n\r\n<div class=\"scene3d\">\r\n  <div class=\"scene3d__model\">\r\n    <model-viewer\r\n      src=\"https:\/\/digitaldesignonline.ru\/wp-content\/uploads\/2026\/10\/Room2.glb\"\r\n      camera-controls\r\n      touch-action=\"pan-y\"\r\n      interaction-prompt=\"none\"\r\n      environment-image=\"neutral\"\r\n      shadow-intensity=\"1.2\"\r\n      shadow-softness=\"1\"\r\n      exposure=\"0.6\"\r\n      tone-mapping=\"aces\"\r\n      camera-orbit=\"30deg 75deg auto\"\r\n      min-field-of-view=\"10deg\"\r\n      max-field-of-view=\"45deg\"\r\n      loading=\"eager\"\r\n      reveal=\"auto\">\r\n    <\/model-viewer>\r\n  <\/div>\r\n\r\n  <div class=\"scene3d__carousel\" id=\"scene3dCarousel\">\r\n    <!-- \u043c\u0438\u043d\u0438\u043c\u0443\u043c 3 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f -->\r\n    <div class=\"scene3d__slide\"><img decoding=\"async\" src=\"https:\/\/digitaldesignonline.ru\/wp-content\/uploads\/2026\/10\/124424134244124.jpg\" alt=\"\"><\/div>\r\n    <div class=\"scene3d__slide\"><img decoding=\"async\" src=\"https:\/\/digitaldesignonline.ru\/wp-content\/uploads\/2026\/10\/124424122424.jpg\" alt=\"\"><\/div>\r\n    <div class=\"scene3d__slide\"><img decoding=\"async\" src=\"https:\/\/digitaldesignonline.ru\/wp-content\/uploads\/2026\/10\/1244241234554124.jpg\" alt=\"\"><\/div>\r\n\r\n\r\n    <button class=\"scene3d__btn scene3d__prev\" aria-label=\"prev\"><svg viewBox=\"0 0 24 24\"><path d=\"M15 5l-7 7 7 7\"\/><\/svg><\/button>\r\n    <button class=\"scene3d__btn scene3d__next\" aria-label=\"next\"><svg viewBox=\"0 0 24 24\"><path d=\"M9 5l7 7-7 7\"\/><\/svg><\/button>\r\n  <\/div>\r\n<\/div>\r\n\r\n<script type=\"module\" src=\"https:\/\/ajax.googleapis.com\/ajax\/libs\/model-viewer\/3.5.0\/model-viewer.min.js\"><\/script>\r\n\r\n<script>\r\n(function () {\r\n  var root = document.getElementById('scene3dCarousel');\r\n  var slides = Array.prototype.slice.call(root.querySelectorAll('.scene3d__slide'));\r\n  var n = slides.length, half = Math.floor(n \/ 2);\r\n  var i = 0;\r\n\r\n  function render() {\r\n    slides.forEach(function (s, k) {\r\n      var d = ((k - i + n + half) % n) - half;      \/\/ \u043f\u043e\u0437\u0438\u0446\u0438\u044f \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e\r\n      s.setAttribute('data-pos', Math.abs(d) <= 1 ? d : 'x');\r\n    });\r\n  }\r\n  function go(k) { i = (k + n) % n; render(); }\r\n\r\n  slides.forEach(function (s, k) {\r\n    s.addEventListener('click', function () {\r\n      var pos = s.getAttribute('data-pos');\r\n      if (pos === '-1' || pos === '1') { go(k); }\r\n    });\r\n  });\r\n  root.querySelector('.scene3d__prev').onclick = function () { go(i - 1); };\r\n  root.querySelector('.scene3d__next').onclick = function () { go(i + 1); };\r\n\r\n  var x0 = null;\r\n  root.addEventListener('pointerdown', function (e) { x0 = e.clientX; });\r\n  root.addEventListener('pointerup', function (e) {\r\n    if (x0 === null) return;\r\n    var dx = e.clientX - x0;\r\n    if (Math.abs(dx) > 40) { go(i + (dx < 0 ? 1 : -1)); }\r\n    x0 = null;\r\n  });\r\n\r\n  render();\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3184530 e-con-full e-flex e-con e-child\" data-id=\"3184530\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-66e8d30 elementor-widget elementor-widget-html\" data-id=\"66e8d30\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- Elementor: paste into an \"HTML\" widget. Pure CSS 3D, no external requests, ~3 KB. -->\r\n<section class=\"cs3d\" id=\"cs3d\" aria-label=\"More coming soon\">\r\n<style>\r\n.cs3d{--c1:#cab8a3;--c2:#712d3d;--s:84px;position:relative;overflow:hidden;box-sizing:border-box;\r\nmin-height:clamp(420px,70vh,640px);display:flex;flex-direction:column;align-items:center;justify-content:center;\r\npadding:40px 20px;text-align:center;color:var(--c1);\r\nfont-family:system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;\r\nbackground:radial-gradient(110% 90% at 50% 0%,#8a3a4d 0%,var(--c2) 50%,#4a1b27 100%);\r\ncontain:layout paint}\r\n.cs3d *,.cs3d *::before,.cs3d *::after{box-sizing:border-box}\r\n.cs3d__floor{position:absolute;left:-25%;right:-25%;bottom:-6%;height:50%;pointer-events:none;\r\nbackground:repeating-linear-gradient(90deg,rgba(202,184,163,.18) 0 1px,transparent 1px 64px),\r\nrepeating-linear-gradient(0deg,rgba(202,184,163,.18) 0 1px,transparent 1px 64px);\r\ntransform:perspective(520px) rotateX(62deg);transform-origin:50% 100%;\r\n-webkit-mask-image:linear-gradient(to top,#000 10%,transparent 90%);mask-image:linear-gradient(to top,#000 10%,transparent 90%)}\r\n.cs3d__stage{position:relative;perspective:700px;margin-bottom:36px}\r\n.cs3d__cube{position:relative;width:var(--s);height:var(--s);transform-style:preserve-3d;animation:cs3d-spin 14s linear infinite}\r\n.cs3d__cube i{position:absolute;inset:0;border:1px solid rgba(113,45,61,.35);backface-visibility:hidden}\r\n.cs3d__cube i:nth-child(1){transform:translateZ(calc(var(--s)\/2));background:#d8c9b7}\r\n.cs3d__cube i:nth-child(2){transform:rotateY(180deg) translateZ(calc(var(--s)\/2));background:#b9a590}\r\n.cs3d__cube i:nth-child(3){transform:rotateY(90deg) translateZ(calc(var(--s)\/2));background:#cab8a3}\r\n.cs3d__cube i:nth-child(4){transform:rotateY(-90deg) translateZ(calc(var(--s)\/2));background:#a8927b}\r\n.cs3d__cube i:nth-child(5){transform:rotateX(90deg) translateZ(calc(var(--s)\/2));background:#e6dac9}\r\n.cs3d__cube i:nth-child(6){transform:rotateX(-90deg) translateZ(calc(var(--s)\/2));background:#8f7a66}\r\n@keyframes cs3d-spin{from{transform:rotateX(-24deg) rotateY(0)}to{transform:rotateX(-24deg) rotateY(360deg)}}\r\n.cs3d__title{position:relative;margin:0;font-size:clamp(34px,7vw,72px);line-height:1.05;font-weight:800;letter-spacing:-.02em;color:var(--c1);\r\ntext-shadow:0 1px 0 #a8927b,0 2px 0 #8f7a66,0 3px 0 #75634f,0 8px 18px rgba(0,0,0,.35)}\r\n.cs3d.is-paused .cs3d__cube{animation-play-state:paused}\r\n@media (prefers-reduced-motion:reduce){.cs3d__cube{animation:none}}\r\n@media (max-width:600px){.cs3d{--s:68px}}\r\n<\/style>\r\n\r\n<div class=\"cs3d__floor\" aria-hidden=\"true\"><\/div>\r\n<div class=\"cs3d__stage\" aria-hidden=\"true\">\r\n  <div class=\"cs3d__cube\"><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><\/div>\r\n<\/div>\r\n<h2 class=\"cs3d__title\">More coming soon<\/h2>\r\n\r\n<script>\r\n(function(){\r\n  var el=document.getElementById('cs3d');\r\n  if(!el||!('IntersectionObserver' in window))return;\r\n  new IntersectionObserver(function(e){el.classList.toggle('is-paused',!e[0].isIntersecting)}).observe(el);\r\n})();\r\n<\/script>\r\n<\/section>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>LOADING 0% Your move (White) Play WhitePlay Black EasyMediumHardZeroChance New game More coming soon<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"class_list":["post-987","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/pages\/987","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/comments?post=987"}],"version-history":[{"count":209,"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/pages\/987\/revisions"}],"predecessor-version":[{"id":1515,"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/pages\/987\/revisions\/1515"}],"wp:attachment":[{"href":"https:\/\/digitaldesignonline.ru\/ru\/wp-json\/wp\/v2\/media?parent=987"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}