Files
Learn_System/frontend/textbooks/physics_8_ch1.html
T
Maxim Dolgolyov 2dac331aa3 feat(phys8 ch1): Phase 1 Wave 5 — §10 испарение + §11 кипение + Финал главы 1
§10 Испарение:
- 3 теории: что такое испарение, факторы скорости, примеры
- IV-1: симуляция с 28 частицами над поверхностью жидкости —
  испарение зависит от T (slider) и ветра (slider); испарившиеся
  становятся серыми и улетают вверх+вправо, при пропадании за края
  «возвращаются»; счётчик испарившихся; солнце + стрелки ветра
- IV-2: викторина 6 пар сравнения
- IV-3: DnD 8 факторов на ускоряет/замедляет
- IV-4: 6 MCQ

§11 Кипение + Q=Lm:
- 3 теории: кипение, формула Lm, зависимость T_кип от давления
- IV-1: ГЛАВНЫЙ ВИЗУАЛ — полный график T(t) «лёд→вода→пар»
  с 5 цветными сегментами и 2 плато (плавление 0°C, кипение 100°C),
  длительности пропорциональны реальным q_i / Q_total
- IV-2: калькулятор Q=Lm с переводом в кВт·ч и эквивалент нагрева воды
- IV-3: DnD 8 процессов на 3 категории (плавление/испарение/конденсация)
- IV-4: 6 числовых задач (включая полный цикл лёд→пар)

ФИНАЛ ГЛАВЫ 1:
- Шпаргалка 6 формул и понятий
- 7 интегрированных боссов: расчёт ΔT, смешивание, плавление, кипение,
  цепочка нагрева, КПД котла, полный цикл лёд→пар
- Прогресс-бар победы + ачивка «Мастер теплоты» (+50 XP) при 7/7
- Per-boss XP (+10) и hint-кнопки

ACH_LABELS дополнен thermal_master.

Глава 1 «Тепловые явления» завершена: 11 § + финал = 12 секций, все
с симуляциями, калькуляторами, DnD и тренажёрами.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 23:08:54 +03:00

