refactor(labs): переработка панели управления Динамики

- Расширена с 248px до 300px
- Mode selector: 5 в ряд → 2 ряда (Песочница/Классика, I/II/III законы) с понятными названиями + tooltips
- Sandbox-панель: секции Мир/Отображение/Время/Пресеты обёрнуты в <details> (collapsible) с акцентом-стрелкой
- 21 пресет сгруппирован по 5 категориям: Базовые/Столкновения/Пружины и осцилляторы/Маятники и блоки/Горки и стопки
- Шрифты увеличены с .65-.72rem до .78-.82rem (mode buttons, tool grid, checkboxes, presets, подсказки)
- Newton-панель: сцены A/B/C, классические Атвуд/Наклон/Качение — кнопки крупнее
- Topbar ctrl-dynamics: .65rem → .78rem для всех инструментов и сцен
- Подсказки (help boxes) перерисованы с большим контрастом и шрифтом
- Новый CSS-блок .dyn-panel-modern с детализированным acc-styling
This commit is contained in:
Maxim Dolgolyov
2026-05-26 18:25:57 +03:00
parent be1e558be9
commit 46d80c0bdf
2 changed files with 306 additions and 121 deletions
+151 -121
View File
@@ -225,24 +225,24 @@
<div id="ctrl-dynamics" class="sim-zoom-btns" style="display:none">
<!-- sandbox tools (shown in sandbox mode) -->
<span id="ctrl-dyn-sb" style="display:contents">
<button class="zoom-btn sb-tool-btn active" id="sbt-box" onclick="sbTool('box',this)" style="font-size:.65rem;font-weight:800"><svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/></svg> Блок</button>
<button class="zoom-btn sb-tool-btn" id="sbt-ball" onclick="sbTool('ball',this)" style="font-size:.65rem;font-weight:800"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8" fill="currentColor" stroke="none"/></svg> Шар</button>
<button class="zoom-btn sb-tool-btn" id="sbt-spring" onclick="sbTool('spring',this)" style="font-size:.65rem;font-weight:800"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Пружина</button>
<button class="zoom-btn sb-tool-btn" id="sbt-rope" onclick="sbTool('rope',this)" style="font-size:.65rem;font-weight:800">— Нить</button>
<button class="zoom-btn sb-tool-btn" id="sbt-anchor" onclick="sbTool('anchor',this)" style="font-size:.65rem;font-weight:800"><svg class="ic" viewBox="0 0 24 24"><path d="M12 2 2 12 12 22 22 12Z"/></svg> Якорь</button>
<button class="zoom-btn sb-tool-btn" id="sbt-erase" onclick="sbTool('erase',this)" style="font-size:.65rem;font-weight:800"><svg class="ic" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> Ластик</button>
<button class="zoom-btn sb-tool-btn active" id="sbt-box" onclick="sbTool('box',this)" style="font-size:.78rem;font-weight:700"><svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/></svg> Блок</button>
<button class="zoom-btn sb-tool-btn" id="sbt-ball" onclick="sbTool('ball',this)" style="font-size:.78rem;font-weight:700"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8" fill="currentColor" stroke="none"/></svg> Шар</button>
<button class="zoom-btn sb-tool-btn" id="sbt-spring" onclick="sbTool('spring',this)" style="font-size:.78rem;font-weight:700"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Пружина</button>
<button class="zoom-btn sb-tool-btn" id="sbt-rope" onclick="sbTool('rope',this)" style="font-size:.78rem;font-weight:700">— Нить</button>
<button class="zoom-btn sb-tool-btn" id="sbt-anchor" onclick="sbTool('anchor',this)" style="font-size:.78rem;font-weight:700"><svg class="ic" viewBox="0 0 24 24"><path d="M12 2 2 12 12 22 22 12Z"/></svg> Якорь</button>
<button class="zoom-btn sb-tool-btn" id="sbt-erase" onclick="sbTool('erase',this)" style="font-size:.78rem;font-weight:700"><svg class="ic" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> Ластик</button>
</span>
<!-- newton tools (shown in law modes) -->
<span id="ctrl-dyn-nw" style="display:none">
<button class="zoom-btn nscene-btn active" id="nscn-A" onclick="newtonScene('A',this)" style="font-size:.65rem;font-weight:800">A</button>
<button class="zoom-btn nscene-btn" id="nscn-B" onclick="newtonScene('B',this)" style="font-size:.65rem;font-weight:800">B</button>
<button class="zoom-btn nscene-btn" id="nscn-C" onclick="newtonScene('C',this)" style="font-size:.65rem;font-weight:800">C</button>
<button class="zoom-btn nscene-btn active" id="nscn-A" onclick="newtonScene('A',this)" style="font-size:.82rem;font-weight:700;padding:4px 12px">A</button>
<button class="zoom-btn nscene-btn" id="nscn-B" onclick="newtonScene('B',this)" style="font-size:.82rem;font-weight:700;padding:4px 12px">B</button>
<button class="zoom-btn nscene-btn" id="nscn-C" onclick="newtonScene('C',this)" style="font-size:.82rem;font-weight:700;padding:4px 12px">C</button>
<!-- classic scenes (law 4, hidden by default) -->
<button class="zoom-btn nscene-btn cl-scene-btn" id="nscn-cl-atwood" data-scene="atwood" onclick="classicScene('atwood')" style="display:none;font-size:.6rem;font-weight:800">Атвуд</button>
<button class="zoom-btn nscene-btn cl-scene-btn" id="nscn-cl-ramp" data-scene="ramp" onclick="classicScene('ramp')" style="display:none;font-size:.6rem;font-weight:800">Наклон</button>
<button class="zoom-btn nscene-btn cl-scene-btn" id="nscn-cl-roll" data-scene="roll" onclick="classicScene('roll')" style="display:none;font-size:.6rem;font-weight:800">Качение</button>
<button class="zoom-btn nscene-btn cl-scene-btn" id="nscn-cl-atwood" data-scene="atwood" onclick="classicScene('atwood')" style="display:none;font-size:.78rem;font-weight:700">Атвуд</button>
<button class="zoom-btn nscene-btn cl-scene-btn" id="nscn-cl-ramp" data-scene="ramp" onclick="classicScene('ramp')" style="display:none;font-size:.78rem;font-weight:700">Наклон</button>
<button class="zoom-btn nscene-btn cl-scene-btn" id="nscn-cl-roll" data-scene="roll" onclick="classicScene('roll')" style="display:none;font-size:.78rem;font-weight:700">Качение</button>
<div style="width:1px;height:20px;background:rgba(255,255,255,0.15);margin:0 3px"></div>
<button class="zoom-btn" id="newton-action-top" onclick="newtonAction()" style="font-size:.65rem;font-weight:800;font-family:Manrope,sans-serif"><svg class="ic" viewBox="0 0 24 24"><polygon points="5 3 19 12 5 21 5 3"/></svg> Действие</button>
<button class="zoom-btn" id="newton-action-top" onclick="newtonAction()" style="font-size:.78rem;font-weight:700;font-family:Manrope,sans-serif"><svg class="ic" viewBox="0 0 24 24"><polygon points="5 3 19 12 5 21 5 3"/></svg> Действие</button>
</span>
<div style="width:1px;height:20px;background:rgba(255,255,255,0.15);margin:0 3px"></div>
<button class="zoom-btn" onclick="dynPause()" title="Пауза" style="font-size:.75rem"><svg class="ic" viewBox="0 0 24 24"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg></button>
@@ -1495,15 +1495,17 @@
<div id="sim-dynamics" class="sim-proj-wrap" style="display:none">
<div class="sim-body-wrap">
<div class="proj-panel" style="width:248px;gap:0">
<div class="proj-panel dyn-panel-modern" style="width:300px;gap:0">
<!-- ══ Mode selector ══ -->
<div style="display:flex;gap:4px;margin-bottom:12px;padding:3px;background:rgba(255,255,255,0.04);border-radius:10px;border:1px solid var(--border)">
<button class="mag-mode-btn dyn-mode active" id="dyn-mode-sandbox" onclick="dynMode('sandbox',this)" style="flex:1;font-size:.68rem;padding:5px 0">Песочница</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law1" onclick="dynMode('law1',this)" style="flex:1;font-size:.68rem;padding:5px 0">I закон</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law2" onclick="dynMode('law2',this)" style="flex:1;font-size:.68rem;padding:5px 0">II закон</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law3" onclick="dynMode('law3',this)" style="flex:1;font-size:.68rem;padding:5px 0">III закон</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law4" onclick="dynMode('law4',this)" style="flex:1;font-size:.68rem;padding:5px 0">Классич.</button>
<div class="dyn-mode-bar">
<button class="mag-mode-btn dyn-mode active" id="dyn-mode-sandbox" onclick="dynMode('sandbox',this)" title="Песочница — свободный режим">Песочница</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law4" onclick="dynMode('law4',this)" title="Классические сцены: Атвуд, Наклон, Качение">Классика</button>
</div>
<div class="dyn-mode-bar" style="margin-bottom:14px">
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law1" onclick="dynMode('law1',this)" title="I закон Ньютона — инерция">I закон</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law2" onclick="dynMode('law2',this)" title="II закон Ньютона — F=ma">II закон</button>
<button class="mag-mode-btn dyn-mode" id="dyn-mode-law3" onclick="dynMode('law3',this)" title="III закон Ньютона — действие=противодействие">III закон</button>
</div>
<!-- ══ Newton controls (shown in law modes) ══ -->
@@ -1511,18 +1513,18 @@
<!-- Scene selector (standard A/B/C — hidden for law 4) -->
<div class="gp-section-title" style="margin-bottom:8px" id="newton-scene-title">Сцена</div>
<div style="display:flex;gap:5px;margin-bottom:12px" id="newton-scene-row">
<button class="mag-mode-btn nscene-btn active" id="nscn-panel-A" onclick="newtonScene('A',null,this)" style="flex:1;font-size:.72rem">A</button>
<button class="mag-mode-btn nscene-btn" id="nscn-panel-B" onclick="newtonScene('B',null,this)" style="flex:1;font-size:.72rem">B</button>
<button class="mag-mode-btn nscene-btn" id="nscn-panel-C" onclick="newtonScene('C',null,this)" style="flex:1;font-size:.72rem">C</button>
<div class="dyn-row-4" style="grid-template-columns:1fr 1fr 1fr;margin-bottom:12px" id="newton-scene-row">
<button class="mag-mode-btn nscene-btn active" id="nscn-panel-A" onclick="newtonScene('A',null,this)">A</button>
<button class="mag-mode-btn nscene-btn" id="nscn-panel-B" onclick="newtonScene('B',null,this)">B</button>
<button class="mag-mode-btn nscene-btn" id="nscn-panel-C" onclick="newtonScene('C',null,this)">C</button>
</div>
<!-- Classic scene selector (law 4 only) -->
<div id="newton-classic-panel" style="display:none">
<div style="display:flex;gap:5px;margin-bottom:10px">
<button class="mag-mode-btn cl-scene-btn active" id="nscn-panel-cl-atwood" data-scene="atwood" onclick="classicScene('atwood')" style="flex:1;font-size:.68rem">Атвуд</button>
<button class="mag-mode-btn cl-scene-btn" id="nscn-panel-cl-ramp" data-scene="ramp" onclick="classicScene('ramp')" style="flex:1;font-size:.68rem">Наклон</button>
<button class="mag-mode-btn cl-scene-btn" id="nscn-panel-cl-roll" data-scene="roll" onclick="classicScene('roll')" style="flex:1;font-size:.68rem">Качение</button>
<div class="dyn-row-4" style="grid-template-columns:1fr 1fr 1fr;margin-bottom:10px">
<button class="mag-mode-btn cl-scene-btn active" id="nscn-panel-cl-atwood" data-scene="atwood" onclick="classicScene('atwood')">Атвуд</button>
<button class="mag-mode-btn cl-scene-btn" id="nscn-panel-cl-ramp" data-scene="ramp" onclick="classicScene('ramp')">Наклон</button>
<button class="mag-mode-btn cl-scene-btn" id="nscn-panel-cl-roll" data-scene="roll" onclick="classicScene('roll')">Качение</button>
</div>
<!-- Atwood sub-panel -->
@@ -1536,9 +1538,9 @@
<input type="range" class="param-slider" id="sl-atw-m2" min="1" max="20" value="8" oninput="atwM2Change()">
</div>
<div class="gp-section-title" style="margin:4px 0">Блок</div>
<div style="display:flex;gap:5px;margin-bottom:8px">
<button class="mag-mode-btn atw-massive-btn active" data-val="false" onclick="atwMassiveToggle(false)" style="flex:1;font-size:.68rem">Идеальный</button>
<button class="mag-mode-btn atw-massive-btn" data-val="true" onclick="atwMassiveToggle(true)" style="flex:1;font-size:.68rem">Массивный</button>
<div class="dyn-row-2">
<button class="mag-mode-btn atw-massive-btn active" data-val="false" onclick="atwMassiveToggle(false)">Идеальный</button>
<button class="mag-mode-btn atw-massive-btn" data-val="true" onclick="atwMassiveToggle(true)">Массивный</button>
</div>
</div>
@@ -1569,11 +1571,11 @@
<input type="range" class="param-slider" id="sl-roll-alpha" min="5" max="60" value="20" oninput="rollAlphaChange()">
</div>
<div class="gp-section-title" style="margin:4px 0">Режим</div>
<div style="display:flex;gap:5px;margin-bottom:8px">
<button class="mag-mode-btn roll-friction-btn active" data-val="false" onclick="rollFrictionToggle(false)" style="flex:1;font-size:.68rem">Качение</button>
<button class="mag-mode-btn roll-friction-btn" data-val="true" onclick="rollFrictionToggle(true)" style="flex:1;font-size:.68rem">+Трение</button>
<div class="dyn-row-2">
<button class="mag-mode-btn roll-friction-btn active" data-val="false" onclick="rollFrictionToggle(false)">Качение</button>
<button class="mag-mode-btn roll-friction-btn" data-val="true" onclick="rollFrictionToggle(true)">+Трение</button>
</div>
<div style="font-size:0.68rem;color:var(--text-3);line-height:1.5;padding:4px 6px;background:rgba(255,255,255,0.03);border-radius:6px;border:1px solid var(--border);margin-bottom:6px">
<div style="font-size:.78rem;color:var(--text-2);line-height:1.55;padding:7px 10px;background:rgba(255,255,255,0.035);border-radius:8px;border:1px solid var(--border);margin-bottom:8px">
Шар: a = (5/7)g·sinα<br>
Цилиндр: a = (2/3)g·sinα<br>
Обруч: a = (1/2)g·sinα
@@ -1582,7 +1584,7 @@
</div><!-- /#newton-classic-panel -->
<!-- Scene description -->
<div id="newton-scene-desc" style="font-size:0.71rem;color:var(--text-3);line-height:1.6;margin-bottom:10px;padding:6px 8px;background:rgba(255,255,255,0.03);border-radius:8px;border:1px solid var(--border)">
<div id="newton-scene-desc" style="font-size:.82rem;color:var(--text-2);line-height:1.6;margin-bottom:12px;padding:9px 11px;background:rgba(255,255,255,0.04);border-radius:8px;border:1px solid var(--border)">
Закон инерции: тело движется равномерно при отсутствии сил.
</div>
@@ -1625,15 +1627,15 @@
</div>
<!-- Newton graphs toggle -->
<div style="margin-top:10px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.06)">
<button id="btn-newton-graphs" onclick="newtonToggleGraphs()" style="width:100%;font-size:.75rem;padding:5px;border-radius:8px;border:1px solid rgba(6,214,224,0.3);background:rgba(6,214,224,0.08);color:#06D6E0;cursor:pointer">&#1043;&#1088;&#1072;&#1092;&#1080;&#1082;&#1080; x/v/a</button>
<div style="margin-top:12px;padding-top:10px;border-top:1px solid rgba(255,255,255,0.07)">
<button id="btn-newton-graphs" onclick="newtonToggleGraphs()" style="width:100%;font-size:.82rem;font-weight:700;padding:8px;border-radius:8px;border:1px solid rgba(6,214,224,0.35);background:rgba(6,214,224,0.1);color:#06D6E0;cursor:pointer">Графики x/v/a</button>
</div>
<!-- Energy bars toggle — newton -->
<div class="gp-section-title" style="margin-top:10px">&#1047;&#1072;&#1082;&#1086;&#1085; &#1089;&#1086;&#1093;&#1088;&#1072;&#1085;&#1077;&#1085;&#1080;&#1103; &#1101;&#1085;&#1077;&#1088;&#1075;&#1080;&#1080;</div>
<button id="nwt-energy-btn" class="proj-preset-chip" onclick="dynToggleEnergy()" style="width:100%;text-align:left;display:flex;align-items:center;gap:6px">
<button id="nwt-energy-btn" class="proj-preset-chip" onclick="dynToggleEnergy()" style="width:100%;font-size:.82rem;text-align:left;display:flex;align-items:center;gap:7px;padding:8px 11px">
<svg class="ic" viewBox="0 0 24 24"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
<span>&#1069;&#1085;&#1077;&#1088;&#1075;&#1080;&#1103;: &#1042;&#1099;&#1082;&#1083;</span>
<span>Энергия: Выкл</span>
</button>
</div><!-- /#dyn-newton-panel -->
@@ -1642,21 +1644,19 @@
<div id="dyn-sandbox-panel">
<div class="gp-section-title" style="margin-bottom:8px">Инструмент</div>
<div style="display:flex;gap:5px;margin-bottom:4px">
<button class="mag-mode-btn sb-panel-tool active" id="sbpt-box" onclick="sbTool('box',this)" style="flex:1;font-size:.72rem"><svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/></svg> Блок</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-ball" onclick="sbTool('ball',this)" style="flex:1;font-size:.72rem"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8" fill="currentColor" stroke="none"/></svg> Шар</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-erase" onclick="sbTool('erase',this)" style="flex:1;font-size:.72rem"><svg class="ic" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
</div>
<div style="display:flex;gap:5px;margin-bottom:10px">
<button class="mag-mode-btn sb-panel-tool" id="sbpt-spring" onclick="sbTool('spring',this)" style="flex:1;font-size:.72rem"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Пружина</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-rope" onclick="sbTool('rope',this)" style="flex:1;font-size:.72rem">— Нить</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-anchor" onclick="sbTool('anchor',this)" style="flex:1;font-size:.72rem"><svg class="ic" viewBox="0 0 24 24"><path d="M12 2 2 12 12 22 22 12Z"/></svg> Якорь</button>
<div class="dyn-tool-grid">
<button class="mag-mode-btn sb-panel-tool active" id="sbpt-box" onclick="sbTool('box',this)"><svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/></svg> Блок</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-ball" onclick="sbTool('ball',this)"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8" fill="currentColor" stroke="none"/></svg> Шар</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-erase" onclick="sbTool('erase',this)"><svg class="ic" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> Ластик</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-spring" onclick="sbTool('spring',this)"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Пружина</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-rope" onclick="sbTool('rope',this)">— Нить</button>
<button class="mag-mode-btn sb-panel-tool" id="sbpt-anchor" onclick="sbTool('anchor',this)"><svg class="ic" viewBox="0 0 24 24"><path d="M12 2 2 12 12 22 22 12Z"/></svg> Якорь</button>
</div>
<div class="gp-section-title" style="margin-bottom:8px">Режим силы</div>
<div style="display:flex;gap:5px;margin-bottom:10px">
<button class="mag-mode-btn sb-fmode active" id="sbfm-constant" onclick="sbForceMode('constant',this)" style="flex:1;font-size:.72rem">Постоянная</button>
<button class="mag-mode-btn sb-fmode" id="sbfm-impulse" onclick="sbForceMode('impulse',this)" style="flex:1;font-size:.72rem">Импульс</button>
<div class="gp-section-title">Режим силы</div>
<div class="dyn-row-2">
<button class="mag-mode-btn sb-fmode active" id="sbfm-constant" onclick="sbForceMode('constant',this)">Постоянная</button>
<button class="mag-mode-btn sb-fmode" id="sbfm-impulse" onclick="sbForceMode('impulse',this)">Импульс</button>
</div>
<div class="param-block">
@@ -1672,75 +1672,105 @@
<input type="range" class="param-slider" id="sl-sb-springk" min="10" max="600" step="10" value="120" oninput="sbSpringKChange()">
</div>
<div class="gp-section-title" style="margin-top:6px;margin-bottom:6px">Мир</div>
<div style="display:flex;flex-direction:column;gap:5px;margin-bottom:10px">
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-gravity" checked onchange="sbWorldToggle()"> Гравитация</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-floor" checked onchange="sbWorldToggle()"> Пол</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-walls" checked onchange="sbWorldToggle()"> Стенки</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-airdrag" onchange="sbWorldToggle()"> Сопротивление воздуха</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-ramp" onchange="sbRampToggle()"> Наклонная плоскость</label>
</div>
<div id="sb-ramp-block" style="display:none">
<div class="param-block">
<div class="param-header"><span class="param-name">Угол α</span><span class="param-val" id="sb-angle-val">30°</span></div>
<input type="range" class="param-slider" id="sl-sb-angle" min="5" max="75" value="30" oninput="sbAngleChange()">
<details class="dyn-acc" open>
<summary>Мир</summary>
<div class="dyn-acc-body">
<div class="dyn-checks">
<label><input type="checkbox" id="sb-gravity" checked onchange="sbWorldToggle()"> Гравитация</label>
<label><input type="checkbox" id="sb-floor" checked onchange="sbWorldToggle()"> Пол</label>
<label><input type="checkbox" id="sb-walls" checked onchange="sbWorldToggle()"> Стенки</label>
<label><input type="checkbox" id="sb-airdrag" onchange="sbWorldToggle()"> Сопротивление воздуха</label>
<label><input type="checkbox" id="sb-ramp" onchange="sbRampToggle()"> Наклонная плоскость</label>
</div>
<div id="sb-ramp-block" style="display:none">
<div class="param-block">
<div class="param-header"><span class="param-name">Угол α</span><span class="param-val" id="sb-angle-val">30°</span></div>
<input type="range" class="param-slider" id="sl-sb-angle" min="5" max="75" value="30" oninput="sbAngleChange()">
</div>
<div class="param-block">
<div class="param-header"><span class="param-name">Трение горки μ</span><span class="param-val" id="sb-rampmu-val">0.20</span></div>
<input type="range" class="param-slider" id="sl-sb-rampmu" min="0" max="1" step="0.01" value="0.20" oninput="sbRampMuChange()">
</div>
<label class="dyn-check-inline"><input type="checkbox" id="sb-decomp" checked onchange="sbDecompToggle()"> Разложение сил</label>
</div>
<div class="param-block">
<div class="param-header"><span class="param-name">Трение пола μ</span><span class="param-val" id="sb-floormu-val">0.30</span></div>
<input type="range" class="param-slider" id="sl-sb-floormu" min="0" max="1" step="0.01" value="0.30" oninput="sbFloorMuChange()">
</div>
</div>
<div class="param-block">
<div class="param-header"><span class="param-name">Трение горки μ</span><span class="param-val" id="sb-rampmu-val">0.20</span></div>
<input type="range" class="param-slider" id="sl-sb-rampmu" min="0" max="1" step="0.01" value="0.20" oninput="sbRampMuChange()">
</details>
<details class="dyn-acc">
<summary>Отображение</summary>
<div class="dyn-acc-body">
<div class="dyn-checks">
<label><input type="checkbox" id="sb-forces" checked onchange="sbDisplayToggle()"> Силы</label>
<label><input type="checkbox" id="sb-vel" checked onchange="sbDisplayToggle()"> Скорости</label>
<label><input type="checkbox" id="sb-fbd" onchange="sbDisplayToggle()"> FBD-диаграмма</label>
<label><input type="checkbox" id="sb-energy" checked onchange="sbDisplayToggle()"> Энергия</label>
<label><input type="checkbox" id="sb-trail" checked onchange="sbDisplayToggle()"> Стробоскоп</label>
</div>
</div>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer;margin-bottom:6px"><input type="checkbox" id="sb-decomp" checked onchange="sbDecompToggle()"> Разложение сил</label>
</div>
<div class="param-block">
<div class="param-header"><span class="param-name">Трение пола μ</span><span class="param-val" id="sb-floormu-val">0.30</span></div>
<input type="range" class="param-slider" id="sl-sb-floormu" min="0" max="1" step="0.01" value="0.30" oninput="sbFloorMuChange()">
</div>
</details>
<div class="gp-section-title" style="margin-top:6px;margin-bottom:6px">Отображение</div>
<div style="display:flex;flex-direction:column;gap:5px;margin-bottom:10px">
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-forces" checked onchange="sbDisplayToggle()"> Силы</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-vel" checked onchange="sbDisplayToggle()"> Скорости</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-fbd" onchange="sbDisplayToggle()"> FBD-диаграмма</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-energy" checked onchange="sbDisplayToggle()"> Энергия</label>
<label style="display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--text-2);cursor:pointer"><input type="checkbox" id="sb-trail" checked onchange="sbDisplayToggle()"> Стробоскоп</label>
</div>
<details class="dyn-acc">
<summary>Время</summary>
<div class="dyn-acc-body">
<div class="dyn-row-4">
<button class="mag-mode-btn sb-time" onclick="sbTimeScale(0.25,this)">×0.25</button>
<button class="mag-mode-btn sb-time" onclick="sbTimeScale(0.5,this)">×0.5</button>
<button class="mag-mode-btn sb-time active" onclick="sbTimeScale(1,this)">×1</button>
<button class="mag-mode-btn sb-time" onclick="sbTimeScale(2,this)">×2</button>
</div>
</div>
</details>
<div class="gp-section-title" style="margin-top:2px;margin-bottom:6px">Время</div>
<div style="display:flex;gap:5px;margin-bottom:10px">
<button class="mag-mode-btn sb-time" onclick="sbTimeScale(0.25,this)" style="flex:1;font-size:.68rem">×0.25</button>
<button class="mag-mode-btn sb-time" onclick="sbTimeScale(0.5,this)" style="flex:1;font-size:.68rem">×0.5</button>
<button class="mag-mode-btn sb-time active" onclick="sbTimeScale(1,this)" style="flex:1;font-size:.68rem">×1</button>
<button class="mag-mode-btn sb-time" onclick="sbTimeScale(2,this)" style="flex:1;font-size:.68rem">×2</button>
</div>
<details class="dyn-acc" open>
<summary>Пресеты</summary>
<div class="dyn-acc-body">
<div class="dyn-preset-cat">Базовые</div>
<div class="dyn-preset-grp">
<button class="proj-preset-chip" onclick="sbPreset('freefall')">Падение</button>
<button class="proj-preset-chip" onclick="sbPreset('friction')">Трение</button>
<button class="proj-preset-chip" onclick="sbPreset('balance')">Равновесие</button>
<button class="proj-preset-chip" onclick="sbPreset('projectile_angle')">Снаряд 45°</button>
<button class="proj-preset-chip" onclick="sbPreset('circular_motion')">Круговое</button>
</div>
<div class="dyn-preset-cat">Столкновения</div>
<div class="dyn-preset-grp">
<button class="proj-preset-chip" onclick="sbPreset('collision')">Столкновение</button>
<button class="proj-preset-chip" onclick="sbPreset('elastic_collision')">Упругий</button>
<button class="proj-preset-chip" onclick="sbPreset('inelastic_collision')">Неупругий</button>
<button class="proj-preset-chip" onclick="sbPreset('newton_cradle')">Колыбель Ньютона</button>
</div>
<div class="dyn-preset-cat">Пружины и осцилляторы</div>
<div class="dyn-preset-grp">
<button class="proj-preset-chip" onclick="sbPreset('spring_bounce')">Пружина</button>
<button class="proj-preset-chip" onclick="sbPreset('spring_chain')">Цепочка</button>
<button class="proj-preset-chip" onclick="sbPreset('harmonic_oscillator')">Осциллятор</button>
<button class="proj-preset-chip" onclick="sbPreset('coupled_oscillators')">Связанные</button>
</div>
<div class="dyn-preset-cat">Маятники и блоки</div>
<div class="dyn-preset-grp">
<button class="proj-preset-chip" onclick="sbPreset('pendulum')">Маятник</button>
<button class="proj-preset-chip" onclick="sbPreset('double_pendulum')">Двойной маятник</button>
<button class="proj-preset-chip" onclick="sbPreset('atwood')">Атвуд</button>
<button class="proj-preset-chip" onclick="sbPreset('two_body')">Два тела</button>
<button class="proj-preset-chip" onclick="sbPreset('tug')">Перетягивание</button>
</div>
<div class="dyn-preset-cat">Горки и стопки</div>
<div class="dyn-preset-grp">
<button class="proj-preset-chip" onclick="sbPreset('ramp_slide')">Горка</button>
<button class="proj-preset-chip" onclick="sbPreset('ramp_angle')">Крутой спуск</button>
<button class="proj-preset-chip" onclick="sbPreset('ramp_friction')">Трение на горке</button>
<button class="proj-preset-chip" onclick="sbPreset('pulley_ramp')">Горка+блок</button>
<button class="proj-preset-chip" onclick="sbPreset('stacked_boxes')">Стопка</button>
</div>
</div>
</details>
<div class="gp-section-title" style="margin-top:2px">Пресеты</div>
<div style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px">
<button class="proj-preset-chip" onclick="sbPreset('freefall')">Падение</button>
<button class="proj-preset-chip" onclick="sbPreset('collision')">Столкновение</button>
<button class="proj-preset-chip" onclick="sbPreset('friction')">Трение</button>
<button class="proj-preset-chip" onclick="sbPreset('tug')">Перетягивание</button>
<button class="proj-preset-chip" onclick="sbPreset('balance')">Равновесие</button>
<button class="proj-preset-chip" onclick="sbPreset('ramp_slide')">Горка</button>
<button class="proj-preset-chip" onclick="sbPreset('ramp_angle')"><svg class="ic" viewBox="0 0 24 24"><path d="m8 3 4 8 5-5 5 15H2L8 3z"/></svg> Крутой спуск</button>
<button class="proj-preset-chip" onclick="sbPreset('ramp_friction')"><svg class="ic" viewBox="0 0 24 24"><rect width="20" height="5" x="2" y="3" rx="1"/><rect width="8" height="5" x="2" y="11" rx="1"/><rect width="8" height="5" x="14" y="11" rx="1"/><rect width="20" height="5" x="2" y="19" rx="1"/></svg> Трение на горке</button>
<button class="proj-preset-chip" onclick="sbPreset('spring_bounce')"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Пружина</button>
<button class="proj-preset-chip" onclick="sbPreset('spring_chain')"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Цепочка</button>
<button class="proj-preset-chip" onclick="sbPreset('pendulum')">⬤ Маятник</button>
<button class="proj-preset-chip" onclick="sbPreset('atwood')"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93 17.66 6.34M21 12h-2M19.07 19.07l-1.41-1.41M12 21v-2M6.34 17.66 4.93 19.07M3 12h2M4.93 4.93l1.41 1.41M12 3v2"/><circle cx="12" cy="12" r="7"/></svg> Машина Атвуда</button>
<button class="proj-preset-chip" onclick="sbPreset('two_body')"><svg class="ic" viewBox="0 0 24 24"><line x1="12" y1="21" x2="12" y2="3"/><polyline points="7 8 12 3 17 8"/><polyline points="17 16 12 21 7 16"/></svg> Два тела</button>
<button class="proj-preset-chip" onclick="sbPreset('elastic_collision')"><svg class="ic" viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg> Упругий удар</button>
<button class="proj-preset-chip" onclick="sbPreset('inelastic_collision')"><svg class="ic" viewBox="0 0 24 24"><path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z"/></svg> Неупругий</button>
<button class="proj-preset-chip" onclick="sbPreset('newton_cradle')"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93 17.66 6.34M21 12h-2M19.07 19.07l-1.41-1.41M12 21v-2M6.34 17.66 4.93 19.07M3 12h2M4.93 4.93l1.41 1.41M12 3v2"/><circle cx="12" cy="12" r="7"/></svg> Колыбель Ньютона</button>
<button class="proj-preset-chip" onclick="sbPreset('harmonic_oscillator')"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Осциллятор</button>
<button class="proj-preset-chip" onclick="sbPreset('double_pendulum')">⬤⬤ Двойной маятник</button>
<button class="proj-preset-chip" onclick="sbPreset('coupled_oscillators')"><svg class="ic" viewBox="0 0 24 24" fill="none"><path d="M3 12 L6 8 L9 16 L12 8 L15 16 L18 8 L21 12"/></svg> Связанные</button>
<button class="proj-preset-chip" onclick="sbPreset('stacked_boxes')"><svg class="ic" viewBox="0 0 24 24"><rect width="20" height="5" x="2" y="3" rx="1"/><rect width="8" height="5" x="2" y="11" rx="1"/><rect width="8" height="5" x="14" y="11" rx="1"/><rect width="20" height="5" x="2" y="19" rx="1"/></svg> Стопка</button>
<button class="proj-preset-chip" onclick="sbPreset('pulley_ramp')"><svg class="ic" viewBox="0 0 24 24"><path d="m8 3 4 8 5-5 5 15H2L8 3z"/></svg> Горка+блок</button>
<button class="proj-preset-chip" onclick="sbPreset('circular_motion')">⭕ Круговое</button>
<button class="proj-preset-chip" onclick="sbPreset('projectile_angle')"><svg class="ic" viewBox="0 0 24 24"><line x1="7" y1="17" x2="17" y2="7"/><polyline points="7 7 17 7 17 17"/></svg> Снаряд 45°</button>
</div>
<div style="font-size:.65rem;color:var(--text-3);line-height:1.5;margin-top:auto;padding:6px 8px;background:rgba(255,255,255,0.03);border-radius:8px;border:1px solid var(--border)">
<div style="font-size:.78rem;color:var(--text-2);line-height:1.55;margin-top:14px;padding:9px 11px;background:rgba(255,255,255,0.035);border-radius:8px;border:1px solid var(--border)">
<b style="color:var(--violet);font-size:.78rem;display:block;margin-bottom:4px">Управление</b>
ЛКМ — создать тело · Drag — сила<br>
Shift+drag — импульс · ПКМ — удалить<br>
DblClick — закрепить / открепить<br>
@@ -1748,14 +1778,14 @@
</div>
<!-- Sandbox graphs toggle -->
<div style="margin-top:8px">
<button id="btn-sandbox-graphs" onclick="sandboxToggleGraphs()" style="width:100%;font-size:.75rem;padding:5px;border-radius:8px;border:1px solid rgba(6,214,224,0.3);background:rgba(6,214,224,0.08);color:#06D6E0;cursor:pointer">&#1043;&#1088;&#1072;&#1092;&#1080;&#1082;&#1080; &#1090;&#1077;&#1083;&#1072;</button>
<div style="margin-top:10px">
<button id="btn-sandbox-graphs" onclick="sandboxToggleGraphs()" style="width:100%;font-size:.82rem;font-weight:700;padding:8px;border-radius:8px;border:1px solid rgba(6,214,224,0.35);background:rgba(6,214,224,0.1);color:#06D6E0;cursor:pointer">Графики тела</button>
</div>
<!-- Energy bars toggle — sandbox -->
<button id="fsb-energy-btn" class="proj-preset-chip" onclick="fsbToggleEnergy()" style="width:100%;text-align:left;display:flex;align-items:center;gap:6px;margin-top:6px">
<button id="fsb-energy-btn" class="proj-preset-chip" onclick="fsbToggleEnergy()" style="width:100%;font-size:.82rem;text-align:left;display:flex;align-items:center;gap:7px;margin-top:8px;padding:8px 11px">
<svg class="ic" viewBox="0 0 24 24"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
<span>&#1069;&#1085;&#1077;&#1088;&#1075;&#1080;&#1103;+: &#1042;&#1099;&#1082;&#1083;</span>
<span>Энергия+: Выкл</span>
</button>
</div><!-- /#dyn-sandbox-panel -->