feat(phys8 ch1): Phase 1 Wave 2 — §3 теплопроводность + §4 конвекция + §5 излучение

§3 Теплопроводность:
- Главный визуал: симуляция стержня через PHYS.createHeatBar — slider'ы
  T_горячий, T_холодный, α (от шерсти до серебра), 1D-уравнение тепла
- Викторина «лучший проводник»: 6 пар материалов
- DnD: 8 материалов на 2 категории (хорошие/плохие)
- MCQ 6 вопросов

§4 Конвекция:
- Симуляция тороидального потока: 30 частиц в сосуде, нагреватель снизу,
  тёплые поднимаются по центру, холодные опускаются по краям, цвет
  по tempColor
- Викторина «возможна ли конвекция?» с 6 ситуациями
- DnD: 8 ситуаций (возможна/невозможна)
- MCQ 6 вопросов

§5 Излучение:
- Симуляция «Солнце греет чёрную и белую пластины»: лучи к чёрной
  поглощаются, от белой отражаются; температуры растут с разной
  скоростью (чёрная до 75°C, белая до 35°C)
- True/False квикфайр (7 утверждений)
- DnD: 9 примеров на 3 вида теплопередачи (главный синтез главы)
- MCQ 6 вопросов

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-29 22:55:42 +03:00
parent d52ad9b06f
commit 2ae70dd48f
+742 -9
View File
@@ -283,9 +283,24 @@ const SIDEBARS = {
["Теплопередача","проводность, конвекция, излучение"],
["Знак","нагрев $\\Rightarrow$ $\\Delta U > 0$; охлаждение $\\Rightarrow$ $\\Delta U < 0$"]
]},
p3:{title:"Шпаргалка § 3",rows:[["В разработке","Phase 1 Wave 2"]]},
p4:{title:"Шпаргалка § 4",rows:[["В разработке","Phase 1 Wave 2"]]},
p5:{title:"Шпаргалка § 5",rows:[["В разработке","Phase 1 Wave 2"]]},
p3:{title:"Шпаргалка § 3",rows:[
["Теплопроводность","передача $U$ без переноса вещества"],
["Хорошие","металлы (медь, серебро, алюминий)"],
["Плохие","дерево, пластик, воздух, шерсть, вакуум"],
["В газах и жидкостях","низкая (исключение — ртуть)"]
]},
p4:{title:"Шпаргалка § 4",rows:[
["Конвекция","перенос $U$ потоками жидкости / газа"],
["Механизм","тёплое $\\uparrow$, холодное $\\downarrow$"],
["В твёрдых телах","невозможна"],
["Примеры","батарея, ветер, кипение"]
]},
p5:{title:"Шпаргалка § 5",rows:[
["Излучение","перенос $U$ электромагнитными волнами"],
["Среда","не нужна — идёт через вакуум"],
["Излучают всё","чем горячее, тем сильнее"],
["Тёмные тела","поглощают и излучают сильнее светлых"]
]},
p6:{title:"Шпаргалка § 6",rows:[["В разработке","Phase 1 Wave 3"]]},
p7:{title:"Шпаргалка § 7",rows:[["В разработке","Phase 1 Wave 3"]]},
p8:{title:"Шпаргалка § 8",rows:[["В разработке","Phase 1 Wave 4"]]},
@@ -298,9 +313,9 @@ const SIDEBARS = {
const TIPS=[
{sec:'p1',html:"Тело состоит из молекул. Они движутся (есть $E_k$) и взаимодействуют (есть $E_p$). Их сумма — внутренняя энергия $U$. Главное: $U$ не зависит от того, движется ли тело и на какой высоте оно лежит."},
{sec:'p2',html:"Изменить $U$ можно двумя способами: совершить работу (трение, сжатие, удар) или передать тепло без работы (контакт, поток, излучение). Чай в стакане остывает — это теплопередача. Спички в коробке нагреваются от тряски — это работа."},
{sec:'p3',html:"Параграф § 3 будет реализован в Phase 1 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p4',html:"Параграф § 4 будет реализован в Phase 1 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p5',html:"Параграф § 5 будет реализован в Phase 1 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p3',html:"Один конец металлического стержня в огне — другой нагревается, хотя ничто не движется. Это <b>теплопроводность</b>: молекулы передают энергию соседям. У металлов это работает быстро, у дерева — медленно."},
{sec:'p4',html:"Тёплый воздух легче холодного и поднимается вверх. Так батарея греет всю комнату — это <b>конвекция</b>. В твёрдых телах конвекции нет, потому что молекулы не могут свободно двигаться."},
{sec:'p5',html:"Солнце греет Землю через космический вакуум — теплопроводность и конвекция тут невозможны. Это <b>излучение</b> электромагнитными волнами. Чёрная футболка в жаркий день нагревается сильнее белой."},
{sec:'p6',html:"Параграф § 6 будет реализован в Phase 1 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p7',html:"Параграф § 7 будет реализован в Phase 1 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p8',html:"Параграф § 8 будет реализован в Phase 1 Wave 4. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
@@ -313,9 +328,9 @@ const TIPS=[
const BUILDERS = {
p1: ()=>{ build_p1(); },
p2: ()=>{ build_p2(); },
p3: ()=>{ const box=document.getElementById('p3-body'); box.innerHTML = buildStub('p3', 'Теплопроводность', 'Phase 1 Wave 2') + secNavFor('p3') + readButton('p3'); renderMath(box); wireReadBtn('p3'); },
p4: ()=>{ const box=document.getElementById('p4-body'); box.innerHTML = buildStub('p4', 'Конвекция', 'Phase 1 Wave 2') + secNavFor('p4') + readButton('p4'); renderMath(box); wireReadBtn('p4'); },
p5: ()=>{ const box=document.getElementById('p5-body'); box.innerHTML = buildStub('p5', 'Излучение', 'Phase 1 Wave 2') + secNavFor('p5') + readButton('p5'); renderMath(box); wireReadBtn('p5'); },
p3: ()=>{ build_p3(); },
p4: ()=>{ build_p4(); },
p5: ()=>{ build_p5(); },
p6: ()=>{ const box=document.getElementById('p6-body'); box.innerHTML = buildStub('p6', 'Расчёт количества теплоты при нагревании и охлаждении. Удельная теплоёмкость', 'Phase 1 Wave 3') + secNavFor('p6') + readButton('p6'); renderMath(box); wireReadBtn('p6'); },
p7: ()=>{ const box=document.getElementById('p7-body'); box.innerHTML = buildStub('p7', 'Горение. Удельная теплота сгорания топлива', 'Phase 1 Wave 3') + secNavFor('p7') + readButton('p7'); renderMath(box); wireReadBtn('p7'); },
p8: ()=>{ const box=document.getElementById('p8-body'); box.innerHTML = buildStub('p8', 'Плавление и кристаллизация', 'Phase 1 Wave 4') + secNavFor('p8') + readButton('p8'); renderMath(box); wireReadBtn('p8'); },
@@ -1297,6 +1312,724 @@ function _initP2_mcq(){
render();
}
/* ======================================================================
PHASE 1 · WAVE 2 — §3, §4, §5
====================================================================== */
/* ======== §3 — Теплопроводность ======== */
function build_p3(){
const box = document.getElementById('p3-body');
let h = '';
h += makeCard('theory', 'Что такое теплопроводность', '§ 3.1',
'<p><b>Теплопроводность</b> — передача внутренней энергии от более горячих частей тела к более холодным <b>без переноса вещества</b>.</p>'
+'<p>Механизм: быстрые молекулы (в горячей части) сталкиваются с медленными (в холодной) и передают им часть своей $E_k$. Так энергия путешествует через тело молекула за молекулой.</p>'
+'<p>Само вещество при этом остаётся на месте — двигается только энергия.</p>'
);
h += makeCard('rule', 'Хорошие и плохие проводники', '§ 3.2',
'<p><b>Хорошие проводники</b> тепла:</p>'
+'<ul style="padding-left:20px;margin:4px 0"><li>металлы: серебро, медь, алюминий, железо;</li><li>в них свободные электроны быстро переносят энергию.</li></ul>'
+'<p style="margin-top:8px"><b>Плохие проводники</b> (тепловые изоляторы):</p>'
+'<ul style="padding-left:20px;margin:4px 0"><li>дерево, стекло, кирпич, пластик;</li><li>шерсть, мех, пух, вата (они «держат» воздух между волокнами);</li><li>сами газы и жидкости (кроме ртути);</li><li><b>вакуум</b> — идеальный изолятор: нет частиц $\\Rightarrow$ нечем передавать.</li></ul>'
);
h += makeCard('example', 'Зачем это нам', '§ 3.3',
'<ul style="padding-left:20px;margin:4px 0">'
+'<li>Ручка сковороды — из дерева или пластика, чтобы не обжечь руку.</li>'
+'<li>Шуба греет не сама — между волосками много воздуха.</li>'
+'<li>Стеклопакет: два стекла + воздух между ними плохо проводят тепло.</li>'
+'<li>В термосе между двумя стенками — вакуум (минимум теплопроводности).</li>'
+'</ul>'
);
/* IV1 — симуляция теплопроводности */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Нагрев стержня</div></div>'
+'<div class="wg-help">Один конец стержня — горячий (<b>$T_{гор}$</b>), другой — холодный (<b>$T_{хол}$</b>). Тепло перетекает с горячего конца на холодный. Цвет показывает температуру вдоль стержня. Меняй $\\alpha$ — коэффициент теплопроводности.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$T_{гор}$, &#176;C: <b id="p3-th">200</b><input type="range" id="p3-thr" min="50" max="500" step="10" value="200"></label>'
+'<label>$T_{хол}$, &#176;C: <b id="p3-tc">0</b><input type="range" id="p3-tcr" min="-50" max="100" step="5" value="0"></label>'
+'<label>$\\alpha$ (проводимость): <b id="p3-av">медь</b><input type="range" id="p3-ar" min="1" max="100" step="1" value="50"></label>'
+'</div>'
+'<svg id="p3-sim" viewBox="0 0 460 80" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="actions" style="margin-top:8px"><button class="btn" id="p3-reset">Сброс</button></div>'
+'</div>';
/* IV2 — викторина «лучший проводник» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Какой материал проводит тепло лучше?</div></div>'
+'<div class="wg-help">Выбери материал с лучшей теплопроводностью.</div>'
+'<div id="p3-quiz"></div>'
+'<div class="actions"><button class="btn" id="p3-quiz-next">Следующий раунд</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p3-quiz-r">1</b> / 6</span><span>Правильно: <b id="p3-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD хороший/плохой */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Сортировка материалов</div></div>'
+'<div class="wg-help">Перетащи материалы в нужную группу.</div>'
+'<div id="p3-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">'
+'<div class="drop-box"><h5>Хорошие проводники</h5><div class="drop-items" data-cat="good"></div></div>'
+'<div class="drop-box"><h5>Плохие проводники</h5><div class="drop-items" data-cat="bad"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p3-dnd-check">Проверить</button><button class="btn" id="p3-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p3-dnd-fb"></div>'
+'</div>';
/* IV4 — MCQ */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 вопросов</div></div>'
+'<div class="wg-help">4+ правильных ответа — +15 XP.</div>'
+'<div id="p3-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p3-mcq-i">1</b> / 6</span><span>Правильно: <b id="p3-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p3') + readButton('p3');
renderMath(box);
wireReadBtn('p3');
_initP3_sim();
_initP3_quiz();
_initP3_dnd();
_initP3_mcq();
}
function _initP3_sim(){
_killSim('p3sim');
const svg = document.getElementById('p3-sim'); if(!svg) return;
const W=460, H=80, padX=24, barY=20, barH=44;
let bar = window.PHYS.createHeatBar({ N: 40, tHot: 200, tCold: 0, alpha: 0.5 });
const MAT_LABELS = ['вакуум','шерсть','дерево','стекло','вода','свинец','железо','алюминий','медь','серебро'];
function alphaFromSlider(v){
/* v in 1..100, exponential mapping to 0.02..0.95 */
return 0.02 + (v/100) * 0.93;
}
function matLabel(v){
const idx = Math.min(MAT_LABELS.length-1, Math.floor(v/10));
return MAT_LABELS[idx];
}
let lastT = performance.now();
function tick(now){
if(!_isVisible('p3')){ _SIMS.p3sim.raf = requestAnimationFrame(tick); return; }
const dt = Math.min(50, now - lastT); lastT = now;
bar.step(dt * 0.06);
/* render */
let s = bar.render(padX, barY, W - 2*padX, barH);
/* подписи концов */
s += '<text x="'+(padX-4)+'" y="'+(barY-4)+'" text-anchor="start" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#dc2626">горячий</text>';
s += '<text x="'+(W-padX+4)+'" y="'+(barY-4)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#2563eb">холодный</text>';
svg.innerHTML = s;
_SIMS.p3sim.raf = requestAnimationFrame(tick);
}
_SIMS.p3sim = { raf: 0 };
_SIMS.p3sim.raf = requestAnimationFrame(tick);
function applyAll(){
const th = +document.getElementById('p3-thr').value;
const tc = +document.getElementById('p3-tcr').value;
const av = +document.getElementById('p3-ar').value;
document.getElementById('p3-th').textContent = th;
document.getElementById('p3-tc').textContent = tc;
document.getElementById('p3-av').textContent = matLabel(av);
bar.setTHot(th); bar.setTCold(tc); bar.alpha = alphaFromSlider(av);
}
['p3-thr','p3-tcr','p3-ar'].forEach(id => document.getElementById(id).addEventListener('input', applyAll));
document.getElementById('p3-reset').addEventListener('click', ()=>{
bar = window.PHYS.createHeatBar({ N: 40, tHot: +document.getElementById('p3-thr').value, tCold: +document.getElementById('p3-tcr').value, alpha: alphaFromSlider(+document.getElementById('p3-ar').value) });
});
applyAll();
}
function _initP3_quiz(){
const QS = [
{A:'медная ложка', B:'деревянная ложка', ans:'A', why:'Медь — отличный проводник, дерево — изолятор.'},
{A:'железная дверная ручка', B:'пластиковая дверная ручка', ans:'A', why:'Железо проводит тепло гораздо лучше пластика, поэтому железная ручка зимой кажется холоднее.'},
{A:'воздух', B:'вода', ans:'B', why:'Жидкости проводят тепло лучше газов, хотя обе плохо.'},
{A:'стекло', B:'серебро', ans:'B', why:'Серебро — лучший проводник тепла среди обычных металлов.'},
{A:'шерстяной свитер', B:'хлопковая футболка', ans:'B', why:'Хлопок проводит тепло немного лучше шерсти; шерсть лучше задерживает воздух и потому теплее (но проводит хуже).'},
{A:'вакуум', B:'воздух', ans:'B', why:'В вакууме нет частиц, поэтому теплопроводность нулевая. Воздух хоть и плохой, но проводник.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i];
const wrap = document.getElementById('p3-quiz');
if(!wrap) return;
wrap.innerHTML =
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">'
+'<button class="btn" data-pick="A" style="padding:14px;text-align:left"><b>A.</b> '+q.A+'</button>'
+'<button class="btn" data-pick="B" style="padding:14px;text-align:left"><b>B.</b> '+q.B+'</button>'
+'</div>'
+'<div class="feedback" id="p3-quiz-fb"></div>';
document.getElementById('p3-quiz-r').textContent = (i+1);
document.getElementById('p3-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return;
const pick = btn.dataset.pick;
wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
const fb = document.getElementById('p3-quiz-fb');
if(pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p3-quiz'); bumpProgress('p3', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p3-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p3-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP3_dnd(){
const items = [
{id:'cu', cat:'good', html:'медь'},
{id:'ag', cat:'good', html:'серебро'},
{id:'al', cat:'good', html:'алюминий'},
{id:'fe', cat:'good', html:'железо'},
{id:'wo', cat:'bad', html:'дерево'},
{id:'pl', cat:'bad', html:'пластик'},
{id:'sh', cat:'bad', html:'шерсть'},
{id:'va', cat:'bad', html:'вакуум'}
];
const dnd = setupSorter({ poolId:'p3-dnd-pool', scopeSelector:'#sec-p3', cats:['good','bad'], items, columnLayout:false });
document.getElementById('p3-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p3-dnd-fb');
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='&#10003; Идеально! +15 XP. Металлы — отличные проводники, остальное — плохие.'; addXp(15,'p3-dnd'); bumpProgress('p3', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: металлы — это хорошие проводники.'; }
});
document.getElementById('p3-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p3-dnd-fb'); fb.style.display='none'; });
}
function _initP3_mcq(){
const QS = [
{q:'Что происходит при теплопроводности?', opts:['Переносится вещество и энергия','Переносится только энергия','Переносится только вещество','Тело меняет форму'], ans:1, why:'Молекулы передают $E_k$ соседям, оставаясь на месте.'},
{q:'В каком веществе теплопроводность нулевая?', opts:['в воде','в воздухе','в вакууме','в стекле'], ans:2, why:'В вакууме нет частиц — нечем передавать.'},
{q:'Почему шуба греет?', opts:['Сама вырабатывает тепло','Между ворсинками воздух — плохой проводник','Отражает тепло','Уменьшает излучение'], ans:1, why:'Шерсть удерживает прослойки воздуха, которые плохо проводят тепло.'},
{q:'У какого вещества теплопроводность выше?', opts:['у воды','у льда','у пара','одинакова'], ans:1, why:'У льда (твёрдое — упорядоченные молекулы передают энергию быстрее).'},
{q:'Зачем стеклопакет с воздухом между стёклами?', opts:['Для прочности','Для красоты','Воздух — плохой проводник, теряется меньше тепла','Чтобы стекло не треснуло'], ans:2, why:'Прослойка воздуха работает как теплоизолятор.'},
{q:'Какие частицы переносят тепло в металлах быстрее всего?', opts:['Атомы','Ионы','Молекулы','Свободные электроны'], ans:3, why:'Свободные электроны движутся быстро и переносят $E_k$ через весь металл.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p3-mcq'); if(!wrap) return;
let h = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div>';
h += '<div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt, k)=>{ h += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+(String.fromCharCode(65+k))+'. '+opt+'</button>'; });
h += '</div><div class="feedback" id="p3-mcq-fb"></div><div class="actions"><button class="btn" id="p3-mcq-next">Следующий вопрос</button></div>';
wrap.innerHTML = h;
document.getElementById('p3-mcq-i').textContent = (i+1);
document.getElementById('p3-mcq-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p3-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p3-mcq'); bumpProgress('p3', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p3-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf = document.getElementById('p3-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').'; addXp(15,'p3-mcq-bonus'); bumpProgress('p3', 15); }, 600); }
});
});
const nb = document.getElementById('p3-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §4 — Конвекция ======== */
function build_p4(){
const box = document.getElementById('p4-body');
let h = '';
h += makeCard('theory', 'Что такое конвекция', '§ 4.1',
'<p><b>Конвекция</b> — передача внутренней энергии <b>потоками</b> жидкости или газа.</p>'
+'<p>Нагретая часть жидкости (газа) <b>расширяется</b>, становится менее плотной и <b>всплывает</b> вверх. Холодная — опускается вниз. Так образуется круговорот, который и переносит тепло.</p>'
+'<p>В <b>твёрдых телах</b> конвекции нет — молекулы зафиксированы, потоков образоваться не может.</p>'
);
h += makeCard('rule', 'Условия для конвекции', '§ 4.2',
'<ul style="padding-left:20px;margin:4px 0">'
+'<li>есть текучая среда (газ или жидкость);</li>'
+'<li>есть <b>разность температур</b> сверху и снизу: нагрев должен быть снизу или охлаждение сверху;</li>'
+'<li>есть гравитация (в невесомости конвекции почти нет).</li>'
+'</ul>'
+'<p style="margin-top:6px">Если греть сверху — конвекции не будет: горячий слой и так наверху.</p>'
);
h += makeCard('example', 'Где встречается', '§ 4.3',
'<ul style="padding-left:20px;margin:4px 0">'
+'<li>Батарея греет всю комнату — горячий воздух поднимается к потолку, холодный спускается к полу.</li>'
+'<li>Кипение чайника: пузырьки и потоки воды видны глазом.</li>'
+'<li>Ветры в атмосфере: тёплый экватор и холодные полюса.</li>'
+'<li>Течения в океане: тёплый Гольфстрим греет Европу.</li>'
+'</ul>'
);
/* IV1 — анимация конвекции */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Конвекция в сосуде</div></div>'
+'<div class="wg-help">Включи / выключи нагреватель снизу. Тёплая жидкость <b>поднимается</b> по центру, холодная <b>опускается</b> по краям.</div>'
+'<svg id="p4-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="actions" style="margin-top:8px"><button class="btn primary" id="p4-on">Включить нагрев</button><button class="btn" id="p4-reset">Сброс</button></div>'
+'</div>';
/* IV2 — викторина «где конвекция?» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Возможна ли тут конвекция?</div></div>'
+'<div class="wg-help">Назови среду или ситуацию — определи, бывает ли в ней конвекция.</div>'
+'<div id="p4-quiz"></div>'
+'<div class="actions"><button class="btn" id="p4-quiz-next">Следующий раунд</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p4-quiz-r">1</b> / 6</span><span>Правильно: <b id="p4-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Где конвекция, а где — нет?</div></div>'
+'<div class="wg-help">Перетащи ситуации в нужную колонку.</div>'
+'<div id="p4-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">'
+'<div class="drop-box"><h5>Конвекция возможна</h5><div class="drop-items" data-cat="yes"></div></div>'
+'<div class="drop-box"><h5>Конвекция невозможна</h5><div class="drop-items" data-cat="no"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p4-dnd-check">Проверить</button><button class="btn" id="p4-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p4-dnd-fb"></div>'
+'</div>';
/* IV4 — MCQ */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 вопросов</div></div>'
+'<div class="wg-help">4+ правильных ответа — +15 XP.</div>'
+'<div id="p4-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p4-mcq-i">1</b> / 6</span><span>Правильно: <b id="p4-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p4') + readButton('p4');
renderMath(box);
wireReadBtn('p4');
_initP4_sim();
_initP4_quiz();
_initP4_dnd();
_initP4_mcq();
}
function _initP4_sim(){
_killSim('p4sim');
const svg = document.getElementById('p4-sim'); if(!svg) return;
const W=460, H=240, vesselX=120, vesselY=20, vesselW=220, vesselH=180;
/* 30 частиц жидкости */
const N = 30;
const ps = [];
for(let i=0;i<N;i++){
ps.push({
x: vesselX + 10 + Math.random()*(vesselW-20),
y: vesselY + 10 + Math.random()*(vesselH-20),
vx: 0, vy: 0,
T: 20
});
}
let heaterOn = false;
function tick(){
if(!_isVisible('p4')){ _SIMS.p4sim.raf = requestAnimationFrame(tick); return; }
/* Движение по тороидальному полю при включённом нагреве */
for(const p of ps){
if(heaterOn){
/* нагрев в нижней зоне */
const distFromBottom = (vesselY + vesselH) - p.y;
if(distFromBottom < 30) p.T = Math.min(90, p.T + 0.3);
else p.T = Math.max(20, p.T - 0.04);
/* поле скорости: тёплая частица в нижней половине → вверх,
холодная в верхней → вниз. По краям движение в обратные стороны. */
const cx = vesselX + vesselW/2;
const dx = p.x - cx;
const yNorm = (p.y - vesselY) / vesselH; /* 0 верх .. 1 низ */
/* основной вертикальный поток: вверх у центра, вниз у стенок */
if(Math.abs(dx) < vesselW*0.25) p.vy = -0.9 * (p.T - 20)/70 - 0.2;
else p.vy = 0.6 * (1 - yNorm)*0.8 + 0.2;
/* горизонтальное замыкание петли */
if(yNorm < 0.18) p.vx = (dx > 0 ? 1 : -1) * 0.8;
else if(yNorm > 0.82) p.vx = (dx < 0 ? 1 : -1) * 0.8;
else p.vx *= 0.85;
} else {
p.T = Math.max(20, p.T - 0.1);
p.vx *= 0.94; p.vy *= 0.94;
}
p.x += p.vx; p.y += p.vy;
/* стенки */
if(p.x < vesselX+6){ p.x = vesselX+6; p.vx = Math.abs(p.vx); }
if(p.x > vesselX+vesselW-6){ p.x = vesselX+vesselW-6; p.vx = -Math.abs(p.vx); }
if(p.y < vesselY+6){ p.y = vesselY+6; p.vy = Math.abs(p.vy); }
if(p.y > vesselY+vesselH-6){ p.y = vesselY+vesselH-6; p.vy = -Math.abs(p.vy); }
}
let s = '';
/* сосуд */
s += '<rect x="'+vesselX+'" y="'+vesselY+'" width="'+vesselW+'" height="'+vesselH+'" fill="#e0f2fe" stroke="#0f172a" stroke-width="2" rx="6"/>';
/* нагреватель снизу */
s += '<rect x="'+(vesselX-4)+'" y="'+(vesselY+vesselH+4)+'" width="'+(vesselW+8)+'" height="18" fill="'+(heaterOn?'#dc2626':'#94a3b8')+'" stroke="#0f172a" stroke-width="1.5" rx="4"/>';
s += '<text x="'+(vesselX+vesselW/2)+'" y="'+(vesselY+vesselH+17)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#fff">'+(heaterOn?'НАГРЕВАТЕЛЬ ВКЛ':'выкл')+'</text>';
/* частицы */
for(const p of ps){
const c = window.PHYS.tempColor(p.T, 20, 90);
s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="5" fill="'+c+'" stroke="#0f172a" stroke-width="0.5"/>';
}
/* стрелки потока (декоративные) */
if(heaterOn){
const cx = vesselX + vesselW/2;
s += window.PHYS.drawArrow(cx, vesselY+vesselH-20, cx, vesselY+30, '#dc2626', 2, 9);
s += window.PHYS.drawArrow(vesselX+18, vesselY+30, vesselX+18, vesselY+vesselH-20, '#2563eb', 2, 9);
s += window.PHYS.drawArrow(vesselX+vesselW-18, vesselY+30, vesselX+vesselW-18, vesselY+vesselH-20, '#2563eb', 2, 9);
}
svg.innerHTML = s;
_SIMS.p4sim.raf = requestAnimationFrame(tick);
}
_SIMS.p4sim = { raf: 0 };
_SIMS.p4sim.raf = requestAnimationFrame(tick);
document.getElementById('p4-on').addEventListener('click', ()=>{
heaterOn = !heaterOn;
document.getElementById('p4-on').textContent = heaterOn ? 'Выключить нагрев' : 'Включить нагрев';
});
document.getElementById('p4-reset').addEventListener('click', ()=>{
heaterOn = false;
document.getElementById('p4-on').textContent = 'Включить нагрев';
for(let i=0;i<N;i++){
ps[i].x = vesselX + 10 + Math.random()*(vesselW-20);
ps[i].y = vesselY + 10 + Math.random()*(vesselH-20);
ps[i].vx = 0; ps[i].vy = 0; ps[i].T = 20;
}
});
}
function _initP4_quiz(){
const QS = [
{sit:'Воздух в комнате с радиатором у стены', ans:'Y', why:'Тёплый воздух поднимается, холодный спускается — классическая конвекция.'},
{sit:'Вода в кастрюле, нагреваемой снизу', ans:'Y', why:'Нагрев снизу + жидкость = конвекционный поток.'},
{sit:'Камень во время летнего полудня', ans:'N', why:'Камень — твёрдое тело, частицы не движутся, конвекции нет.'},
{sit:'Вода в сосуде, нагреваемом сверху', ans:'N', why:'Горячий слой уже наверху — он там и останется, потоков нет.'},
{sit:'Атмосфера планеты', ans:'Y', why:'Газ + разность температур (день/ночь, экватор/полюса) = ветры.'},
{sit:'Металлический стержень, нагреваемый с конца', ans:'N', why:'Это твёрдое тело — теплопроводность, а не конвекция.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p4-quiz'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin:8px 0;line-height:1.5">'+q.sit+'</div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
+'<button class="btn" data-pick="Y" style="padding:14px"><b>Да, конвекция</b></button>'
+'<button class="btn" data-pick="N" style="padding:14px"><b>Нет, не конвекция</b></button>'
+'</div>'
+'<div class="feedback" id="p4-quiz-fb"></div>';
document.getElementById('p4-quiz-r').textContent = (i+1);
document.getElementById('p4-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
const fb = document.getElementById('p4-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p4-quiz'); bumpProgress('p4', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p4-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p4-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP4_dnd(){
const items = [
{id:'rad', cat:'yes', html:'воздух у радиатора'},
{id:'ket', cat:'yes', html:'вода в кипящем чайнике'},
{id:'oce', cat:'yes', html:'течения в океане'},
{id:'wnd', cat:'yes', html:'ветер в атмосфере'},
{id:'sto', cat:'no', html:'нагрев стального стержня'},
{id:'top', cat:'no', html:'вода, нагреваемая сверху'},
{id:'vac', cat:'no', html:'жидкость в невесомости'},
{id:'wal', cat:'no', html:'кирпичная стена'}
];
const dnd = setupSorter({ poolId:'p4-dnd-pool', scopeSelector:'#sec-p4', cats:['yes','no'], items, columnLayout:false });
document.getElementById('p4-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p4-dnd-fb');
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='&#10003; Идеально! +15 XP. Конвекция требует текучей среды + разности температур + гравитации.'; addXp(15,'p4-dnd'); bumpProgress('p4', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. В твёрдых телах и при нагреве сверху конвекции нет.'; }
});
document.getElementById('p4-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p4-dnd-fb'); fb.style.display='none'; });
}
function _initP4_mcq(){
const QS = [
{q:'Что переносится при конвекции?', opts:['Только энергия','Только вещество','Вещество вместе с энергией','Электромагнитные волны'], ans:2, why:'Жидкость или газ движется вместе с теплотой.'},
{q:'Почему в твёрдых телах нет конвекции?', opts:['Они холодные','Молекулы не могут свободно перемещаться','Они не проводят тепло','Они отражают энергию'], ans:1, why:'В твёрдом теле молекулы прочно связаны и не образуют потоков.'},
{q:'Как направлен поток в нагреваемой снизу жидкости?', opts:['Горячий вниз, холодный вверх','Горячий вверх, холодный вниз','Хаотически','Только по краям'], ans:1, why:'Нагретая часть менее плотная — всплывает.'},
{q:'Зачем радиатор ставят под окном?', opts:['Чтобы не мешал','Чтобы тёплый воздух поднимался, не пропуская холодного с улицы','Так красивее','Для экономии труб'], ans:1, why:'Восходящий тёплый поток сразу отсекает падающий холодный.'},
{q:'Возможна ли конвекция в открытом космосе?', opts:['Да, как на Земле','Только в атмосфере планет','Нет, нужна гравитация и среда','Только у звёзд'], ans:2, why:'В вакууме нет среды; в невесомости нет всплывания.'},
{q:'Куда направить пламя свечи, чтобы быстрее закипела вода в стакане?', opts:['Сверху','Снизу','Сбоку','Не имеет значения'], ans:1, why:'Нагрев снизу запускает конвекцию — вода прогреется быстрее.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p4-mcq'); if(!wrap) return;
let h = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div>';
h += '<div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt, k)=>{ h += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+(String.fromCharCode(65+k))+'. '+opt+'</button>'; });
h += '</div><div class="feedback" id="p4-mcq-fb"></div><div class="actions"><button class="btn" id="p4-mcq-next">Следующий вопрос</button></div>';
wrap.innerHTML = h;
document.getElementById('p4-mcq-i').textContent = (i+1);
document.getElementById('p4-mcq-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p4-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p4-mcq'); bumpProgress('p4', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p4-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf = document.getElementById('p4-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').'; addXp(15,'p4-mcq-bonus'); bumpProgress('p4', 15); }, 600); }
});
});
const nb = document.getElementById('p4-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §5 — Излучение ======== */
function build_p5(){
const box = document.getElementById('p5-body');
let h = '';
h += makeCard('theory', 'Что такое тепловое излучение', '§ 5.1',
'<p><b>Излучение</b> — передача энергии электромагнитными волнами. Не требует среды: идёт через вакуум.</p>'
+'<p>Любое тело с температурой выше абсолютного нуля излучает. Чем выше $T$, тем интенсивнее излучение и тем более коротковолновое (горячая печь — инфракрасный диапазон; Солнце — видимый свет).</p>'
+'<p>Так Солнце греет Землю, прошив 150 млн км вакуума за 8 минут.</p>'
);
h += makeCard('rule', 'Поглощение и отражение', '§ 5.2',
'<ul style="padding-left:20px;margin:4px 0">'
+'<li><b>Тёмные</b> и шероховатые поверхности — <b>хорошо поглощают</b> излучение (нагреваются быстрее).</li>'
+'<li><b>Светлые</b> и зеркальные — <b>отражают</b> излучение (нагреваются медленнее).</li>'
+'<li>Те же тёмные поверхности и <b>излучают</b> сильнее: чёрный чайник остывает быстрее.</li>'
+'</ul>'
+'<p style="margin-top:6px"><b>Правило:</b> что хорошо поглощает, то хорошо и излучает.</p>'
);
h += makeCard('example', 'Применение', '§ 5.3',
'<ul style="padding-left:20px;margin:4px 0">'
+'<li>Чёрные машины в жару раскаляются сильнее белых.</li>'
+'<li>Зеркальные стенки термоса уменьшают потерю излучения.</li>'
+'<li>Космические корабли покрывают фольгой, чтобы отражать солнечное излучение.</li>'
+'<li>Тёмная футболка летом — жарко; белая — прохладно.</li>'
+'</ul>'
);
/* IV1 — солнечный нагрев чёрного и белого */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Чёрное и белое под Солнцем</div></div>'
+'<div class="wg-help">Включи Солнце. Чёрная пластина <b>поглощает</b> излучение и быстро нагревается, белая <b>отражает</b> и нагревается медленно.</div>'
+'<svg id="p5-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="actions" style="margin-top:8px"><button class="btn primary" id="p5-on">Включить Солнце</button><button class="btn" id="p5-reset">Сброс</button></div>'
+'<div class="score-display" style="margin-top:10px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>Чёрная пластина: $T = $ <b id="p5-tb">20</b> &#176;C</span>'
+'<span>Белая пластина: $T = $ <b id="p5-tw">20</b> &#176;C</span>'
+'</div>'
+'</div>';
/* IV2 — квикфайр «правда/ложь» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Правда или ложь?</div></div>'
+'<div class="wg-help">Прочитай утверждение и реши, правдиво оно или нет.</div>'
+'<div id="p5-quiz"></div>'
+'<div class="actions"><button class="btn" id="p5-quiz-next">Следующее</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p5-quiz-r">1</b> / 7</span><span>Правильно: <b id="p5-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD 3 вида теплопередачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">3 вида теплопередачи</div></div>'
+'<div class="wg-help">Сортируй примеры на три группы: проводность, конвекция, излучение.</div>'
+'<div id="p5-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-top:10px">'
+'<div class="drop-box"><h5>Проводность</h5><div class="drop-items" data-cat="cond"></div></div>'
+'<div class="drop-box"><h5>Конвекция</h5><div class="drop-items" data-cat="conv"></div></div>'
+'<div class="drop-box"><h5>Излучение</h5><div class="drop-items" data-cat="rad"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p5-dnd-check">Проверить</button><button class="btn" id="p5-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p5-dnd-fb"></div>'
+'</div>';
/* IV4 — MCQ */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 вопросов</div></div>'
+'<div class="wg-help">4+ правильных — +15 XP.</div>'
+'<div id="p5-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p5-mcq-i">1</b> / 6</span><span>Правильно: <b id="p5-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p5') + readButton('p5');
renderMath(box);
wireReadBtn('p5');
_initP5_sim();
_initP5_quiz();
_initP5_dnd();
_initP5_mcq();
}
function _initP5_sim(){
_killSim('p5sim');
const svg = document.getElementById('p5-sim'); if(!svg) return;
const W=460, H=240;
let sunOn = false;
let tB = 20, tW = 20; /* black, white plate temps */
function reset(){ tB=20; tW=20; sunOn=false; document.getElementById('p5-on').textContent='Включить Солнце'; document.getElementById('p5-tb').textContent='20'; document.getElementById('p5-tw').textContent='20'; }
function tick(){
if(!_isVisible('p5')){ _SIMS.p5sim.raf = requestAnimationFrame(tick); return; }
if(sunOn){
/* чёрная: поглощает 0.92, белая: 0.15 */
if(tB < 75) tB += 0.32;
if(tW < 35) tW += 0.05;
} else {
if(tB > 20) tB -= 0.10;
if(tW > 20) tW -= 0.10;
}
document.getElementById('p5-tb').textContent = tB.toFixed(0);
document.getElementById('p5-tw').textContent = tW.toFixed(0);
/* draw */
let s = '';
/* Солнце */
const cx = 230, cy = 36;
s += '<circle cx="'+cx+'" cy="'+cy+'" r="22" fill="'+(sunOn?'#fbbf24':'#cbd5e1')+'" stroke="#0f172a" stroke-width="1.5"/>';
if(sunOn){
for(let i=0;i<12;i++){
const a = i*Math.PI/6;
const x1 = cx + 26*Math.cos(a), y1 = cy + 26*Math.sin(a);
const x2 = cx + 36*Math.cos(a), y2 = cy + 36*Math.sin(a);
s += '<line x1="'+x1.toFixed(1)+'" y1="'+y1.toFixed(1)+'" x2="'+x2.toFixed(1)+'" y2="'+y2.toFixed(1)+'" stroke="#fbbf24" stroke-width="2.5" stroke-linecap="round"/>';
}
}
/* Лучи к пластинам */
if(sunOn){
const ray = (x1,y1,x2,y2,col)=>'<line x1="'+x1+'" y1="'+y1+'" x2="'+x2+'" y2="'+y2+'" stroke="'+col+'" stroke-width="1.6" stroke-dasharray="6 4" opacity="0.8"/>';
/* к чёрной */
s += ray(cx-22, cy+18, 120, 175, '#fbbf24');
s += ray(cx-10, cy+22, 145, 175, '#fbbf24');
s += ray(cx, cy+22, 170, 175, '#fbbf24');
/* к белой */
s += ray(cx+22, cy+18, 340, 175, '#fbbf24');
s += ray(cx+10, cy+22, 315, 175, '#fbbf24');
s += ray(cx, cy+22, 290, 175, '#fbbf24');
/* отражённые от белой (вверх) */
s += '<line x1="290" y1="172" x2="270" y2="100" stroke="#cbd5e1" stroke-width="1.4" stroke-dasharray="4 4" opacity="0.7"/>';
s += '<line x1="315" y1="172" x2="310" y2="100" stroke="#cbd5e1" stroke-width="1.4" stroke-dasharray="4 4" opacity="0.7"/>';
s += '<line x1="340" y1="172" x2="350" y2="100" stroke="#cbd5e1" stroke-width="1.4" stroke-dasharray="4 4" opacity="0.7"/>';
}
/* чёрная пластина */
const cB = sunOn ? window.PHYS.tempColor(tB, 20, 80) : '#1f2937';
s += '<rect x="100" y="175" width="100" height="40" fill="'+cB+'" stroke="#0f172a" stroke-width="2" rx="3"/>';
s += '<text x="150" y="200" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="'+(tB>50?'#fff':'#0f172a')+'">'+tB.toFixed(0)+' &#176;C</text>';
s += '<text x="150" y="230" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#0f172a">ЧЁРНАЯ</text>';
/* белая пластина */
s += '<rect x="270" y="175" width="100" height="40" fill="#f1f5f9" stroke="#0f172a" stroke-width="2" rx="3"/>';
s += '<text x="320" y="200" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#0f172a">'+tW.toFixed(0)+' &#176;C</text>';
s += '<text x="320" y="230" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#0f172a">БЕЛАЯ</text>';
svg.innerHTML = s;
_SIMS.p5sim.raf = requestAnimationFrame(tick);
}
_SIMS.p5sim = { raf: 0 };
_SIMS.p5sim.raf = requestAnimationFrame(tick);
document.getElementById('p5-on').addEventListener('click', ()=>{
sunOn = !sunOn;
document.getElementById('p5-on').textContent = sunOn ? 'Выключить Солнце' : 'Включить Солнце';
});
document.getElementById('p5-reset').addEventListener('click', reset);
}
function _initP5_quiz(){
const QS = [
{st:'Излучение требует среды (например, воздуха) для распространения.', ans:'F', why:'Излучение проходит через вакуум, что и доказывает солнечный свет.'},
{st:'Тёмные тела сильнее нагреваются под Солнцем, чем светлые.', ans:'T', why:'Тёмная поверхность поглощает большую долю излучения.'},
{st:'Любое тело с $T > 0$ К излучает.', ans:'T', why:'Тепловое излучение есть всегда, чем горячее — тем сильнее.'},
{st:'Чем горячее тело, тем меньше излучения оно отдаёт.', ans:'F', why:'Наоборот: интенсивность излучения резко растёт с температурой.'},
{st:'Хороший поглотитель — хороший излучатель.', ans:'T', why:'Это правило Кирхгофа: $a = \\varepsilon$.'},
{st:'Зеркальная поверхность плохо излучает.', ans:'T', why:'Зеркало почти всё отражает — а значит и излучает мало.'},
{st:'Излучение нельзя обнаружить, если тело не светится видимым светом.', ans:'F', why:'Тело при комнатной температуре излучает инфракрасные волны — их видит тепловизор.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p5-quiz'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin:8px 0;line-height:1.5">"'+q.st+'"</div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
+'<button class="btn" data-pick="T" style="padding:14px"><b>Правда</b></button>'
+'<button class="btn" data-pick="F" style="padding:14px"><b>Ложь</b></button>'
+'</div>'
+'<div class="feedback" id="p5-quiz-fb"></div>';
document.getElementById('p5-quiz-r').textContent = (i+1);
document.getElementById('p5-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
const fb = document.getElementById('p5-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p5-quiz'); bumpProgress('p5', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p5-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p5-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP5_dnd(){
const items = [
{id:'spoon', cat:'cond', html:'нагрев ложки в супе'},
{id:'bar', cat:'cond', html:'нагрев конца железного прута'},
{id:'iron', cat:'cond', html:'утюг гладит бельё'},
{id:'rad', cat:'conv', html:'батарея греет комнату'},
{id:'ket', cat:'conv', html:'вода в кипящем чайнике'},
{id:'wnd', cat:'conv', html:'ветры в атмосфере'},
{id:'sun', cat:'rad', html:'Солнце греет Землю'},
{id:'fire', cat:'rad', html:'тепло от костра на расстоянии'},
{id:'lamp', cat:'rad', html:'нагрев руки под лампой накаливания'}
];
const dnd = setupSorter({ poolId:'p5-dnd-pool', scopeSelector:'#sec-p5', cats:['cond','conv','rad'], items, columnLayout:false });
document.getElementById('p5-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p5-dnd-fb');
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='&#10003; Идеально! +20 XP. Ты освоил все 3 вида теплопередачи.'; addXp(20,'p5-dnd'); bumpProgress('p5', 25); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Проверь: контакт = проводность; потоки = конвекция; через пустоту = излучение.'; }
});
document.getElementById('p5-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p5-dnd-fb'); fb.style.display='none'; });
}
function _initP5_mcq(){
const QS = [
{q:'Через какую среду НЕ может идти излучение?', opts:['Через воздух','Через стекло','Через вакуум','Излучение проходит через всё перечисленное'], ans:3, why:'Излучение распространяется и в среде, и в вакууме.'},
{q:'Какая поверхность нагреется сильнее на Солнце?', opts:['Зеркальная','Белая','Чёрная','Прозрачная'], ans:2, why:'Чёрная поглощает больше всего излучения.'},
{q:'Какие тела излучают?', opts:['Только горячие','Только светящиеся','Любые с $T > 0$ К','Только Солнце'], ans:2, why:'Тепловое излучение существует у любого тела с ненулевой температурой.'},
{q:'Какой вид теплопередачи переносит тепло от Солнца к Земле?', opts:['Теплопроводность','Конвекция','Излучение','Все три'], ans:2, why:'Между Солнцем и Землёй вакуум — работает только излучение.'},
{q:'Чем покрашен правильный термос изнутри?', opts:['Чёрной краской','Зеркальной плёнкой','Деревом','Стеклом'], ans:1, why:'Зеркальная поверхность плохо излучает — тепло сохраняется.'},
{q:'Чёрная футболка в жару нагревается потому что…', opts:['легче белой','лучше поглощает излучение','быстрее остывает','тоньше'], ans:1, why:'Тёмная ткань поглощает большую часть солнечного излучения.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p5-mcq'); if(!wrap) return;
let h = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div>';
h += '<div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt, k)=>{ h += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+(String.fromCharCode(65+k))+'. '+opt+'</button>'; });
h += '</div><div class="feedback" id="p5-mcq-fb"></div><div class="actions"><button class="btn" id="p5-mcq-next">Следующий вопрос</button></div>';
wrap.innerHTML = h;
document.getElementById('p5-mcq-i').textContent = (i+1);
document.getElementById('p5-mcq-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p5-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p5-mcq'); bumpProgress('p5', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p5-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf = document.getElementById('p5-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').'; addXp(15,'p5-mcq-bonus'); bumpProgress('p5', 15); }, 600); }
});
});
const nb = document.getElementById('p5-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);