3587 lines
262 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>Физика 8 · Глава 1 · «Тепловые явления»</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/g3d.js" defer></script>
<script src="/js/phys.js" defer></script>
<script src="/js/optics.js" defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#fef2f2; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#7c3aed; --pri2:#5b21b6; --pri-soft:#ede9fe;
--acc:#a78bfa; --acc2:#7c3aed; --acc-soft:#ede9fe;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#0a0a0e; --card:#13120a; --card-soft:#18160a; --text:#fef9e7; --ink:#fef9e7; --muted:#a39070; --border:#2a2512}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.hdr{position:relative;background:linear-gradient(110deg,#7f1d1d 0%,#dc2626 55%,#fca5a5 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(255,255,255,.2);min-height:130px}
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(0,0,0,.18)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(0,0,0,.12);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(0,0,0,.18);font-family:'Unbounded',sans-serif}
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
.psel-name{font-size:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(0,0,0,.10);border-radius:3px;overflow:hidden}
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
.psel-card.final{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft))}
.psel-card.final .psel-num{color:var(--warn)}
.sec[id="sec-p1"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p2"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p3"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p4"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p5"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p6"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p7"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p8"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p9"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p10"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p11"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec[id="sec-final1"]{ --sec-acc:#dc2626; --sec-acc-d:#991b1b; --sec-acc-soft:#fee2e2; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.6rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(0,0,0,.04);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(0,0,0,.08)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.card-body p:last-child{margin-bottom:0}
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
.sidecard-row:last-child{margin-bottom:0}
@media(max-width:980px){.col-side{position:static;max-height:none}}
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
.xp-bar{height:9px;background:rgba(0,0,0,.10);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,var(--pri),var(--acc));color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(0,0,0,.32);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
.ach-popup.show{display:flex}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
.col-side-backdrop.show{display:block}
@media(max-width:980px){
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
.col-side.open{transform:none}
}
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
.search-modal.show{display:flex}
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
.search-results{flex:1;overflow-y:auto;padding:6px 0}
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border:0;width:100%;color:var(--text)}
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px}
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
.sec{transition:opacity .25s}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Физика 8 · Глава 1</h1>
<div class="hdr-sub">Внутренняя энергия · теплопередача · фазовые переходы</div>
</div>
<div class="hdr-side">
<a href="/textbook/physics-8" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К физике 8</a>
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Тепловые явления — как энергия переходит между телами</h2>
<p>Внутренняя энергия зависит от температуры тела. Тепло передаётся теплопроводностью, конвекцией и излучением. При нагревании, плавлении и кипении нужно разное количество теплоты.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 1</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" data-gamified></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec"><div class="sec-header"><span class="sec-num">&sect; 1</span><h2 class="sec-h">Внутренняя энергия</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec"><div class="sec-header"><span class="sec-num">&sect; 2</span><h2 class="sec-h">Способы изменения внутренней энергии</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec"><div class="sec-header"><span class="sec-num">&sect; 3</span><h2 class="sec-h">Теплопроводность</h2></div><div id="p3-body"></div></section>
<section id="sec-p4" class="sec"><div class="sec-header"><span class="sec-num">&sect; 4</span><h2 class="sec-h">Конвекция</h2></div><div id="p4-body"></div></section>
<section id="sec-p5" class="sec"><div class="sec-header"><span class="sec-num">&sect; 5</span><h2 class="sec-h">Излучение</h2></div><div id="p5-body"></div></section>
<section id="sec-p6" class="sec"><div class="sec-header"><span class="sec-num">&sect; 6</span><h2 class="sec-h">Расчёт количества теплоты при нагревании и охлаждении. Удельная теплоёмкость</h2></div><div id="p6-body"></div></section>
<section id="sec-p7" class="sec"><div class="sec-header"><span class="sec-num">&sect; 7</span><h2 class="sec-h">Горение. Удельная теплота сгорания топлива</h2></div><div id="p7-body"></div></section>
<section id="sec-p8" class="sec"><div class="sec-header"><span class="sec-num">&sect; 8</span><h2 class="sec-h">Плавление и кристаллизация</h2></div><div id="p8-body"></div></section>
<section id="sec-p9" class="sec"><div class="sec-header"><span class="sec-num">&sect; 9</span><h2 class="sec-h">Удельная теплота плавления и кристаллизации</h2></div><div id="p9-body"></div></section>
<section id="sec-p10" class="sec"><div class="sec-header"><span class="sec-num">&sect; 10</span><h2 class="sec-h">Испарение жидкостей. Факторы, влияющие на скорость испарения</h2></div><div id="p10-body"></div></section>
<section id="sec-p11" class="sec"><div class="sec-header"><span class="sec-num">&sect; 11</span><h2 class="sec-h">Кипение жидкостей. Удельная теплота парообразования</h2></div><div id="p11-body"></div></section>
<section id="sec-final1" class="sec"><div class="sec-header"><span class="sec-num">&#9733;</span><h2 class="sec-h">Финал главы</h2></div><div id="final1-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Физика 8» · Глава 1 · «Тепловые явления» · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<div id="search-modal" class="search-modal" role="dialog">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot"><span><kbd>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE = { current:'p1', progress:{}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 12;
const _TB_SLUG = 'physics-8-ch1';
const LS_PREFIX = 'physics8_ch1';
const LS_XP = 'physics8_xp';
const PARAS = [
{ id:'p1', num:'\u00a7 1', name:'Внутренняя энергия', sub:'$U$ зависит от $T$' },
{ id:'p2', num:'\u00a7 2', name:'Способы изменения внутренней энергии', sub:'Работа и теплопередача' },
{ id:'p3', num:'\u00a7 3', name:'Теплопроводность', sub:'Передача без переноса в-ва' },
{ id:'p4', num:'\u00a7 4', name:'Конвекция', sub:'Перенос потоками' },
{ id:'p5', num:'\u00a7 5', name:'Излучение', sub:'Тепловое излучение' },
{ id:'p6', num:'\u00a7 6', name:'Расчёт количества теплоты при нагревании и охлаждении. Удельная теплоёмкость', sub:'$Q = cm\\Delta T$' },
{ id:'p7', num:'\u00a7 7', name:'Горение. Удельная теплота сгорания топлива', sub:'$Q = qm$' },
{ id:'p8', num:'\u00a7 8', name:'Плавление и кристаллизация', sub:'$T_{пл}$, графики $T(t)$' },
{ id:'p9', num:'\u00a7 9', name:'Удельная теплота плавления и кристаллизации', sub:'$Q = \\lambda m$' },
{ id:'p10', num:'\u00a7 10', name:'Испарение жидкостей. Факторы, влияющие на скорость испарения', sub:'Зависит от $T$, $S$' },
{ id:'p11', num:'\u00a7 11', name:'Кипение жидкостей. Удельная теплота парообразования', sub:'$Q = Lm$' },
{ id:'final1', num:'\u2605', name:'Финал главы', sub:'Итоги · 7 боссов', final:true }
];
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
const ACH_LABELS = {
start:"Начало главы 1!",
p1_done:"Внутренняя энергия освоен!",
p2_done:"Способы изменения внутренней энергии освоен!",
p3_done:"Теплопроводность освоен!",
p4_done:"Конвекция освоен!",
p5_done:"Излучение освоен!",
p6_done:"Расчёт количества теплоты при нагревании и охлаждении. Удельная теплоёмкость освоен!",
p7_done:"Горение. Удельная теплота сгорания топлива освоен!",
p8_done:"Плавление и кристаллизация освоен!",
p9_done:"Удельная теплота плавления и кристаллизации освоен!",
p10_done:"Испарение жидкостей. Факторы, влияющие на скорость испарения освоен!",
p11_done:"Кипение жидкостей. Удельная теплота парообразования освоен!",
ch1_done:"Глава 1 пройдена!",
thermal_master:"Мастер теплоты — все боссы главы 1 повержены!"
};
const SIDEBARS = {
p1:{title:"Шпаргалка § 1",rows:[
["$U$","сумма $E_k$ хаотич. движения молекул и $E_p$ их взаимодействия"],
["Зависит от","$T$, $m$, агрегатного состояния, рода вещества"],
["Не зависит от","скорости тела как целого, высоты, формы, цвета"],
["При нагреве","молекулы быстрее $\\Rightarrow$ $U$ растёт"]
]},
p2:{title:"Шпаргалка § 2",rows:[
["Способа 2","совершение работы и теплопередача"],
["Работа","трение, удар, сжатие газа, деформация"],
["Теплопередача","проводность, конвекция, излучение"],
["Знак","нагрев $\\Rightarrow$ $\\Delta U > 0$; охлаждение $\\Rightarrow$ $\\Delta U < 0$"]
]},
p3:{title:"Шпаргалка § 3",rows:[
["Теплопроводность","передача $U$ без переноса вещества"],
["Хорошие","металлы (медь, серебро, алюминий)"],
["Плохие","дерево, пластик, воздух, шерсть, вакуум"],
["В газах и жидкостях","низкая (исключение — ртуть)"]
]},
p4:{title:"Шпаргалка § 4",rows:[
["Конвекция","перенос $U$ потоками жидкости / газа"],
["Механизм","тёплое $\\uparrow$, холодное $\\downarrow$"],
["В твёрдых телах","невозможна"],
["Примеры","батарея, ветер, кипение"]
]},
p5:{title:"Шпаргалка § 5",rows:[
["Излучение","перенос $U$ электромагнитными волнами"],
["Среда","не нужна — идёт через вакуум"],
["Излучают всё","чем горячее, тем сильнее"],
["Тёмные тела","поглощают и излучают сильнее светлых"]
]},
p6:{title:"Шпаргалка § 6",rows:[
["Закон","$Q = c m \\Delta T$"],
["$c$ — уд. теплоёмкость","Дж/(кг·К)"],
["вода","$c = 4200$"],
["лёд","$c = 2100$"],
["алюминий","$c = 920$"],
["медь","$c = 380$"],
["железо","$c = 460$"],
["Баланс","$Q_{отд} = Q_{пол}$"]
]},
p7:{title:"Шпаргалка § 7",rows:[
["Закон","$Q = q m$"],
["$q$ — уд. теплота сгорания","Дж/кг"],
["дрова","$q = 10^7$"],
["уголь","$q = 3 \\cdot 10^7$"],
["бензин","$q = 4{,}6 \\cdot 10^7$"],
["природ. газ","$q = 4{,}4 \\cdot 10^7$"],
["КПД котла","$\\eta = Q_{пол}/(q m)$"]
]},
p8:{title:"Шпаргалка § 8",rows:[
["Плавление","$T = T_{пл} = $ const"],
["Кристаллизация","$T = T_{кр} = T_{пл}$"],
["На графике","горизонт. плато"],
["Энергия идёт на","разрушение / построение решётки"],
["лёд","$T_{пл} = 0$ &#176;C"],
["алюминий","$T_{пл} = 660$ &#176;C"],
["железо","$T_{пл} = 1539$ &#176;C"]
]},
p9:{title:"Шпаргалка § 9",rows:[
["Формула","$Q = \\lambda m$"],
["$\\lambda$","удельная теплота плавления, Дж/кг"],
["лёд","$\\lambda = 3{,}34 \\cdot 10^5$"],
["свинец","$\\lambda = 2{,}5 \\cdot 10^4$"],
["железо","$\\lambda = 2{,}7 \\cdot 10^5$"],
["Баланс","$Q_{нагр} + Q_{пл} + Q_{нагр.ж}$"]
]},
p10:{title:"Шпаргалка § 10",rows:[
["Испарение","с поверхности при любой $T$"],
["Скорость зависит от","$T$, $S$, ветра, рода жидкости"],
["Жидкость","охлаждается (уходят быстрые)"],
["Конденсация","пар $\\to$ жидкость, тепло выделяется"]
]},
p11:{title:"Шпаргалка § 11",rows:[
["Кипение","по всему объёму при $T = T_{кип}$"],
["Формула","$Q = Lm$"],
["$L$ для воды","$2{,}26 \\cdot 10^6$ Дж/кг"],
["$T_{кип}$ воды","100 &#176;C (при 1 атм)"],
["Давление $\\downarrow$","$T_{кип} \\downarrow$ (на горе)"]
]},
final1:{title:"Финал главы 1",rows:[
["Формулы","$Q = cm\\Delta T$, $Q = qm$, $Q = \\lambda m$, $Q = Lm$"],
["3 вида теплопередачи","проводность, конвекция, излучение"],
["Награда","+50 XP + «Мастер теплоты»"]
]}
};
const TIPS=[
{sec:'p1',html:"Тело состоит из молекул. Они движутся (есть $E_k$) и взаимодействуют (есть $E_p$). Их сумма — внутренняя энергия $U$. Главное: $U$ не зависит от того, движется ли тело и на какой высоте оно лежит."},
{sec:'p2',html:"Изменить $U$ можно двумя способами: совершить работу (трение, сжатие, удар) или передать тепло без работы (контакт, поток, излучение). Чай в стакане остывает — это теплопередача. Спички в коробке нагреваются от тряски — это работа."},
{sec:'p3',html:"Один конец металлического стержня в огне — другой нагревается, хотя ничто не движется. Это <b>теплопроводность</b>: молекулы передают энергию соседям. У металлов это работает быстро, у дерева — медленно."},
{sec:'p4',html:"Тёплый воздух легче холодного и поднимается вверх. Так батарея греет всю комнату — это <b>конвекция</b>. В твёрдых телах конвекции нет, потому что молекулы не могут свободно двигаться."},
{sec:'p5',html:"Солнце греет Землю через космический вакуум — теплопроводность и конвекция тут невозможны. Это <b>излучение</b> электромагнитными волнами. Чёрная футболка в жаркий день нагревается сильнее белой."},
{sec:'p6',html:"Чтобы нагреть тело массой $m$ на $\\Delta T$ градусов, нужно $Q = c m \\Delta T$. Здесь $c$ — это «сколько энергии съедает 1 кг этого вещества на 1 градус». У воды $c$ самое большое — поэтому вода долго греется и долго остывает."},
{sec:'p7',html:"При сгорании 1 кг топлива выделяется $q$ Дж энергии. Полное выделение: $Q = q m$. У бензина $q$ в 4,5 раза больше, чем у дров, — поэтому литр бензина греет дольше, чем литр дров."},
{sec:'p8',html:"Пока лёд плавится, температура смеси «лёд + вода» сидит на 0 &#176;C — даже если плита продолжает греть. На графике $T(t)$ это видно как горизонтальная площадка (плато)."},
{sec:'p9',html:"Чтобы расплавить 1 кг льда (без нагрева!), нужно $\\lambda = 334$ кДж. Это столько же, сколько на нагрев той же массы воды от 0 до 80 &#176;C. Поэтому лёд — хороший «холодильник»."},
{sec:'p10',html:"Лужа высыхает даже зимой — это испарение. Чем теплее и ветренее, тем быстрее. При этом сама жидкость <b>остывает</b>, потому что её покидают самые быстрые молекулы — вот почему мокрая рука зябнет."},
{sec:'p11',html:"При кипении пузырьки пара образуются <b>внутри</b> жидкости. Чтобы перевести 1 кг воды (100 &#176;C) в пар, нужно $L = 2{,}26 \\cdot 10^6$ Дж — это в 5,4 раза больше, чем на нагрев той же воды от 0 до 100 &#176;C."},
{sec:'final1',html:"Финал главы — синтез 11 параграфов. 7 интегрированных боссов на формулы $Q = cm\\Delta T$, $\\lambda m$, $Lm$, $qm$, баланс смешивания, КПД, цепные расчёты. За победу — +50 XP и ачивка «Мастер теплоты»."}
];
const BUILDERS = {
p1: ()=>{ build_p1(); },
p2: ()=>{ build_p2(); },
p3: ()=>{ build_p3(); },
p4: ()=>{ build_p4(); },
p5: ()=>{ build_p5(); },
p6: ()=>{ build_p6(); },
p7: ()=>{ build_p7(); },
p8: ()=>{ build_p8(); },
p9: ()=>{ build_p9(); },
p10: ()=>{ build_p10(); },
p11: ()=>{ build_p11(); },
final1: ()=>{ build_final1(); }
};
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
function loadProgress(){
try{
const s=localStorage.getItem(LS_PREFIX+'_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem(LS_PREFIX+'_achievements');
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
STATE.xp=+(localStorage.getItem(LS_XP)||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem(LS_PREFIX+'_progress', JSON.stringify(STATE.progress));
localStorage.setItem(LS_PREFIX+'_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem(LS_XP, String(STATE.xp));
}catch(e){}
}
function bumpProgress(key, delta){
STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));
saveProgress(); refreshProgressUI();
if(STATE.progress[key]>=50) markParaRead(key);
}
const _markedRead=new Set();
let _pendingProgressBody=null, _progressTimer=null;
function _flushProgress(){
const body=_pendingProgressBody; _pendingProgressBody=null; if(!body) return;
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});
}
function _queueProgress(patch){ _pendingProgressBody=Object.assign(_pendingProgressBody||{},patch); if(_progressTimer) clearTimeout(_progressTimer); _progressTimer=setTimeout(_flushProgress, 600); }
function markLastPara(id){ _queueProgress({last_para:id}); }
function markParaRead(id){ if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({mark_read:id}); }
window.addEventListener('beforeunload', _flushProgress);
function loadServerReadState(){
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
fetch('/api/textbooks/'+_TB_SLUG,{headers:{'Authorization':'Bearer '+tok}}).then(r=>r.ok?r.json():null).then(d=>{
if(!d||!d.progress) return;
(d.progress.read||[]).forEach(k=>{_markedRead.add(k); if((STATE.progress[k]||0)<50) STATE.progress[k]=100;});
saveProgress(); refreshProgressUI();
}).catch(()=>{});
}
function addXp(n,src){
if(!n) return;
const prev=STATE.level; STATE.xp=Math.max(0,(STATE.xp||0)+n); STATE.level=calcLevel(STATE.xp);
saveProgress(); refreshProgressUI();
if(window.LS&&window.LS.xp) window.LS.xp.add(n, LS_PREFIX+'-'+(src||'misc'));
if(STATE.level>prev){
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
}
}
function refreshProgressUI(){
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
const xpBadge=document.getElementById('hero-xp-badge');
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
}
function achievement(id,text){
if(STATE.achievements.has(id)) return;
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
addXp(20,'ach-'+id);
}
function buildParaSelector(){
const g=document.getElementById('psel-grid'); g.innerHTML='';
PARAS.forEach(p=>{
const card=document.createElement('div');
card.className='psel-card'+(p.final?' final':'');
card.dataset.id=p.id; card.dataset.progCard=p.id;
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
card.addEventListener('click', ()=>goTo(p.id));
g.appendChild(card);
});
}
const BUILT=new Set();
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current=id; ensureBuilt(id);
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
const el=document.getElementById('sec-'+id); if(el) el.classList.add('active');
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
buildSidebar(id);
window.scrollTo({top:0,behavior:'smooth'});
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
markLastPara(id);
}
function buildSidebar(id){
const box=document.getElementById('sidebar-content');
const sb=SIDEBARS[id]||SIDEBARS[PARAS[0].id];
let html='';
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
html+='<div class="xp-card" data-gamified><div class="xp-card-title" data-gamified><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' \u2014 '+v:'')+'</div>'; });
html+='</div>';
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
if(tip){
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
}
if(STATE.achievements.size>0){
html+='<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">'+STATE.achievements.size+'</span></h4>';
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">&#10003; '+text+'</div>'; });
html+='</div>';
}
box.innerHTML=html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
function initTheme(){
const t=localStorage.getItem(LS_PREFIX+'_theme')||'light';
if(t==='dark') document.documentElement.classList.add('dark');
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
document.getElementById('theme-btn').addEventListener('click', ()=>{
document.documentElement.classList.toggle('dark');
const dark=document.documentElement.classList.contains('dark');
localStorage.setItem(LS_PREFIX+'_theme', dark?'dark':'light');
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
});
}
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'&#10003; Верно!':'&#10007; Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
function ipow(base, exp){ let r=1; for(let i=0;i<Math.abs(exp);i++) r*=base; return exp<0 ? 1/r : r; }
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
}
/* === SVG-хелперы === */
function axes2D(W, H, pad, xmin, xmax, ymin, ymax){
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (ymax - ymin);
const toX = v => pad + (v - xmin) * ux;
const toY = v => H - pad - (v - ymin) * uy;
let g = '';
g += '<g stroke="#e5e7eb" stroke-width="1">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
g += '<line x1="'+toX(x)+'" y1="'+pad+'" x2="'+toX(x)+'" y2="'+(H-pad)+'"/>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
g += '<line x1="'+pad+'" y1="'+toY(y)+'" x2="'+(W-pad)+'" y2="'+toY(y)+'"/>';
}
g += '</g>';
const y0 = toY(0), x0 = toX(0);
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+x0+'" y1="'+pad+'" x2="'+x0+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(W-pad+2)+'" y="'+(y0-4)+'" font-size="11" fill="#0f172a">x</text>';
g += '<text x="'+(x0+4)+'" y="'+(pad-2)+'" font-size="11" fill="#0f172a">y</text>';
g += '<g font-size="10" fill="#64748b">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
if (x !== 0) g += '<text x="'+(toX(x)-3)+'" y="'+(y0+12)+'">'+x+'</text>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
if (y !== 0) g += '<text x="'+(x0+4)+'" y="'+(toY(y)+3)+'">'+y+'</text>';
}
g += '<text x="'+(x0+4)+'" y="'+(y0+12)+'">0</text>';
g += '</g>';
return { content: g, toX, toY, ux, uy };
}
function plotFunc(f, xmin, xmax, toX, toY, color, N){
N = N || 200;
let d = '';
let prevValid = false;
for (let i = 0; i <= N; i++){
const x = xmin + (xmax - xmin) * i / N;
let y;
try { y = f(x); } catch(e){ y = NaN; }
if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; }
d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2);
prevValid = true;
}
return '<path d="'+d+'" stroke="'+color+'" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';
}
function pointWithDrop(x, fx, toX, toY, color, label){
const px = toX(x), py = toY(fx);
let s = '';
s += '<line x1="'+px+'" y1="'+py+'" x2="'+px+'" y2="'+toY(0)+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
s += '<line x1="'+px+'" y1="'+py+'" x2="'+toX(0)+'" y2="'+py+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
s += '<circle cx="'+px+'" cy="'+py+'" r="4.5" fill="'+color+'" stroke="#fff" stroke-width="2"/>';
if (label){
s += '<text x="'+(px+8)+'" y="'+(py-8)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+color+'">'+label+'</text>';
}
return s;
}
function asymptote(orientation, value, toX, toY, xmin, xmax, ymin, ymax, color){
color = color || '#94a3b8';
if (orientation === 'h'){
const y = toY(value);
return '<line x1="'+toX(xmin)+'" y1="'+y+'" x2="'+toX(xmax)+'" y2="'+y+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
} else {
const x = toX(value);
return '<line x1="'+x+'" y1="'+toY(ymin)+'" x2="'+x+'" y2="'+toY(ymax)+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
}
}
function snapToValue(value, snapPoints, tolerance){
tolerance = tolerance || 0.1;
for (const sp of snapPoints){
if (Math.abs(value - sp) < tolerance) return sp;
}
return value;
}
function rightAngleMark(V, uIn, wIn, s){
s = s || 9;
const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y};
const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y};
const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y};
return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y;
}
function angleArcAuto(V, uA, uB, R){
const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y};
const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y};
const cross = uA.x*uB.y - uA.y*uB.x;
const sweep = cross > 0 ? 1 : 0;
return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y;
}
function unitVec(p1, p2){
const dx = p2.x - p1.x, dy = p2.y - p1.y;
const len = Math.sqrt(dx*dx + dy*dy) || 1;
return {x: dx/len, y: dy/len};
}
function deg2rad(d){ return d * Math.PI / 180; }
const ICONS = {
repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
oral:'<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>'
};
function secNavFor(curId){
const idx = PARAS.findIndex(p => p.id === curId);
const prev = idx > 0 ? PARAS[idx-1].id : null;
const next = idx < PARAS.length - 1 ? PARAS[idx+1].id : null;
return secNav(prev, next);
}
function secNav(prev, next){
function lbl(id){ if(!id) return ''; const p=PARAS.find(x=>x.id===id); return p?p.num:id; }
let h='<div class="sec-nav">';
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+lbl(prev)+'</button>':'<span></span>';
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+lbl(next)+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
h+='</div>'; return h;
}
function readButton(paraId){
const p = PARAS.find(x => x.id === paraId);
const labelTail = p && p.final ? 'финал' : (p ? p.num : '?');
return '<div style="margin-top:18px;display:flex;justify-content:center">'
+'<button class="btn primary" id="'+paraId+'-read-btn">'
+'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
+' Я прочитал \u2014 '+labelTail+' (+10 XP)'
+'</button></div>';
}
function wireReadBtn(paraId){
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
btn.addEventListener('click', ()=>{
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
const aId = paraId+'_done';
if(ACH_LABELS[aId]) achievement(aId);
});
}
function setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
function buildStub(id, name, phase){
return '<div class="card" style="background:linear-gradient(135deg,var(--sec-acc-soft),var(--card));border:1.5px dashed var(--sec-acc)">'
+ '<div class="card-header"><div class="card-icon theory">'+ICONS.theory+'</div><div class="card-title">В разработке</div></div>'
+ '<div class="card-body"><p>Контент <b>'+name+'</b> будет реализован в <b>'+phase+'</b> по плану <code>PLAN_PHYSICS_8.md</code>.</p>'
+ '<p style="margin-top:8px;color:var(--muted);font-size:.9rem">Phase 0 \u2014 это каркас (skeleton). Все 4 интерактива, 3 теоретические карточки и тренажёр задач будут добавлены в волне.</p>'
+ '</div></div>';
}
/* ===== Search ===== */
const SEARCH_INDEX = (function(){
const arr=[];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
return arr;
})();
function initSearch(){
const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');
if(!modal||!inp||!out) return;
let cur=0,rows=[];
function score(q,it){ const t=(it.title+' '+it.desc).toLowerCase(); if(t.includes(q)) return 100+(it.title.toLowerCase().startsWith(q)?50:0); let s=0; q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;}); return s; }
function rank(q){ q=q.trim().toLowerCase(); if(!q) return SEARCH_INDEX.slice(0,12); return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it); }
function render(){ cur=0; if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;} out.innerHTML=rows.map((r,i)=>'<button class="search-row'+(i===0?' active':'')+'" data-i="'+i+'"><div class="sr-kind">'+r.kind+'</div><div class="sr-title">'+r.title+'</div>'+(r.desc?'<div class="sr-desc">'+(r.desc.length>90?r.desc.slice(0,90)+'\u2026':r.desc)+'</div>':'')+'</button>').join(''); out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();})); }
function pick(){ const r=rows[cur]; if(!r) return; close(); goTo(r.sec); }
function move(d){ const items=out.querySelectorAll('.search-row'); if(!items.length) return; items[cur]&&items[cur].classList.remove('active'); cur=(cur+d+items.length)%items.length; items[cur].classList.add('active'); items[cur].scrollIntoView({block:'nearest'}); }
function open(){ modal.classList.add('show'); inp.value=''; rows=rank(''); render(); setTimeout(()=>inp.focus(),50); }
function close(){ modal.classList.remove('show'); }
btn&&btn.addEventListener('click',open);
modal.addEventListener('click',e=>{if(e.target===modal)close();});
inp.addEventListener('input',()=>{rows=rank(inp.value);render();});
inp.addEventListener('keydown',e=>{ if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();} });
document.addEventListener('keydown',e=>{ if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); if(modal.classList.contains('show')) close(); else open(); } });
}
function initSidebarToggle(){
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
if(!side||!btn) return;
function open(){ side.classList.add('open'); back.classList.add('show'); }
function close(){ side.classList.remove('open'); back.classList.remove('show'); }
btn.addEventListener('click',()=>{ if(side.classList.contains('open')) close(); else open(); });
back.addEventListener('click',close);
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
}
/* ======================================================================
PHASE 1 · WAVE 1 — §1, §2
====================================================================== */
/* Глобальное состояние симуляций — чтобы можно было остановить при unmount */
const _SIMS = {};
function _killSim(key){ if(_SIMS[key] && _SIMS[key].raf){ cancelAnimationFrame(_SIMS[key].raf); _SIMS[key].raf=0; } }
function _isVisible(secId){ const el=document.getElementById('sec-'+secId); return el && el.classList.contains('active'); }
/* ======== §1 — Внутренняя энергия ======== */
function build_p1(){
const box = document.getElementById('p1-body');
let h = '';
/* 3 теоретические карточки */
h += makeCard('theory', 'Что такое внутренняя энергия', '§ 1.1',
'<p>Любое тело состоит из <b>молекул</b>. Молекулы непрерывно движутся (имеют кинетическую энергию $E_k$) и взаимодействуют друг с другом (имеют потенциальную энергию $E_p$).</p>'
+'<p><b>Внутренняя энергия</b> $U$ тела — это сумма $E_k$ хаотического движения всех молекул и $E_p$ их взаимодействия:</p>'
+'<p style="text-align:center;margin:8px 0">$U = \\sum E_k\\,(\\text{молекул}) + \\sum E_p\\,(\\text{взаимодействия})$</p>'
+'<p>В отличие от механической энергии, $U$ <b>не зависит</b> от того, движется ли тело и на какой оно высоте.</p>'
);
h += makeCard('rule', 'От чего зависит $U$', '§ 1.2',
'<p>Внутренняя энергия тела <b>растёт</b>, если:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>повышается температура $T$ (молекулы движутся быстрее);</li>'
+'<li>увеличивается масса $m$ (становится больше молекул);</li>'
+'<li>меняется агрегатное состояние: лёд $\\to$ вода $\\to$ пар (рвутся связи между молекулами).</li>'
+'</ul>'
+'<p style="margin-top:6px">$U$ <b>не зависит</b> от того, движется ли тело как целое, и от его высоты над поверхностью земли.</p>'
);
h += makeCard('example', 'Сравнение', '§ 1.3',
'<p>В стакане 200 г воды при $t = 20$ &#176;C. Сравним $U$ в трёх ситуациях:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li>стакан стоит на столе;</li>'
+'<li>стакан подняли на полку высотой 1,5 м;</li>'
+'<li>стакан несут в поезде со скоростью 60 км/ч.</li>'
+'</ol>'
+'<p>Во всех трёх случаях $U$ <b>одинакова</b>, потому что $T$, $m$ и агрегатное состояние воды не изменились.</p>'
);
/* IV1 — Симуляция «Холодный vs горячий газ» */
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">Двигайте slider\'ы $T_1$ и $T_2$ — наблюдайте, как меняется скорость молекул. Чем выше $T$, тем быстрее они движутся и тем больше внутренняя энергия $U$.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$T_1$, &#176;C: <b id="p1-t1v">20</b><input type="range" id="p1-t1" min="-100" max="500" step="5" value="20"></label>'
+'<label>$T_2$, &#176;C: <b id="p1-t2v">300</b><input type="range" id="p1-t2" min="-100" max="500" step="5" value="300"></label>'
+'</div>'
+'<svg id="p1-sim" viewBox="0 0 460 200" 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>Левый: $T_1 = $ <b id="p1-t1k">293</b> К,&nbsp; ср. скорость молекул <b id="p1-v1">100</b>%</span>'
+'<span>Правый: $T_2 = $ <b id="p1-t2k">573</b> К,&nbsp; ср. скорость молекул <b id="p1-v2">140</b>%</span>'
+'<span style="font-size:.84rem;color:var(--muted)">У какого тела больше $U$? &mdash; у того, где молекулы быстрее.</span>'
+'</div>'
+'</div>';
/* IV2 — Викторина «Где больше U?» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">У какого тела больше $U$?</div></div>'
+'<div class="wg-help">Сравните две ситуации и выберите ту, у которой <b>больше</b> внутренняя энергия, или «одинаково», если они равны.</div>'
+'<div id="p1-quiz"></div>'
+'<div class="actions"><button class="btn" id="p1-quiz-next">Следующий раунд</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p1-quiz-r">1</b> / 6</span><span>Правильно: <b id="p1-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">От чего зависит $U$?</div></div>'
+'<div class="wg-help">Перетащите чипы в нужную колонку. <b>Подсказка:</b> $U$ — это о молекулах, а не о теле как целом.</div>'
+'<div id="p1-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="dep"></div></div>'
+'<div class="drop-box"><h5>Не зависит</h5><div class="drop-items" data-cat="indep"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p1-dnd-check">Проверить</button><button class="btn" id="p1-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p1-dnd-fb"></div>'
+'</div>';
/* IV4 — MCQ тренажёр */
h += '<div class="wg" id="p1-mcq-wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 вопросов</div></div>'
+'<div class="wg-help">Ответьте на 6 вопросов о внутренней энергии. Достаточно 4 правильных, чтобы получить +15 XP.</div>'
+'<div id="p1-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p1-mcq-i">1</b> / 6</span><span>Правильно: <b id="p1-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p1') + readButton('p1');
renderMath(box);
wireReadBtn('p1');
_initP1_sim();
_initP1_quiz();
_initP1_dnd();
_initP1_mcq();
}
/* === §1 IV-1: газ-симуляция === */
function _initP1_sim(){
_killSim('p1sim');
const svg = document.getElementById('p1-sim'); if(!svg) return;
const W=460, H=200, boxW=200, boxH=160, gap=20;
const L = {x:gap, y:(H-boxH)/2, w:boxW, h:boxH};
const R = {x:W-gap-boxW, y:(H-boxH)/2, w:boxW, h:boxH};
/* частицы */
const Nl = 22, Nr = 22;
function makeParticles(N, box){
const arr = [];
for(let i=0;i<N;i++) arr.push({
x: box.x + 6 + Math.random()*(box.w-12),
y: box.y + 6 + Math.random()*(box.h-12),
vx: (Math.random()-0.5)*2,
vy: (Math.random()-0.5)*2
});
return arr;
}
const pL = makeParticles(Nl, L);
const pR = makeParticles(Nr, R);
let T1 = 20, T2 = 300;
function getSpeed(tC){
/* скорость пропорциональна sqrt(T_K). T_K = 273+t. */
const tK = Math.max(1, 273 + tC);
/* масштаб: при 20°C (293К) — 1.0 */
return Math.sqrt(tK / 293);
}
function step(parts, box, k){
for(const p of parts){
p.x += p.vx * k * 1.6;
p.y += p.vy * k * 1.6;
if(p.x < box.x+4) { p.x = box.x+4; p.vx = -p.vx; }
if(p.x > box.x+box.w-4) { p.x = box.x+box.w-4; p.vx = -p.vx; }
if(p.y < box.y+4) { p.y = box.y+4; p.vy = -p.vy; }
if(p.y > box.y+box.h-4) { p.y = box.y+box.h-4; p.vy = -p.vy; }
}
}
function render(){
if(!_isVisible('p1')) { _SIMS.p1sim.raf = requestAnimationFrame(render); return; }
const k1 = getSpeed(T1), k2 = getSpeed(T2);
step(pL, L, k1); step(pR, R, k2);
/* HSL цвет — холодный → синий, горячий → красный */
const cL = window.PHYS.tempColor(T1, -100, 500);
const cR = window.PHYS.tempColor(T2, -100, 500);
let s = '';
s += '<rect x="'+L.x+'" y="'+L.y+'" width="'+L.w+'" height="'+L.h+'" fill="white" stroke="#0f172a" stroke-width="2" rx="6"/>';
s += '<rect x="'+R.x+'" y="'+R.y+'" width="'+R.w+'" height="'+R.h+'" fill="white" stroke="#0f172a" stroke-width="2" rx="6"/>';
for(const p of pL) s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="4" fill="'+cL+'" stroke="#0f172a" stroke-width="0.6"/>';
for(const p of pR) s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="4" fill="'+cR+'" stroke="#0f172a" stroke-width="0.6"/>';
s += '<text x="'+(L.x+L.w/2)+'" y="'+(L.y-6)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+cL+'">T = '+T1+' &#176;C</text>';
s += '<text x="'+(R.x+R.w/2)+'" y="'+(R.y-6)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+cR+'">T = '+T2+' &#176;C</text>';
svg.innerHTML = s;
_SIMS.p1sim.raf = requestAnimationFrame(render);
}
_SIMS.p1sim = { raf: 0 };
_SIMS.p1sim.raf = requestAnimationFrame(render);
function update(){
T1 = +document.getElementById('p1-t1').value;
T2 = +document.getElementById('p1-t2').value;
document.getElementById('p1-t1v').textContent = T1;
document.getElementById('p1-t2v').textContent = T2;
document.getElementById('p1-t1k').textContent = (273 + T1);
document.getElementById('p1-t2k').textContent = (273 + T2);
const v1 = Math.round(getSpeed(T1)*100);
const v2 = Math.round(getSpeed(T2)*100);
document.getElementById('p1-v1').textContent = v1;
document.getElementById('p1-v2').textContent = v2;
}
document.getElementById('p1-t1').addEventListener('input', update);
document.getElementById('p1-t2').addEventListener('input', update);
update();
}
/* === §1 IV-2: викторина «Где больше U?» === */
function _initP1_quiz(){
const QS = [
{A:'1 кг воды при $t = 20$ &#176;C', B:'1 кг воды при $t = 80$ &#176;C', ans:'B', why:'При большей $T$ молекулы движутся быстрее $\\Rightarrow$ больше $U$.'},
{A:'1 кг льда при $0$ &#176;C', B:'1 кг воды при $0$ &#176;C', ans:'B', why:'При плавлении (0 &#176;C, лёд$\\to$вода) поглощается теплота, поэтому у воды $U$ больше.'},
{A:'2 кг железа при $100$ &#176;C', B:'1 кг железа при $100$ &#176;C', ans:'A', why:'У большей массы — больше молекул, значит больше $U$.'},
{A:'Камень на полу', B:'Тот же камень на полке (1 м выше)', ans:'C', why:'$U$ не зависит от высоты. Это потенциальная механическая энергия, а не внутренняя.'},
{A:'Покоящийся теннисный мяч', B:'Тот же мяч, летящий со скоростью 20 м/с', ans:'C', why:'$U$ не зависит от скорости тела как целого.'},
{A:'1 кг воды при $100$ &#176;C', B:'1 кг водяного пара при $100$ &#176;C', ans:'B', why:'При кипении (вода$\\to$пар) поглощается большая теплота. У пара $U$ больше.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i];
const wrap = document.getElementById('p1-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="text-align:left;padding:14px"><b>A.</b> '+q.A+'</button>'
+'<button class="btn" data-pick="B" style="text-align:left;padding:14px"><b>B.</b> '+q.B+'</button>'
+'</div>'
+'<div style="display:flex;justify-content:center;margin-top:8px"><button class="btn" data-pick="C" style="padding:10px 20px">$U$ одинакова</button></div>'
+'<div class="feedback" id="p1-quiz-fb"></div>';
document.getElementById('p1-quiz-r').textContent = (i+1);
document.getElementById('p1-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('p1-quiz-fb');
if(pick === q.ans){
ok++;
fb.className = 'feedback ok'; fb.innerHTML = '&#10003; Верно. '+q.why;
addXp(3, 'p1-quiz'); bumpProgress('p1', 4);
} else {
fb.className = 'feedback fail'; fb.innerHTML = '&#10007; Не то. '+q.why;
}
document.getElementById('p1-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p1-quiz-next').addEventListener('click', ()=>{
i = (i+1) % QS.length; render();
});
render();
}
/* === §1 IV-3: DnD сортировка === */
function _initP1_dnd(){
const items = [
{id:'t', cat:'dep', html:'температура $T$'},
{id:'m', cat:'dep', html:'масса $m$'},
{id:'ag', cat:'dep', html:'агрегатное состояние'},
{id:'rd', cat:'dep', html:'род вещества'},
{id:'sp', cat:'indep', html:'скорость тела как целого'},
{id:'h', cat:'indep', html:'высота над землёй'},
{id:'fm', cat:'indep', html:'форма тела'},
{id:'cl', cat:'indep', html:'цвет тела'}
];
const wg = document.querySelector('#sec-p1 .wg:nth-of-type(7)'); /* IV-3 — 7-й (3 теории + IV1+IV2+IV3) */
const dnd = setupSorter({
poolId:'p1-dnd-pool',
scopeSelector:'#sec-p1',
cats:['dep','indep'],
items: items,
columnLayout:false
});
document.getElementById('p1-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p1-dnd-fb');
let wrong = 0, total = items.length;
items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong === 0){
fb.className = 'feedback ok'; fb.innerHTML = '&#10003; Идеально! +15 XP. $U$ — это про молекулы, а не про движение тела или его положение.';
addXp(15, 'p1-dnd'); bumpProgress('p1', 20);
} else {
fb.className = 'feedback fail'; fb.innerHTML = '&#10007; Ошибок: '+wrong+' из '+total+'. Помните: $U$ зависит от $T$, $m$, агрегатного состояния и рода вещества.';
}
renderMath(fb);
});
document.getElementById('p1-dnd-reset').addEventListener('click', ()=>{
dnd.reset();
const fb = document.getElementById('p1-dnd-fb'); fb.style.display='none';
});
}
/* === §1 IV-4: MCQ-тренажёр === */
function _initP1_mcq(){
const QS = [
{q:'Из чего складывается внутренняя энергия?', opts:['Только из $E_k$ молекул','$E_k$ + $E_p$ всех молекул','Из массы и объёма','Из температуры'], ans:1, why:'$U$ — сумма кинетических энергий хаотического движения и потенциальных энергий взаимодействия молекул.'},
{q:'При нагревании тела его внутренняя энергия…', opts:['уменьшается','растёт','не меняется','становится отрицательной'], ans:1, why:'Молекулы движутся быстрее $\\Rightarrow$ $U$ растёт.'},
{q:'Зависит ли $U$ от скорости движения тела как целого?', opts:['да','нет','только для газов','только при $v &gt; 100$ м/с'], ans:1, why:'$U$ зависит только от <i>хаотического</i> движения молекул внутри тела.'},
{q:'У какого тела $U$ больше: у стакана воды на столе или у того же стакана, поднятого на 1 м?', opts:['на полу','на полке','одинакова','зависит от формы стакана'], ans:2, why:'Высота не влияет на $U$ — это про механическую $E_p$, а не про внутреннюю.'},
{q:'Изменится ли $U$, если вещество переходит из твёрдого в жидкое состояние при той же температуре?', opts:['нет','да, растёт','да, уменьшается','зависит от формы тела'], ans:1, why:'При плавлении рвутся связи между молекулами, поглощается теплота $\\Rightarrow$ $U$ растёт.'},
{q:'Какое из утверждений верно?', opts:['$U$ — это $mv^2/2$ тела','$U$ — это $mgh$ тела','$U$ — это $E_k+E_p$ молекул внутри','$U$ — это объём $\\times$ давление'], ans:2, why:'$U$ — энергия молекул, не имеет отношения к движению или положению тела как целого.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i];
const wrap = document.getElementById('p1-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="p1-mcq-fb"></div>';
h += '<div class="actions"><button class="btn" id="p1-mcq-next">Следующий вопрос</button></div>';
wrap.innerHTML = h;
document.getElementById('p1-mcq-i').textContent = (i+1);
document.getElementById('p1-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('p1-mcq-fb');
if(k === q.ans){
ok++; done++;
fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why;
addXp(2,'p1-mcq'); bumpProgress('p1', 3);
} else {
done++;
fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why;
}
document.getElementById('p1-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){
awarded = true;
setTimeout(()=>{
const wgFb = document.getElementById('p1-mcq-fb');
wgFb.className='feedback ok';
wgFb.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').';
addXp(15,'p1-mcq-bonus'); bumpProgress('p1', 15);
}, 600);
}
});
});
const nextBtn = document.getElementById('p1-mcq-next');
if(nextBtn) nextBtn.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §2 — Способы изменения внутренней энергии ======== */
function build_p2(){
const box = document.getElementById('p2-body');
let h = '';
h += makeCard('theory', 'Два способа', '§ 2.1',
'<p>Внутреннюю энергию тела $U$ можно изменить двумя принципиально разными способами:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>Совершение работы.</b> Над телом или само тело совершает работу (трение, сжатие газа, удар, деформация). $U$ растёт.</li>'
+'<li><b>Теплопередача.</b> Энергия передаётся от более горячего тела к более холодному без совершения работы.</li>'
+'</ul>'
+'<p>Изменение внутренней энергии обозначают $\\Delta U$. Если $T$ растёт — $\\Delta U > 0$, если падает — $\\Delta U < 0$.</p>'
);
h += makeCard('rule', 'Три вида теплопередачи', '§ 2.2',
'<p>Теплопередача бывает трёх видов:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li><b>Теплопроводность</b> (§ 3) — через прямой контакт частиц. Металлическая ложка в чае нагревается.</li>'
+'<li><b>Конвекция</b> (§ 4) — потоками жидкости или газа. Радиатор греет комнату.</li>'
+'<li><b>Излучение</b> (§ 5) — электромагнитными волнами, даже через вакуум. Солнце греет Землю.</li>'
+'</ol>'
);
h += makeCard('example', 'Примеры из жизни', '§ 2.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Трёшь ладони — нагреваются. <b>Работа</b> сил трения.</li>'
+'<li>Спички в коробке встряхиваешь — нагреваются. <b>Работа</b>.</li>'
+'<li>Чай в стакане остывает. <b>Теплопередача</b> (излучение + конвекция).</li>'
+'<li>Ложка в горячем чае нагревается. <b>Теплопередача</b> (теплопроводность).</li>'
+'<li>Велосипедный насос нагревается при работе. <b>Работа</b> (сжатие газа).</li>'
+'</ul>'
);
/* IV1 — анимация: трение vs контакт */
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="p2-sim" viewBox="0 0 460 200" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="actions" style="margin-top:10px"><button class="btn primary" id="p2-sim-start">Запустить</button><button class="btn" id="p2-sim-reset">Сброс</button></div>'
+'<div class="score-display" style="margin-top:10px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>Брусок (трение): $T = $ <b id="p2-tA">20</b> &#176;C, $\\Delta U$ <b id="p2-uA">растёт</b></span>'
+'<span>Контакт: $T_{гор} = $ <b id="p2-tH">90</b> &#176;C, $T_{хол} = $ <b id="p2-tC">10</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="p2-quiz"></div>'
+'<div class="actions"><button class="btn" id="p2-quiz-next">Следующий раунд</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p2-quiz-r">1</b> / 8</span><span>Правильно: <b id="p2-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD «работа vs теплопередача» */
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">Перетащите чипы в нужную колонку. <b>Работа</b> — когда силы перемещают что-то (трение, сжатие, удар). <b>Теплопередача</b> — без работы, просто через контакт, поток или излучение.</div>'
+'<div id="p2-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="work"></div></div>'
+'<div class="drop-box"><h5>Теплопередача</h5><div class="drop-items" data-cat="heat"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p2-dnd-check">Проверить</button><button class="btn" id="p2-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p2-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="p2-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p2-mcq-i">1</b> / 6</span><span>Правильно: <b id="p2-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p2') + readButton('p2');
renderMath(box);
wireReadBtn('p2');
_initP2_sim();
_initP2_quiz();
_initP2_dnd();
_initP2_mcq();
}
/* === §2 IV-1: симуляция «работа vs теплопередача» === */
function _initP2_sim(){
_killSim('p2sim');
const svg = document.getElementById('p2-sim'); if(!svg) return;
const W=460, H=200;
/* левая сцена: брусок скользит по доске */
/* правая сцена: два тела касаются, температуры → среднее */
let running = false;
let tA = 20; /* температура бруска */
let tH = 90, tC = 10; /* контакт горячий/холодный */
let blockX = 60;
let t = 0;
function reset(){
tA = 20; tH = 90; tC = 10; blockX = 60; t = 0;
document.getElementById('p2-tA').textContent = '20';
document.getElementById('p2-uA').textContent = '0';
document.getElementById('p2-tH').textContent = '90';
document.getElementById('p2-tC').textContent = '10';
paint();
}
function paint(){
let s = '';
/* === ЛЕВАЯ СЦЕНА: трение === */
/* доска */
s += '<rect x="20" y="120" width="200" height="14" fill="#a16207" stroke="#0f172a" stroke-width="1.5" rx="2"/>';
/* штриховка под доской */
for(let i=22;i<218;i+=10) s += '<line x1="'+i+'" y1="134" x2="'+(i+4)+'" y2="140" stroke="#92400e" stroke-width="1"/>';
/* брусок (цвет по температуре) */
const cA = window.PHYS.tempColor(tA, 20, 80);
s += '<rect x="'+blockX+'" y="92" width="50" height="28" fill="'+cA+'" stroke="#0f172a" stroke-width="1.6" rx="3"/>';
/* стрелка-сила F (рука толкает) */
s += window.PHYS.drawArrow ? window.PHYS.drawArrow(blockX-30, 106, blockX-5, 106, '#10b981', 2.2, 9) : '';
s += '<text x="'+(blockX-32)+'" y="100" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#10b981">F</text>';
/* трение (внизу бруска) */
s += '<text x="'+(blockX+25)+'" y="80" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#dc2626">трение нагревает</text>';
/* термометр для бруска */
s += window.PHYS.thermometer(220, 50, 80, 0, 100, tA);
/* подпись */
s += '<text x="120" y="170" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">РАБОТА (трение)</text>';
/* === разделитель === */
s += '<line x1="240" y1="20" x2="240" y2="180" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="4 4"/>';
/* === ПРАВАЯ СЦЕНА: контакт === */
const cH = window.PHYS.tempColor(tH, 0, 100);
const cCo = window.PHYS.tempColor(tC, 0, 100);
s += '<rect x="270" y="80" width="60" height="60" fill="'+cH+'" stroke="#0f172a" stroke-width="1.6" rx="4"/>';
s += '<rect x="335" y="80" width="60" height="60" fill="'+cCo+'" stroke="#0f172a" stroke-width="1.6" rx="4"/>';
/* стрелки потока тепла */
s += window.PHYS.drawArrow ? window.PHYS.drawArrow(326, 100, 344, 100, '#dc2626', 2.2, 8) : '';
s += window.PHYS.drawArrow ? window.PHYS.drawArrow(326, 120, 344, 120, '#dc2626', 2.2, 8) : '';
s += '<text x="335" y="72" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#dc2626">Q</text>';
/* подписи температур */
s += '<text x="300" y="160" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a">'+tH.toFixed(0)+' &#176;C</text>';
s += '<text x="365" y="160" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a">'+tC.toFixed(0)+' &#176;C</text>';
s += '<text x="332" y="174" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">ТЕПЛОПЕРЕДАЧА</text>';
svg.innerHTML = s;
}
function tick(){
if(!running){ _SIMS.p2sim.raf = requestAnimationFrame(tick); return; }
if(!_isVisible('p2')){ _SIMS.p2sim.raf = requestAnimationFrame(tick); return; }
t += 1;
/* брусок двигается слева направо и нагревается */
blockX += 0.8;
if(blockX > 170){ blockX = 60; }
if(tA < 80) tA += 0.18;
/* контакт: tH и tC сходятся к среднему */
const avg = (tH + tC) / 2;
if(Math.abs(tH - avg) > 0.05){
tH -= (tH - avg) * 0.012;
tC += (avg - tC) * 0.012;
}
document.getElementById('p2-tA').textContent = tA.toFixed(0);
document.getElementById('p2-uA').textContent = (tA - 20).toFixed(0) === '0' ? '0' : '+'+(tA - 20).toFixed(0);
document.getElementById('p2-tH').textContent = tH.toFixed(0);
document.getElementById('p2-tC').textContent = tC.toFixed(0);
paint();
_SIMS.p2sim.raf = requestAnimationFrame(tick);
}
document.getElementById('p2-sim-start').addEventListener('click', ()=>{
running = !running;
document.getElementById('p2-sim-start').textContent = running ? 'Пауза' : 'Запустить';
});
document.getElementById('p2-sim-reset').addEventListener('click', ()=>{
running = false;
document.getElementById('p2-sim-start').textContent = 'Запустить';
reset();
});
_SIMS.p2sim = { raf: 0 };
reset();
_SIMS.p2sim.raf = requestAnimationFrame(tick);
}
/* === §2 IV-2: викторина работа/теплопередача === */
function _initP2_quiz(){
const QS = [
{sit:'Долго трёшь руки одна о другую — они нагреваются.', ans:'W', why:'Работа сил трения переходит во внутреннюю энергию рук.'},
{sit:'Чай в стакане остывает на столе.', ans:'H', why:'Теплопередача от чая в воздух и стенки стакана — без совершения работы.'},
{sit:'Велосипедный насос нагревается при накачивании колеса.', ans:'W', why:'Воздух в насосе сжимается — над ним совершается работа.'},
{sit:'Металлическая ложка в горячем чае нагревается.', ans:'H', why:'Теплопередача (теплопроводность) от чая к ложке.'},
{sit:'Гвоздь забивают молотком — шляпка гвоздя нагревается.', ans:'W', why:'Кинетическая энергия молотка переходит в работу деформации/трения.'},
{sit:'Лёд в холодильнике плавится, если открыть дверцу.', ans:'H', why:'Тёплый воздух комнаты передаёт энергию льду.'},
{sit:'Греется проволока в фене — горячий воздух дует на руки.', ans:'H', why:'Конвекция: поток горячего воздуха переносит энергию.'},
{sit:'Спички в коробке от долгой тряски нагрелись.', ans:'W', why:'Работа сил трения внутри коробки.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i];
const wrap = document.getElementById('p2-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="W" style="padding:14px"><b>Работа</b></button>'
+'<button class="btn" data-pick="H" style="padding:14px"><b>Теплопередача</b></button>'
+'</div>'
+'<div class="feedback" id="p2-quiz-fb"></div>';
document.getElementById('p2-quiz-r').textContent = (i+1);
document.getElementById('p2-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('p2-quiz-fb');
if(pick === q.ans){
ok++;
fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why;
addXp(3,'p2-quiz'); bumpProgress('p2', 4);
} else {
fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why;
}
document.getElementById('p2-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p2-quiz-next').addEventListener('click', ()=>{
i = (i+1) % QS.length; render();
});
render();
}
/* === §2 IV-3: DnD сортировка === */
function _initP2_dnd(){
const items = [
{id:'a', cat:'work', html:'трение ладонями'},
{id:'b', cat:'work', html:'сжатие воздуха в насосе'},
{id:'c', cat:'work', html:'удар молотком по гвоздю'},
{id:'d', cat:'work', html:'строгание доски рубанком'},
{id:'e', cat:'heat', html:'остывание чая в стакане'},
{id:'f', cat:'heat', html:'нагрев ложки в супе'},
{id:'g', cat:'heat', html:'Солнце греет асфальт'},
{id:'h', cat:'heat', html:'батарея греет комнату'}
];
const dnd = setupSorter({
poolId:'p2-dnd-pool',
scopeSelector:'#sec-p2',
cats:['work','heat'],
items: items,
columnLayout:false
});
document.getElementById('p2-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p2-dnd-fb');
let wrong = 0, total = items.length;
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,'p2-dnd'); bumpProgress('p2', 20);
} else {
fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+' из '+total+'. Подсказка: трение, сжатие, удар, строгание — это работа.';
}
});
document.getElementById('p2-dnd-reset').addEventListener('click', ()=>{
dnd.reset();
const fb = document.getElementById('p2-dnd-fb'); fb.style.display='none';
});
}
/* === §2 IV-4: MCQ тренажёр === */
function _initP2_mcq(){
const QS = [
{q:'Какими способами можно изменить $U$ тела?', opts:['Только работой','Только теплопередачей','Работой и теплопередачей','Только нагревом'], ans:2, why:'Это два равноправных способа.'},
{q:'Что нагревает наковальню при ковке?', opts:['Теплопередача','Работа удара','Излучение','Конвекция'], ans:1, why:'Кинетическая энергия молотка совершает работу деформации.'},
{q:'Какой вид теплопередачи греет руку от костра, если не подходить близко?', opts:['Теплопроводность','Конвекция','Излучение','Работа'], ans:2, why:'Через воздух (плохой проводник) и без контакта — это излучение.'},
{q:'Чай в термосе долго не остывает, потому что…', opts:['у термоса большая масса','стенки термоса тонкие','двойные стенки и вакуум между ними подавляют все виды теплопередачи','чай совершает работу'], ans:2, why:'Вакуум — нет конвекции и проводности; зеркальные стенки уменьшают излучение.'},
{q:'Какой пример НЕ относится к теплопередаче?', opts:['Ложка нагрелась в супе','Воздух у радиатора поднимается вверх','Снег тает на солнце','Гвоздь нагрелся, когда его забили молотком'], ans:3, why:'Гвоздь нагрелся работой удара.'},
{q:'Знак $\\Delta U$ для остывающего тела?', opts:['$\\Delta U > 0$','$\\Delta U < 0$','$\\Delta U = 0$','зависит от массы'], ans:1, why:'$T$ падает $\\Rightarrow$ $U$ уменьшается $\\Rightarrow$ $\\Delta U < 0$.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i];
const wrap = document.getElementById('p2-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="p2-mcq-fb"></div>';
h += '<div class="actions"><button class="btn" id="p2-mcq-next">Следующий вопрос</button></div>';
wrap.innerHTML = h;
document.getElementById('p2-mcq-i').textContent = (i+1);
document.getElementById('p2-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('p2-mcq-fb');
if(k === q.ans){
ok++; done++;
fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why;
addXp(2,'p2-mcq'); bumpProgress('p2', 3);
} else {
done++;
fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why;
}
document.getElementById('p2-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){
awarded = true;
setTimeout(()=>{
const wgFb = document.getElementById('p2-mcq-fb');
wgFb.className='feedback ok';
wgFb.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').';
addXp(15,'p2-mcq-bonus'); bumpProgress('p2', 15);
}, 600);
}
});
});
const nextBtn = document.getElementById('p2-mcq-next');
if(nextBtn) nextBtn.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
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();
}
/* ======================================================================
PHASE 1 · WAVE 3 — §6, §7
====================================================================== */
/* Таблица удельных теплоёмкостей (Дж/(кг·К)) */
const MAT_C = [
{key:'water', name:'вода', c:4200},
{key:'ice', name:'лёд', c:2100},
{key:'oil', name:'масло раст.',c:2000},
{key:'wood', name:'дерево', c:2400},
{key:'glass', name:'стекло', c:840 },
{key:'al', name:'алюминий', c:920 },
{key:'fe', name:'железо', c:460 },
{key:'brass', name:'латунь', c:380 },
{key:'cu', name:'медь', c:380 },
{key:'lead', name:'свинец', c:130 },
{key:'hg', name:'ртуть', c:140 }
];
/* Таблица удельных теплот сгорания (Дж/кг) */
const MAT_Q = [
{key:'wood', name:'дрова', q:1.0e7},
{key:'peat', name:'торф', q:1.5e7},
{key:'coal', name:'каменный уголь', q:3.0e7},
{key:'alc', name:'спирт', q:2.7e7},
{key:'oil', name:'нефть', q:4.4e7},
{key:'gas', name:'природный газ', q:4.4e7},
{key:'kero', name:'керосин', q:4.6e7},
{key:'gasoline',name:'бензин', q:4.6e7}
];
/* ======== §6 — Q = cm ΔT ======== */
function build_p6(){
const box = document.getElementById('p6-body');
let h = '';
h += makeCard('theory', 'Закон нагревания', '§ 6.1',
'<p>Чтобы изменить температуру тела массой $m$ на $\\Delta T = T_2 - T_1$, нужно сообщить ему (или отнять у него) количество теплоты:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q = c\\,m\\,\\Delta T$$</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>$Q$ — количество теплоты, Дж;</li>'
+'<li>$c$ — <b>удельная теплоёмкость</b> вещества, Дж/(кг·К);</li>'
+'<li>$m$ — масса, кг;</li>'
+'<li>$\\Delta T$ — изменение температуры, К или &#176;C (разница одна).</li>'
+'</ul>'
+'<p>Если $T_2 > T_1$ — тело нагревается, $Q > 0$. Если $T_2 < T_1$ — остывает, $Q < 0$.</p>'
);
h += makeCard('rule', 'Что такое удельная теплоёмкость', '§ 6.2',
'<p><b>$c$</b> — количество теплоты, нужное чтобы нагреть <b>1 кг</b> вещества на <b>1 К</b> (или 1 &#176;C).</p>'
+'<p>У воды $c = 4200$ Дж/(кг·К) — это очень много. Поэтому:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>вода долго греется на плите;</li>'
+'<li>вода долго остывает (поэтому грелка из воды держит тепло);</li>'
+'<li>океан смягчает климат прибрежных стран.</li>'
+'</ul>'
+'<p>У металлов $c$ маленькая: медь 380, железо 460. Они быстро нагреваются и быстро остывают.</p>'
);
h += makeCard('example', 'Уравнение теплового баланса', '§ 6.3',
'<p>Когда горячее тело отдаёт тепло холодному в изолированной системе (без потерь), вся отданная теплота получена холодным:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q_{отд} = Q_{пол}$$</p>'
+'<p>Если смешать массы $m_1$ при $T_1$ и $m_2$ при $T_2$ одного вещества:</p>'
+'<p style="text-align:center">$$T = \\dfrac{m_1 T_1 + m_2 T_2}{m_1 + m_2}$$</p>'
+'<p>Это <b>средневзвешенная</b> температура.</p>'
);
/* IV1 — калькулятор Q = cmΔT с анимацией */
let optsC = '';
MAT_C.forEach(m=>{ optsC += '<option value="'+m.c+'">'+m.name+' (c = '+m.c+')</option>'; });
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Калькулятор $Q = cm\\Delta T$</div></div>'
+'<div class="wg-help">Выбери вещество, массу и изменение температуры — увидь, сколько энергии нужно. Термометр покажет процесс.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Вещество: <select id="p6-mat" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'+optsC+'</select></label>'
+'<label>$m$, кг: <b id="p6-mv">1.0</b><input type="range" id="p6-m" min="0.1" max="10" step="0.1" value="1"></label>'
+'<label>$\\Delta T$, &#176;C: <b id="p6-dv">50</b><input type="range" id="p6-d" min="-50" max="100" step="5" value="50"></label>'
+'</div>'
+'<svg id="p6-sim" viewBox="0 0 460 160" 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>$c$ = <b id="p6-cv">4200</b> Дж/(кг·К)</span>'
+'<span>$Q$ = <b id="p6-q">2.10 &times; 10^5</b> Дж = <b id="p6-qkj">210</b> кДж</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Это столько энергии съедает плита или электрочайник.</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">В термос налили $m_1$ кг воды при $T_1$ &#176;C и долили $m_2$ кг воды при $T_2$ &#176;C. Какая будет итоговая температура (без потерь)? Формула: $T = (m_1 T_1 + m_2 T_2)/(m_1 + m_2)$.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$m_1$, кг: <b id="p6-mxv1">1.0</b><input type="range" id="p6-mx1" min="0.1" max="5" step="0.1" value="1"></label>'
+'<label>$T_1$, &#176;C: <b id="p6-txv1">90</b><input type="range" id="p6-tx1" min="0" max="100" step="5" value="90"></label>'
+'<label>$m_2$, кг: <b id="p6-mxv2">2.0</b><input type="range" id="p6-mx2" min="0.1" max="5" step="0.1" value="2"></label>'
+'<label>$T_2$, &#176;C: <b id="p6-txv2">10</b><input type="range" id="p6-tx2" min="0" max="100" step="5" value="10"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:6px"><span>$T_{итог} = $ <b id="p6-tres">36.7</b> &#176;C</span></div>'
+'</div>';
/* IV3 — DnD ранжирование c */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">У какого вещества $c$ больше?</div></div>'
+'<div class="wg-help">Перетащи вещества так, чтобы они шли от <b>меньшего</b> $c$ к <b>большему</b>. Подсказка: у воды $c$ рекордно велика, у металлов — мала.</div>'
+'<div id="p6-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="p6-dnd-check">Проверить</button><button class="btn" id="p6-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p6-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. Допуск ±2%.</div>'
+'<div id="p6-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p6-task-i">1</b> / 6</span><span>Правильно: <b id="p6-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p6') + readButton('p6');
renderMath(box);
wireReadBtn('p6');
_initP6_calc();
_initP6_mix();
_initP6_dnd();
_initP6_tasks();
}
function _initP6_calc(){
const svg = document.getElementById('p6-sim'); if(!svg) return;
function fmtSci(x){
if(Math.abs(x) < 1000) return x.toFixed(0);
const e = Math.floor(Math.log10(Math.abs(x)));
const mant = (x / Math.pow(10, e)).toFixed(2);
return mant+' &times; 10<sup>'+e+'</sup>';
}
function update(){
const c = +document.getElementById('p6-mat').value;
const m = +document.getElementById('p6-m').value;
const d = +document.getElementById('p6-d').value;
document.getElementById('p6-mv').textContent = m.toFixed(1);
document.getElementById('p6-dv').textContent = d;
document.getElementById('p6-cv').textContent = c;
const Q = c * m * d;
document.getElementById('p6-q').innerHTML = fmtSci(Q);
document.getElementById('p6-qkj').textContent = (Q/1000).toFixed(1);
/* sim: куб + термометр, цвет по итоговой T (20 + d) */
const Tfinal = 20 + d;
const col = window.PHYS.tempColor(Tfinal, -50, 150);
let s = '';
s += '<rect x="60" y="40" width="100" height="80" fill="'+col+'" stroke="#0f172a" stroke-width="2" rx="6"/>';
s += '<text x="110" y="86" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="'+(Tfinal>50?'#fff':'#0f172a')+'">m = '+m.toFixed(1)+' кг</text>';
s += '<text x="110" y="140" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#475569">'+(d>0?'нагреваем':d<0?'охлаждаем':'без изменения')+'</text>';
/* термометр */
s += window.PHYS.thermometer(220, 30, 100, -50, 150, Tfinal);
/* подпись Q */
s += '<text x="320" y="60" font-family="JetBrains Mono,monospace" font-size="13" fill="#0f172a">Q = c &middot; m &middot; &Delta;T</text>';
s += '<text x="320" y="82" font-family="JetBrains Mono,monospace" font-size="12" fill="#475569">= '+c+' &middot; '+m.toFixed(1)+' &middot; '+d+'</text>';
s += '<text x="320" y="104" font-family="JetBrains Mono,monospace" font-size="14" font-weight="800" fill="'+(Q>=0?'#dc2626':'#2563eb')+'">Q = '+(Q/1000).toFixed(1)+' кДж</text>';
svg.innerHTML = s;
}
document.getElementById('p6-mat').addEventListener('change', update);
document.getElementById('p6-m').addEventListener('input', update);
document.getElementById('p6-d').addEventListener('input', update);
update();
}
function _initP6_mix(){
function update(){
const m1 = +document.getElementById('p6-mx1').value;
const t1 = +document.getElementById('p6-tx1').value;
const m2 = +document.getElementById('p6-mx2').value;
const t2 = +document.getElementById('p6-tx2').value;
document.getElementById('p6-mxv1').textContent = m1.toFixed(1);
document.getElementById('p6-txv1').textContent = t1;
document.getElementById('p6-mxv2').textContent = m2.toFixed(1);
document.getElementById('p6-txv2').textContent = t2;
const T = (m1*t1 + m2*t2) / (m1 + m2);
document.getElementById('p6-tres').textContent = T.toFixed(1);
}
['p6-mx1','p6-tx1','p6-mx2','p6-tx2'].forEach(id => document.getElementById(id).addEventListener('input', update));
update();
}
function _initP6_dnd(){
/* 5 веществ по возрастанию c: свинец(130) → железо(460) → стекло(840) → дерево(2400) → вода(4200) */
const items = [
{id:'pb', cat:'r1', html:'свинец (130)'},
{id:'fe', cat:'r2', html:'железо (460)'},
{id:'gl', cat:'r3', html:'стекло (840)'},
{id:'wd', cat:'r4', html:'дерево (2400)'},
{id:'wa', cat:'r5', html:'вода (4200)'}
];
const dnd = setupSorter({ poolId:'p6-dnd-pool', scopeSelector:'#sec-p6', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p6-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p6-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. У воды $c$ рекордно велика — её сложно нагреть и сложно охладить.'; addXp(15,'p6-dnd'); bumpProgress('p6', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: металлы — мало, вода — рекорд.'; }
});
document.getElementById('p6-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p6-dnd-fb'); fb.style.display='none'; });
}
function _initP6_tasks(){
const TASKS = [
{q:'Сколько энергии в кДж нужно, чтобы нагреть 2 кг воды от 20 до 100 &#176;C? ($c_{воды} = 4200$)', ans: 672, tol: 14, why:'$Q = 4200 \\cdot 2 \\cdot 80 = 672\\,000$ Дж = $672$ кДж.'},
{q:'Какую массу алюминия (в кг) можно нагреть на 50 &#176;C, имея 92 кДж? ($c = 920$)', ans: 2, tol: 0.05, why:'$m = Q/(c \\Delta T) = 92\\,000/(920 \\cdot 50) = 2$ кг.'},
{q:'На сколько градусов нагреется 0,5 кг меди при $Q = 19\\,000$ Дж? ($c = 380$)', ans: 100, tol: 2, why:'$\\Delta T = Q/(cm) = 19\\,000/(380 \\cdot 0{,}5) = 100$ К.'},
{q:'Смешали 1 кг воды при 80 &#176;C и 3 кг воды при 20 &#176;C. Какая итоговая температура (&#176;C)?', ans: 35, tol: 0.5, why:'$T = (1 \\cdot 80 + 3 \\cdot 20)/4 = 140/4 = 35$ &#176;C.'},
{q:'Какова удельная теплоёмкость (Дж/(кг·К)) вещества, если 3 кг его при $\\Delta T = 40$ К получили 48 кДж?', ans: 400, tol: 10, why:'$c = Q/(m\\Delta T) = 48\\,000/(3 \\cdot 40) = 400$. Это близко к меди.'},
{q:'Найди $Q$ (в кДж) для остывания 4 кг железа с 200 до 50 &#176;C. ($c = 460$). Запиши абсолютную величину.', ans: 276, tol: 6, why:'$|Q| = 460 \\cdot 4 \\cdot 150 = 276\\,000$ Дж = $276$ кДж.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p6-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="p6-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p6-task-go">Ответ</button>'
+'<button class="btn" id="p6-task-hint">Подсказка</button>'
+'<button class="btn" id="p6-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p6-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p6-task-fb"></div>';
document.getElementById('p6-task-i').textContent = (i+1);
document.getElementById('p6-task-ok').textContent = ok;
document.getElementById('p6-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p6-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p6-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,'p6-task'); bumpProgress('p6', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Правильный ответ: '+t.ans+'. '+t.why; }
document.getElementById('p6-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p6-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p6-task-bonus'); bumpProgress('p6', 15); }, 600); }
});
document.getElementById('p6-task-hint').addEventListener('click', ()=>{ document.getElementById('p6-task-hint-txt').classList.toggle('show'); });
document.getElementById('p6-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §7 — Q = qm ======== */
function build_p7(){
const box = document.getElementById('p7-body');
let h = '';
h += makeCard('theory', 'Горение и теплота сгорания', '§ 7.1',
'<p>При <b>горении</b> топливо реагирует с кислородом и выделяет энергию в виде тепла. Энергия эта запасена в химических связях молекул топлива.</p>'
+'<p>Количество теплоты, выделившееся при <b>полном сгорании</b> массы $m$ топлива:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q = q\\,m$$</p>'
+'<p>$q$ — <b>удельная теплота сгорания</b>, Дж/кг. Это энергия, которую даёт сгорание 1 кг данного топлива.</p>'
);
h += makeCard('rule', 'Топлива и их $q$', '§ 7.2',
'<table style="width:100%;border-collapse:collapse;font-size:.92rem"><thead><tr style="background:rgba(15,23,42,.04)"><th style="padding:6px;text-align:left">Топливо</th><th style="padding:6px;text-align:right">$q$, $\\dfrac{\\text{Дж}}{\\text{кг}}$</th></tr></thead><tbody>'
+ MAT_Q.map(m=>'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">'+m.name+'</td><td style="padding:6px;text-align:right;border-bottom:1px dashed var(--border)"><code>'+m.q.toExponential(1).replace('+','')+'</code></td></tr>').join('')
+'</tbody></table>'
+'<p style="margin-top:8px">Чем больше $q$, тем «энергоёмче» топливо. Бензин примерно в 4,5 раза мощнее дров на кг.</p>'
);
h += makeCard('example', 'КПД нагревательного устройства', '§ 7.3',
'<p>Реально <b>не вся</b> выделившаяся при сгорании энергия идёт на полезное дело (нагрев воды, движение машины). Часть теряется в виде тепла в окружающую среду.</p>'
+'<p>$$\\eta = \\dfrac{Q_{пол}}{Q_{сгор}} = \\dfrac{Q_{пол}}{q m}$$</p>'
+'<p>У хорошего котла $\\eta \\approx 80\\%$, у плохой буржуйки — 30%.</p>'
);
/* IV1 — калькулятор Q = qm */
let optsQ = '';
MAT_Q.forEach(m=>{ optsQ += '<option value="'+m.q+'">'+m.name+' ($q = '+m.q.toExponential(1).replace('+','').replace('.','{,}')+'$)</option>'; });
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Калькулятор $Q = qm$</div></div>'
+'<div class="wg-help">Выбери топливо и массу — увидь, сколько энергии выделится при его полном сгорании.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Топливо: <select id="p7-fuel" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'
+ MAT_Q.map(m=>'<option value="'+m.q+'">'+m.name+' (q='+m.q.toExponential(1).replace('+','')+')</option>').join('')
+'</select></label>'
+'<label>$m$, кг: <b id="p7-mv">1.0</b><input type="range" id="p7-m" min="0.1" max="20" step="0.1" value="1"></label>'
+'</div>'
+'<svg id="p7-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>$q$ = <b id="p7-qv">10<sup>7</sup></b> Дж/кг</span>'
+'<span>$Q$ = <b id="p7-q">10 МДж</b> = <b id="p7-qkw">2.8</b> кВт·ч</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Эквивалент: нагрев <b id="p7-eqkg">30</b> кг воды от 20 до 100 &#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="p7-quiz"></div>'
+'<div class="actions"><button class="btn" id="p7-quiz-next">Следующий раунд</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p7-quiz-r">1</b> / 6</span><span>Правильно: <b id="p7-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">Расставь топлива по $q$ (по возрастанию)</div></div>'
+'<div class="wg-help">От самого слабого до самого мощного по теплоте сгорания.</div>'
+'<div id="p7-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="p7-dnd-check">Проверить</button><button class="btn" id="p7-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p7-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. Допуск ±3 %.</div>'
+'<div id="p7-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p7-task-i">1</b> / 5</span><span>Правильно: <b id="p7-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p7') + readButton('p7');
renderMath(box);
wireReadBtn('p7');
_initP7_calc();
_initP7_quiz();
_initP7_dnd();
_initP7_tasks();
}
function _initP7_calc(){
const svg = document.getElementById('p7-sim'); if(!svg) return;
function update(){
const q = +document.getElementById('p7-fuel').value;
const m = +document.getElementById('p7-m').value;
document.getElementById('p7-mv').textContent = m.toFixed(1);
document.getElementById('p7-qv').innerHTML = q.toExponential(1).replace('+','').replace('e', ' &times; 10<sup>')+'</sup>';
const Q = q * m;
document.getElementById('p7-q').innerHTML = (Q/1e6).toFixed(1)+' МДж';
document.getElementById('p7-qkw').textContent = (Q/3.6e6).toFixed(2);
/* эквивалент: нагрев воды от 20 до 100 → ΔT=80, Q=cmΔT, m = Q/(c·80) */
document.getElementById('p7-eqkg').textContent = (Q/(4200*80)).toFixed(1);
/* SVG: огонь + котелок */
let s = '';
/* печь */
s += '<rect x="40" y="50" width="120" height="80" fill="#374151" stroke="#0f172a" stroke-width="2" rx="4"/>';
/* пламя */
const flameH = 30 + Math.min(50, m*5);
s += '<path d="M 70 90 Q 65 '+(90-flameH)+' 80 '+(80-flameH/2)+' Q 90 '+(85-flameH)+' 100 '+(70-flameH/2)+' Q 110 '+(85-flameH)+' 120 '+(80-flameH/2)+' Q 135 '+(90-flameH)+' 130 90 Z" fill="#f59e0b"/>';
s += '<path d="M 80 90 Q 78 '+(90-flameH*0.6)+' 90 '+(85-flameH*0.3)+' Q 100 '+(88-flameH*0.6)+' 110 '+(85-flameH*0.3)+' Q 122 '+(90-flameH*0.6)+' 120 90 Z" fill="#fbbf24"/>';
/* топливо */
s += '<text x="100" y="115" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#fde047">'+m.toFixed(1)+' кг</text>';
/* стрелка-энергия */
s += '<text x="200" y="60" font-family="JetBrains Mono,monospace" font-size="13" fill="#0f172a">Q = q m</text>';
s += '<text x="200" y="84" font-family="JetBrains Mono,monospace" font-size="12" fill="#475569">= '+q.toExponential(1).replace('+','')+' &middot; '+m.toFixed(1)+'</text>';
s += '<text x="200" y="108" font-family="JetBrains Mono,monospace" font-size="14" font-weight="800" fill="#dc2626">Q = '+(Q/1e6).toFixed(1)+' МДж</text>';
s += '<text x="200" y="128" font-family="Inter,sans-serif" font-size="11" fill="#475569">'+(Q/3.6e6).toFixed(2)+' кВт&middot;ч</text>';
svg.innerHTML = s;
}
document.getElementById('p7-fuel').addEventListener('change', update);
document.getElementById('p7-m').addEventListener('input', update);
update();
}
function _initP7_quiz(){
const QS = [
{A:'дрова', B:'бензин', ans:'B', why:'У бензина $q \\approx 4{,}6 \\cdot 10^7$, у дров $\\approx 10^7$ — бензин в 4,5 раза мощнее.'},
{A:'торф', B:'каменный уголь', ans:'B', why:'У угля $q$ вдвое больше, чем у торфа.'},
{A:'спирт', B:'природный газ', ans:'B', why:'У газа $4{,}4 \\cdot 10^7$, у спирта $2{,}7 \\cdot 10^7$.'},
{A:'керосин', B:'дрова', ans:'A', why:'Керосин — продукт переработки нефти, $q$ в 4,6 раза больше.'},
{A:'нефть', B:'бензин', ans:'B', why:'У бензина чуть больше: $4{,}6$ против $4{,}4 \\cdot 10^7$.'},
{A:'торф', B:'дрова', ans:'B', why:'У дров $q \\approx 10^7$, у торфа $1{,}5 \\cdot 10^7$ — торф мощнее. Правильный ответ: <b>торф</b>, ответ A.', flip:true}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p7-quiz'); if(!wrap) return;
/* для flip-задачи правильный ответ A */
const correctAns = q.flip ? 'A' : q.ans;
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"><b>A.</b> '+q.A+'</button>'
+'<button class="btn" data-pick="B" style="padding:14px"><b>B.</b> '+q.B+'</button>'
+'</div>'
+'<div class="feedback" id="p7-quiz-fb"></div>';
document.getElementById('p7-quiz-r').textContent = (i+1);
document.getElementById('p7-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('p7-quiz-fb');
if(btn.dataset.pick === correctAns){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p7-quiz'); bumpProgress('p7', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p7-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p7-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP7_dnd(){
/* 5 топлив по возрастанию q: дрова(1) → торф(1.5) → спирт(2.7) → уголь(3) → бензин(4.6), все ×10^7 */
const items = [
{id:'wd', cat:'r1', html:'дрова ($10^7$)'},
{id:'pt', cat:'r2', html:'торф ($1{,}5 \\cdot 10^7$)'},
{id:'al', cat:'r3', html:'спирт ($2{,}7 \\cdot 10^7$)'},
{id:'cl', cat:'r4', html:'уголь ($3 \\cdot 10^7$)'},
{id:'gs', cat:'r5', html:'бензин ($4{,}6 \\cdot 10^7$)'}
];
const dnd = setupSorter({ poolId:'p7-dnd-pool', scopeSelector:'#sec-p7', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p7-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p7-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,'p7-dnd'); bumpProgress('p7', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: бензин — самый мощный из распространённых.'; }
});
document.getElementById('p7-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p7-dnd-fb'); fb.style.display='none'; });
}
function _initP7_tasks(){
const TASKS = [
{q:'Сколько энергии (в МДж) выделится при полном сгорании 2 кг бензина? ($q = 4{,}6 \\cdot 10^7$)', ans: 92, tol: 2, why:'$Q = qm = 4{,}6 \\cdot 10^7 \\cdot 2 = 9{,}2 \\cdot 10^7 = 92$ МДж.'},
{q:'Какую массу (в кг) дров нужно сжечь, чтобы получить 50 МДж энергии? ($q = 10^7$)', ans: 5, tol: 0.1, why:'$m = Q/q = 5 \\cdot 10^7 / 10^7 = 5$ кг.'},
{q:'При сгорании 0,5 кг угля выделилось 15 МДж. Чему равно $q$ (в МДж/кг)?', ans: 30, tol: 1, why:'$q = Q/m = 15/0{,}5 = 30$ МДж/кг — это каменный уголь.'},
{q:'Котёл с $\\eta = 80\\%$ сжёг 2 кг дров. Какая полезная энергия (в МДж)? ($q = 10^7$)', ans: 16, tol: 0.3, why:'$Q_{сгор}=2 \\cdot 10^7 = 20$ МДж. $Q_{пол} = \\eta \\cdot Q_{сгор} = 0{,}8 \\cdot 20 = 16$ МДж.'},
{q:'Сколько кг бензина даст столько же энергии, что и 9,2 кг дров? ($q_{дров}=10^7$, $q_{бенз}=4{,}6 \\cdot 10^7$)', ans: 2, tol: 0.05, why:'Энергия дров: $9{,}2 \\cdot 10^7$ Дж. $m_{бенз}=E/q_{бенз}=9{,}2 \\cdot 10^7/(4{,}6 \\cdot 10^7) = 2$ кг.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p7-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="p7-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p7-task-go">Ответ</button>'
+'<button class="btn" id="p7-task-hint">Подсказка</button>'
+'<button class="btn" id="p7-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p7-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p7-task-fb"></div>';
document.getElementById('p7-task-i').textContent = (i+1);
document.getElementById('p7-task-ok').textContent = ok;
document.getElementById('p7-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p7-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p7-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,'p7-task'); bumpProgress('p7', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Правильный ответ: '+t.ans+'. '+t.why; }
document.getElementById('p7-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p7-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p7-task-bonus'); bumpProgress('p7', 15); }, 600); }
});
document.getElementById('p7-task-hint').addEventListener('click', ()=>{ document.getElementById('p7-task-hint-txt').classList.toggle('show'); });
document.getElementById('p7-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 1 · WAVE 4 — §8, §9
====================================================================== */
/* Таблица температур плавления и λ */
const MAT_MELT = [
{key:'mercury',name:'ртуть', Tm:-39, lam:1.18e4},
{key:'ice', name:'лёд', Tm:0, lam:3.34e5},
{key:'lead', name:'свинец', Tm:327, lam:2.5e4},
{key:'zinc', name:'цинк', Tm:420, lam:1.12e5},
{key:'al', name:'алюминий', Tm:660, lam:3.9e5},
{key:'cu', name:'медь', Tm:1085, lam:2.1e5},
{key:'fe', name:'железо', Tm:1539, lam:2.7e5}
];
/* ======== §8 — Плавление и кристаллизация ======== */
function build_p8(){
const box = document.getElementById('p8-body');
let h = '';
h += makeCard('theory', 'Плавление', '§ 8.1',
'<p><b>Плавление</b> — переход вещества из твёрдого состояния в жидкое.</p>'
+'<p>У каждого <b>кристаллического</b> вещества есть своя <b>температура плавления</b> $T_{пл}$ — она одинакова и для плавления, и для обратного процесса — кристаллизации.</p>'
+'<p>Во время плавления температура смеси «твёрдое + жидкое» <b>не меняется</b>, пока всё не расплавится. Подведённая теплота тратится на разрушение кристаллической решётки.</p>'
);
h += makeCard('rule', 'График фазового перехода', '§ 8.2',
'<p>Если нагревать твёрдое тело и записывать температуру со временем, получится характерный график с <b>плато</b>:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li>нагрев твёрдого ($T$ растёт);</li>'
+'<li>плавление ($T = T_{пл} = $ const, идёт время);</li>'
+'<li>нагрев жидкости ($T$ снова растёт).</li>'
+'</ol>'
+'<p>При охлаждении — зеркальный процесс: $T$ падает $\\to$ плато кристаллизации $\\to$ $T$ продолжает падать.</p>'
);
h += makeCard('example', 'Примеры', '§ 8.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Лёд в стакане воды держит $T = 0$ &#176;C, пока не растает весь.</li>'
+'<li>Снег зимой переносит много энергии при таянии — поэтому весной долго холодно.</li>'
+'<li>Расплавленный металл (магма, чугун) застывает в кристаллы при остывании.</li>'
+'<li>Аморфные тела (стекло, смола) <b>не имеют</b> чёткой $T_{пл}$ — они плавятся плавно.</li>'
+'</ul>'
);
/* IV1 — главный визуал: график T(t) с плато */
let optsT = '';
MAT_MELT.forEach(m=>{ optsT += '<option value="'+m.Tm+'">'+m.name+' ($T_{пл}='+m.Tm+'$ &#176;C)</option>'; });
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">График плавления $T(t)$</div></div>'
+'<div class="wg-help">Выбери вещество — увидь характерный «ступенчатый» график. Время плато ∝ массе и удельной теплоте плавления.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Вещество: <select id="p8-mat" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'+optsT+'</select></label>'
+'<label>Длительность плато: <b id="p8-pv">средняя</b><input type="range" id="p8-p" min="1" max="5" step="1" value="3"></label>'
+'</div>'
+'<svg id="p8-sim" viewBox="0 0 460 280" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:3px">'
+'<span><span style="color:#dc2626">&#9632;</span> участок 1 — нагрев твёрдого</span>'
+'<span><span style="color:#f59e0b">&#9632;</span> участок 2 — плавление ($T = T_{пл}$)</span>'
+'<span><span style="color:#2563eb">&#9632;</span> участок 3 — нагрев жидкости</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="p8-quiz"></div>'
+'<div class="actions"><button class="btn" id="p8-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p8-quiz-r">1</b> / 6</span><span>Правильно: <b id="p8-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD веществ по T_пл */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Расставь по $T_{пл}$ (по возрастанию)</div></div>'
+'<div class="wg-help">От самого «мягкого» (плавится при низкой $T$) до самого тугоплавкого.</div>'
+'<div id="p8-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="p8-dnd-check">Проверить</button><button class="btn" id="p8-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p8-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="p8-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p8-mcq-i">1</b> / 6</span><span>Правильно: <b id="p8-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p8') + readButton('p8');
renderMath(box);
wireReadBtn('p8');
_initP8_graph();
_initP8_quiz();
_initP8_dnd();
_initP8_mcq();
}
function _initP8_graph(){
const svg = document.getElementById('p8-sim'); if(!svg) return;
function draw(){
const Tm = +document.getElementById('p8-mat').value;
const p = +document.getElementById('p8-p').value;
document.getElementById('p8-pv').textContent = ['очень кратк.','краткая','средняя','длинная','очень длин.'][p-1];
const W = 460, H = 280, pad = 36;
/* Сегменты графика T(t).
Подбор: ось T от (Tm - 60) до (Tm + 80), ось t от 0 до 10. */
const Tmin = Math.min(Tm - 60, -50);
const Tmax = Math.max(Tm + 100, 50);
const tStart = Tm - 50;
const plateauW = p * 0.8; /* 0.8..4 единиц времени */
const segs = [
{ tStart: 0, tEnd: 2, Tstart: tStart, Tend: Tm, label: 'твёрдое' },
{ tStart: 2, tEnd: 2+plateauW, Tstart: Tm, Tend: Tm, label: 'плавление' },
{ tStart: 2+plateauW,tEnd: 10, Tstart: Tm, Tend: Tm + 60, label: 'жидкость' }
];
const r = window.PHYS.phaseGraphTT(W, H, pad, segs, 10, Tmin, Tmax);
/* Подсветка плато */
const x1 = r.toX(2), x2 = r.toX(2+plateauW), yp = r.toY(Tm);
let extra = '<rect x="'+x1+'" y="'+(yp-10)+'" width="'+(x2-x1)+'" height="20" fill="#fef3c7" opacity="0.6"/>';
/* Подпись T_пл */
extra += '<line x1="'+pad+'" y1="'+yp+'" x2="'+(W-pad)+'" y2="'+yp+'" stroke="#f59e0b" stroke-width="1.2" stroke-dasharray="4 3" opacity="0.7"/>';
extra += '<text x="'+(pad+4)+'" y="'+(yp-4)+'" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#92400e">T_пл = '+Tm+' &#176;C</text>';
/* Перекрашиваем сегменты разными цветами */
let seg1 = '<line x1="'+r.toX(0)+'" y1="'+r.toY(tStart)+'" x2="'+r.toX(2)+'" y2="'+r.toY(Tm)+'" stroke="#dc2626" stroke-width="3"/>';
let seg2 = '<line x1="'+r.toX(2)+'" y1="'+yp+'" x2="'+r.toX(2+plateauW)+'" y2="'+yp+'" stroke="#f59e0b" stroke-width="3"/>';
let seg3 = '<line x1="'+r.toX(2+plateauW)+'" y1="'+yp+'" x2="'+r.toX(10)+'" y2="'+r.toY(Tm+60)+'" stroke="#2563eb" stroke-width="3"/>';
/* собираем: оси из r.svg, нужно вырезать только path и оставить оси/подписи */
/* r.svg уже содержит оси и красный path; мы его перерисуем поверх */
svg.innerHTML = r.svg + extra + seg1 + seg2 + seg3;
}
document.getElementById('p8-mat').addEventListener('change', draw);
document.getElementById('p8-p').addEventListener('input', draw);
draw();
}
function _initP8_quiz(){
const QS = [
{sit:'В стакане плавающие кусочки льда + вода. Какая температура смеси?', opts:['-5 &#176;C','0 &#176;C','+5 &#176;C','+10 &#176;C'], ans:1, why:'Лёд и вода в равновесии — это точка плавления, $T = 0$ &#176;C.'},
{sit:'Нагреваем железо. Температура 1539 &#176;C, на плите плато. Что происходит?', opts:['Греется как обычно','Плавится','Остывает','Кипит'], ans:1, why:'1539 &#176;C — это $T_{пл}$ железа.'},
{sit:'У стекла НЕТ чёткой температуры плавления. Что это значит?', opts:['Оно никогда не плавится','Это аморфное вещество','Оно газ','Оно ядовито'], ans:1, why:'Стекло — аморфное тело, нет дальнего порядка, плавится плавно.'},
{sit:'Свинец плавится при 327 &#176;C. При 320 &#176;C он …', opts:['твёрдый','плавится','жидкий','газ'], ans:0, why:'320 &lt; 327, значит ещё твёрдый.'},
{sit:'Жидкая ртуть остывает с 0 &#176;C до -50 &#176;C. Где плато на графике?', opts:['нет плато','на -50','на -39','на 0'], ans:2, why:'$T_{пл}$ ртути $= -39$ &#176;C — там плато кристаллизации.'},
{sit:'Куда уходит подведённая теплота во время плавления?', opts:['На нагрев','На разрушение связей в кристалле','На испарение','На сжатие'], ans:1, why:'Энергия рвёт кристаллическую решётку, $T$ не меняется.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p8-quiz'); if(!wrap) return;
let html = '<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:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="padding:10px 14px;text-align:left">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p8-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p8-quiz-r').textContent = (i+1);
document.getElementById('p8-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('p8-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p8-quiz'); bumpProgress('p8', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p8-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p8-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP8_dnd(){
/* 5 веществ по возрастанию T_пл: ртуть(-39) → лёд(0) → свинец(327) → алюминий(660) → железо(1539) */
const items = [
{id:'hg', cat:'r1', html:'ртуть (-39)'},
{id:'ic', cat:'r2', html:'лёд (0)'},
{id:'pb', cat:'r3', html:'свинец (327)'},
{id:'al', cat:'r4', html:'алюминий (660)'},
{id:'fe', cat:'r5', html:'железо (1539)'}
];
const dnd = setupSorter({ poolId:'p8-dnd-pool', scopeSelector:'#sec-p8', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p8-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p8-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. Чем прочнее связи между атомами, тем выше $T_{пл}$.'; addXp(15,'p8-dnd'); bumpProgress('p8', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. У ртути минимум, у железа максимум.'; }
});
document.getElementById('p8-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p8-dnd-fb'); fb.style.display='none'; });
}
function _initP8_mcq(){
const QS = [
{q:'Что происходит во время плавления?', opts:['$T$ растёт','$T$ не меняется','$T$ падает','зависит от вещества'], ans:1, why:'Энергия идёт на разрушение решётки, $T$ постоянна.'},
{q:'Какой график соответствует нагреву и плавлению?', opts:['прямая линия','зигзаг','рост, плато, рост','падение, плато, падение'], ans:2, why:'Плато плавления разделяет две прямые роста.'},
{q:'Аморфные тела (стекло, смола) …', opts:['не плавятся','имеют резкую $T_{пл}$','плавятся плавно без плато','состоят из жидкости'], ans:2, why:'Нет кристаллической решётки $\\Rightarrow$ нет резкого перехода.'},
{q:'Когда лёд тает в воде, температура смеси…', opts:['опускается','растёт','равна 0 &#176;C, пока тает весь лёд','зависит от количества'], ans:2, why:'Это точка плавления льда.'},
{q:'Что происходит при кристаллизации?', opts:['тепло поглощается','тепло выделяется','$T$ растёт','$T$ падает резко'], ans:1, why:'Молекулы выстраиваются в решётку и отдают связанную энергию.'},
{q:'$T_{пл}$ и $T_{кр}$ одного вещества…', opts:['$T_{пл} > T_{кр}$','$T_{пл} < T_{кр}$','равны','зависит от давления'], ans:2, why:'Это одна и та же температура — точка фазового равновесия.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p8-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><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="p8-mcq-fb"></div><div class="actions"><button class="btn" id="p8-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p8-mcq-i').textContent = (i+1);
document.getElementById('p8-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('p8-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p8-mcq'); bumpProgress('p8', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p8-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p8-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p8-mcq-bonus'); bumpProgress('p8', 15); }, 600); }
});
});
const nb = document.getElementById('p8-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §9 — Q = λm ======== */
function build_p9(){
const box = document.getElementById('p9-body');
let h = '';
h += makeCard('theory', 'Удельная теплота плавления', '§ 9.1',
'<p>Чтобы расплавить массу $m$ кристаллического вещества при $T = T_{пл}$, нужно подвести количество теплоты:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q = \\lambda\\,m$$</p>'
+'<p>$\\lambda$ (лямбда) — <b>удельная теплота плавления</b>, Дж/кг.</p>'
+'<p>Это «цена входа» в жидкое состояние: сколько энергии нужно потратить, чтобы расплавить 1 кг. При кристаллизации та же $Q$ возвращается в окружающую среду.</p>'
);
h += makeCard('rule', 'Таблица $\\lambda$', '§ 9.2',
'<table style="width:100%;border-collapse:collapse;font-size:.92rem"><thead><tr style="background:rgba(15,23,42,.04)"><th style="padding:6px;text-align:left">Вещество</th><th style="padding:6px;text-align:right">$T_{пл}$, &#176;C</th><th style="padding:6px;text-align:right">$\\lambda$, $\\dfrac{\\text{Дж}}{\\text{кг}}$</th></tr></thead><tbody>'
+ MAT_MELT.map(m=>'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">'+m.name+'</td><td style="padding:6px;text-align:right;border-bottom:1px dashed var(--border)">'+m.Tm+'</td><td style="padding:6px;text-align:right;border-bottom:1px dashed var(--border)"><code>'+m.lam.toExponential(2).replace('+','')+'</code></td></tr>').join('')
+'</tbody></table>'
+'<p style="margin-top:8px">У льда $\\lambda$ велика — поэтому весной снег тает медленно, поглощая много энергии.</p>'
);
h += makeCard('example', 'Сложная задача: «лёд → вода → пар»', '§ 9.3',
'<p>Если у нас есть кусок льда при -10 &#176;C и мы хотим довести его до 50 &#176;C, нужно три порции теплоты:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li>$Q_1 = c_{льда} \\cdot m \\cdot (0 - (-10))$ — нагрев льда до 0 &#176;C;</li>'
+'<li>$Q_2 = \\lambda \\cdot m$ — плавление льда при 0 &#176;C;</li>'
+'<li>$Q_3 = c_{воды} \\cdot m \\cdot (50 - 0)$ — нагрев воды до 50 &#176;C.</li>'
+'</ol>'
+'<p>Всего: $Q = Q_1 + Q_2 + Q_3$.</p>'
);
/* IV1 — калькулятор Q = λm с визуалом */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Калькулятор $Q = \\lambda m$</div></div>'
+'<div class="wg-help">Выбери вещество и массу — увидь, сколько теплоты нужно для плавления.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Вещество: <select id="p9-mat" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'
+ MAT_MELT.map(m=>'<option value="'+m.lam+'" data-name="'+m.name+'" data-tm="'+m.Tm+'">'+m.name+' ($\\lambda='+m.lam.toExponential(1).replace('+','')+'$)</option>').join('')
+'</select></label>'
+'<label>$m$, кг: <b id="p9-mv">1.0</b><input type="range" id="p9-m" min="0.1" max="10" step="0.1" value="1"></label>'
+'</div>'
+'<svg id="p9-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>$\\lambda$ = <b id="p9-lv">3.34&times;10<sup>5</sup></b> Дж/кг</span>'
+'<span>$Q$ = <b id="p9-q">334 кДж</b></span>'
+'<span style="font-size:.84rem;color:var(--muted)">Это столько же, сколько на нагрев <b id="p9-eq">80</b> кг воды на 1 К.</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">У нас $m$ кг льда при $T_1$ &#176;C. Сколько энергии нужно, чтобы довести его до $T_2$ &#176;C (жидкой воды)?</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$m$, кг: <b id="p9-ymv">1.0</b><input type="range" id="p9-ym" min="0.1" max="5" step="0.1" value="1"></label>'
+'<label>$T_1$, &#176;C: <b id="p9-y1v">-20</b><input type="range" id="p9-y1" min="-40" max="0" step="5" value="-20"></label>'
+'<label>$T_2$, &#176;C: <b id="p9-y2v">40</b><input type="range" id="p9-y2" min="10" max="100" step="5" value="40"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>1. Нагрев льда: $Q_1 = c_{л} m (0 - T_1)$ = <b id="p9-q1">42</b> кДж</span>'
+'<span>2. Плавление: $Q_2 = \\lambda m$ = <b id="p9-q2">334</b> кДж</span>'
+'<span>3. Нагрев воды: $Q_3 = c_{в} m (T_2 - 0)$ = <b id="p9-q3">168</b> кДж</span>'
+'<span style="margin-top:4px;font-weight:800">Итого: $Q$ = <b id="p9-qall">544</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">Расставь $\\lambda$ по возрастанию</div></div>'
+'<div class="wg-help">От самого «лёгкого в плавке» к самому «энергоёмкому».</div>'
+'<div id="p9-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="p9-dnd-check">Проверить</button><button class="btn" id="p9-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p9-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. Допуск ±3 %.</div>'
+'<div id="p9-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p9-task-i">1</b> / 6</span><span>Правильно: <b id="p9-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p9') + readButton('p9');
renderMath(box);
wireReadBtn('p9');
_initP9_calc();
_initP9_chain();
_initP9_dnd();
_initP9_tasks();
}
function _initP9_calc(){
const svg = document.getElementById('p9-sim'); if(!svg) return;
function update(){
const sel = document.getElementById('p9-mat');
const opt = sel.options[sel.selectedIndex];
const lam = +sel.value;
const m = +document.getElementById('p9-m').value;
const name = opt.getAttribute('data-name');
const Tm = opt.getAttribute('data-tm');
document.getElementById('p9-mv').textContent = m.toFixed(1);
document.getElementById('p9-lv').innerHTML = lam.toExponential(2).replace('+','').replace('e','&times;10<sup>')+'</sup>';
const Q = lam * m;
const QkJ = Q / 1000;
document.getElementById('p9-q').textContent = QkJ.toFixed(0)+' кДж';
/* эквивалент в "кг воды на 1 K" */
const eqkg = Q / 4200;
document.getElementById('p9-eq').textContent = eqkg.toFixed(1);
/* SVG */
let s = '';
/* куб льда / металла, превращается в лужицу */
s += '<rect x="50" y="40" width="80" height="60" fill="#bfdbfe" stroke="#0f172a" stroke-width="1.8" rx="4"/>';
s += '<text x="90" y="74" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">'+name+'</text>';
s += '<text x="90" y="118" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#475569">m = '+m.toFixed(1)+' кг, T = '+Tm+' &#176;C</text>';
/* arrow */
s += window.PHYS.drawArrow(140, 70, 220, 70, '#f59e0b', 2.4, 10);
s += '<text x="180" y="58" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">Q = &lambda;m</text>';
/* жидкость */
s += '<path d="M 240 90 Q 270 65 305 65 Q 340 65 370 90 L 370 110 L 240 110 Z" fill="#60a5fa" stroke="#0f172a" stroke-width="1.6"/>';
s += '<text x="305" y="100" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#fff">расплав '+name+'</text>';
/* подпись Q */
s += '<text x="305" y="130" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="800" fill="#dc2626">Q = '+QkJ.toFixed(0)+' кДж</text>';
svg.innerHTML = s;
}
document.getElementById('p9-mat').addEventListener('change', update);
document.getElementById('p9-m').addEventListener('input', update);
update();
}
function _initP9_chain(){
function update(){
const m = +document.getElementById('p9-ym').value;
const T1 = +document.getElementById('p9-y1').value;
const T2 = +document.getElementById('p9-y2').value;
document.getElementById('p9-ymv').textContent = m.toFixed(1);
document.getElementById('p9-y1v').textContent = T1;
document.getElementById('p9-y2v').textContent = T2;
const cIce = 2100, lam = 3.34e5, cW = 4200;
const Q1 = cIce * m * (0 - T1);
const Q2 = lam * m;
const Q3 = cW * m * (T2 - 0);
document.getElementById('p9-q1').textContent = (Q1/1000).toFixed(0);
document.getElementById('p9-q2').textContent = (Q2/1000).toFixed(0);
document.getElementById('p9-q3').textContent = (Q3/1000).toFixed(0);
document.getElementById('p9-qall').textContent = ((Q1+Q2+Q3)/1000).toFixed(0);
}
['p9-ym','p9-y1','p9-y2'].forEach(id => document.getElementById(id).addEventListener('input', update));
update();
}
function _initP9_dnd(){
/* 5 веществ по возрастанию λ:
ртуть(1.18e4) → свинец(2.5e4) → цинк(1.12e5) → железо(2.7e5) → лёд(3.34e5)
(алюминий 3.9e5 не используем) */
const items = [
{id:'hg', cat:'r1', html:'ртуть ($1{,}2\\cdot10^4$)'},
{id:'pb', cat:'r2', html:'свинец ($2{,}5\\cdot10^4$)'},
{id:'zn', cat:'r3', html:'цинк ($1{,}1\\cdot10^5$)'},
{id:'fe', cat:'r4', html:'железо ($2{,}7\\cdot10^5$)'},
{id:'ic', cat:'r5', html:'лёд ($3{,}3\\cdot10^5$)'}
];
const dnd = setupSorter({ poolId:'p9-dnd-pool', scopeSelector:'#sec-p9', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p9-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p9-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. У льда $\\lambda$ велика — поэтому снег тает медленно.'; addXp(15,'p9-dnd'); bumpProgress('p9', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: у льда $\\lambda$ больше всех в этом списке.'; renderMath(fb); }
});
document.getElementById('p9-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p9-dnd-fb'); fb.style.display='none'; });
}
function _initP9_tasks(){
const TASKS = [
{q:'Сколько энергии (в кДж) нужно, чтобы расплавить 0,5 кг льда при 0 &#176;C? ($\\lambda = 3{,}34 \\cdot 10^5$)', ans: 167, tol: 3, why:'$Q = \\lambda m = 3{,}34\\cdot10^5 \\cdot 0{,}5 = 1{,}67\\cdot10^5$ Дж = $167$ кДж.'},
{q:'Сколько кг свинца можно расплавить при $T_{пл}$, имея 25 кДж? ($\\lambda = 2{,}5 \\cdot 10^4$)', ans: 1, tol: 0.05, why:'$m = Q/\\lambda = 25\\,000 / 2{,}5\\cdot10^4 = 1$ кг.'},
{q:'Лёд массой 2 кг при -10 &#176;C довести до воды при 0 &#176;C. Сколько кДж нужно? ($c_{л}=2100$, $\\lambda=3{,}34\\cdot10^5$)', ans: 710, tol: 14, why:'$Q_1 = 2100 \\cdot 2 \\cdot 10 = 42\\,000$, $Q_2 = 3{,}34\\cdot10^5 \\cdot 2 = 668\\,000$, итого $710\\,000$ Дж $= 710$ кДж.'},
{q:'У какого вещества $\\lambda$ выше: у воды (лёд → жидкость) или у железа? Запиши 1 — если у льда, 2 — если у железа.', ans: 1, tol: 0.1, why:'$\\lambda_{льда} = 3{,}3\\cdot10^5$ > $\\lambda_{железа} = 2{,}7\\cdot10^5$.'},
{q:'Сколько кДж выделится при кристаллизации 1,5 кг олова? ($\\lambda \\approx 5{,}9 \\cdot 10^4$)', ans: 88.5, tol: 2, why:'$Q = \\lambda m = 5{,}9\\cdot10^4 \\cdot 1{,}5 = 88\\,500$ Дж = $88{,}5$ кДж.'},
{q:'Кубик льда 200 г (0 &#176;C) положили в стакан с водой и он расплавился. Сколько кДж теплоты он отнял у воды?', ans: 66.8, tol: 2, why:'$Q = \\lambda m = 3{,}34\\cdot10^5 \\cdot 0{,}2 = 66\\,800$ Дж = $66{,}8$ кДж.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p9-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="p9-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p9-task-go">Ответ</button>'
+'<button class="btn" id="p9-task-hint">Подсказка</button>'
+'<button class="btn" id="p9-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p9-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p9-task-fb"></div>';
document.getElementById('p9-task-i').textContent = (i+1);
document.getElementById('p9-task-ok').textContent = ok;
document.getElementById('p9-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p9-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p9-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,'p9-task'); bumpProgress('p9', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Правильный ответ: '+t.ans+'. '+t.why; }
document.getElementById('p9-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p9-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p9-task-bonus'); bumpProgress('p9', 15); }, 600); }
});
document.getElementById('p9-task-hint').addEventListener('click', ()=>{ document.getElementById('p9-task-hint-txt').classList.toggle('show'); });
document.getElementById('p9-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 1 · WAVE 5 — §10, §11, FINAL 1
====================================================================== */
/* ======== §10 — Испарение жидкостей ======== */
function build_p10(){
const box = document.getElementById('p10-body');
let h = '';
h += makeCard('theory', 'Что такое испарение', '§ 10.1',
'<p><b>Испарение</b> — переход жидкости в пар <b>с поверхности</b>. Идёт при любой температуре (даже при 0 &#176;C, даже зимой).</p>'
+'<p>Механизм: молекулы у поверхности движутся хаотически. Самые быстрые из них преодолевают притяжение остальных и улетают в воздух — становятся паром.</p>'
+'<p>Поэтому при испарении жидкость <b>остывает</b> — её покидают наиболее «горячие» молекулы, средняя $E_k$ оставшихся падает.</p>'
);
h += makeCard('rule', 'От чего зависит скорость испарения', '§ 10.2',
'<ol style="padding-left:20px;margin:6px 0">'
+'<li><b>Температура</b>: чем выше $T$, тем больше быстрых молекул, испарение интенсивнее.</li>'
+'<li><b>Площадь свободной поверхности</b>: чем больше $S$, тем больше «выходов».</li>'
+'<li><b>Движение воздуха</b> (ветер): унося пар, ветер не даёт ему вернуться обратно.</li>'
+'<li><b>Род жидкости</b>: эфир, спирт испаряются быстро; вода медленнее; ртуть — почти не испаряется.</li>'
+'</ol>'
+'<p>Обратный процесс — <b>конденсация</b> (пар $\\to$ жидкость), при ней теплота <b>выделяется</b>.</p>'
);
h += makeCard('example', 'Где встречается', '§ 10.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Пот охлаждает тело — испаряясь, он уносит тепло.</li>'
+'<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">Меняй температуру и ветер — наблюдай, как быстро молекулы покидают жидкость и уносятся в воздух.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$T$, &#176;C: <b id="p10-tv">40</b><input type="range" id="p10-t" min="5" max="90" step="5" value="40"></label>'
+'<label>Ветер: <b id="p10-wv">умеренный</b><input type="range" id="p10-w" min="0" max="3" step="1" value="1"></label>'
+'</div>'
+'<svg id="p10-sim" viewBox="0 0 460 220" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="score-display" style="margin-top:10px"><span>Испарилось молекул: <b id="p10-evap">0</b></span><span>Скорость: <b id="p10-rate">средняя</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="p10-quiz"></div>'
+'<div class="actions"><button class="btn" id="p10-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p10-quiz-r">1</b> / 6</span><span>Правильно: <b id="p10-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="p10-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="up"></div></div>'
+'<div class="drop-box"><h5>Замедляет</h5><div class="drop-items" data-cat="dn"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p10-dnd-check">Проверить</button><button class="btn" id="p10-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p10-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="p10-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p10-mcq-i">1</b> / 6</span><span>Правильно: <b id="p10-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p10') + readButton('p10');
renderMath(box);
wireReadBtn('p10');
_initP10_sim();
_initP10_quiz();
_initP10_dnd();
_initP10_mcq();
}
function _initP10_sim(){
_killSim('p10sim');
const svg = document.getElementById('p10-sim'); if(!svg) return;
const W = 460, H = 220;
/* жидкость в нижней половине, частицы вылетают вверх */
const liqY = 130;
const N = 28;
const ps = [];
for(let i=0;i<N;i++) ps.push({ x: 80+Math.random()*300, y: liqY+10+Math.random()*70, vx: 0, vy: 0, evap: false });
let T = 40, wind = 1;
let evapCount = 0;
function tick(){
if(!_isVisible('p10')){ _SIMS.p10sim.raf = requestAnimationFrame(tick); return; }
/* вероятность испарения для частиц у поверхности */
const pEvap = (T / 90) * 0.018; /* при 90°C около 1.8% за тик */
for(const p of ps){
if(p.evap){
p.x += p.vx; p.y += p.vy;
p.vx += wind * 0.04;
if(p.x > W + 10 || p.y < -10){
/* "вернуть" молекулу обратно */
p.x = 80 + Math.random()*300; p.y = liqY + 10 + Math.random()*70;
p.vx = 0; p.vy = 0; p.evap = false;
}
} else {
/* у поверхности (y близок к liqY) есть шанс испариться */
if(p.y < liqY + 14 && Math.random() < pEvap){
p.evap = true;
p.vy = -1.0 - Math.random()*0.6;
p.vx = (Math.random() - 0.4) * 0.5 + wind*0.2;
evapCount++;
} else {
/* небольшое броуновское движение */
p.x += (Math.random()-0.5)*0.6;
p.y += (Math.random()-0.5)*0.6;
if(p.x < 75) p.x = 75; if(p.x > 385) p.x = 385;
if(p.y < liqY + 6) p.y = liqY + 6;
if(p.y > 200) p.y = 200;
}
}
}
/* draw */
let s = '';
/* сосуд + жидкость */
s += '<rect x="60" y="125" width="340" height="85" fill="#bfdbfe" stroke="#0f172a" stroke-width="2" rx="4"/>';
s += '<line x1="60" y1="'+liqY+'" x2="400" y2="'+liqY+'" stroke="#1d4ed8" stroke-width="1.6"/>';
/* небо */
s += '<rect x="0" y="0" width="'+W+'" height="125" fill="#f0f9ff"/>';
/* солнце-температура */
const tCol = window.PHYS.tempColor(T, 5, 90);
s += '<circle cx="420" cy="30" r="16" fill="'+tCol+'" stroke="#0f172a" stroke-width="1.4"/>';
s += '<text x="420" y="34" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#fff">'+T+'&#176;</text>';
/* ветер-стрелки */
if(wind > 0){
for(let i=0;i<wind;i++){
const wy = 50 + i*15;
s += window.PHYS.drawArrow(20, wy, 80, wy, '#0891b2', 1.6, 8);
}
}
/* частицы */
for(const p of ps){
const col = p.evap ? '#94a3b8' : '#3b82f6';
s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="3.6" fill="'+col+'" stroke="#0f172a" stroke-width="0.4"/>';
}
svg.innerHTML = s;
_SIMS.p10sim.raf = requestAnimationFrame(tick);
}
_SIMS.p10sim = { raf: 0 };
_SIMS.p10sim.raf = requestAnimationFrame(tick);
function update(){
T = +document.getElementById('p10-t').value;
wind = +document.getElementById('p10-w').value;
document.getElementById('p10-tv').textContent = T;
document.getElementById('p10-wv').textContent = ['нет','слабый','умеренный','сильный'][wind];
const rate = T*0.4 + wind*5;
let rateLabel = 'низкая';
if(rate > 25) rateLabel = 'высокая';
else if(rate > 15) rateLabel = 'средняя';
document.getElementById('p10-rate').textContent = rateLabel;
document.getElementById('p10-evap').textContent = evapCount;
}
document.getElementById('p10-t').addEventListener('input', update);
document.getElementById('p10-w').addEventListener('input', update);
setInterval(()=>{ document.getElementById('p10-evap').textContent = evapCount; }, 500);
update();
}
function _initP10_quiz(){
const QS = [
{A:'лужа в безветренной комнате при 20 &#176;C', B:'лужа на ветреной площади при 20 &#176;C', ans:'B', why:'Ветер ускоряет испарение, унося пар.'},
{A:'вода в стакане', B:'та же вода, разлитая по тарелке', ans:'B', why:'Больше площадь свободной поверхности — быстрее испарение.'},
{A:'мокрая ткань на 20 &#176;C', B:'та же ткань на 5 &#176;C', ans:'A', why:'Выше $T$ — больше быстрых молекул.'},
{A:'вода', B:'эфир (при той же $T$)', ans:'B', why:'У эфира слабее связи между молекулами, он испаряется в десятки раз быстрее воды.'},
{A:'бельё в подвале', B:'бельё на солнце и ветру', ans:'B', why:'Солнечное излучение нагревает + ветер уносит пар.'},
{A:'мокрая рука на воздухе', B:'мокрая рука в воде', ans:'A', why:'В воде испарения почти нет — нет свободной поверхности «жидкость-воздух».'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p10-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="p10-quiz-fb"></div>';
document.getElementById('p10-quiz-r').textContent = (i+1);
document.getElementById('p10-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('p10-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p10-quiz'); bumpProgress('p10', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p10-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p10-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP10_dnd(){
const items = [
{id:'up_t', cat:'up', html:'нагрев'},
{id:'up_s', cat:'up', html:'большая площадь'},
{id:'up_w', cat:'up', html:'ветер'},
{id:'up_e', cat:'up', html:'эфир вместо воды'},
{id:'dn_t', cat:'dn', html:'охлаждение'},
{id:'dn_s', cat:'dn', html:'узкое горлышко'},
{id:'dn_w', cat:'dn', html:'закрытая банка'},
{id:'dn_m', cat:'dn', html:'ртуть вместо воды'}
];
const dnd = setupSorter({ poolId:'p10-dnd-pool', scopeSelector:'#sec-p10', cats:['up','dn'], items, columnLayout:false });
document.getElementById('p10-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p10-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. Четыре фактора: T, S, ветер, род жидкости.'; addXp(15,'p10-dnd'); bumpProgress('p10', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: ртуть и закрытая банка — это блокирует испарение.'; }
});
document.getElementById('p10-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p10-dnd-fb'); fb.style.display='none'; });
}
function _initP10_mcq(){
const QS = [
{q:'При испарении жидкость …', opts:['нагревается','остывает','не меняет $T$','становится твёрже'], ans:1, why:'Уходят самые быстрые молекулы — средняя $E_k$ остающихся падает.'},
{q:'При какой температуре идёт испарение?', opts:['только при $T_{кип}$','при любой $T$','только зимой','только под солнцем'], ans:1, why:'Испарение идёт всегда, даже у льда (возгонка).'},
{q:'Почему мокрая рука зябнет на ветру?', opts:['ветер холодный','вода уносит тепло','испарение охлаждает','оба B и C'], ans:3, 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('p10-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><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="p10-mcq-fb"></div><div class="actions"><button class="btn" id="p10-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p10-mcq-i').textContent = (i+1);
document.getElementById('p10-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('p10-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p10-mcq'); bumpProgress('p10', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p10-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p10-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p10-mcq-bonus'); bumpProgress('p10', 15); }, 600); }
});
});
const nb = document.getElementById('p10-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §11 — Кипение, удельная теплота парообразования ======== */
function build_p11(){
const box = document.getElementById('p11-body');
let h = '';
h += makeCard('theory', 'Что такое кипение', '§ 11.1',
'<p><b>Кипение</b> — переход жидкости в пар <b>по всему объёму</b> при определённой температуре $T_{кип}$.</p>'
+'<p>В жидкости всегда есть растворённый воздух с пузырьками пара. При $T = T_{кип}$ давление пара в пузырьках становится равно атмосферному, пузырьки растут, всплывают и лопаются — это и есть кипение.</p>'
+'<p>Во время кипения температура жидкости <b>не меняется</b>, как и при плавлении.</p>'
);
h += makeCard('rule', '$Q = Lm$ — удельная теплота парообразования', '§ 11.2',
'<p>Чтобы перевести массу $m$ жидкости (при $T = T_{кип}$) в пар, нужно:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q = L\\,m$$</p>'
+'<p>$L$ — удельная теплота парообразования, Дж/кг. Для воды:</p>'
+'<p style="text-align:center"><code>L = 2,26 &middot; 10<sup>6</sup> Дж/кг = 2260 кДж/кг</code></p>'
+'<p>Это огромная величина — поэтому ожог паром опаснее ожога кипятком (пар конденсируется на коже и выделяет $L$).</p>'
);
h += makeCard('example', '$T_{кип}$ зависит от давления', '§ 11.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>При нормальном давлении (1 атм $= 10^5$ Па) вода кипит при $100$ &#176;C.</li>'
+'<li>На горе $T_{кип}$ ниже (давление меньше). На Эвересте — около $70$ &#176;C.</li>'
+'<li>В скороварке давление выше — $T_{кип}$ выше ($120$ &#176;C), еда варится быстрее.</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">Нагреваем 1 кг льда от -20 &#176;C до пара при 100 &#176;C. График $T(t)$ имеет <b>два плато</b>: плавление и кипение.</div>'
+'<svg id="p11-sim" viewBox="0 0 460 280" 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:3px">'
+'<span><span style="color:#2563eb">&#9632;</span> 1. Нагрев льда</span>'
+'<span><span style="color:#f59e0b">&#9632;</span> 2. Плавление при 0 &#176;C ($\\lambda m$)</span>'
+'<span><span style="color:#dc2626">&#9632;</span> 3. Нагрев воды</span>'
+'<span><span style="color:#ef4444">&#9632;</span> 4. Кипение при 100 &#176;C ($L m$) — самое длинное плато!</span>'
+'<span><span style="color:#7c3aed">&#9632;</span> 5. Нагрев пара</span>'
+'</div>'
+'</div>';
/* IV2 — калькулятор Q = Lm */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Калькулятор $Q = Lm$</div></div>'
+'<div class="wg-help">Сколько энергии нужно, чтобы перевести жидкость в пар при $T = T_{кип}$?</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Жидкость: <select id="p11-liq" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'
+'<option value="2.26e6">вода ($L = 2{,}26 \\cdot 10^6$)</option>'
+'<option value="9.0e5">спирт ($L = 9{,}0 \\cdot 10^5$)</option>'
+'<option value="3.0e5">эфир ($L = 3{,}0 \\cdot 10^5$)</option>'
+'<option value="2.85e5">ртуть ($L = 2{,}85 \\cdot 10^5$)</option>'
+'</select></label>'
+'<label>$m$, кг: <b id="p11-mv">1.0</b><input type="range" id="p11-m" min="0.1" max="5" step="0.1" value="1"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$Q$ = <b id="p11-q">2.26 МДж</b> = <b id="p11-qkw">0.63</b> кВт·ч</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Эквивалент: нагрев <b id="p11-eqkg">6.7</b> кг воды от 20 до 100 &#176;C.</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="p11-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="melt"></div></div>'
+'<div class="drop-box"><h5>Испарение / кипение</h5><div class="drop-items" data-cat="evap"></div></div>'
+'<div class="drop-box"><h5>Конденсация / кристаллизация</h5><div class="drop-items" data-cat="cond"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p11-dnd-check">Проверить</button><button class="btn" id="p11-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p11-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="p11-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p11-task-i">1</b> / 6</span><span>Правильно: <b id="p11-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p11') + readButton('p11');
renderMath(box);
wireReadBtn('p11');
_initP11_graph();
_initP11_calc();
_initP11_dnd();
_initP11_tasks();
}
function _initP11_graph(){
const svg = document.getElementById('p11-sim'); if(!svg) return;
/* строим график T(t) для 1 кг воды/льда/пара
сегменты по времени:
1) -20→0 (лёд): t = m·c_ice·20 / P
2) плавление: t = m·λ / P
3) 0→100 (вода): t = m·c_w·100 / P
4) кипение: t = m·L / P
5) пар после 100, нагрев на 80 К (с pp = 2010 для пара)
Используем относительные времена. */
const m = 1;
const cIce=2100, lam=3.34e5, cW=4200, L=2.26e6, cSt=2010;
const t1 = m*cIce*20; /* 42 000 */
const t2 = m*lam; /* 334 000 */
const t3 = m*cW*100; /* 420 000 */
const t4 = m*L; /* 2 260 000 */
const t5 = m*cSt*80; /* 160 800 */
const T1 = 0, T2 = 100, T3 = 180;
const tot = t1+t2+t3+t4+t5; /* нормируем к 10 */
const sc = 10/tot;
/* points */
let cur = 0;
const segs = [
{ tStart: cur, tEnd: (cur+=t1*sc)*1, Tstart: -20, Tend: 0, col: '#2563eb', label:'лёд' },
{ tStart: (cur), tEnd: (cur+=t2*sc), Tstart: 0, Tend: 0, col: '#f59e0b', label:'плавление' },
{ tStart: (cur), tEnd: (cur+=t3*sc), Tstart: 0, Tend: 100, col: '#dc2626', label:'вода' },
{ tStart: (cur), tEnd: (cur+=t4*sc), Tstart: 100, Tend: 100, col: '#ef4444', label:'кипение' },
{ tStart: (cur), tEnd: (cur+=t5*sc), Tstart: 100, Tend: 180, col: '#7c3aed', label:'пар' }
];
const W=460, H=280, pad=36;
const r = window.PHYS.phaseGraphTT(W, H, pad, segs.map(s=>({tStart:s.tStart,tEnd:s.tEnd,Tstart:s.Tstart,Tend:s.Tend})), 10, -40, 200);
/* Раскрашиваем сегменты разными цветами поверх */
let extra = '';
/* Горизонталь 0 и 100 */
extra += '<line x1="'+pad+'" y1="'+r.toY(0)+'" x2="'+(W-pad)+'" y2="'+r.toY(0)+'" stroke="#f59e0b" stroke-width="1" stroke-dasharray="3 3" opacity="0.6"/>';
extra += '<line x1="'+pad+'" y1="'+r.toY(100)+'" x2="'+(W-pad)+'" y2="'+r.toY(100)+'" stroke="#ef4444" stroke-width="1" stroke-dasharray="3 3" opacity="0.6"/>';
/* подписи 0 и 100 */
extra += '<text x="'+(pad+4)+'" y="'+(r.toY(0)-4)+'" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#92400e">0 &#176;C (плавление)</text>';
extra += '<text x="'+(pad+4)+'" y="'+(r.toY(100)-4)+'" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#7f1d1d">100 &#176;C (кипение)</text>';
/* перерисовываем сегменты */
for(const s of segs){
extra += '<line x1="'+r.toX(s.tStart)+'" y1="'+r.toY(s.Tstart)+'" x2="'+r.toX(s.tEnd)+'" y2="'+r.toY(s.Tend)+'" stroke="'+s.col+'" stroke-width="3.5"/>';
}
svg.innerHTML = r.svg + extra;
}
function _initP11_calc(){
function update(){
const L = +document.getElementById('p11-liq').value;
const m = +document.getElementById('p11-m').value;
document.getElementById('p11-mv').textContent = m.toFixed(1);
const Q = L*m;
document.getElementById('p11-q').textContent = (Q/1e6).toFixed(2)+' МДж';
document.getElementById('p11-qkw').textContent = (Q/3.6e6).toFixed(2);
/* эквивалент: нагрев воды от 20 до 100 (ΔT=80, c=4200) */
document.getElementById('p11-eqkg').textContent = (Q/(4200*80)).toFixed(1);
}
document.getElementById('p11-liq').addEventListener('change', update);
document.getElementById('p11-m').addEventListener('input', update);
update();
}
function _initP11_dnd(){
const items = [
{id:'a', cat:'melt', html:'лёд тает в стакане'},
{id:'b', cat:'melt', html:'свинец становится жидким'},
{id:'c', cat:'evap', html:'лужа высыхает'},
{id:'d', cat:'evap', html:'кипящий чайник пускает пар'},
{id:'e', cat:'evap', html:'спирт испаряется с кожи'},
{id:'f', cat:'cond', html:'роса утром на траве'},
{id:'g', cat:'cond', html:'пар оседает на холодном стекле'},
{id:'h', cat:'cond', html:'жидкое золото застывает в слитки'}
];
const dnd = setupSorter({ poolId:'p11-dnd-pool', scopeSelector:'#sec-p11', cats:['melt','evap','cond'], items, columnLayout:false });
document.getElementById('p11-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p11-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,'p11-dnd'); bumpProgress('p11', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: роса — это пар → жидкость (конденсация).'; }
});
document.getElementById('p11-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p11-dnd-fb'); fb.style.display='none'; });
}
function _initP11_tasks(){
const TASKS = [
{q:'Сколько энергии (в МДж) нужно, чтобы превратить 0,5 кг воды (100 &#176;C) в пар? ($L = 2{,}26 \\cdot 10^6$)', ans: 1.13, tol: 0.04, why:'$Q = Lm = 2{,}26\\cdot10^6 \\cdot 0{,}5 = 1{,}13\\cdot10^6$ Дж = $1{,}13$ МДж.'},
{q:'Сколько кг воды можно превратить в пар, имея 11,3 МДж энергии?', ans: 5, tol: 0.1, why:'$m = Q/L = 1{,}13\\cdot10^7/(2{,}26\\cdot10^6) = 5$ кг.'},
{q:'$Q_1$ — нагрев 1 кг воды от 20 до 100 &#176;C. $Q_2$ — испарение этой воды. Найди отношение $Q_2/Q_1$ (целое число).', ans: 7, tol: 0.5, why:'$Q_1 = 4200 \\cdot 80 = 336$ кДж. $Q_2 = 2260$ кДж. $Q_2/Q_1 \\approx 6{,}7 \\approx 7$.'},
{q:'Спирт массой 2 кг полностью испаряется. Сколько кДж нужно? ($L = 9 \\cdot 10^5$)', ans: 1800, tol: 30, why:'$Q = 9\\cdot10^5 \\cdot 2 = 1{,}8\\cdot10^6$ Дж = $1800$ кДж.'},
{q:'При конденсации 100 г пара (100 &#176;C) сколько кДж выделится?', ans: 226, tol: 6, why:'$Q = Lm = 2{,}26\\cdot10^6 \\cdot 0{,}1 = 2{,}26\\cdot10^5$ Дж = $226$ кДж.'},
{q:'1 кг льда (-10 &#176;C) превратить полностью в пар (100 &#176;C). Сколько МДж нужно? ($c_л=2100$, $\\lambda=3{,}34\\cdot10^5$, $c_в=4200$, $L=2{,}26\\cdot10^6$)', ans: 3.04, tol: 0.06, why:'$Q_1=21\\,000$, $Q_2=334\\,000$, $Q_3=420\\,000$, $Q_4=2\\,260\\,000$. Итого $3{,}04$ МДж.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p11-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="p11-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p11-task-go">Ответ</button>'
+'<button class="btn" id="p11-task-hint">Подсказка</button>'
+'<button class="btn" id="p11-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p11-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p11-task-fb"></div>';
document.getElementById('p11-task-i').textContent = (i+1);
document.getElementById('p11-task-ok').textContent = ok;
document.getElementById('p11-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p11-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p11-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,'p11-task'); bumpProgress('p11', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Правильный ответ: '+t.ans+'. '+t.why; }
document.getElementById('p11-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p11-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p11-task-bonus'); bumpProgress('p11', 15); }, 600); }
});
document.getElementById('p11-task-hint').addEventListener('click', ()=>{ document.getElementById('p11-task-hint-txt').classList.toggle('show'); });
document.getElementById('p11-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== ФИНАЛ ГЛАВЫ 1 ======== */
function build_final1(){
const box = document.getElementById('final1-body');
let h = '';
/* Шпаргалка главы */
h += '<div class="card" style="background:linear-gradient(135deg,var(--sec-acc-soft),var(--card));border:1.5px solid var(--sec-acc)">'
+'<div class="card-header"><div class="card-icon rule">'+ICONS.rule+'</div><div class="card-title">Шпаргалка главы 1</div></div>'
+'<div class="card-body" style="display:grid;grid-template-columns:1fr 1fr;gap:14px">'
+'<div><b>Тепло</b> при изменении $T$: $Q = c m \\Delta T$<br><i>c — Дж/(кг·К). У воды 4200.</i></div>'
+'<div><b>Тепло</b> при сгорании: $Q = q m$<br><i>q — Дж/кг. У бензина 4{,}6·10⁷.</i></div>'
+'<div><b>Тепло</b> на плавление: $Q = \\lambda m$<br><i>λ — Дж/кг. У льда 3{,}34·10⁵.</i></div>'
+'<div><b>Тепло</b> на парообразование: $Q = L m$<br><i>L — Дж/кг. У воды 2{,}26·10⁶.</i></div>'
+'<div><b>Баланс</b>: $Q_{отд} = Q_{пол}$</div>'
+'<div><b>3 вида теплопередачи</b>: проводность, конвекция, излучение</div>'
+'</div></div>';
/* 7 интегрированных боссов */
const BOSSES = [
{n:1, title:'Нагрев + расчёт', q:'На сколько градусов нагреется 2 кг алюминия при подведении 92 кДж? ($c = 920$)', hint:'$\\Delta T = Q/(cm) = 92\\,000/(920 \\cdot 2) = 50$ К.', ans:50, tol:1, step:'1'},
{n:2, title:'Смешивание воды', q:'Смешали 2 кг воды при $80$ &#176;C и 3 кг воды при $20$ &#176;C. Итоговая $T$ (&#176;C)?', hint:'$T = (2\\cdot80 + 3\\cdot20)/5 = 220/5 = 44$ &#176;C.', ans:44, tol:0.5, step:'1'},
{n:3, title:'Плавление', q:'Сколько кДж нужно, чтобы расплавить 3 кг льда при 0 &#176;C? ($\\lambda = 3{,}34\\cdot10^5$)', hint:'$Q = \\lambda m = 3{,}34\\cdot10^5 \\cdot 3 = 1{,}002\\cdot10^6$ Дж = $1002$ кДж.', ans:1002, tol:10, step:'1'},
{n:4, title:'Кипение', q:'1 кг воды при 100 &#176;C полностью испарили. Сколько МДж энергии затрачено? ($L = 2{,}26\\cdot10^6$)', hint:'$Q = Lm = 2{,}26$ МДж.', ans:2.26, tol:0.05, step:'0.01'},
{n:5, title:'Цепная задача', q:'1 кг льда при -10 &#176;C довели до воды при 50 &#176;C. Сколько кДж? ($c_л=2100$, $\\lambda=3{,}34\\cdot10^5$, $c_в=4200$)', hint:'$Q_1 = 21\\,000$, $Q_2 = 334\\,000$, $Q_3 = 210\\,000$, итого $565\\,000$ Дж = $565$ кДж.', ans:565, tol:12, step:'1'},
{n:6, title:'Топливо + КПД', q:'Котёл с $\\eta = 60\\%$ сжёг 5 кг дров. Какая полезная энергия в МДж? ($q = 10^7$)', hint:'$Q_{сгор} = 5\\cdot10^7 = 50$ МДж. $Q_{пол} = 0{,}6 \\cdot 50 = 30$ МДж.', ans:30, tol:0.5, step:'1'},
{n:7, title:'Полный цикл', q:'Сколько МДж нужно, чтобы 0,5 кг льда (0 &#176;C) превратить в пар (100 &#176;C)? ($\\lambda = 3{,}34\\cdot10^5$, $c_в = 4200$, $L = 2{,}26\\cdot10^6$). Округли до сотых.', hint:'$Q_{пл}=167\\,000$, $Q_{нагр}=210\\,000$, $Q_{исп}=1\\,130\\,000$. Итого $1{,}51$ МДж.', ans:1.51, tol:0.04, step:'0.01'}
];
h += '<div class="card" style="margin-top:14px"><div class="card-header"><div class="card-icon example">'+ICONS.example+'</div><div class="card-title">Боссы главы 1</div></div><div class="card-body">'
+'<div class="boss-overall-bar" style="background:linear-gradient(135deg,rgba(15,23,42,.04),rgba(124,58,237,.04));border-radius:11px;padding:12px;display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin-bottom:14px">'
+'<span style="font-weight:700">Боссов побеждено: <b id="f1-won">0</b> / 7</span>'
+'<div style="flex:1;min-width:160px;height:8px;background:rgba(0,0,0,.08);border-radius:4px;overflow:hidden">'
+'<div id="f1-bar" style="height:100%;background:linear-gradient(90deg,var(--sec-acc),var(--sec-acc-d));width:0%;transition:width .4s"></div>'
+'</div></div>'
+'<div id="f1-bosses"></div>'
+'</div></div>';
box.innerHTML = h + secNavFor('final1') + readButton('final1');
renderMath(box);
wireReadBtn('final1');
_initFinal1_bosses(BOSSES);
}
function _initFinal1_bosses(BOSSES){
const KEY = 'physics8_ch1_bosses';
function loadState(){ try { return JSON.parse(localStorage.getItem(KEY) || '{}') || {}; } catch(e){ return {}; } }
function saveState(s){ try { localStorage.setItem(KEY, JSON.stringify(s)); } catch(e){} }
function updateBar(){
const s = loadState();
let won = 0; for(const k in s) if(s[k]) won++;
document.getElementById('f1-won').textContent = won;
document.getElementById('f1-bar').style.width = Math.round(won*100/BOSSES.length)+'%';
if(won >= BOSSES.length && !STATE.achievements.has('thermal_master')){
addXp(50, 'thermal-master');
achievement('thermal_master');
}
return won;
}
function renderAll(){
const cont = document.getElementById('f1-bosses');
const state = loadState();
let html = '';
BOSSES.forEach(b=>{
const solved = state[b.n];
html += '<div class="boss-card'+(solved?' solved':'')+'" id="f1-boss-'+b.n+'" style="border:2px solid '+(solved?'#10b981':'var(--border)')+';border-radius:12px;padding:14px;margin-bottom:10px;background:var(--card)">'
+'<div style="display:flex;gap:10px;align-items:center;margin-bottom:8px"><span style="font-family:Unbounded,sans-serif;font-size:.7rem;font-weight:800;padding:3px 8px;border-radius:99px;background:var(--sec-acc-soft);color:var(--sec-acc-d);text-transform:uppercase">Босс '+b.n+'</span><span style="font-weight:700">'+b.title+'</span></div>'
+'<div style="padding:10px 12px;background:rgba(15,23,42,.04);border-radius:8px;margin-bottom:8px;font-size:.94rem;line-height:1.5">'+b.q+'</div>'
+'<div class="boss-row">'
+'<input type="number" step="'+b.step+'" class="tinp" id="f1-b'+b.n+'-inp" placeholder="число" style="width:140px"'+(solved?' value="'+b.ans+'" disabled':'')+'>'
+'<button class="btn primary" id="f1-b'+b.n+'-go"'+(solved?' disabled':'')+'>Атаковать</button>'
+'<button class="btn" id="f1-b'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="boss-hint-txt" id="f1-b'+b.n+'-ht" style="margin-top:8px;padding:9px 13px;background:rgba(245,158,11,.12);border-left:3px solid #f59e0b;border-radius:6px;font-size:.86rem;display:none;line-height:1.5">'+b.hint+'</div>'
+'<div class="feedback'+(solved?' ok':'')+'" id="f1-b'+b.n+'-fb" style="display:'+(solved?'block':'none')+'">'+(solved?'&#10003; Победа! +10 XP. Босс повержен.':'')+'</div>'
+'</div>';
});
cont.innerHTML = html;
BOSSES.forEach(b=>{
const go = document.getElementById('f1-b'+b.n+'-go');
const inp = document.getElementById('f1-b'+b.n+'-inp');
const fb = document.getElementById('f1-b'+b.n+'-fb');
const ht = document.getElementById('f1-b'+b.n+'-ht');
const hintBtn = document.getElementById('f1-b'+b.n+'-hint');
if(hintBtn) hintBtn.addEventListener('click', ()=>{ ht.style.display = ht.style.display==='block'?'none':'block'; });
if(!go || go.disabled) return;
go.addEventListener('click', ()=>{
const v = parseFloat((inp.value || '').replace(',','.'));
if(isNaN(v)){ fb.style.display='block'; fb.className='feedback fail'; fb.innerHTML='Введите число.'; return; }
if(Math.abs(v - b.ans) < b.tol){
fb.style.display='block'; fb.className='feedback ok'; fb.innerHTML='&#10003; Победа! +10 XP. '+b.hint;
go.disabled = true; inp.disabled = true;
document.getElementById('f1-boss-'+b.n).classList.add('solved');
const s = loadState();
if(!s[b.n]){ s[b.n]=true; saveState(s); addXp(10,'f1-boss-'+b.n); bumpProgress('final1', 12); }
updateBar();
renderMath(fb);
} else {
fb.style.display='block'; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Перепроверь и попробуй снова.';
}
});
inp.addEventListener('keydown', e=>{ if(e.key === 'Enter') go.click(); });
});
renderMath(cont);
updateBar();
}
renderAll();
}
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);
setTimeout(()=>achievement('start'), 600);
if(window.LS&&window.LS.xp){
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
}
}
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>