feat(phys8 ch2): Phase 3 Wave 4 — §26 P=UI/Джоуль-Ленц + §27 электроэнергия

§26 Работа и мощность тока:
- 3 теории: A=UIt, P=UI=I²R=U²/R, закон Джоуля-Ленца
- IV-1: калькулятор + анимация нагрева резистора по tempColor,
  цвет меняется от синего до красного в зависимости от P,
  glow при высокой мощности
- IV-2: 5 раундов «какую формулу использовать?»
- IV-3: DnD 5 приборов по возрастанию P (LED → автомобиль)
- IV-4: 6 числовых задач

§27 Электроэнергия. Безопасность:
- 3 теории: кВт·ч, экономия, правила ТБ
- IV-1: ГЛАВНЫЙ ВИЗУАЛ — счётчик за месяц: 4 прибора (лампа, ТВ,
  чайник, холодильник) + slider'ы часов/день и тарифа,
  показывает кВт·ч и руб + «самый прожорливый прибор»
- IV-2: 6 ситуаций «безопасно/опасно»
- IV-3: DnD 8 ситуаций «экономит/расходует»
- IV-4: 5 задач (включая обогреватель за месяц)

Со Phase 3 завершён: §19-27 (постоянный ток, 9 параграфов).
Phase 4 → §28-31 + Финал главы 2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-29 23:46:36 +03:00
parent a1b57d8936
commit 24d7f2b1d9
+450 -6
View File
@@ -408,8 +408,21 @@ const SIDEBARS = {
["Два равных","$R_{общ} = R/2$"],
["Поломка одного","остальные работают"]
]},
p26:{title:"Шпаргалка § 26",rows:[["В разработке","Phase 3 Wave 4"]]},
p27:{title:"Шпаргалка § 27",rows:[["В разработке","Phase 3 Wave 4"]]},
p26:{title:"Шпаргалка § 26",rows:[
["Работа тока","$A = U I t$"],
["Мощность","$P = U I = A/t$"],
["Также","$P = U^2/R = I^2 R$"],
["Джоуль-Ленц","$Q = I^2 R t$"],
["[P]","Вт"],
["1 кВт·ч","$3{,}6 \\cdot 10^6$ Дж"]
]},
p27:{title:"Шпаргалка § 27",rows:[
["Энергия","$W = P \\tau$ (кВт·ч)"],
["Стоимость","$\\text{руб} = W \\cdot \\text{тариф}$"],
["1-фазная розетка","220 В, 16 А max"],
["ТБ","не голыми руками!"],
["Заземление","для защиты"]
]},
p28:{title:"Шпаргалка § 28",rows:[["В разработке","Phase 4 Wave 1"]]},
p29:{title:"Шпаргалка § 29",rows:[["В разработке","Phase 4 Wave 1"]]},
p30:{title:"Шпаргалка § 30",rows:[["В разработке","Phase 4 Wave 2"]]},
@@ -432,8 +445,8 @@ const TIPS=[
{sec:'p23',html:"Сопротивление провода зависит от трёх вещей: <b>материала</b> ($\\rho$), <b>длины</b> ($l$) и <b>толщины</b> ($S$). Длинный тонкий нихром — большое $R$ (спираль чайника), короткий толстый медный — маленькое $R$ (провод)."},
{sec:'p24',html:"При последовательном соединении ток течёт через все элементы один и тот же. Напряжения складываются. Сопротивления складываются. Реостат — переменный резистор, при сдвиге движка меняется длина включённой проволоки."},
{sec:'p25',html:"При параллельном соединении на каждой ветви одно и то же напряжение. Токи складываются. Сопротивление считается по особой формуле — в итоге $R_{общ}$ <b>меньше</b> любого из $R_1$, $R_2$."},
{sec:'p26',html:"Параграф § 26 будет реализован в Phase 3 Wave 4. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p27',html:"Параграф § 27 будет реализован в Phase 3 Wave 4. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p26',html:"$A = UIt$ — энергия, которую ток отдаёт прибору. $P = UI$ — это мощность (Дж/с = Вт). В резисторе вся эта энергия превращается в тепло — это <b>закон Джоуля-Ленца</b>: $Q = I^2 R t$."},
{sec:'p27',html:"Счётчик в квартире меряет энергию в <b>кВт·ч</b>: 1 кВт·ч — это работа за 1 час мощностью 1 кВт. Стоимость = энергия × тариф. ТБ: розетка под 220 В может ударить смертельным током."},
{sec:'p28',html:"Параграф § 28 будет реализован в Phase 4 Wave 1. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p29',html:"Параграф § 29 будет реализован в Phase 4 Wave 1. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p30',html:"Параграф § 30 будет реализован в Phase 4 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
@@ -456,8 +469,8 @@ const BUILDERS = {
p23: ()=>{ build_p23(); },
p24: ()=>{ build_p24(); },
p25: ()=>{ build_p25(); },
p26: ()=>{ const box=document.getElementById('p26-body'); box.innerHTML = buildStub('p26', 'Работа и мощность электрического тока. Закон Джоуля — Ленца', 'Phase 3 Wave 4') + secNavFor('p26') + readButton('p26'); renderMath(box); wireReadBtn('p26'); },
p27: ()=>{ const box=document.getElementById('p27-body'); box.innerHTML = buildStub('p27', 'Использование и экономия электроэнергии. Безопасность', 'Phase 3 Wave 4') + secNavFor('p27') + readButton('p27'); renderMath(box); wireReadBtn('p27'); },
p26: ()=>{ build_p26(); },
p27: ()=>{ build_p27(); },
p28: ()=>{ const box=document.getElementById('p28-body'); box.innerHTML = buildStub('p28', 'Постоянные магниты', 'Phase 4 Wave 1') + secNavFor('p28') + readButton('p28'); renderMath(box); wireReadBtn('p28'); },
p29: ()=>{ const box=document.getElementById('p29-body'); box.innerHTML = buildStub('p29', 'Магнитное поле', 'Phase 4 Wave 1') + secNavFor('p29') + readButton('p29'); renderMath(box); wireReadBtn('p29'); },
p30: ()=>{ const box=document.getElementById('p30-body'); box.innerHTML = buildStub('p30', 'Магнитное поле тока', 'Phase 4 Wave 2') + secNavFor('p30') + readButton('p30'); renderMath(box); wireReadBtn('p30'); },
@@ -3893,6 +3906,437 @@ function _initP25_tasks(){
render();
}
/* ======================================================================
PHASE 3 · WAVE 4 — §26, §27
====================================================================== */
/* ======== §26 — Работа и мощность тока. Джоуль-Ленц ======== */
function build_p26(){
const box = document.getElementById('p26-body');
let h = '';
h += makeCard('theory', 'Работа тока', '§ 26.1',
'<p>Электрическое поле, прогоняя заряд через проводник, совершает работу. За время $t$ через сечение проходит заряд $q = It$. На каждый кулон поле совершает работу $U$ Джоулей. Значит:</p>'
+'<p style="text-align:center;margin:8px 0">$$A = U I t$$</p>'
+'<p>Это <b>работа электрического тока</b> — энергия, которую ток отдаёт прибору. Она превращается в тепло, свет, движение, звук — в зависимости от прибора.</p>'
);
h += makeCard('rule', 'Мощность тока', '§ 26.2',
'<p><b>Мощность</b> $P$ — работа за единицу времени:</p>'
+'<p style="text-align:center;margin:8px 0">$$P = \\dfrac{A}{t} = U I$$</p>'
+'<p>Эквиваленты (используя закон Ома):</p>'
+'<p style="text-align:center;margin:8px 0">$$P = U I = I^2 R = \\dfrac{U^2}{R}$$</p>'
+'<p>Единица: 1 <b>Ватт</b> (Вт) — это 1 Дж/с. Названа в честь Джеймса Уатта.</p>'
);
h += makeCard('example', 'Закон Джоуля — Ленца', '§ 26.3',
'<p>В резисторе вся электрическая энергия превращается в <b>тепло</b>:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q = I^2 R t$$</p>'
+'<p>Это <b>закон Джоуля-Ленца</b>. Поэтому работают спирали чайника, утюга, кипятильника. Чем больше $I$ или $R$, тем больше тепла.</p>'
+'<p>В лампе накаливания нить нагревается до $\\sim 2500$ &#176;C и светится. КПД у такой лампы низкий — 5%; остальное тепло.</p>'
);
/* IV1 — калькулятор P+анимация нагрева */
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">Меняй $U$ и $R$ — увидь мощность и как нагревается резистор.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$U$, В: <b id="p26-uv">220</b><input type="range" id="p26-u" min="1" max="240" step="1" value="220"></label>'
+'<label>$R$, Ом: <b id="p26-rv">100</b><input type="range" id="p26-r" min="1" max="500" step="1" value="100"></label>'
+'</div>'
+'<svg id="p26-sim" viewBox="0 0 460 140" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="score-display" style="margin-top:10px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$I = U/R$ = <b id="p26-ic">2.2</b> А</span>'
+'<span>$P = UI$ = <b id="p26-pc">484</b> Вт</span>'
+'<span>$Q$ за 1 минуту = <b id="p26-q1">29.0</b> кДж</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">Даны величины — выбери удобную формулу для $P$.</div>'
+'<div id="p26-quiz"></div>'
+'<div class="actions"><button class="btn" id="p26-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p26-quiz-r">1</b> / 5</span><span>Правильно: <b id="p26-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="p26-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin-top:10px">'
+'<div class="drop-box"><h5>1. Меньше</h5><div class="drop-items" data-cat="r1"></div></div>'
+'<div class="drop-box"><h5>2</h5><div class="drop-items" data-cat="r2"></div></div>'
+'<div class="drop-box"><h5>3</h5><div class="drop-items" data-cat="r3"></div></div>'
+'<div class="drop-box"><h5>4</h5><div class="drop-items" data-cat="r4"></div></div>'
+'<div class="drop-box"><h5>5. Больше</h5><div class="drop-items" data-cat="r5"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p26-dnd-check">Проверить</button><button class="btn" id="p26-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p26-dnd-fb"></div>'
+'</div>';
/* IV4 — задачи */
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="p26-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p26-task-i">1</b> / 6</span><span>Правильно: <b id="p26-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p26') + readButton('p26');
renderMath(box);
wireReadBtn('p26');
_initP26_sim();
_initP26_quiz();
_initP26_dnd();
_initP26_tasks();
}
function _initP26_sim(){
const svg = document.getElementById('p26-sim'); if(!svg) return;
function update(){
const U = +document.getElementById('p26-u').value;
const R = +document.getElementById('p26-r').value;
document.getElementById('p26-uv').textContent = U;
document.getElementById('p26-rv').textContent = R;
const I = U/R;
const P = U*I;
const Q1 = P * 60; /* за 1 мин */
document.getElementById('p26-ic').textContent = I.toFixed(2);
document.getElementById('p26-pc').textContent = P.toFixed(0);
document.getElementById('p26-q1').textContent = (Q1/1000).toFixed(1);
/* SVG: резистор + цвет нагрева */
let s = '';
s += window.PHYS.batteryEMF(60, 70, U+' В', 'h');
s += window.PHYS.wire(60, 52, 60, 30);
s += window.PHYS.wire(60, 30, 200, 30);
/* резистор */
const tCol = window.PHYS.tempColor(Math.min(150, P/8), 20, 150);
s += '<rect x="200" y="20" width="80" height="20" fill="'+tCol+'" stroke="#0f172a" stroke-width="1.8" rx="3"/>';
s += '<text x="240" y="34" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="'+(P>500?'#fff':'#0f172a')+'">R = '+R+' Ом</text>';
/* glow при высокой мощности */
if(P > 100){
const glow = Math.min(1, P/2000);
s += '<rect x="200" y="20" width="80" height="20" fill="#fbbf24" opacity="'+(glow*0.5).toFixed(2)+'" rx="3"/>';
}
s += window.PHYS.wire(280, 30, 400, 30);
s += window.PHYS.wire(400, 30, 400, 70);
s += window.PHYS.wire(80, 70, 400, 70);
/* подпись P */
s += '<text x="240" y="65" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">P = '+P.toFixed(0)+' Вт</text>';
s += '<text x="240" y="100" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">за 1 мин: Q = '+(Q1/1000).toFixed(1)+' кДж</text>';
s += '<text x="240" y="118" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#475569">'+(P<60 ? 'слабый разогрев' : P<500 ? 'тёплый' : P<1500 ? 'горячо' : 'опасно может оплавиться')+'</text>';
svg.innerHTML = s;
}
document.getElementById('p26-u').addEventListener('input', update);
document.getElementById('p26-r').addEventListener('input', update);
update();
}
function _initP26_quiz(){
const QS = [
{sit:'Дано $U = 220$ В, $I = 5$ А. Какая формула?', opts:['$P = UI$','$P = I^2R$','$P = U^2/R$','любая'], ans:0, why:'Когда дано $U$ и $I$ — прямой подсчёт через $UI$.'},
{sit:'Дано $I = 3$ А, $R = 50$ Ом. Какая?', opts:['$P = UI$','$P = I^2R$','$P = U^2/R$','нельзя'], ans:1, why:'Когда дано $I$ и $R$ — $I^2R$.'},
{sit:'Дано $U = 220$ В, $R = 100$ Ом. Какая?', opts:['$P = UI$','$P = I^2R$','$P = U^2/R$','нельзя'], ans:2, why:'$U^2/R$ удобнее.'},
{sit:'$P$ нагревательного прибора 1500 Вт при $U = 220$ В. Какой ток?', opts:['$I = P/U \\approx 6{,}8$ А','$I = U/R$','$I = UP$','нельзя'], ans:0, why:'$I = P/U = 1500/220 \\approx 6{,}82$ А.'},
{sit:'$Q$ за 10 с в резисторе $R$ при токе $I$. Какая формула?', opts:['$Q = It$','$Q = UI/t$','$Q = I^2 R t$','$Q = U^2/R$'], ans:2, why:'Закон Джоуля-Ленца.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p26-quiz'); if(!wrap) return;
let html = '<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.sit+'</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p26-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p26-quiz-r').textContent = (i+1);
document.getElementById('p26-quiz-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('p26-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p26-quiz'); bumpProgress('p26', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p26-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p26-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP26_dnd(){
const items = [
{id:'led', cat:'r1', html:'светодиод (~0,1 Вт)'},
{id:'ph', cat:'r2', html:'телефон-зарядка (~5 Вт)'},
{id:'lamp',cat:'r3', html:'лампа накаливания (60 Вт)'},
{id:'ket', cat:'r4', html:'чайник (2000 Вт)'},
{id:'car', cat:'r5', html:'электромобиль (10 000 Вт)'}
];
const dnd = setupSorter({ poolId:'p26-dnd-pool', scopeSelector:'#sec-p26', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p26-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p26-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,'p26-dnd'); bumpProgress('p26', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p26-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p26-dnd-fb'); fb.style.display='none'; });
}
function _initP26_tasks(){
const TASKS = [
{q:'$U = 220$ В, $I = 0{,}5$ А. Найди $P$ (Вт).', ans:110, tol:1, why:'$P = UI = 220 \\cdot 0{,}5 = 110$ Вт.'},
{q:'Лампа 60 Вт работает 5 часов. Какая работа $A$ (Дж)?', ans:1080000, tol:5000, why:'$A = Pt = 60 \\cdot 18\\,000 = 1{,}08 \\cdot 10^6$ Дж.'},
{q:'$I = 2$ А, $R = 50$ Ом, $t = 60$ с. Сколько $Q$ выделится (кДж)?', ans:12, tol:0.3, why:'$Q = I^2 R t = 4 \\cdot 50 \\cdot 60 = 12\\,000$ Дж = $12$ кДж.'},
{q:'$P = 2000$ Вт работают 30 мин. Сколько $A$ в кВт·ч?', ans:1, tol:0.05, why:'$A = 2 \\cdot 0{,}5 = 1$ кВт·ч.'},
{q:'Чайник 1500 Вт, $U = 220$ В. Какой ток (А, до сотых)?', ans:6.82, tol:0.1, why:'$I = P/U = 1500/220 \\approx 6{,}82$ А.'},
{q:'$P = 100$ Вт, $R = 484$ Ом. Найди $U$ (В).', ans:220, tol:3, why:'$U = \\sqrt{PR} = \\sqrt{48\\,400} = 220$ В.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p26-task'); if(!wrap) return;
wrap.innerHTML =
'<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> '+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p26-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p26-task-go">Ответ</button>'
+'<button class="btn" id="p26-task-hint">Подсказка</button>'
+'<button class="btn" id="p26-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p26-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p26-task-fb"></div>';
document.getElementById('p26-task-i').textContent = (i+1);
document.getElementById('p26-task-ok').textContent = ok;
document.getElementById('p26-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p26-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p26-task-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
done++;
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(4,'p26-task'); bumpProgress('p26', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p26-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p26-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p26-task-bonus'); bumpProgress('p26', 15); }, 600); }
});
document.getElementById('p26-task-hint').addEventListener('click', ()=>{ document.getElementById('p26-task-hint-txt').classList.toggle('show'); });
document.getElementById('p26-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §27 — Электроэнергия. Безопасность ======== */
function build_p27(){
const box = document.getElementById('p27-body');
let h = '';
h += makeCard('theory', 'Энергия в быту: кВт·ч', '§ 27.1',
'<p>В квартире электросчётчик измеряет потреблённую энергию в <b>киловатт-часах (кВт·ч)</b>.</p>'
+'<p>$1$ кВт·ч — работа за 1 час прибором мощностью 1 кВт = 1000 Вт.</p>'
+'<p style="text-align:center;margin:8px 0">$$1 \\text{ кВт·ч} = 3{,}6 \\cdot 10^6 \\text{ Дж}$$</p>'
+'<p>Стоимость:</p>'
+'<p style="text-align:center;margin:8px 0">$$\\text{руб} = W (\\text{кВт·ч}) \\times \\text{тариф (руб/кВт·ч)}$$</p>'
);
h += makeCard('rule', 'Как сэкономить', '§ 27.2',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Светодиоды вместо ламп накаливания (60 Вт → 8 Вт при том же свете).</li>'
+'<li>Выключать приборы из розетки (не оставлять в режиме standby).</li>'
+'<li>Энергосберегающие чайники, мультиварки.</li>'
+'<li>Утеплять дом (меньше работает обогреватель).</li>'
+'</ul>'
);
h += makeCard('example', 'Безопасность при работе с током', '§ 27.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Не трогать оголённые провода и розетки голыми руками.</li>'
+'<li>Не использовать электроприборы в ванной без УЗО.</li>'
+'<li>При коротком замыкании срабатывает <b>предохранитель</b> или <b>автомат</b> — отключает цепь.</li>'
+'<li><b>Заземление</b> отводит ток на землю при пробое корпуса прибора.</li>'
+'<li>Опасно для жизни: ток &gt; $50$ мА (это $\\sim 12$ В через мокрую кожу).</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">Сколько часов в день работают приборы — узнай счёт за месяц (30 дней).</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Лампа $60$ Вт, ч/день: <b id="p27-h1">5</b><input type="range" id="p27-h1r" min="0" max="24" step="0.5" value="5"></label>'
+'<label>ТВ $100$ Вт, ч/день: <b id="p27-h2">4</b><input type="range" id="p27-h2r" min="0" max="24" step="0.5" value="4"></label>'
+'<label>Чайник $2000$ Вт, мин/день: <b id="p27-h3">10</b><input type="range" id="p27-h3r" min="0" max="60" step="5" value="10"></label>'
+'<label>Холодильник $150$ Вт, ч/день: <b id="p27-h4">24</b><input type="range" id="p27-h4r" min="0" max="24" step="1" value="24"></label>'
+'<label>Тариф, руб/(кВт·ч): <b id="p27-tar">0.25</b><input type="range" id="p27-tarr" min="0.1" max="2" step="0.05" value="0.25"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:3px">'
+'<span>За месяц израсходуется: <b id="p27-w">186</b> кВт·ч</span>'
+'<span>Стоимость: <b id="p27-cost">46.5</b> руб</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Самый «прожорливый» прибор: <b id="p27-top">холодильник</b>.</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="p27-quiz"></div>'
+'<div class="actions"><button class="btn" id="p27-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p27-quiz-r">1</b> / 6</span><span>Правильно: <b id="p27-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 id="p27-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="save"></div></div>'
+'<div class="drop-box"><h5>Расходует</h5><div class="drop-items" data-cat="waste"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p27-dnd-check">Проверить</button><button class="btn" id="p27-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p27-dnd-fb"></div>'
+'</div>';
/* IV4 — задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p27-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p27-task-i">1</b> / 5</span><span>Правильно: <b id="p27-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p27') + readButton('p27');
renderMath(box);
wireReadBtn('p27');
_initP27_meter();
_initP27_quiz();
_initP27_dnd();
_initP27_tasks();
}
function _initP27_meter(){
function update(){
const h1 = +document.getElementById('p27-h1r').value;
const h2 = +document.getElementById('p27-h2r').value;
const h3 = +document.getElementById('p27-h3r').value;
const h4 = +document.getElementById('p27-h4r').value;
const tar = +document.getElementById('p27-tarr').value;
document.getElementById('p27-h1').textContent = h1.toFixed(1);
document.getElementById('p27-h2').textContent = h2.toFixed(1);
document.getElementById('p27-h3').textContent = h3.toFixed(0);
document.getElementById('p27-h4').textContent = h4.toFixed(0);
document.getElementById('p27-tar').textContent = tar.toFixed(2);
/* кВт·ч за день */
const w1 = 0.060 * h1;
const w2 = 0.100 * h2;
const w3 = 2.000 * (h3/60);
const w4 = 0.150 * h4;
const wDay = w1+w2+w3+w4;
const wMon = wDay * 30;
const cost = wMon * tar;
document.getElementById('p27-w').textContent = wMon.toFixed(1);
document.getElementById('p27-cost').textContent = cost.toFixed(2);
const all = [['лампа',w1],['ТВ',w2],['чайник',w3],['холодильник',w4]];
all.sort((a,b)=>b[1]-a[1]);
document.getElementById('p27-top').textContent = all[0][0];
}
['p27-h1r','p27-h2r','p27-h3r','p27-h4r','p27-tarr'].forEach(id => document.getElementById(id).addEventListener('input', update));
update();
}
function _initP27_quiz(){
const QS = [
{sit:'Менять лампочку в горячей люстре, не отключив выключатель.', ans:'N', why:'Опасно — можно получить удар током. Сначала выключить.'},
{sit:'Использовать электрочайник с заземлённой розеткой.', ans:'Y', why:'Заземление защищает в случае пробоя корпуса.'},
{sit:'Сушить волосы феном, стоя в ванной с водой.', ans:'N', why:'Очень опасно — вода резко увеличивает риск удара.'},
{sit:'Удлинитель с предохранителем для розеток.', ans:'Y', why:'Предохранитель сработает при перегрузке.'},
{sit:'Сэкономить — обмотать оголённый провод изолентой.', ans:'N', why:'Временный «костыль». Нужно заменить провод.'},
{sit:'Светодиодная лампа вместо лампы накаливания.', ans:'Y', why:'Безопаснее (меньше тепла) и экономичнее.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p27-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="p27-quiz-fb"></div>';
document.getElementById('p27-quiz-r').textContent = (i+1);
document.getElementById('p27-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('p27-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p27-quiz'); bumpProgress('p27', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p27-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p27-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP27_dnd(){
const items = [
{id:'a', cat:'save', html:'светодиодная лампа'},
{id:'b', cat:'save', html:'утеплитель на окнах'},
{id:'c', cat:'save', html:'выключать ТВ из розетки'},
{id:'d', cat:'save', html:'натуральный свет днём'},
{id:'e', cat:'waste', html:'лампа накаливания 100 Вт'},
{id:'f', cat:'waste', html:'оставлять зарядки в розетке'},
{id:'g', cat:'waste', html:'кипятить полный чайник для 1 чашки'},
{id:'h', cat:'waste', html:'обогреватель при открытой двери'}
];
const dnd = setupSorter({ poolId:'p27-dnd-pool', scopeSelector:'#sec-p27', cats:['save','waste'], items, columnLayout:false });
document.getElementById('p27-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p27-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,'p27-dnd'); bumpProgress('p27', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p27-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p27-dnd-fb'); fb.style.display='none'; });
}
function _initP27_tasks(){
const TASKS = [
{q:'Чайник $P = 2$ кВт работает 30 мин. Сколько кВт·ч?', ans:1, tol:0.05, why:'$W = P \\tau = 2 \\cdot 0{,}5 = 1$ кВт·ч.'},
{q:'Лампа $60$ Вт горит 5 часов. Сколько кВт·ч?', ans:0.3, tol:0.02, why:'$W = 0{,}06 \\cdot 5 = 0{,}3$ кВт·ч.'},
{q:'Тариф $0{,}25$ руб/(кВт·ч). За месяц израсходовали 200 кВт·ч. Стоимость (руб)?', ans:50, tol:1, why:'$200 \\cdot 0{,}25 = 50$ руб.'},
{q:'Холодильник $150$ Вт работает 24 ч/день. Сколько кВт·ч за месяц (30 дней)?', ans:108, tol:2, why:'$W = 0{,}15 \\cdot 24 \\cdot 30 = 108$ кВт·ч.'},
{q:'Электрообогреватель 2 кВт работает 8 ч/день, тариф $0{,}3$ руб. Сколько за месяц (руб)?', ans:144, tol:3, why:'$W = 2 \\cdot 8 \\cdot 30 = 480$ кВт·ч, $480 \\cdot 0{,}3 = 144$ руб.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p27-task'); if(!wrap) return;
wrap.innerHTML =
'<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> '+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p27-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p27-task-go">Ответ</button>'
+'<button class="btn" id="p27-task-hint">Подсказка</button>'
+'<button class="btn" id="p27-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p27-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p27-task-fb"></div>';
document.getElementById('p27-task-i').textContent = (i+1);
document.getElementById('p27-task-ok').textContent = ok;
document.getElementById('p27-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p27-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p27-task-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
done++;
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(4,'p27-task'); bumpProgress('p27', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p27-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p27-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p27-task-bonus'); bumpProgress('p27', 15); }, 600); }
});
document.getElementById('p27-task-hint').addEventListener('click', ()=>{ document.getElementById('p27-task-hint-txt').classList.toggle('show'); });
document.getElementById('p27-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);