Files
Learn_System/frontend/textbooks/physics_8_ch2.html
T
Maxim Dolgolyov 3727417810 feat(phys8 ch2): Phase 4 Wave 2 — §30 Эрстед + §31 электромагнит + Финал главы 2
§30 Опыт Эрстеда:
- 3 теории: открытие 1820, значение опыта, применения
- IV-1: симуляция Эрстеда — провод + стрелка, slider'ы ключа и
  направления тока; без тока стрелка указывает на N (Землю),
  при включении тока отклоняется на 60° (по/против часовой
  в зависимости от направления)
- IV-2: 5 вопросов о значимости опыта
- IV-3: DnD 8 «есть/нет поля» (магниты, токи, нейтр. тела)
- IV-4: 6 MCQ

§31 Поле прямого провода + электромагнит:
- 3 теории: окружности линий и правило правой руки, соленоид,
  электромагнит
- IV-1: ГЛАВНЫЙ ВИЗУАЛ — электромагнит-конструктор: slider'ы I, N
  и dropdown сердечника (воздух/железо μ=500); катушка с витками,
  стержень, рассчитанный |B| и число поднимаемых скрепок
- IV-2: 5 вопросов «правило правой руки»
- IV-3: DnD 8 действий «усилит/ослабит поле»
- IV-4: 6 MCQ

ФИНАЛ ГЛАВЫ 2:
- Шпаргалка из 12 формул и понятий
- 10 интегрированных боссов: закон Ома, R=ρl/S, последов., параллельная,
  смешанная цепь, мощность, Джоуль-Ленц, кВт·ч за месяц, тариф,
  магистр электромагнетизма
- Прогресс-бар + ачивка em_master (+50 XP) при 10/10

Глава 2 «Электромагнитные явления» (§§12-31, 20 параграфов) закончена.

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

5376 lines
397 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 · Глава 2 · «Электромагнитные явления»</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:#fffbeb; --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,#78350f 0%,#d97706 55%,#fcd34d 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-p12"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p13"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p14"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p15"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p16"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p17"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p18"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p19"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p20"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p21"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p22"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p23"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p24"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p25"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p26"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p27"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p28"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p29"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p30"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p31"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.sec[id="sec-final2"]{ --sec-acc:#d97706; --sec-acc-d:#92400e; --sec-acc-soft:#fef3c7; }
.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 · Глава 2</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>Заряды притягиваются и отталкиваются, образуют электрическое поле. По проводнику течёт ток, и закон Ома связывает $U$, $I$, $R$. У постоянных магнитов и проводников с током есть магнитное поле.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p12')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 12</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-p12" class="sec"><div class="sec-header"><span class="sec-num">&sect; 12</span><h2 class="sec-h">Электризация тел. Взаимодействие зарядов</h2></div><div id="p12-body"></div></section>
<section id="sec-p13" class="sec"><div class="sec-header"><span class="sec-num">&sect; 13</span><h2 class="sec-h">Проводники и диэлектрики</h2></div><div id="p13-body"></div></section>
<section id="sec-p14" class="sec"><div class="sec-header"><span class="sec-num">&sect; 14</span><h2 class="sec-h">Электризация через влияние</h2></div><div id="p14-body"></div></section>
<section id="sec-p15" class="sec"><div class="sec-header"><span class="sec-num">&sect; 15</span><h2 class="sec-h">Электрический заряд. Элементарный заряд</h2></div><div id="p15-body"></div></section>
<section id="sec-p16" class="sec"><div class="sec-header"><span class="sec-num">&sect; 16</span><h2 class="sec-h">Строение атома. Ионы</h2></div><div id="p16-body"></div></section>
<section id="sec-p17" class="sec"><div class="sec-header"><span class="sec-num">&sect; 17</span><h2 class="sec-h">Электрическое поле. Электрическое напряжение</h2></div><div id="p17-body"></div></section>
<section id="sec-p18" class="sec"><div class="sec-header"><span class="sec-num">&sect; 18</span><h2 class="sec-h">Единица электрического напряжения. Расчёт работы в электрическом поле</h2></div><div id="p18-body"></div></section>
<section id="sec-p19" class="sec"><div class="sec-header"><span class="sec-num">&sect; 19</span><h2 class="sec-h">Электрический ток. Источники тока</h2></div><div id="p19-body"></div></section>
<section id="sec-p20" class="sec"><div class="sec-header"><span class="sec-num">&sect; 20</span><h2 class="sec-h">Сила и направление электрического тока</h2></div><div id="p20-body"></div></section>
<section id="sec-p21" class="sec"><div class="sec-header"><span class="sec-num">&sect; 21</span><h2 class="sec-h">Электрическая цепь. Измерение силы тока и напряжения</h2></div><div id="p21-body"></div></section>
<section id="sec-p22" class="sec"><div class="sec-header"><span class="sec-num">&sect; 22</span><h2 class="sec-h">Связь силы тока и напряжения. Закон Ома для участка электрической цепи</h2></div><div id="p22-body"></div></section>
<section id="sec-p23" class="sec"><div class="sec-header"><span class="sec-num">&sect; 23</span><h2 class="sec-h">Единица сопротивления. Расчёт сопротивления</h2></div><div id="p23-body"></div></section>
<section id="sec-p24" class="sec"><div class="sec-header"><span class="sec-num">&sect; 24</span><h2 class="sec-h">Последовательное соединение проводников. Реостат</h2></div><div id="p24-body"></div></section>
<section id="sec-p25" class="sec"><div class="sec-header"><span class="sec-num">&sect; 25</span><h2 class="sec-h">Параллельное соединение проводников</h2></div><div id="p25-body"></div></section>
<section id="sec-p26" class="sec"><div class="sec-header"><span class="sec-num">&sect; 26</span><h2 class="sec-h">Работа и мощность электрического тока. Закон Джоуля — Ленца</h2></div><div id="p26-body"></div></section>
<section id="sec-p27" class="sec"><div class="sec-header"><span class="sec-num">&sect; 27</span><h2 class="sec-h">Использование и экономия электроэнергии. Безопасность</h2></div><div id="p27-body"></div></section>
<section id="sec-p28" class="sec"><div class="sec-header"><span class="sec-num">&sect; 28</span><h2 class="sec-h">Постоянные магниты</h2></div><div id="p28-body"></div></section>
<section id="sec-p29" class="sec"><div class="sec-header"><span class="sec-num">&sect; 29</span><h2 class="sec-h">Магнитное поле</h2></div><div id="p29-body"></div></section>
<section id="sec-p30" class="sec"><div class="sec-header"><span class="sec-num">&sect; 30</span><h2 class="sec-h">Магнитное поле тока</h2></div><div id="p30-body"></div></section>
<section id="sec-p31" class="sec"><div class="sec-header"><span class="sec-num">&sect; 31</span><h2 class="sec-h">Магнитное поле прямого проводника и катушки с током. Электромагнит</h2></div><div id="p31-body"></div></section>
<section id="sec-final2" class="sec"><div class="sec-header"><span class="sec-num">&#9733;</span><h2 class="sec-h">Финал главы</h2></div><div id="final2-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» · Глава 2 · «Электромагнитные явления» · 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:'p12', progress:{}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 21;
const _TB_SLUG = 'physics-8-ch2';
const LS_PREFIX = 'physics8_ch2';
const LS_XP = 'physics8_xp';
const PARAS = [
{ id:'p12', num:'\u00a7 12', name:'Электризация тел. Взаимодействие зарядов', sub:'Два рода зарядов' },
{ id:'p13', num:'\u00a7 13', name:'Проводники и диэлектрики', sub:'Свободные носители' },
{ id:'p14', num:'\u00a7 14', name:'Электризация через влияние', sub:'Индукция' },
{ id:'p15', num:'\u00a7 15', name:'Электрический заряд. Элементарный заряд', sub:'$e = 1{,}6 \\cdot 10^{-19}$ Кл' },
{ id:'p16', num:'\u00a7 16', name:'Строение атома. Ионы', sub:'Ядро + электроны' },
{ id:'p17', num:'\u00a7 17', name:'Электрическое поле. Электрическое напряжение', sub:'$U$ как работа поля' },
{ id:'p18', num:'\u00a7 18', name:'Единица электрического напряжения. Расчёт работы в электрическом поле', sub:'$A = qU$' },
{ id:'p19', num:'\u00a7 19', name:'Электрический ток. Источники тока', sub:'Упорядоченное движение' },
{ id:'p20', num:'\u00a7 20', name:'Сила и направление электрического тока', sub:'$I = q/t$' },
{ id:'p21', num:'\u00a7 21', name:'Электрическая цепь. Измерение силы тока и напряжения', sub:'A — последов., V — паралл.' },
{ id:'p22', num:'\u00a7 22', name:'Связь силы тока и напряжения. Закон Ома для участка электрической цепи', sub:'$I = U/R$' },
{ id:'p23', num:'\u00a7 23', name:'Единица сопротивления. Расчёт сопротивления', sub:'$R = \\rho l/S$' },
{ id:'p24', num:'\u00a7 24', name:'Последовательное соединение проводников. Реостат', sub:'$R = R_1 + R_2$' },
{ id:'p25', num:'\u00a7 25', name:'Параллельное соединение проводников', sub:'$1/R = 1/R_1 + 1/R_2$' },
{ id:'p26', num:'\u00a7 26', name:'Работа и мощность электрического тока. Закон Джоуля — Ленца', sub:'$P = UI$, $Q = I^2 Rt$' },
{ id:'p27', num:'\u00a7 27', name:'Использование и экономия электроэнергии. Безопасность', sub:'кВт·ч, ТБ' },
{ id:'p28', num:'\u00a7 28', name:'Постоянные магниты', sub:'N, S, поле Земли' },
{ id:'p29', num:'\u00a7 29', name:'Магнитное поле', sub:'$\\vec{B}$, линии' },
{ id:'p30', num:'\u00a7 30', name:'Магнитное поле тока', sub:'Опыт Эрстеда' },
{ id:'p31', num:'\u00a7 31', name:'Магнитное поле прямого проводника и катушки с током. Электромагнит', sub:'Правило правой руки' },
{ id:'final2', num:'\u2605', name:'Финал главы', sub:'Итоги · 10 боссов', final:true }
];
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
const ACH_LABELS = {
start:"Начало главы 2!",
p12_done:"Электризация тел. Взаимодействие зарядов освоен!",
p13_done:"Проводники и диэлектрики освоен!",
p14_done:"Электризация через влияние освоен!",
p15_done:"Электрический заряд. Элементарный заряд освоен!",
p16_done:"Строение атома. Ионы освоен!",
p17_done:"Электрическое поле. Электрическое напряжение освоен!",
p18_done:"Единица электрического напряжения. Расчёт работы в электрическом поле освоен!",
p19_done:"Электрический ток. Источники тока освоен!",
p20_done:"Сила и направление электрического тока освоен!",
p21_done:"Электрическая цепь. Измерение силы тока и напряжения освоен!",
p22_done:"Связь силы тока и напряжения. Закон Ома для участка электрической цепи освоен!",
p23_done:"Единица сопротивления. Расчёт сопротивления освоен!",
p24_done:"Последовательное соединение проводников. Реостат освоен!",
p25_done:"Параллельное соединение проводников освоен!",
p26_done:"Работа и мощность электрического тока. Закон Джоуля — Ленца освоен!",
p27_done:"Использование и экономия электроэнергии. Безопасность освоен!",
p28_done:"Постоянные магниты освоен!",
p29_done:"Магнитное поле освоен!",
p30_done:"Магнитное поле тока освоен!",
p31_done:"Магнитное поле прямого проводника и катушки с током. Электромагнит освоен!",
ch2_done:"Глава 2 пройдена!",
em_master:"Мастер электромагнетизма — все боссы главы 2 повержены!"
};
const SIDEBARS = {
p12:{title:"Шпаргалка § 12",rows:[
["2 рода зарядов","+ (стекло о шёлк), &minus; (эбонит о шерсть)"],
["Одноимённые","отталкиваются"],
["Разноимённые","притягиваются"],
["При трении","заряд один — &plus;, другой — &minus;"],
["Сумма","сохраняется (закон сохр. заряда)"]
]},
p13:{title:"Шпаргалка § 13",rows:[
["Проводники","есть свободные носители"],
["Примеры пров.","металлы, графит, растворы солей, кислот, щелочей"],
["Диэлектрики","эбонит, стекло, дерево, пластик, дист. вода"],
["В проводнике","заряд по поверхности"],
["В диэлектрике","остаётся где появился"]
]},
p14:{title:"Шпаргалка § 14",rows:[
["Индукция","перераспределение зарядов в проводнике"],
["Без касания","нейтральный проводник $\\to$ разделение"],
["Ближний конец","заряд противоположного знака"],
["Если разделить","на 2 заряженные части"]
]},
p15:{title:"Шпаргалка § 15",rows:[
["Элементарный заряд","$e = 1{,}6 \\cdot 10^{-19}$ Кл"],
["Формула","$q = N e$ ($N$ — число избыт. электронов)"],
["Знак $-q$","избыток электронов"],
["Знак $+q$","нехватка электронов"],
["1 Кл","заряд $6{,}25 \\cdot 10^{18}$ электронов"],
["Сохранение","$\\sum q = $ const"]
]},
p16:{title:"Шпаргалка § 16",rows:[
["Атом","ядро (+) + электроны (&minus;)"],
["Ядро","протоны + нейтроны"],
["Нейтр. атом","$N_{электр} = Z$ (атомный номер)"],
["Катион (+)","потерял электрон(ы)"],
["Анион (&minus;)","принял электрон(ы)"],
["Электронные оболочки","K, L, M, …"]
]},
p17:{title:"Шпаргалка § 17",rows:[
["Эл. поле","материя вокруг заряда"],
["Действует","силой на другие заряды"],
["Линии поля","от + к &minus;"],
["Напряжение $U$","энергет. характеристика"],
["$U_{AB}$","разность потенциалов между A и B"]
]},
p18:{title:"Шпаргалка § 18",rows:[
["Формула","$A = q U$"],
["Единица","[U] = В = Дж/Кл"],
["Батарейка","1,5 В"],
["Аккумулятор","12 В"],
["Розетка","220 В"],
["1 В","работа 1 Дж на 1 Кл"]
]},
p19:{title:"Шпаргалка § 19",rows:[
["Ток","упорядоченное движение зарядов"],
["Условие 1","свободные носители"],
["Условие 2","эл. поле от источника"],
["Источники","батарейки, аккумуляторы, генераторы"],
["Внутри источника","сторонние силы $\\to$ ЭДС"]
]},
p20:{title:"Шпаргалка § 20",rows:[
["Формула","$I = q/t$"],
["Единица","[I] = А = Кл/с"],
["1 А","заряд 1 Кл за 1 с"],
["Направление тока","от + к &minus; (исторически)"],
["В металлах","электроны против тока"]
]},
p21:{title:"Шпаргалка § 21",rows:[
["Цепь","источник, проводник, потребитель, ключ"],
["Амперметр","ПОСЛЕДОВАТЕЛЬНО, малое $R$"],
["Вольтметр","ПАРАЛЛЕЛЬНО, большое $R$"],
["Шкала А","А, мА, мкА"],
["Шкала В","В, мВ, кВ"]
]},
p22:{title:"Шпаргалка § 22",rows:[
["Закон Ома","$I = U / R$"],
["$R$","сопротивление, Ом"],
["ВАХ металла","прямая через 0"],
["Больше $U$","больше $I$ (пропорц.)"],
["Больше $R$","меньше $I$"]
]},
p23:{title:"Шпаргалка § 23",rows:[
["Формула","$R = \\rho \\, l / S$"],
["$\\rho$","удельное сопр., Ом·мм²/м"],
["медь","$\\rho = 0{,}017$"],
["алюминий","$\\rho = 0{,}028$"],
["нихром","$\\rho = 1{,}1$"],
["1 Ом","$U=1$ В $\\to I=1$ А"]
]},
p24:{title:"Шпаргалка § 24",rows:[
["Послед. соед.","$I = $ const"],
["Напряжение","$U = U_1 + U_2$"],
["Сопротивл.","$R = R_1 + R_2$"],
["Реостат","переменный резистор"],
["Поломка одного","вся цепь обесточена"]
]},
p25:{title:"Шпаргалка § 25",rows:[
["Паралл. соед.","$U = $ const"],
["Ток","$I = I_1 + I_2$"],
["Сопротивл.","$1/R = 1/R_1 + 1/R_2$"],
["Два равных","$R_{общ} = R/2$"],
["Поломка одного","остальные работают"]
]},
p26:{title:"Шпаргалка § 26",rows:[
["Работа тока","$A = U I t$"],
["Мощность","$P = U I = A/t$"],
["Также","$P = U^2/R = I^2 R$"],
["Джоуль-Ленц","$Q = I^2 R t$"],
["[P]","Вт"],
["1 кВт·ч","$3{,}6 \\cdot 10^6$ Дж"]
]},
p27:{title:"Шпаргалка § 27",rows:[
["Энергия","$W = P \\tau$ (кВт·ч)"],
["Стоимость","$\\text{руб} = W \\cdot \\text{тариф}$"],
["1-фазная розетка","220 В, 16 А max"],
["ТБ","не голыми руками!"],
["Заземление","для защиты"]
]},
p28:{title:"Шпаргалка § 28",rows:[
["Полюсы","N (север), S (юг)"],
["Одноим.","отталкиваются"],
["Разноим.","притягиваются"],
["Разрез магнита","2 новых магнита (нет монополя)"],
["Магн. поле Земли","ось $\\approx$ географ."]
]},
p29:{title:"Шпаргалка § 29",rows:[
["$\\vec B$","магнитная индукция, Тл"],
["Линии $\\vec B$","выходят из N, входят в S"],
["Линии замкнуты","нет источника / стока"],
["Опилки","показывают линии $\\vec B$"]
]},
p30:{title:"Шпаргалка § 30",rows:[
["Опыт Эрстеда","1820 г"],
["Вывод","ток создаёт магн. поле"],
["Связь","электричество $\\leftrightarrow$ магнетизм"],
["Стрелка над током","отклоняется"]
]},
p31:{title:"Шпаргалка § 31",rows:[
["Прямой проводник","линии — концентр. окружности"],
["Правило прав. руки","большой палец = ток"],
["Соленоид","как полосовой магнит"],
["Электромагнит","катушка + сердечник"],
["$|B|$ катушки","$\\propto I N$"]
]},
final2:{title:"Финал главы 2",rows:[
["§§12-31","электромагнитные явления"],
["3 части","статика, ток, магнетизм"],
["Награда","+50 XP + Мастер ЭМ"]
]}
};
const TIPS=[
{sec:'p12',html:"Потри расчёску о волосы — она начнёт притягивать клочки бумаги. Это <b>электризация</b>. При трении один предмет получает <b>положительный</b> заряд, другой — <b>отрицательный</b>. Одноимённые отталкиваются, разноимённые — притягиваются."},
{sec:'p13',html:"Металлы — отличные проводники: их электроны легко двигаются. Эбонит, стекло, пластик — диэлектрики: электроны связаны атомами. Поэтому ручка отвёртки из пластика — а сама отвёртка металлическая."},
{sec:'p14',html:"Поднеси заряженный шар к незаряженному металлическому шарику — он притянется. В нейтральном проводнике под действием внешнего заряда электроны перераспределяются, и ближний конец получает противоположный знак."},
{sec:'p15',html:"Заряд не бывает «дробным» — все заряды кратны элементарному $e = 1{,}6 \\cdot 10^{-19}$ Кл. Если у тела «лишние» $N$ электронов, его заряд $q = -Ne$. 1 Кл — это очень много: примерно $6 \\cdot 10^{18}$ электронов."},
{sec:'p16',html:"Атом — это плотное положительное ядро (протоны+нейтроны) и облако отрицательных электронов вокруг. В обычном атоме число электронов равно числу протонов — он нейтрален. Если потерять электрон → ион +, если получить → ион &minus;."},
{sec:'p17',html:"Заряд не «торкает» другие заряды напрямую — он создаёт вокруг себя <b>электрическое поле</b>, а уже оно действует силой на другие заряды. Линии поля идут от + к &minus; и показывают направление силы на пробный +заряд."},
{sec:'p18',html:"Напряжение $U$ — это работа поля по перемещению единичного заряда: $A = qU$. Единица — 1 Вольт. Розетка 220 В, батарейка 1,5 В. Чем больше $U$, тем «сильнее» поле толкает заряд."},
{sec:'p19',html:"Ток — это упорядоченное движение свободных зарядов. Чтобы оно поддерживалось, нужен <b>источник тока</b> — батарейка, аккумулятор, генератор. Внутри источника <i>сторонние силы</i> переносят заряды против поля — это и есть «насос электричества»."},
{sec:'p20',html:"$I = q/t$ — сколько Кулонов прошло через сечение провода за 1 секунду. Единица — <b>Ампер</b> ($1$ А = $1$ Кл/с). За направление тока приняли движение «+» зарядов — хотя в металлах реально двигаются электроны (против тока)."},
{sec:'p21',html:"Простейшая цепь: батарея, лампа, ключ, провода. Чтобы измерить ток через лампу — амперметр включают <b>последовательно</b> с лампой. Чтобы измерить напряжение на лампе — вольтметр включают <b>параллельно</b>."},
{sec:'p22',html:"Закон Ома для участка цепи: $I = U/R$. Увеличил напряжение в 2 раза — ток вырос в 2 раза. Поставил резистор побольше — ток упал. Прямая зависимость для металлов."},
{sec:'p23',html:"Сопротивление провода зависит от трёх вещей: <b>материала</b> ($\\rho$), <b>длины</b> ($l$) и <b>толщины</b> ($S$). Длинный тонкий нихром — большое $R$ (спираль чайника), короткий толстый медный — маленькое $R$ (провод)."},
{sec:'p24',html:"При последовательном соединении ток течёт через все элементы один и тот же. Напряжения складываются. Сопротивления складываются. Реостат — переменный резистор, при сдвиге движка меняется длина включённой проволоки."},
{sec:'p25',html:"При параллельном соединении на каждой ветви одно и то же напряжение. Токи складываются. Сопротивление считается по особой формуле — в итоге $R_{общ}$ <b>меньше</b> любого из $R_1$, $R_2$."},
{sec:'p26',html:"$A = UIt$ — энергия, которую ток отдаёт прибору. $P = UI$ — это мощность (Дж/с = Вт). В резисторе вся эта энергия превращается в тепло — это <b>закон Джоуля-Ленца</b>: $Q = I^2 R t$."},
{sec:'p27',html:"Счётчик в квартире меряет энергию в <b>кВт·ч</b>: 1 кВт·ч — это работа за 1 час мощностью 1 кВт. Стоимость = энергия × тариф. ТБ: розетка под 220 В может ударить смертельным током."},
{sec:'p28',html:"Два полюса магнита: N (север) и S (юг). Как и заряды: одноимённые отталкиваются, разноимённые притягиваются. Но в отличие от зарядов, разрезать магнит и получить «только N» нельзя — выскочат новые два полюса."},
{sec:'p29',html:"Вокруг магнита есть магнитное поле — оно действует на железные предметы и магнитные стрелки. Линии поля выходят из N и входят в S, и они <b>замкнуты</b> (в отличие от линий эл. поля). Опилки на бумаге над магнитом красиво «рисуют» эти линии."},
{sec:'p30',html:"Великое открытие Эрстеда (1820): магнитная стрелка отклоняется около проводника с током. Значит, ток создаёт магнитное поле! Это первый «мост» между электричеством и магнетизмом."},
{sec:'p31',html:"Линии поля вокруг прямого провода — концентрические окружности. Направление определяет правило правой руки. Катушка с током (соленоид) ведёт себя как полосовой магнит. Электромагнит = катушка + железный сердечник: сильный, управляемый магнит."},
{sec:'final2',html:"Финал главы 2 — 10 интегрированных боссов по всей электротехнике 8 класса. Заряды, поле, ток, закон Ома, соединения, мощность, кВт·ч, магнетизм. +50 XP и ачивка «Мастер электромагнетизма»."}
];
const BUILDERS = {
p12: ()=>{ build_p12(); },
p13: ()=>{ build_p13(); },
p14: ()=>{ build_p14(); },
p15: ()=>{ build_p15(); },
p16: ()=>{ build_p16(); },
p17: ()=>{ build_p17(); },
p18: ()=>{ build_p18(); },
p19: ()=>{ build_p19(); },
p20: ()=>{ build_p20(); },
p21: ()=>{ build_p21(); },
p22: ()=>{ build_p22(); },
p23: ()=>{ build_p23(); },
p24: ()=>{ build_p24(); },
p25: ()=>{ build_p25(); },
p26: ()=>{ build_p26(); },
p27: ()=>{ build_p27(); },
p28: ()=>{ build_p28(); },
p29: ()=>{ build_p29(); },
p30: ()=>{ build_p30(); },
p31: ()=>{ build_p31(); },
final2: ()=>{ build_final2(); }
};
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 2 · WAVE 1 — §12, §13, §14
====================================================================== */
/* Sim-management */
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'); }
/* ======== §12 — Электризация тел ======== */
function build_p12(){
const box = document.getElementById('p12-body');
let h = '';
h += makeCard('theory', 'Электрический заряд', '§ 12.1',
'<p>При трении некоторых тел друг о друга они приобретают свойство притягивать лёгкие предметы — клочки бумаги, волоски. Говорят: тело <b>электризуется</b>, на нём появляется <b>электрический заряд</b>.</p>'
+'<p>Опыты показывают: существует <b>два рода</b> зарядов.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>Положительный (+):</b> возникает на стекле, потёртом о шёлк.</li>'
+'<li><b>Отрицательный (&minus;):</b> возникает на эбоните или пластике, потёртом о шерсть.</li>'
+'</ul>'
);
h += makeCard('rule', 'Закон взаимодействия зарядов', '§ 12.2',
'<p><b>Одноимённые</b> заряды (++ или &minus;&minus;) — <b>отталкиваются</b>.<br>'
+'<b>Разноимённые</b> заряды (+&minus;) — <b>притягиваются</b>.</p>'
+'<p>При электризации трением заряды на двух телах <b>равны по модулю</b> и <b>противоположны</b> по знаку. Это закон <b>сохранения электрического заряда</b>.</p>'
+'<p style="margin-top:6px">Электризация происходит из-за переноса электронов с одного тела на другое: потерявшее электроны становится «+», получившее — «&minus;».</p>'
);
h += makeCard('example', 'Примеры', '§ 12.3',
'<ul style="padding-left:20px;margin:6px 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">Потри палочку о ткань — она зарядится. Поднеси её к электроскопу — листочки разойдутся: одноимённые заряды отталкиваются.</div>'
+'<svg id="p12-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:10px"><button class="btn primary" id="p12-rub">Потереть палочку</button><button class="btn" id="p12-touch">Поднести к электроскопу</button><button class="btn" id="p12-reset">Сброс</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Заряд палочки: <b id="p12-q">0</b></span><span>Угол листочков: <b id="p12-a">0&#176;</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="p12-quiz"></div>'
+'<div class="actions"><button class="btn" id="p12-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p12-quiz-r">1</b> / 5</span><span>Правильно: <b id="p12-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="p12-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="attr"></div></div>'
+'<div class="drop-box"><h5>Отталкиваются</h5><div class="drop-items" data-cat="rep"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p12-dnd-check">Проверить</button><button class="btn" id="p12-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p12-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="p12-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p12-mcq-i">1</b> / 6</span><span>Правильно: <b id="p12-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p12') + readButton('p12');
renderMath(box);
wireReadBtn('p12');
_initP12_sim();
_initP12_quiz();
_initP12_dnd();
_initP12_mcq();
}
function _initP12_sim(){
const svg = document.getElementById('p12-sim'); if(!svg) return;
let charged = false; /* палочка заряжена */
let touched = false; /* поднесено к электроскопу */
function draw(){
let s = '';
/* электроскоп: стеклянная колба, металлический стержень, два листочка */
const escCx = 320, escCy = 130;
s += '<rect x="'+(escCx-50)+'" y="'+(escCy-20)+'" width="100" height="120" fill="rgba(186,230,253,.35)" stroke="#0f172a" stroke-width="1.6" rx="4"/>';
/* шар сверху */
s += '<circle cx="'+escCx+'" cy="'+(escCy-40)+'" r="14" fill="#94a3b8" stroke="#0f172a" stroke-width="1.6"/>';
/* стержень */
s += '<line x1="'+escCx+'" y1="'+(escCy-26)+'" x2="'+escCx+'" y2="'+(escCy+50)+'" stroke="#475569" stroke-width="3"/>';
/* листочки */
const leafAng = touched && charged ? 35 : 4;
document.getElementById('p12-a').textContent = leafAng+'&#176;';
const lx1 = escCx, ly1 = escCy+50;
const len = 36;
const lx2L = lx1 - len*Math.sin(leafAng*Math.PI/180);
const ly2L = ly1 + len*Math.cos(leafAng*Math.PI/180);
const lx2R = lx1 + len*Math.sin(leafAng*Math.PI/180);
const ly2R = ly1 + len*Math.cos(leafAng*Math.PI/180);
s += '<line x1="'+lx1+'" y1="'+ly1+'" x2="'+lx2L+'" y2="'+ly2L+'" stroke="#fbbf24" stroke-width="3"/>';
s += '<line x1="'+lx1+'" y1="'+ly1+'" x2="'+lx2R+'" y2="'+ly2R+'" stroke="#fbbf24" stroke-width="3"/>';
/* если заряжен и поднесён — рисуем + + на листочках */
if(touched && charged){
s += '<text x="'+(lx2L-8)+'" y="'+(ly2L+6)+'" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#dc2626">&minus;</text>';
s += '<text x="'+(lx2R+4)+'" y="'+(ly2R+6)+'" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#dc2626">&minus;</text>';
s += '<text x="'+escCx+'" y="'+(escCy-40+4)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="#dc2626">&minus;</text>';
}
/* палочка — слева */
const rodX = touched ? 260 : 100;
const rodY = 100;
s += '<rect x="'+rodX+'" y="'+(rodY-10)+'" width="100" height="20" fill="#1e1b4b" stroke="#0f172a" stroke-width="1.5" rx="3"/>';
if(charged){
for(let i=0;i<5;i++) s += '<text x="'+(rodX+10+i*20)+'" y="'+(rodY+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#fca5a5">&minus;</text>';
}
/* ткань (если ещё не тёрли) */
if(!charged){
s += '<rect x="20" y="160" width="80" height="40" fill="#f87171" stroke="#0f172a" stroke-width="1.5" rx="3"/>';
s += '<text x="60" y="184" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#fff">шерсть</text>';
}
/* подпись */
s += '<text x="150" y="50" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#475569">палочка (эбонит)</text>';
s += '<text x="320" y="220" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#475569">электроскоп</text>';
svg.innerHTML = s;
}
document.getElementById('p12-rub').addEventListener('click', ()=>{ charged = true; document.getElementById('p12-q').textContent = '5 нКл'; draw(); });
document.getElementById('p12-touch').addEventListener('click', ()=>{
if(!charged){
const fb = document.getElementById('p12-q'); fb.textContent = 'сначала потри!'; return;
}
touched = true; draw();
});
document.getElementById('p12-reset').addEventListener('click', ()=>{ charged=false; touched=false; document.getElementById('p12-q').textContent='0'; draw(); });
draw();
}
function _initP12_quiz(){
const QS = [
{sit:'Стеклянная палочка, потёртая о шёлк', ans:'+', why:'По таблице: стекло о шёлк — стекло положительное.'},
{sit:'Эбонитовая палочка о шерсть', ans:'-', why:'Эбонит получает электроны от шерсти — становится отрицательным.'},
{sit:'Шёлк после трения о стекло', ans:'-', why:'Шёлк забирает электроны со стекла — отрицательный.'},
{sit:'Шерсть после трения об эбонит', ans:'+', why:'Шерсть отдала электроны эбониту — положительная.'},
{sit:'Воздушный шарик о волосы', ans:'-', why:'Шарик принимает электроны с волос.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p12-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="+" style="padding:14px;font-size:1.1rem"><b>+</b> положительный</button>'
+'<button class="btn" data-pick="-" style="padding:14px;font-size:1.1rem"><b>&minus;</b> отрицательный</button>'
+'</div>'
+'<div class="feedback" id="p12-quiz-fb"></div>';
document.getElementById('p12-quiz-r').textContent = (i+1);
document.getElementById('p12-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('p12-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p12-quiz'); bumpProgress('p12', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p12-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p12-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP12_dnd(){
const items = [
{id:'pp', cat:'rep', html:'$+q$ и $+q$'},
{id:'mm', cat:'rep', html:'$-q$ и $-q$'},
{id:'pm', cat:'attr', html:'$+q$ и $-q$'},
{id:'mp', cat:'attr', html:'$-q$ и $+q$'},
{id:'p0', cat:'attr', html:'$+q$ и нейтральный проводник'},
{id:'m0', cat:'attr', html:'$-q$ и нейтральный проводник'},
{id:'pn', cat:'attr', html:'$+q$ и капля воды (диэл.)'},
{id:'pf', cat:'attr', html:'$+q$ и металл. шарик'}
];
const dnd = setupSorter({ poolId:'p12-dnd-pool', scopeSelector:'#sec-p12', cats:['attr','rep'], items, columnLayout:false });
document.getElementById('p12-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p12-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. Заряженный объект <b>всегда</b> притягивает нейтральные (индукция).'; addXp(15,'p12-dnd'); bumpProgress('p12', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: одноимённые отталкиваются; нейтральные всегда притягиваются.'; }
});
document.getElementById('p12-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p12-dnd-fb'); fb.style.display='none'; });
}
function _initP12_mcq(){
const QS = [
{q:'Сколько родов зарядов существует?', opts:['один','два','три','бесконечно много'], ans:1, why:'+ и &minus;, всего два.'},
{q:'При электризации стекла о шёлк стекло становится…', opts:['отрицательным','положительным','не заряжается','любым'], ans:1, why:'Стекло теряет электроны → +.'},
{q:'Что переносится при трении?', opts:['атомы','молекулы','электроны','ядра'], ans:2, why:'Электроны переходят с одного тела на другое.'},
{q:'Два одинаково заряженных шарика…', opts:['притягиваются','отталкиваются','неподвижны','зависит от размера'], ans:1, why:'Одноимённые всегда отталкиваются.'},
{q:'Сумма зарядов при трении двух нейтральных тел…', opts:['растёт','становится 0','остаётся 0','становится +'], ans:2, why:'Заряд сохраняется: было 0, стало $+q$ и $-q$, сумма всё ещё 0.'},
{q:'Почему стенка притягивает шарик после трения о голову?', opts:['стенка тоже зарядилась','индукция в нейтральной стенке','гравитация','клей'], ans:1, why:'В диэлектрике стенки происходит небольшая поляризация — она притягивается к шарику.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p12-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="p12-mcq-fb"></div><div class="actions"><button class="btn" id="p12-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p12-mcq-i').textContent = (i+1);
document.getElementById('p12-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('p12-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p12-mcq'); bumpProgress('p12', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p12-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p12-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p12-mcq-bonus'); bumpProgress('p12', 15); }, 600); }
});
});
const nb = document.getElementById('p12-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §13 — Проводники и диэлектрики ======== */
function build_p13(){
const box = document.getElementById('p13-body');
let h = '';
h += makeCard('theory', 'Свободные носители заряда', '§ 13.1',
'<p>Все вещества разделяют на два больших класса по способности проводить электрический заряд:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>Проводники</b> — есть свободные носители заряда (например, свободные электроны у металлов). Заряд легко перемещается внутри.</li>'
+'<li><b>Диэлектрики</b> (изоляторы) — носители связаны с атомами/молекулами и не могут свободно перемещаться. Заряд «застревает» там, куда попал.</li>'
+'</ul>'
);
h += makeCard('rule', 'Примеры', '§ 13.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:left">Диэлектрики</th></tr></thead>'
+'<tbody><tr><td style="padding:6px;border-bottom:1px dashed var(--border)">все металлы</td><td style="padding:6px;border-bottom:1px dashed var(--border)">эбонит, стекло, янтарь</td></tr>'
+'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">графит</td><td style="padding:6px;border-bottom:1px dashed var(--border)">дерево (сухое), пластик, резина</td></tr>'
+'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">растворы солей, кислот, щелочей</td><td style="padding:6px;border-bottom:1px dashed var(--border)">фарфор, бумага (сухая)</td></tr>'
+'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">тело человека (через жидкости)</td><td style="padding:6px;border-bottom:1px dashed var(--border)">дистиллированная вода, воздух (сухой)</td></tr>'
+'<tr><td style="padding:6px">влажная земля</td><td style="padding:6px">шёлк, мех</td></tr></tbody></table>'
);
h += makeCard('example', 'Где это видно', '§ 13.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Провода — медные (проводник), но в изоляции из пластика (диэлектрик).</li>'
+'<li>Розетки и выключатели — пластиковые корпуса.</li>'
+'<li>Заряд на металлическом шарике распределяется по всей поверхности — потому что он проводник.</li>'
+'<li>Заряд на пластиковой расчёске остаётся в месте трения — там, где «потёрли».</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> по всей поверхности. На пластиковом — остаётся в одном месте.</div>'
+'<svg id="p13-sim" viewBox="0 0 460 220" 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="p13-charge">Дать заряд в одной точке</button><button class="btn" id="p13-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="p13-quiz"></div>'
+'<div class="actions"><button class="btn" id="p13-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p13-quiz-r">1</b> / 8</span><span>Правильно: <b id="p13-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="p13-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="cond"></div></div>'
+'<div class="drop-box"><h5>Диэлектрики</h5><div class="drop-items" data-cat="diel"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p13-dnd-check">Проверить</button><button class="btn" id="p13-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p13-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="p13-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p13-mcq-i">1</b> / 6</span><span>Правильно: <b id="p13-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p13') + readButton('p13');
renderMath(box);
wireReadBtn('p13');
_initP13_sim();
_initP13_quiz();
_initP13_dnd();
_initP13_mcq();
}
function _initP13_sim(){
_killSim('p13sim');
const svg = document.getElementById('p13-sim'); if(!svg) return;
/* два шара: металлический (слева) и пластиковый (справа). При нажатии «дать заряд» — на метал. заряды разлетаются по поверхности, на пласт. остаются. */
const metCx = 130, metCy = 110, R = 60;
const plCx = 330, plCy = 110;
let charges = []; /* {x, y, target: 'met'|'pla', angTarget, sett: bool, settAng } */
let phase = 0; /* 0 = пусто, 1 = заряд дан */
function reset(){ charges = []; phase = 0; draw(); }
function giveCharge(){
/* добавляем 14 минус-зарядов в одной точке (верх каждого шара) */
if(phase > 0) return;
phase = 1;
for(let i=0;i<14;i++){
const a = Math.PI * 2 * i / 14;
charges.push({ targetA: a, x: metCx, y: metCy - R + 6, kind:'met', settled: false, t: 0 });
charges.push({ targetA: 0, x: plCx, y: plCy - R + 6, kind:'pla', settled: true, t: 0 });
}
}
function tick(){
if(!_isVisible('p13')){ _SIMS.p13sim.raf = requestAnimationFrame(tick); return; }
for(const c of charges){
if(c.kind === 'met' && !c.settled){
/* движение к точке на окружности (под углом targetA) */
c.t += 0.025;
const tx = metCx + (R-8) * Math.cos(c.targetA);
const ty = metCy + (R-8) * Math.sin(c.targetA);
c.x += (tx - c.x) * 0.06;
c.y += (ty - c.y) * 0.06;
if(Math.abs(c.x - tx) < 1 && Math.abs(c.y - ty) < 1) c.settled = true;
}
/* пластиковые сразу осели */
}
let s = '';
/* шары */
s += '<circle cx="'+metCx+'" cy="'+metCy+'" r="'+R+'" fill="#cbd5e1" stroke="#0f172a" stroke-width="2"/>';
s += '<text x="'+metCx+'" y="'+(metCy+R+22)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">МЕТАЛЛ (проводник)</text>';
s += '<circle cx="'+plCx+'" cy="'+plCy+'" r="'+R+'" fill="#fde68a" stroke="#0f172a" stroke-width="2"/>';
s += '<text x="'+plCx+'" y="'+(plCy+R+22)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">ПЛАСТИК (диэлектрик)</text>';
/* заряды */
for(const c of charges){
s += '<text x="'+c.x.toFixed(1)+'" y="'+(c.y+5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#dc2626">&minus;</text>';
}
/* стрелки касания если phase=0 */
if(phase === 0){
s += window.PHYS.drawArrow(metCx-30, metCy-R-30, metCx, metCy-R+2, '#10b981', 2, 9);
s += window.PHYS.drawArrow(plCx-30, plCy-R-30, plCx, plCy-R+2, '#10b981', 2, 9);
s += '<text x="'+(metCx-60)+'" y="'+(metCy-R-34)+'" font-family="Inter,sans-serif" font-size="11" fill="#0f172a">касание</text>';
s += '<text x="'+(plCx-60)+'" y="'+(plCy-R-34)+'" font-family="Inter,sans-serif" font-size="11" fill="#0f172a">касание</text>';
}
svg.innerHTML = s;
_SIMS.p13sim.raf = requestAnimationFrame(tick);
}
_SIMS.p13sim = { raf: 0 };
_SIMS.p13sim.raf = requestAnimationFrame(tick);
document.getElementById('p13-charge').addEventListener('click', giveCharge);
document.getElementById('p13-reset').addEventListener('click', reset);
}
function _initP13_quiz(){
const QS = [
{mat:'медь', ans:'C', why:'Металл — проводник.'},
{mat:'эбонит', ans:'D', why:'Эбонит — классический диэлектрик.'},
{mat:'графит (карандаш)', ans:'C', why:'Графит — проводник.'},
{mat:'стекло', ans:'D', why:'Стекло не проводит ток.'},
{mat:'раствор поваренной соли', ans:'C', why:'В растворе есть свободные ионы — проводит.'},
{mat:'дистиллированная вода', ans:'D', why:'Чистая вода — диэлектрик (нет ионов).'},
{mat:'железо', ans:'C', why:'Металл.'},
{mat:'фарфор', ans:'D', why:'Фарфор — изолятор, поэтому из него делают изоляторы на линиях электропередачи.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p13-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;font-weight:700;font-size:1rem">'+q.mat+'</div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
+'<button class="btn" data-pick="C" style="padding:14px"><b>Проводник</b></button>'
+'<button class="btn" data-pick="D" style="padding:14px"><b>Диэлектрик</b></button>'
+'</div>'
+'<div class="feedback" id="p13-quiz-fb"></div>';
document.getElementById('p13-quiz-r').textContent = (i+1);
document.getElementById('p13-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('p13-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p13-quiz'); bumpProgress('p13', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p13-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p13-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP13_dnd(){
const items = [
{id:'cu', cat:'cond', html:'медь'},
{id:'al', cat:'cond', html:'алюминий'},
{id:'gr', cat:'cond', html:'графит'},
{id:'sl', cat:'cond', html:'солёная вода'},
{id:'eb', cat:'diel', html:'эбонит'},
{id:'gl', cat:'diel', html:'стекло'},
{id:'pl', cat:'diel', html:'пластик'},
{id:'dw', cat:'diel', html:'дистил. вода'}
];
const dnd = setupSorter({ poolId:'p13-dnd-pool', scopeSelector:'#sec-p13', cats:['cond','diel'], items, columnLayout:false });
document.getElementById('p13-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p13-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,'p13-dnd'); bumpProgress('p13', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Дистиллированная вода — диэлектрик (нет ионов).'; }
});
document.getElementById('p13-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p13-dnd-fb'); fb.style.display='none'; });
}
function _initP13_mcq(){
const QS = [
{q:'Чем отличаются проводники от диэлектриков?', opts:['массой','цветом','свободными носителями зарядов','температурой'], ans:2, why:'У проводников есть свободные заряды, у диэлектриков — нет.'},
{q:'Какие частицы свободны в металлах?', opts:['атомы','протоны','электроны','нейтроны'], ans:2, why:'Свободные электроны переносят заряд в металлах.'},
{q:'Заряд на металлическом шарике распределяется…', opts:['в центре','в одной точке','по всей поверхности','внутри'], ans:2, 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('p13-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="p13-mcq-fb"></div><div class="actions"><button class="btn" id="p13-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p13-mcq-i').textContent = (i+1);
document.getElementById('p13-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('p13-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p13-mcq'); bumpProgress('p13', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p13-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p13-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p13-mcq-bonus'); bumpProgress('p13', 15); }, 600); }
});
});
const nb = document.getElementById('p13-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §14 — Электризация через влияние (индукция) ======== */
function build_p14(){
const box = document.getElementById('p14-body');
let h = '';
h += makeCard('theory', 'Что такое индукция', '§ 14.1',
'<p>Если поднести заряженное тело к нейтральному <b>проводнику</b>, не касаясь его, в проводнике произойдёт <b>перераспределение</b> свободных электронов.</p>'
+'<p><b>Электризация через влияние</b> (или <b>индукция</b>) — это разделение зарядов в проводнике под действием внешнего заряда без непосредственного контакта.</p>'
+'<p>Ближний к источнику конец проводника получает заряд <b>противоположного</b> знака, дальний — того же знака.</p>'
);
h += makeCard('rule', 'Как это работает', '§ 14.2',
'<p>Подносим $+q$ к незаряженному металлическому шарику:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li>Свободные электроны (отрицательные) притягиваются к $+q$ — собираются на <b>ближнем</b> конце.</li>'
+'<li>На <b>дальнем</b> конце остаются положительные ионы — некомпенсированный $+q$.</li>'
+'<li>В целом шарик остался нейтральным, но <b>заряды разделились</b>.</li>'
+'<li>Если теперь шарик разделить на 2 половинки — каждая будет заряжена!</li>'
+'</ol>'
);
h += makeCard('example', 'Примеры явления', '§ 14.3',
'<ul style="padding-left:20px;margin:6px 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">Двигай заряженную палочку (slider) — наблюдай, как электроны в металлическом шарике перераспределяются.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Положение палочки: <b id="p14-xv">слева</b><input type="range" id="p14-x" min="40" max="160" step="5" value="60"></label>'
+'<label>Знак: <select id="p14-sig" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem"><option value="-1">отрицательная (&minus;)</option><option value="1">положительная (+)</option></select></label>'
+'</div>'
+'<svg id="p14-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>Ближний конец шара: <b id="p14-near">+</b> (противоположный палочке)</span>'
+'<span>Дальний конец шара: <b id="p14-far">&minus;</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="p14-quiz"></div>'
+'<div class="actions"><button class="btn" id="p14-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p14-quiz-r">1</b> / 5</span><span>Правильно: <b id="p14-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">В проводниках — <b>индукция</b> (электроны бегут); в диэлектриках — <b>поляризация</b> (молекулы поворачиваются).</div>'
+'<div id="p14-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="ind"></div></div>'
+'<div class="drop-box"><h5>Поляризация (в диэлектрике)</h5><div class="drop-items" data-cat="pol"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p14-dnd-check">Проверить</button><button class="btn" id="p14-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p14-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="p14-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p14-mcq-i">1</b> / 6</span><span>Правильно: <b id="p14-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p14') + readButton('p14');
renderMath(box);
wireReadBtn('p14');
_initP14_sim();
_initP14_quiz();
_initP14_dnd();
_initP14_mcq();
}
function _initP14_sim(){
const svg = document.getElementById('p14-sim'); if(!svg) return;
function draw(){
const sx = +document.getElementById('p14-x').value;
const sig = +document.getElementById('p14-sig').value;
document.getElementById('p14-xv').textContent = sx < 80 ? 'далеко' : (sx < 130 ? 'близко' : 'почти касается');
/* у шарика: nearLabel = противоп. знаку палочки */
const nearSym = sig > 0 ? '&minus;' : '+';
const farSym = sig > 0 ? '+' : '&minus;';
document.getElementById('p14-near').innerHTML = nearSym;
document.getElementById('p14-far').innerHTML = farSym;
/* draw */
let s = '';
/* палочка слева */
s += '<rect x="'+(sx-50)+'" y="80" width="50" height="20" fill="#1e1b4b" stroke="#0f172a" stroke-width="1.5" rx="3"/>';
const rodCol = sig > 0 ? '#dc2626' : '#2563eb';
const rodTxt = sig > 0 ? '+' : '&minus;';
for(let i=0;i<4;i++) s += '<text x="'+(sx-42+i*12)+'" y="95" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="'+rodCol+'">'+rodTxt+'</text>';
/* металлический шарик в центре */
const shCx = 260, shCy = 90, shR = 56;
s += '<circle cx="'+shCx+'" cy="'+shCy+'" r="'+shR+'" fill="#cbd5e1" stroke="#0f172a" stroke-width="2"/>';
/* концентрация электронов на ближнем конце (если sig=+, электроны к палочке) или ионов */
const intensity = Math.min(1, (180 - sx) / 100); /* чем ближе палочка, тем сильнее эффект */
/* near = слева от шара (ближе к палочке), far = справа */
/* ставим знаки. Если палочка -, на ближнем + (т.е. на левой стороне +) */
const nearCol = sig > 0 ? '#2563eb' : '#dc2626';
const farCol = sig > 0 ? '#dc2626' : '#2563eb';
const nearTxt = sig > 0 ? '&minus;' : '+';
const farTxt = sig > 0 ? '+' : '&minus;';
for(let i=0;i<6;i++){
const ang = -Math.PI/2 + (i-2.5)*0.18;
const r = shR - 14;
/* near (слева) */
const nx = shCx + r*Math.cos(Math.PI + ang*0.4);
const ny = shCy + r*Math.sin(Math.PI + ang*0.4) - i*0;
s += '<text x="'+nx.toFixed(1)+'" y="'+(ny+5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="'+nearCol+'" opacity="'+intensity.toFixed(2)+'">'+nearTxt+'</text>';
/* far (справа) */
const fx = shCx + r*Math.cos(ang*0.4);
const fy = shCy + r*Math.sin(ang*0.4);
s += '<text x="'+fx.toFixed(1)+'" y="'+(fy+5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="'+farCol+'" opacity="'+intensity.toFixed(2)+'">'+farTxt+'</text>';
}
s += '<text x="'+shCx+'" y="'+(shCy+shR+18)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">МЕТАЛЛ. ШАР (нейтральный)</text>';
/* подпись индукции */
if(intensity > 0.3){
s += '<text x="230" y="40" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#7c3aed">индукция!</text>';
}
svg.innerHTML = s;
}
document.getElementById('p14-x').addEventListener('input', draw);
document.getElementById('p14-sig').addEventListener('change', draw);
draw();
}
function _initP14_quiz(){
const QS = [
{sit:'$+q$ поднесли к нейтральному металлическому шару, не касаясь.', opts:['шар стал +','шар стал ','шар стал нейтральным с разделёнными зарядами','шар не изменился'], ans:2, why:'Произошла индукция: внутри шара разделились заряды, но в целом он остался нейтральным.'},
{sit:'$-q$ поднесли к шару и коснулись его.', opts:['заряды шара не изменились','шар стал отрицательным','шар стал положительным','шар стал нейтральным'], ans:1, why:'Электроны с палочки перешли на шар.'},
{sit:'$+q$ долго держали возле шара, а потом убрали (не касаясь).', opts:['шар остался заряженным','шар нейтральный','шар стал отрицательным','шар стал положительным'], ans:1, why:'Без касания заряд просто перераспределялся; при удалении источника всё возвращается, шар нейтрален.'},
{sit:'$+q$ поднесли к двум прижатым друг к другу шарам. Их разъединили (не убирая $+q$), затем убрали $+q$.', opts:['оба нейтральны','оба +','оба ','один +, другой '], ans:3, why:'При разделении в присутствии $+q$ заряды зафиксировались: дальний шар стал +, ближний −.'},
{sit:'$-q$ поднесли к листку бумаги (диэлектрик).', opts:['ничего','бумага зарядилась +','в бумаге поляризация, она притянулась','бумага оттолкнулась'], ans:2, why:'В диэлектрике молекулы поляризуются, листок притягивается.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p14-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;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="p14-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p14-quiz-r').textContent = (i+1);
document.getElementById('p14-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('p14-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p14-quiz'); bumpProgress('p14', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p14-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p14-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP14_dnd(){
const items = [
{id:'ms', cat:'ind', html:'$+q$ возле металлического шара'},
{id:'mf', cat:'ind', html:'заряженный шар возле фольги'},
{id:'mp', cat:'ind', html:'заряженный шар возле железной пластины'},
{id:'mw', cat:'ind', html:'$+q$ возле провода'},
{id:'pl', cat:'pol', html:'$-q$ возле клочка бумаги'},
{id:'pg', cat:'pol', html:'$+q$ возле стеклянной палочки'},
{id:'pw', cat:'pol', html:'$-q$ возле деревянной планки'},
{id:'pp', cat:'pol', html:'$+q$ возле пластиковой пластины'}
];
const dnd = setupSorter({ poolId:'p14-dnd-pool', scopeSelector:'#sec-p14', cats:['ind','pol'], items, columnLayout:false });
document.getElementById('p14-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p14-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,'p14-dnd'); bumpProgress('p14', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Металлы и фольга — проводники, остальное — диэлектрики.'; }
});
document.getElementById('p14-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p14-dnd-fb'); fb.style.display='none'; });
}
function _initP14_mcq(){
const QS = [
{q:'Что такое электризация через влияние?', opts:['заряжение трением','перенос заряда касанием','разделение зарядов в проводнике без касания','испускание электронов'], ans:2, why:'Это разделение зарядов внутри проводника под действием внешнего заряда.'},
{q:'Ближний к источнику конец проводника получает заряд…', opts:['того же знака','противоположного','становится нейтральным','положительный'], ans:1, why:'Притягиваются заряды противоположного знака.'},
{q:'После убирания внешнего заряда (без касания) проводник…', opts:['остаётся заряженным','становится нейтральным','меняет знак','раскалывается'], ans:1, why:'Перераспределение исчезает, и проводник снова нейтрален.'},
{q:'Если в присутствии $+q$ разъединить шар на 2 части, что станет с частями после убирания $+q$?', opts:['обе +','обе ','одна +, другая ','обе нейтральны'], ans:2, why:'Разделение зафиксировалось: ближняя часть −, дальняя +.'},
{q:'Почему листочки бумаги притягиваются к расчёске?', opts:['индукция','поляризация молекул в диэлектрике','магнетизм','гравитация'], ans:1, why:'В диэлектрике молекулы поляризуются, и листок притягивается.'},
{q:'Молниеотвод использует…', opts:['индукцию','теплопередачу','излучение','реакцию горения'], ans:0, why:'Индукция собирает заряд из тучи на острие и отводит его в землю.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p14-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="p14-mcq-fb"></div><div class="actions"><button class="btn" id="p14-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p14-mcq-i').textContent = (i+1);
document.getElementById('p14-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('p14-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p14-mcq'); bumpProgress('p14', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p14-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p14-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p14-mcq-bonus'); bumpProgress('p14', 15); }, 600); }
});
});
const nb = document.getElementById('p14-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 2 · WAVE 2 — §15, §16
====================================================================== */
const E_CHARGE = 1.6e-19;
/* ======== §15 — Электрический заряд. Элементарный заряд ======== */
function build_p15(){
const box = document.getElementById('p15-body');
let h = '';
h += makeCard('theory', 'Элементарный заряд', '§ 15.1',
'<p>Эксперимент Милликена с масляными каплями показал: <b>заряд тела всегда кратен</b> наименьшей порции — <b>элементарному заряду</b>:</p>'
+'<p style="text-align:center;margin:8px 0">$$e = 1{,}6 \\cdot 10^{-19} \\text{ Кл}$$</p>'
+'<p>Это заряд одного электрона (со знаком «&minus;») и одного протона (со знаком «+»). Меньше — не бывает (это <b>квантуется</b>).</p>'
);
h += makeCard('rule', 'Формула $q = N e$', '§ 15.2',
'<p>Если у тела «лишних» $N$ электронов, его заряд:</p>'
+'<p style="text-align:center;margin:8px 0">$$q = N \\cdot e$$</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>$q < 0$ — избыток электронов;</li>'
+'<li>$q > 0$ — нехватка электронов;</li>'
+'<li>$q = 0$ — нейтральное тело (число +зарядов = число &minus;зарядов).</li>'
+'</ul>'
+'<p>Сколько электронов в 1 Кл? $N = 1/e = 6{,}25 \\cdot 10^{18}$ — это огромное число.</p>'
);
h += makeCard('example', 'Закон сохранения заряда', '§ 15.3',
'<p>В замкнутой системе сумма всех зарядов <b>не меняется</b>:</p>'
+'<p style="text-align:center;margin:8px 0">$$\\sum q_i = \\text{const}$$</p>'
+'<p>Электризация трением — не «появление» заряда, а его <b>перенос</b>: было два нейтральных тела (всего 0), стало $+q$ и $-q$ (сумма всё ещё 0).</p>'
);
/* IV1 — калькулятор q ↔ N */
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">Преобразуй между числом электронов $N$ и зарядом $q$ в нанокулонах.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Число электронов $N$: <b id="p15-nv">10<sup>9</sup></b><input type="range" id="p15-n" min="6" max="18" step="0.5" value="9"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$N$ = <b id="p15-nval">1 000 000 000</b></span>'
+'<span>$|q| = N e$ = <b id="p15-qval">1.6&times;10<sup>-10</sup></b> Кл = <b id="p15-qnc">0.16</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">Проверь, кратен ли указанный заряд $e = 1{,}6 \\cdot 10^{-19}$ Кл.</div>'
+'<div id="p15-quiz"></div>'
+'<div class="actions"><button class="btn" id="p15-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p15-quiz-r">1</b> / 6</span><span>Правильно: <b id="p15-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="p15-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="keep"></div></div>'
+'<div class="drop-box"><h5>Меняется (внеш. вмешат.)</h5><div class="drop-items" data-cat="ext"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p15-dnd-check">Проверить</button><button class="btn" id="p15-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p15-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. $e = 1{,}6 \\cdot 10^{-19}$ Кл.</div>'
+'<div id="p15-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p15-task-i">1</b> / 5</span><span>Правильно: <b id="p15-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p15') + readButton('p15');
renderMath(box);
wireReadBtn('p15');
_initP15_calc();
_initP15_quiz();
_initP15_dnd();
_initP15_tasks();
}
function _initP15_calc(){
function update(){
const exp = +document.getElementById('p15-n').value;
const N = Math.pow(10, exp);
document.getElementById('p15-nv').innerHTML = '10<sup>'+exp.toFixed(1)+'</sup>';
document.getElementById('p15-nval').textContent = N.toExponential(2).replace('+','');
const q = N * E_CHARGE;
const qExp = Math.floor(Math.log10(q));
const qMant = (q/Math.pow(10,qExp)).toFixed(2);
document.getElementById('p15-qval').innerHTML = qMant+'&times;10<sup>'+qExp+'</sup>';
document.getElementById('p15-qnc').textContent = (q*1e9).toExponential(2).replace('+','');
}
document.getElementById('p15-n').addEventListener('input', update);
update();
}
function _initP15_quiz(){
const QS = [
{q:'$q = 3{,}2 \\cdot 10^{-19}$ Кл', ans:'Y', why:'$N = q/e = 2$ — кратно, существует.'},
{q:'$q = 8 \\cdot 10^{-19}$ Кл', ans:'Y', why:'$N = 5$ — целое, существует.'},
{q:'$q = 2{,}4 \\cdot 10^{-19}$ Кл', ans:'N', why:'$N = 1{,}5$ — не целое, не существует.'},
{q:'$q = 1 \\cdot 10^{-19}$ Кл', ans:'N', why:'$N = 0{,}625$ — не целое.'},
{q:'$q = 1{,}6 \\cdot 10^{-18}$ Кл', ans:'Y', why:'$N = 10$ — целое, существует.'},
{q:'$q = 5 \\cdot 10^{-19}$ Кл', ans:'N', why:'$N = 3{,}125$ — не целое.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p15-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;font-size:1.05rem">'+q.q+'</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="p15-quiz-fb"></div>';
document.getElementById('p15-quiz-r').textContent = (i+1);
document.getElementById('p15-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('p15-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p15-quiz'); bumpProgress('p15', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p15-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p15-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP15_dnd(){
const items = [
{id:'a', cat:'keep', html:'трение двух тел в изолированной системе'},
{id:'b', cat:'keep', html:'переход электронов внутри проводника'},
{id:'c', cat:'keep', html:'индукция в шарике'},
{id:'d', cat:'keep', html:'два шара соприкоснулись и разъединились'},
{id:'e', cat:'ext', html:'тело заземлили (заряд ушёл в землю)'},
{id:'f', cat:'ext', html:'тело потёрли о внешний предмет, а потом убрали'},
{id:'g', cat:'ext', html:'к телу поднесли «насос электронов»'},
{id:'h', cat:'ext', html:'тело облучили рентгеном (выбил электроны)'}
];
const dnd = setupSorter({ poolId:'p15-dnd-pool', scopeSelector:'#sec-p15', cats:['keep','ext'], items, columnLayout:false });
document.getElementById('p15-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p15-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,'p15-dnd'); bumpProgress('p15', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Заземление и внешнее излучение — это внешнее вмешательство.'; }
});
document.getElementById('p15-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p15-dnd-fb'); fb.style.display='none'; });
}
function _initP15_tasks(){
const TASKS = [
{q:'Сколько электронов нужно собрать, чтобы получить заряд $q = -1$ нКл? ($e = 1{,}6 \\cdot 10^{-19}$ Кл) — Ответ в формате $N \\cdot 10^9$, введи $N$ с одним знаком после запятой.', ans:6.25, tol:0.1, why:'$N = q/e = 10^{-9}/(1{,}6\\cdot10^{-19}) = 6{,}25 \\cdot 10^9$. Ответ: $6{,}25$.'},
{q:'У тела «лишних» $5 \\cdot 10^{10}$ электронов. Каков его заряд (в нКл, по модулю)?', ans:8, tol:0.2, why:'$q = Ne = 5\\cdot10^{10} \\cdot 1{,}6\\cdot10^{-19} = 8\\cdot10^{-9}$ Кл = $8$ нКл.'},
{q:'У одного тела $q_1 = +3$ нКл, у другого $q_2 = -5$ нКл. После их соприкосновения и разделения сумма $q_1 + q_2$ равна (в нКл)?', ans:-2, tol:0.05, why:'Заряд сохраняется: $3 + (-5) = -2$ нКл (так и осталось после контакта).'},
{q:'Какой заряд (в Кл, по модулю) получится при $N = 2 \\cdot 10^{19}$? Ответ в формате $a \\cdot 10^0$, введи $a$ (одно число).', ans:3.2, tol:0.05, why:'$q = 2\\cdot10^{19} \\cdot 1{,}6\\cdot10^{-19} = 3{,}2$ Кл — это очень много!'},
{q:'Заряд $q = 4{,}8 \\cdot 10^{-19}$ Кл существует? Если да — сколько в нём электронов? Введи $N$.', ans:3, tol:0.1, why:'$N = q/e = 4{,}8/1{,}6 = 3$. Заряд существует, в нём 3 элементарных.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p15-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="p15-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p15-task-go">Ответ</button>'
+'<button class="btn" id="p15-task-hint">Подсказка</button>'
+'<button class="btn" id="p15-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p15-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p15-task-fb"></div>';
document.getElementById('p15-task-i').textContent = (i+1);
document.getElementById('p15-task-ok').textContent = ok;
document.getElementById('p15-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p15-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p15-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,'p15-task'); bumpProgress('p15', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Правильный ответ: '+t.ans+'. '+t.why; }
document.getElementById('p15-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p15-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p15-task-bonus'); bumpProgress('p15', 15); }, 600); }
});
document.getElementById('p15-task-hint').addEventListener('click', ()=>{ document.getElementById('p15-task-hint-txt').classList.toggle('show'); });
document.getElementById('p15-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §16 — Строение атома. Ионы ======== */
function build_p16(){
const box = document.getElementById('p16-body');
let h = '';
h += makeCard('theory', 'Планетарная модель атома', '§ 16.1',
'<p>Атом устроен как маленькая «солнечная система»:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>Ядро</b> в центре — очень маленькое (10<sup>-15</sup> м), но в нём почти вся масса. Состоит из <b>протонов</b> ($+e$) и <b>нейтронов</b> (нейтральные).</li>'
+'<li><b>Электроны</b> ($-e$) движутся вокруг ядра по оболочкам.</li>'
+'<li>Атом в целом размером $\\sim 10^{-10}$ м — ядро в 100 000 раз меньше всего атома.</li>'
+'</ul>'
+'<p>В <b>нейтральном</b> атоме число электронов = число протонов = <b>атомный номер</b> $Z$.</p>'
);
h += makeCard('rule', 'Ионы', '§ 16.2',
'<p>Если атом <b>потерял</b> 1 или больше электронов — он становится <b>положительным ионом (катионом)</b>: $\\text{Na} \\to \\text{Na}^+$.</p>'
+'<p>Если атом <b>принял</b> лишний электрон — он становится <b>отрицательным ионом (анионом)</b>: $\\text{Cl} \\to \\text{Cl}^-$.</p>'
+'<p>Соли в растворах диссоциируют на ионы — поэтому растворы солей проводят ток.</p>'
);
h += makeCard('example', 'Атомы и ионы', '§ 16.3',
'<table style="width:100%;border-collapse:collapse;font-size:.92rem"><thead><tr style="background:rgba(15,23,42,.04)"><th style="padding:6px">Элемент</th><th style="padding:6px;text-align:center">$Z$</th><th style="padding:6px;text-align:center">Электр.</th><th style="padding:6px;text-align:center">Заряд иона</th></tr></thead>'
+'<tbody><tr><td style="padding:6px;border-bottom:1px dashed var(--border)">водород H</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">1</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">1</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">H<sup>+</sup></td></tr>'
+'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">натрий Na</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">11</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">11</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">Na<sup>+</sup></td></tr>'
+'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">хлор Cl</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">17</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">17</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">Cl<sup>&minus;</sup></td></tr>'
+'<tr><td style="padding:6px;border-bottom:1px dashed var(--border)">кальций Ca</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">20</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">20</td><td style="padding:6px;border-bottom:1px dashed var(--border);text-align:center">Ca<sup>2+</sup></td></tr>'
+'<tr><td style="padding:6px">кислород O</td><td style="padding:6px;text-align:center">8</td><td style="padding:6px;text-align:center">8</td><td style="padding:6px;text-align:center">O<sup>2&minus;</sup></td></tr></tbody></table>'
);
/* 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>Протонов ($Z$): <b id="p16-zv">11</b><input type="range" id="p16-z" min="1" max="20" step="1" value="11"></label>'
+'<label>Электронов: <b id="p16-ev">11</b><input type="range" id="p16-e" min="0" max="20" step="1" value="11"></label>'
+'</div>'
+'<svg id="p16-sim" viewBox="0 0 460 240" 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>Заряд: <b id="p16-qbal">0</b> (нейтрален)</span>'
+'<span>Состояние: <b id="p16-state">нейтральный атом</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="p16-quiz"></div>'
+'<div class="actions"><button class="btn" id="p16-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p16-quiz-r">1</b> / 6</span><span>Правильно: <b id="p16-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="p16-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="pos"></div></div>'
+'<div class="drop-box"><h5>Отрицательные</h5><div class="drop-items" data-cat="neg"></div></div>'
+'<div class="drop-box"><h5>Нейтральные</h5><div class="drop-items" data-cat="neu"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p16-dnd-check">Проверить</button><button class="btn" id="p16-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p16-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="p16-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p16-mcq-i">1</b> / 6</span><span>Правильно: <b id="p16-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p16') + readButton('p16');
renderMath(box);
wireReadBtn('p16');
_initP16_atom();
_initP16_quiz();
_initP16_dnd();
_initP16_mcq();
}
function _initP16_atom(){
const svg = document.getElementById('p16-sim'); if(!svg) return;
function draw(){
const Z = +document.getElementById('p16-z').value;
const N = +document.getElementById('p16-e').value;
document.getElementById('p16-zv').textContent = Z;
document.getElementById('p16-ev').textContent = N;
const delta = Z - N;
document.getElementById('p16-qbal').textContent = (delta > 0 ? '+'+delta : (delta < 0 ? delta : '0'))+' e';
let state = '';
if(delta === 0) state = 'нейтральный атом';
else if(delta > 0) state = 'катион (+'+delta+')';
else state = 'анион ('+delta+')';
document.getElementById('p16-state').textContent = state;
/* draw */
const cx = 230, cy = 120;
let s = '';
/* электронные оболочки */
const shellRs = [50, 80, 110];
for(const r of shellRs) s += '<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3 3"/>';
/* ядро */
s += '<circle cx="'+cx+'" cy="'+cy+'" r="22" fill="#fef3c7" stroke="#dc2626" stroke-width="2"/>';
s += '<text x="'+cx+'" y="'+(cy+5)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="800" fill="#dc2626">'+Z+'p</text>';
/* электроны на оболочках: 2 на K (50), 8 на L (80), остальное на M (110) */
let placed = 0;
const shellMax = [2, 8, 18];
for(let sh = 0; sh < 3 && placed < N; sh++){
const r = shellRs[sh];
const count = Math.min(shellMax[sh], N - placed);
for(let i = 0; i < count; i++){
const ang = 2 * Math.PI * i / count + sh * 0.3;
const ex = cx + r*Math.cos(ang);
const ey = cy + r*Math.sin(ang);
s += '<circle cx="'+ex.toFixed(1)+'" cy="'+ey.toFixed(1)+'" r="5" fill="#2563eb" stroke="#0f172a" stroke-width="0.8"/>';
s += '<text x="'+ex.toFixed(1)+'" y="'+(ey+3).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="9" font-weight="800" fill="#fff">&minus;</text>';
}
placed += count;
}
/* пометка заряда */
if(delta !== 0){
const sign = delta > 0 ? '+' : '';
const col = delta > 0 ? '#dc2626' : '#2563eb';
s += '<text x="'+(cx+130)+'" y="'+(cy-90)+'" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="20" font-weight="900" fill="'+col+'">'+sign+delta+'</text>';
}
svg.innerHTML = s;
}
document.getElementById('p16-z').addEventListener('input', draw);
document.getElementById('p16-e').addEventListener('input', draw);
draw();
}
function _initP16_quiz(){
const QS = [
{q:'Na отдал 1 электрон. Какой ион?', opts:['Na<sup>+</sup>','Na<sup>&minus;</sup>','Na<sup>2+</sup>','Na'], ans:0, why:'Потерял 1 электрон → катион +1.'},
{q:'Cl принял 1 электрон. Какой ион?', opts:['Cl<sup>+</sup>','Cl<sup>&minus;</sup>','Cl<sup>2&minus;</sup>','Cl'], ans:1, why:'Принял 1 → анион &minus;1.'},
{q:'Ca отдал 2 электрона. Заряд иона?', opts:['+1','+2','&minus;1','0'], ans:1, why:'2 потерянных электрона → +2.'},
{q:'O принял 2 электрона. Заряд иона?', opts:['+2','&minus;2','&minus;1','0'], ans:1, why:'2 принятых электрона → &minus;2.'},
{q:'Сколько электронов у Na<sup>+</sup>? ($Z_{Na}$=11)', opts:['10','11','12','9'], ans:0, why:'Был 11, отдал 1, остался 10.'},
{q:'Сколько электронов у Cl<sup>&minus;</sup>? ($Z_{Cl}$=17)', opts:['18','17','16','19'], ans:0, why:'Принял 1, стало 18.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p16-quiz'); 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 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="p16-quiz-fb"></div>';
wrap.innerHTML = h;
document.getElementById('p16-quiz-r').textContent = (i+1);
document.getElementById('p16-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('p16-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p16-quiz'); bumpProgress('p16', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p16-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p16-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP16_dnd(){
const items = [
{id:'p', cat:'pos', html:'протон'},
{id:'na', cat:'pos', html:'ион Na<sup>+</sup>'},
{id:'ca', cat:'pos', html:'ион Ca<sup>2+</sup>'},
{id:'e', cat:'neg', html:'электрон'},
{id:'cl', cat:'neg', html:'ион Cl<sup>&minus;</sup>'},
{id:'o', cat:'neg', html:'ион O<sup>2&minus;</sup>'},
{id:'n', cat:'neu', html:'нейтрон'},
{id:'aH', cat:'neu', html:'атом водорода H'},
{id:'aHe',cat:'neu', html:'атом гелия He'}
];
const dnd = setupSorter({ poolId:'p16-dnd-pool', scopeSelector:'#sec-p16', cats:['pos','neg','neu'], items, columnLayout:false });
document.getElementById('p16-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p16-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,'p16-dnd'); bumpProgress('p16', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Подсказка: нейтрон и сами атомы — нейтральные, ионы — заряжены.'; }
});
document.getElementById('p16-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p16-dnd-fb'); fb.style.display='none'; });
}
function _initP16_mcq(){
const QS = [
{q:'Что находится в ядре атома?', opts:['электроны','протоны и нейтроны','только протоны','только нейтроны'], ans:1, why:'Ядро = протоны + нейтроны.'},
{q:'Какой заряд у протона?', opts:['$-e$','$+e$','0','$+2e$'], ans:1, why:'Протон несёт элементарный + заряд.'},
{q:'Какой заряд у нейтрона?', opts:['$-e$','$+e$','0','$+2e$'], ans:2, why:'Нейтрон нейтрален.'},
{q:'В нейтральном атоме число электронов равно…', opts:['числу нейтронов','числу протонов','массовому числу','$Z+N$'], ans:1, why:'$Z$ электронов компенсируют $Z$ протонов.'},
{q:'Какой ион получится, если атом отдаст 2 электрона?', opts:['&minus;2','&minus;1','+1','+2'], ans:3, why:'2 потерянных электрона → заряд +2.'},
{q:'Что больше: атом или ядро?', opts:['ядро','атом','одинаково','зависит от элемента'], ans:1, why:'Атом примерно в 100 000 раз больше ядра.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p16-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="p16-mcq-fb"></div><div class="actions"><button class="btn" id="p16-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p16-mcq-i').textContent = (i+1);
document.getElementById('p16-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('p16-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p16-mcq'); bumpProgress('p16', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p16-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p16-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p16-mcq-bonus'); bumpProgress('p16', 15); }, 600); }
});
});
const nb = document.getElementById('p16-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 2 · WAVE 3 — §17, §18
====================================================================== */
/* ======== §17 — Электрическое поле. Напряжение ======== */
function build_p17(){
const box = document.getElementById('p17-body');
let h = '';
h += makeCard('theory', 'Что такое электрическое поле', '§ 17.1',
'<p>Заряды действуют друг на друга <b>на расстоянии</b>, без прямого контакта. Как же передаётся это взаимодействие?</p>'
+'<p>Ответ: вокруг каждого заряда существует <b>электрическое поле</b> — особый вид материи. Поле — посредник.</p>'
+'<p>Когда мы помещаем в это поле другой заряд, поле действует на него с некоторой <b>силой</b>:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>от $+q$ поле «отталкивает» другие $+q$ и «притягивает» $-q$;</li>'
+'<li>от $-q$ — наоборот.</li>'
+'</ul>'
);
h += makeCard('rule', 'Линии электрического поля', '§ 17.2',
'<p>Поле наглядно изображают линиями со стрелками. Они показывают направление силы, которая действовала бы на <b>пробный положительный</b> заряд.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Линии выходят из <b>$+q$</b> и входят в <b>$-q$</b>.</li>'
+'<li>Не пересекаются.</li>'
+'<li>Чем гуще линии — тем сильнее поле.</li>'
+'</ul>'
);
h += makeCard('example', 'Напряжение', '§ 17.3',
'<p>Чтобы переместить заряд $q$ внутри поля, поле совершает (или над ним совершают) <b>работу</b>. Эта работа зависит от того, между какими двумя точками идёт перемещение.</p>'
+'<p><b>Электрическое напряжение</b> $U$ между точками A и B — это работа поля по переносу единичного $+q$ из A в B:</p>'
+'<p style="text-align:center;margin:8px 0">$$U_{AB} = \\dfrac{A}{q}$$</p>'
+'<p>Подробно — в § 18.</p>'
);
/* 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>Знак заряда: <select id="p17-sig" class="tinp" style="width:auto;padding:6px 10px"><option value="1">$+q$ (отталкивает)</option><option value="-1">$-q$ (притягивает)</option></select></label>'
+'<label>Сила поля: <b id="p17-sv">средняя</b><input type="range" id="p17-s" min="40" max="120" step="10" value="80"></label>'
+'</div>'
+'<svg id="p17-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'</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="p17-quiz"></div>'
+'<div class="actions"><button class="btn" id="p17-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p17-quiz-r">1</b> / 5</span><span>Правильно: <b id="p17-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="p17-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="t"></div></div>'
+'<div class="drop-box"><h5>Ложь</h5><div class="drop-items" data-cat="f"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p17-dnd-check">Проверить</button><button class="btn" id="p17-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p17-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="p17-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p17-mcq-i">1</b> / 6</span><span>Правильно: <b id="p17-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p17') + readButton('p17');
renderMath(box);
wireReadBtn('p17');
_initP17_field();
_initP17_quiz();
_initP17_dnd();
_initP17_mcq();
}
function _initP17_field(){
const svg = document.getElementById('p17-sim'); if(!svg) return;
function draw(){
const sig = +document.getElementById('p17-sig').value;
const scale = +document.getElementById('p17-s').value;
document.getElementById('p17-sv').textContent = scale < 60 ? 'слабая' : scale < 100 ? 'средняя' : 'сильная';
const cx = 230, cy = 120;
let s = '';
s += window.PHYS.fieldLinesPointCharge(cx, cy, sig, scale, 14);
s += window.PHYS.chargeMark(cx, cy, sig, 22, sig > 0 ? '+q' : 'q');
svg.innerHTML = s;
}
document.getElementById('p17-sig').addEventListener('change', draw);
document.getElementById('p17-s').addEventListener('input', draw);
draw();
}
function _initP17_quiz(){
const QS = [
{sit:'Линии поля около +q идут…', opts:['наружу','внутрь','параллельно','по кругу'], ans:0, why:'От + наружу.'},
{sit:'Линии поля около −q идут…', opts:['наружу','внутрь','параллельно','по кругу'], ans:1, why:'К внутрь.'},
{sit:'Чем гуще линии, тем поле…', opts:['слабее','сильнее','не меняется','исчезает'], ans:1, why:'Густота показывает силу поля.'},
{sit:'Линии поля могут пересекаться?', opts:['да','нет','только в вакууме','только у проводников'], ans:1, why:'В каждой точке поле имеет одно направление.'},
{sit:'Поле двух одинаковых +q посередине между ними…', opts:['большое','нулевое','перпендикулярное','непрерывное'], ans:1, why:'Силы от двух зарядов равны и противоположны — компенсируются.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p17-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">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p17-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p17-quiz-r').textContent = (i+1);
document.getElementById('p17-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('p17-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p17-quiz'); bumpProgress('p17', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p17-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p17-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP17_dnd(){
const items = [
{id:'a', cat:'t', html:'Электрическое поле — особый вид материи'},
{id:'b', cat:'t', html:'Поле существует вокруг любого заряда'},
{id:'c', cat:'t', html:'$U_{AB} = A/q$ — определение напряжения'},
{id:'d', cat:'t', html:'Линии поля начинаются на + и кончаются на &minus;'},
{id:'e', cat:'f', html:'Поле — это вакуум вокруг заряда'},
{id:'f', cat:'f', html:'Линии поля могут пересекаться'},
{id:'g', cat:'f', html:'Напряжение — это сила, действующая на заряд'},
{id:'h', cat:'f', html:'Поле существует только в вакууме'}
];
const dnd = setupSorter({ poolId:'p17-dnd-pool', scopeSelector:'#sec-p17', cats:['t','f'], items, columnLayout:false });
document.getElementById('p17-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p17-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,'p17-dnd'); bumpProgress('p17', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p17-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p17-dnd-fb'); fb.style.display='none'; });
}
function _initP17_mcq(){
const QS = [
{q:'Через что заряды взаимодействуют?', opts:['напрямую','через эл. поле','через гравитацию','через воздух'], ans:1, why:'Поле — посредник взаимодействия.'},
{q:'Откуда выходят линии поля?', opts:['из &minus;','из +','из любого','ниоткуда'], ans:1, why:'Линии начинаются на +.'},
{q:'Что показывают линии поля?', opts:['массу','направление силы на +q','скорость','температуру'], ans:1, why:'Линии — направление силы на пробный +.'},
{q:'Куда направлена сила на $-q$ в поле, идущем направо?', opts:['направо','налево','вверх','никуда'], ans:1, why:'На &minus;q сила направлена против поля.'},
{q:'$U_{AB}$ — это…', opts:['сила поля','энергия заряда','работа поля на ед. заряд','скорость заряда'], ans:2, why:'$U = A/q$ — определение.'},
{q:'Если убрать заряд-источник, поле…', opts:['останется','исчезнет','инвертируется','усилится'], ans:1, why:'Поле существует только пока есть его источник.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p17-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="p17-mcq-fb"></div><div class="actions"><button class="btn" id="p17-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p17-mcq-i').textContent = (i+1);
document.getElementById('p17-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('p17-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p17-mcq'); bumpProgress('p17', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p17-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p17-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p17-mcq-bonus'); bumpProgress('p17', 15); }, 600); }
});
});
const nb = document.getElementById('p17-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §18 — A = qU ======== */
function build_p18(){
const box = document.getElementById('p18-body');
let h = '';
h += makeCard('theory', 'Формула $A = qU$', '§ 18.1',
'<p>Если перенести заряд $q$ через разность потенциалов $U$, поле совершит работу:</p>'
+'<p style="text-align:center;margin:8px 0">$$A = q \\, U$$</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>$A$ — работа, Дж;</li>'
+'<li>$q$ — заряд, Кл;</li>'
+'<li>$U$ — напряжение, В.</li>'
+'</ul>'
+'<p>Если $A > 0$ — поле «помогает» движению заряда; $A < 0$ — заряд движется «против» поля.</p>'
);
h += makeCard('rule', 'Что такое 1 Вольт', '§ 18.2',
'<p>1 Вольт — это такое напряжение, при котором поле совершает работу 1 Джоуль на каждый 1 Кулон переносимого заряда:</p>'
+'<p style="text-align:center;margin:8px 0">$$1 \\text{ В} = \\dfrac{1 \\text{ Дж}}{1 \\text{ Кл}}$$</p>'
+'<p>Один из величайших физиков Алессандро Вольта изобрёл первый источник тока — гальванический элемент. В его честь и названа единица.</p>'
);
h += makeCard('example', 'Напряжения в быту', '§ 18.3',
'<table style="width:100%;border-collapse:collapse;font-size:.92rem"><tbody>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">Батарейка</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>1{,}5 В</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">Крона</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>9 В</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">Автомобильный аккумулятор</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>12 В</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">Зарядка для телефона</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>5 В</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">Розетка</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>220 В</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">ЛЭП высокого напряжения</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>10 000 — 750 000 В</code></td></tr>'
+'<tr><td style="padding:5px">Молния</td><td style="padding:5px;text-align:right"><code>$10^8$ В</code></td></tr>'
+'</tbody></table>'
);
/* IV1 — калькулятор A=qU */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Калькулятор $A = qU$</div></div>'
+'<div class="wg-help">Меняй $q$ и $U$ — увидь работу поля.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$q$, мКл (10<sup>&minus;3</sup> Кл): <b id="p18-qv">1.0</b><input type="range" id="p18-q" min="0.1" max="10" step="0.1" value="1"></label>'
+'<label>$U$, В: <b id="p18-uv">220</b><input type="range" id="p18-u" min="1" max="500" step="1" value="220"></label>'
+'</div>'
+'<svg id="p18-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>$A = q U$ = <b id="p18-a">0.22</b> Дж</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Этой энергии хватит, чтобы <b id="p18-anal">поднять груз 22 г на 1 м</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">По формуле $A = qU$ найди недостающую величину.</div>'
+'<div id="p18-quiz"></div>'
+'<div class="actions"><button class="btn" id="p18-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p18-quiz-r">1</b> / 5</span><span>Правильно: <b id="p18-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="p18-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="p18-dnd-check">Проверить</button><button class="btn" id="p18-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p18-dnd-fb"></div>'
+'</div>';
/* IV4 — расчётные задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ правильных — +15 XP.</div>'
+'<div id="p18-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p18-task-i">1</b> / 5</span><span>Правильно: <b id="p18-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p18') + readButton('p18');
renderMath(box);
wireReadBtn('p18');
_initP18_calc();
_initP18_quiz();
_initP18_dnd();
_initP18_tasks();
}
function _initP18_calc(){
const svg = document.getElementById('p18-sim'); if(!svg) return;
function update(){
const qmC = +document.getElementById('p18-q').value;
const U = +document.getElementById('p18-u').value;
const q = qmC * 1e-3; /* мКл → Кл */
const A = q * U;
document.getElementById('p18-qv').textContent = qmC.toFixed(1);
document.getElementById('p18-uv').textContent = U;
document.getElementById('p18-a').textContent = A.toFixed(3);
/* аналогия: поднять груз на 1 м, m = A/(g*1) */
const g = 9.8;
const massG = (A/g) * 1000;
document.getElementById('p18-anal').textContent = 'поднять груз '+massG.toFixed(0)+' г на 1 м';
/* sim */
let s = '';
/* батарея */
s += window.PHYS.batteryEMF(80, 70, U+' В', 'h');
/* стрелка переноса заряда */
s += '<text x="160" y="60" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">q = '+qmC.toFixed(1)+' мКл</text>';
s += window.PHYS.drawArrow(180, 100, 320, 100, '#10b981', 2.5, 11);
s += '<text x="250" y="120" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" fill="#0f172a">A = qU = '+A.toFixed(3)+' Дж</text>';
/* итог "лампа" */
s += window.PHYS.lightbulbSymbol(360, 80, 22);
s += '<text x="360" y="125" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#475569">энергия</text>';
svg.innerHTML = s;
}
document.getElementById('p18-q').addEventListener('input', update);
document.getElementById('p18-u').addEventListener('input', update);
update();
}
function _initP18_quiz(){
const QS = [
{q:'Заряд $q = 2$ Кл прошёл напряжение $U = 5$ В. Найди $A$ (Дж).', ans:10, tol:0.1, why:'$A = qU = 2 \\cdot 5 = 10$ Дж.'},
{q:'$A = 240$ Дж, $U = 12$ В. Какой заряд прошёл (Кл)?', ans:20, tol:0.5, why:'$q = A/U = 240/12 = 20$ Кл.'},
{q:'$A = 6$ Дж, $q = 30$ мКл $= 0{,}03$ Кл. Найди $U$ (В).', ans:200, tol:2, why:'$U = A/q = 6/0{,}03 = 200$ В.'},
{q:'Поле перенесло $q = 0{,}5$ Кл через $U = 220$ В. Найди $A$ (Дж).', ans:110, tol:1, why:'$A = 0{,}5 \\cdot 220 = 110$ Дж.'},
{q:'$A = 4{,}5$ Дж, $q = 3$ Кл. Найди $U$ (В).', ans:1.5, tol:0.05, why:'$U = 4{,}5/3 = 1{,}5$ В — это батарейка.'}
];
let i = 0, ok = 0;
function render(){
const t = QS[i]; const wrap = document.getElementById('p18-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">'+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p18-quiz-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p18-quiz-go">Ответ</button></div>'
+'<div class="feedback" id="p18-quiz-fb"></div>';
document.getElementById('p18-quiz-r').textContent = (i+1);
document.getElementById('p18-quiz-ok').textContent = ok;
document.getElementById('p18-quiz-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p18-quiz-inp').value || '').replace(',','.'));
const fb = document.getElementById('p18-quiz-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(3,'p18-quiz'); bumpProgress('p18', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p18-quiz-ok').textContent = ok;
renderMath(wrap);
});
renderMath(wrap);
}
document.getElementById('p18-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP18_dnd(){
const items = [
{id:'bat', cat:'r1', html:'батарейка ($1{,}5$ В)'},
{id:'kr', cat:'r2', html:'аккумулятор авто ($12$ В)'},
{id:'sock',cat:'r3', html:'розетка ($220$ В)'},
{id:'lep', cat:'r4', html:'ЛЭП ($10^5$ В)'},
{id:'lit', cat:'r5', html:'молния ($10^8$ В)'}
];
const dnd = setupSorter({ poolId:'p18-dnd-pool', scopeSelector:'#sec-p18', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p18-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p18-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,'p18-dnd'); bumpProgress('p18', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p18-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p18-dnd-fb'); fb.style.display='none'; });
}
function _initP18_tasks(){
const TASKS = [
{q:'Электрон ($q = 1{,}6 \\cdot 10^{-19}$ Кл) прошёл $U = 1$ В. Найди $A$ в эВ (это и есть 1 эВ). Введи в Джоулях, в формате $a \\cdot 10^{-19}$, введи $a$.', ans:1.6, tol:0.05, why:'$A = qU = 1{,}6\\cdot10^{-19} \\cdot 1 = 1{,}6\\cdot10^{-19}$ Дж = 1 эВ.'},
{q:'$U = 220$ В, $q = 5$ Кл. Сколько Дж энергии передало поле?', ans:1100, tol:5, why:'$A = 5 \\cdot 220 = 1100$ Дж.'},
{q:'В лампе работа $A = 60$ Дж за 1 с при $U = 220$ В. Какой заряд прошёл (в Кл, округли до сотых)?', ans:0.27, tol:0.02, why:'$q = A/U = 60/220 \\approx 0{,}273$ Кл.'},
{q:'Заряд $q = 2$ мКл переместился через $U = 100$ В. Найди $A$ в мДж.', ans:200, tol:5, why:'$A = qU = 2\\cdot10^{-3} \\cdot 100 = 0{,}2$ Дж = $200$ мДж.'},
{q:'$A = 1$ Дж, $q = 1$ Кл. Найди $U$.', ans:1, tol:0.05, why:'$U = A/q = 1/1 = 1$ В — определение единицы.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p18-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="p18-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p18-task-go">Ответ</button>'
+'<button class="btn" id="p18-task-hint">Подсказка</button>'
+'<button class="btn" id="p18-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p18-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p18-task-fb"></div>';
document.getElementById('p18-task-i').textContent = (i+1);
document.getElementById('p18-task-ok').textContent = ok;
document.getElementById('p18-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p18-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p18-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,'p18-task'); bumpProgress('p18', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p18-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p18-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p18-task-bonus'); bumpProgress('p18', 15); }, 600); }
});
document.getElementById('p18-task-hint').addEventListener('click', ()=>{ document.getElementById('p18-task-hint-txt').classList.toggle('show'); });
document.getElementById('p18-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 3 · WAVE 1 — §19, §20
====================================================================== */
/* ======== §19 — Электрический ток. Источники тока ======== */
function build_p19(){
const box = document.getElementById('p19-body');
let h = '';
h += makeCard('theory', 'Что такое электрический ток', '§ 19.1',
'<p><b>Электрический ток</b> — упорядоченное (направленное) движение свободных заряженных частиц.</p>'
+'<p>В проводе у каждой точки много электронов, движущихся хаотически. Если приложить эл. поле — все они «сдвигаются» в одну сторону. Это и есть ток.</p>'
+'<p>Для существования тока нужно:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li><b>свободные носители заряда</b> (электроны в металле, ионы в растворе);</li>'
+'<li><b>электрическое поле</b>, заставляющее их двигаться упорядоченно.</li>'
+'</ol>'
);
h += makeCard('rule', 'Источники тока', '§ 19.2',
'<p><b>Источник тока</b> создаёт и поддерживает эл. поле в цепи. Внутри него «<b>сторонние силы</b>» (химические, механические, световые) переносят заряды против поля — от одного электрода к другому.</p>'
+'<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>'
);
h += makeCard('example', 'Простейшая батарейка', '§ 19.3',
'<p>Два разных металла (например, медь и цинк) опущенные в раствор кислоты, дают электрический ток. Цинк отдаёт электроны в раствор и становится «&minus;». Медь принимает электроны и становится «+».</p>'
+'<p>Так устроен <b>гальванический элемент</b> — изобретение Алессандро Вольта (1799 г.). Это была первая в истории батарейка!</p>'
);
/* 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>'
+'<svg id="p19-sim" viewBox="0 0 460 220" 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="p19-on">Замкнуть цепь</button><button class="btn" id="p19-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="p19-quiz"></div>'
+'<div class="actions"><button class="btn" id="p19-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p19-quiz-r">1</b> / 5</span><span>Правильно: <b id="p19-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="p19-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="y"></div></div>'
+'<div class="drop-box"><h5>Нет тока</h5><div class="drop-items" data-cat="n"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p19-dnd-check">Проверить</button><button class="btn" id="p19-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p19-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="p19-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p19-mcq-i">1</b> / 6</span><span>Правильно: <b id="p19-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p19') + readButton('p19');
renderMath(box);
wireReadBtn('p19');
_initP19_sim();
_initP19_quiz();
_initP19_dnd();
_initP19_mcq();
}
function _initP19_sim(){
_killSim('p19sim');
const svg = document.getElementById('p19-sim'); if(!svg) return;
let on = false;
const N = 14;
const electrons = [];
/* Путь: батарея (40,110) → провод вверх → горизонталь → вниз → лампа → возврат */
/* Замкнутый контур по часовой стрелке (электроны двигаются ПРОТИВ тока — против часовой) */
/* Точки контура */
const path = [
{x:80, y:170}, /* − батареи (низ) */
{x:80, y:60}, /* левый верхний угол */
{x:200, y:60}, /* перед лампой */
{x:230, y:90}, /* в лампу */
{x:260, y:60}, /* выход лампы */
{x:380, y:60}, /* правый верхний угол */
{x:380, y:170}, /* + батареи (низ) */
{x:80, y:170} /* возврат через батарею */
];
/* Длина пути */
function pathLen(){
let L = 0;
for(let i=0;i<path.length-1;i++){ L += Math.hypot(path[i+1].x-path[i].x, path[i+1].y-path[i].y); }
return L;
}
const totalLen = pathLen();
for(let i = 0; i < N; i++) electrons.push({ t: i * (totalLen/N) });
function posAt(t){
let rem = ((t % totalLen) + totalLen) % totalLen;
for(let i=0;i<path.length-1;i++){
const seg = Math.hypot(path[i+1].x-path[i].x, path[i+1].y-path[i].y);
if(rem <= seg){
const k = rem/seg;
return { x: path[i].x + (path[i+1].x-path[i].x)*k, y: path[i].y + (path[i+1].y-path[i].y)*k };
}
rem -= seg;
}
return path[0];
}
function tick(){
if(!_isVisible('p19')){ _SIMS.p19sim.raf = requestAnimationFrame(tick); return; }
if(on) for(const e of electrons) e.t += 1.4;
let s = '';
/* провода */
for(let i=0;i<path.length-1;i++){
s += '<line x1="'+path[i].x+'" y1="'+path[i].y+'" x2="'+path[i+1].x+'" y2="'+path[i+1].y+'" stroke="#374151" stroke-width="3"/>';
}
/* батарея слева */
s += '<rect x="40" y="115" width="80" height="60" fill="#e0f2fe" stroke="#0f172a" stroke-width="2" rx="4"/>';
s += '<text x="80" y="135" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#0f172a">источник</text>';
s += '<text x="55" y="170" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#dc2626">+</text>';
s += '<text x="100" y="170" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#2563eb"></text>';
/* стрелка тока I (внутри цепи) */
if(on){
s += '<text x="230" y="40" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I →</text>';
}
/* лампа в центре */
s += window.PHYS.lightbulbSymbol(230, 90, 18);
if(on){
/* свечение */
s += '<circle cx="230" cy="90" r="28" fill="#fde047" opacity="0.45"/>';
s += '<circle cx="230" cy="90" r="40" fill="#fbbf24" opacity="0.18"/>';
}
/* электроны двигаются против тока — то есть против хода path */
for(const e of electrons){
const p = posAt(-e.t);
s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="4.5" fill="#2563eb" stroke="#0f172a" stroke-width="0.6"/>';
s += '<text x="'+p.x.toFixed(1)+'" y="'+(p.y+3).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="9" font-weight="800" fill="#fff">&minus;</text>';
}
svg.innerHTML = s;
_SIMS.p19sim.raf = requestAnimationFrame(tick);
}
_SIMS.p19sim = { raf: 0 };
_SIMS.p19sim.raf = requestAnimationFrame(tick);
document.getElementById('p19-on').addEventListener('click', ()=>{
on = !on;
document.getElementById('p19-on').textContent = on ? 'Разомкнуть' : 'Замкнуть цепь';
});
document.getElementById('p19-reset').addEventListener('click', ()=>{
on = false; document.getElementById('p19-on').textContent = 'Замкнуть цепь';
for(let i=0;i<N;i++) electrons[i].t = i * (totalLen/N);
});
}
function _initP19_quiz(){
const QS = [
{sit:'Гальванический элемент', ans:'C', why:'Химическая (реакция в электролите) → электрическая.'},
{sit:'Гидроэлектростанция (плотина)', ans:'M', why:'Механическая (вращение турбины) → электрическая.'},
{sit:'Солнечная батарея', ans:'L', why:'Световая (фотоны) → электрическая.'},
{sit:'Термопара', ans:'T', why:'Тепловая → электрическая.'},
{sit:'Аккумулятор автомобиля', ans:'C', why:'Химическая, как и батарейка.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p19-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:6px">'
+'<button class="btn" data-pick="C" style="padding:10px"><b>Химическая</b></button>'
+'<button class="btn" data-pick="M" style="padding:10px"><b>Механическая</b></button>'
+'<button class="btn" data-pick="L" style="padding:10px"><b>Световая</b></button>'
+'<button class="btn" data-pick="T" style="padding:10px"><b>Тепловая</b></button>'
+'</div>'
+'<div class="feedback" id="p19-quiz-fb"></div>';
document.getElementById('p19-quiz-r').textContent = (i+1);
document.getElementById('p19-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('p19-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p19-quiz'); bumpProgress('p19', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p19-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p19-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP19_dnd(){
const items = [
{id:'a', cat:'y', html:'медный провод + батарейка + лампа'},
{id:'b', cat:'y', html:'раствор соли + 2 угольных электрода + батарейка'},
{id:'c', cat:'y', html:'аккумулятор автомобиля при стартере'},
{id:'d', cat:'y', html:'солнечная панель в светлый день'},
{id:'e', cat:'n', html:'стеклянный стакан + батарейка'},
{id:'f', cat:'n', html:'разомкнутый ключ + батарейка'},
{id:'g', cat:'n', html:'сухое дерево + батарейка'},
{id:'h', cat:'n', html:'медный провод без источника'}
];
const dnd = setupSorter({ poolId:'p19-dnd-pool', scopeSelector:'#sec-p19', cats:['y','n'], items, columnLayout:false });
document.getElementById('p19-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p19-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,'p19-dnd'); bumpProgress('p19', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Без источника или с разомкнутой цепью тока нет.'; }
});
document.getElementById('p19-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p19-dnd-fb'); fb.style.display='none'; });
}
function _initP19_mcq(){
const QS = [
{q:'Что такое электрический ток?', opts:['хаос. движение','упорядоченное движение зарядов','нагрев тела','сила поля'], ans:1, why:'Ток = направленное движение свободных зарядов.'},
{q:'Что нужно для тока?', opts:['только поле','только проводник','свободные носители + поле','магнит'], ans:2, why:'Носители + поле.'},
{q:'В батарейке энергия…', opts:['световая → эл.','химическая → эл.','механическая → эл.','тепловая → эл.'], ans:1, why:'Химическая реакция переносит заряды.'},
{q:'Зачем нужен источник тока в цепи?', opts:['греть провод','создавать и поддерживать поле','охлаждать','тратить заряд'], ans:1, why:'Источник поддерживает разность потенциалов.'},
{q:'Что переносит заряды внутри источника против поля?', opts:['внешние силы','сторонние силы','гравитация','магнетизм'], ans:1, why:'Это и есть «сторонние силы».'},
{q:'Куда «бегут» свободные электроны в цепи (от куда к куда)?', opts:['от + к &minus;','от &minus; к +','туда-сюда','стоят на месте'], ans:1, why:'Электроны двигаются от &minus; к + по внешней цепи (против тока).'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p19-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="p19-mcq-fb"></div><div class="actions"><button class="btn" id="p19-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p19-mcq-i').textContent = (i+1);
document.getElementById('p19-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('p19-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p19-mcq'); bumpProgress('p19', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p19-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p19-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p19-mcq-bonus'); bumpProgress('p19', 15); }, 600); }
});
});
const nb = document.getElementById('p19-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §20 — Сила и направление тока ======== */
function build_p20(){
const box = document.getElementById('p20-body');
let h = '';
h += makeCard('theory', 'Сила тока', '§ 20.1',
'<p><b>Сила тока</b> $I$ показывает, сколько заряда проходит через поперечное сечение проводника за единицу времени:</p>'
+'<p style="text-align:center;margin:8px 0">$$I = \\dfrac{q}{t}$$</p>'
+'<p>Единица измерения — <b>Ампер (А)</b>. <b>1 А</b> — это $1$ Кл, проходящий за $1$ с (это очень много электронов: $\\approx 6\\cdot 10^{18}$ за секунду!).</p>'
+'<p>Названа в честь французского физика Андре-Мари Ампера.</p>'
);
h += makeCard('rule', 'Направление тока', '§ 20.2',
'<p>Исторически за направление тока приняли направление движения <b>положительных зарядов</b> — от «+» к «&minus;» во внешней цепи.</p>'
+'<p>Однако в металлах реально двигаются <b>электроны</b> — от «&minus;» к «+», то есть <b>против</b> направления тока.</p>'
+'<p>Это «договорённость» — она сформировалась до открытия электрона. Менять её уже поздно.</p>'
);
h += makeCard('example', 'Типичные токи', '§ 20.3',
'<table style="width:100%;border-collapse:collapse;font-size:.92rem"><tbody>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">наушники</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~1 мА</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">светодиод</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~10 мА</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">лампочка карманного фонаря</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~0,3 А</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">лампа 60 Вт в розетке</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~0,27 А</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">чайник электрический</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~10 А</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">стартер автомобиля</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~200 А</code></td></tr>'
+'<tr><td style="padding:5px">молния</td><td style="padding:5px;text-align:right"><code>$\\sim 10^4 - 10^5$ А</code></td></tr>'
+'</tbody></table>'
);
/* 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">Меняй $I$ — увидь, как меняется скорость потока электронов в проводе. Лампа загорается сильнее при большем токе.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$I$, А: <b id="p20-iv">0.3</b><input type="range" id="p20-i" min="0" max="3" step="0.1" value="0.3"></label>'
+'</div>'
+'<svg id="p20-sim" viewBox="0 0 460 180" 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>За 1 с пройдёт <b id="p20-q1">0.3</b> Кл</span><span>Это <b id="p20-ne">1.9&times;10<sup>18</sup></b> электронов</span></div>'
+'</div>';
/* IV2 — викторина «по формуле» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Найди по формуле $I = q/t$</div></div>'
+'<div class="wg-help">Числовые вопросы.</div>'
+'<div id="p20-quiz"></div>'
+'<div class="actions"><button class="btn" id="p20-quiz-next">Следующая</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p20-quiz-r">1</b> / 5</span><span>Правильно: <b id="p20-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="p20-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="p20-dnd-check">Проверить</button><button class="btn" id="p20-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p20-dnd-fb"></div>'
+'</div>';
/* IV4 — расчётные задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p20-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p20-task-i">1</b> / 5</span><span>Правильно: <b id="p20-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p20') + readButton('p20');
renderMath(box);
wireReadBtn('p20');
_initP20_sim();
_initP20_quiz();
_initP20_dnd();
_initP20_tasks();
}
function _initP20_sim(){
_killSim('p20sim');
const svg = document.getElementById('p20-sim'); if(!svg) return;
const N = 22;
const electrons = [];
for(let i=0;i<N;i++) electrons.push({ x: 60 + i*16, y: 90 });
let I = 0.3;
function tick(){
if(!_isVisible('p20')){ _SIMS.p20sim.raf = requestAnimationFrame(tick); return; }
/* скорость пропорциональна I */
const v = I * 1.5;
for(const e of electrons){
e.x -= v; /* электроны двигаются справа налево (против тока, которое идёт слева направо) */
if(e.x < 60) e.x += (400 - 60);
}
let s = '';
/* провод */
s += '<rect x="60" y="80" width="340" height="20" fill="#cbd5e1" stroke="#0f172a" stroke-width="2" rx="3"/>';
/* стрелки тока I (направление + → −, слева направо для удобства) */
if(I > 0){
s += '<text x="230" y="60" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I →</text>';
s += window.PHYS.drawArrow(190, 70, 270, 70, '#d97706', 2.2, 9);
}
/* электроны */
for(const e of electrons){
s += '<circle cx="'+e.x.toFixed(1)+'" cy="'+e.y+'" r="4" fill="#2563eb" stroke="#0f172a" stroke-width="0.5"/>';
}
/* подпись о направлении электронов */
if(I > 0){
s += '<text x="230" y="135" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#2563eb">электроны ← (против тока)</text>';
}
/* лампа справа */
s += window.PHYS.lightbulbSymbol(420, 90, 16);
/* свечение зависит от I */
if(I > 0){
const glow = Math.min(1, I/2);
s += '<circle cx="420" cy="90" r="'+(20+I*10).toFixed(0)+'" fill="#fde047" opacity="'+(glow*0.4).toFixed(2)+'"/>';
s += '<circle cx="420" cy="90" r="'+(34+I*15).toFixed(0)+'" fill="#fbbf24" opacity="'+(glow*0.15).toFixed(2)+'"/>';
}
svg.innerHTML = s;
_SIMS.p20sim.raf = requestAnimationFrame(tick);
}
_SIMS.p20sim = { raf: 0 };
_SIMS.p20sim.raf = requestAnimationFrame(tick);
function update(){
I = +document.getElementById('p20-i').value;
document.getElementById('p20-iv').textContent = I.toFixed(1);
document.getElementById('p20-q1').textContent = I.toFixed(1);
const Ne = I / E_CHARGE;
document.getElementById('p20-ne').innerHTML = (Ne/1e18).toFixed(2)+'&times;10<sup>18</sup>';
}
document.getElementById('p20-i').addEventListener('input', update);
update();
}
function _initP20_quiz(){
const QS = [
{q:'$q = 6$ Кл прошло за $t = 2$ с. Найди $I$ (А).', ans:3, tol:0.05, why:'$I = 6/2 = 3$ А.'},
{q:'$I = 0{,}5$ А, $t = 10$ с. Найди $q$ (Кл).', ans:5, tol:0.05, why:'$q = It = 0{,}5 \\cdot 10 = 5$ Кл.'},
{q:'$q = 60$ Кл, $I = 2$ А. Найди $t$ (с).', ans:30, tol:0.5, why:'$t = q/I = 60/2 = 30$ с.'},
{q:'За 1 минуту прошёл заряд $q = 30$ Кл. Найди $I$ (А).', ans:0.5, tol:0.05, why:'$I = 30/60 = 0{,}5$ А.'},
{q:'$I = 0{,}1$ А течёт 5 мин. Какой заряд (Кл)?', ans:30, tol:0.5, why:'$t = 300$ с, $q = 0{,}1 \\cdot 300 = 30$ Кл.'}
];
let i = 0, ok = 0;
function render(){
const t = QS[i]; const wrap = document.getElementById('p20-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">'+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p20-quiz-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p20-quiz-go">Ответ</button></div>'
+'<div class="feedback" id="p20-quiz-fb"></div>';
document.getElementById('p20-quiz-r').textContent = (i+1);
document.getElementById('p20-quiz-ok').textContent = ok;
document.getElementById('p20-quiz-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p20-quiz-inp').value || '').replace(',','.'));
const fb = document.getElementById('p20-quiz-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(3,'p20-quiz'); bumpProgress('p20', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p20-quiz-ok').textContent = ok;
renderMath(wrap);
});
renderMath(wrap);
}
document.getElementById('p20-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP20_dnd(){
const items = [
{id:'h', cat:'r1', html:'наушники ($1$ мА)'},
{id:'l', cat:'r2', html:'светодиод ($10$ мА)'},
{id:'f', cat:'r3', html:'лампа в розетке ($0{,}3$ А)'},
{id:'k', cat:'r4', html:'чайник ($10$ А)'},
{id:'s', cat:'r5', html:'стартер ($200$ А)'}
];
const dnd = setupSorter({ poolId:'p20-dnd-pool', scopeSelector:'#sec-p20', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p20-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p20-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. Бытовые токи — от миллиампер до 100+ А.'; addXp(15,'p20-dnd'); bumpProgress('p20', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p20-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p20-dnd-fb'); fb.style.display='none'; });
}
function _initP20_tasks(){
const TASKS = [
{q:'Через лампу за 0,5 с прошёл заряд 0,15 Кл. Найди ток (А).', ans:0.3, tol:0.02, why:'$I = q/t = 0{,}15/0{,}5 = 0{,}3$ А.'},
{q:'$I = 2$ А течёт 10 минут. Сколько Кл пройдёт?', ans:1200, tol:10, why:'$t = 600$ с, $q = 2 \\cdot 600 = 1200$ Кл.'},
{q:'Сколько электронов проходит через сечение провода за 1 с при $I = 1$ А? Ответ в формате $a \\cdot 10^{18}$, введи $a$.', ans:6.25, tol:0.05, why:'$N = I \\cdot 1/e = 1/(1{,}6\\cdot10^{-19}) = 6{,}25 \\cdot 10^{18}$.'},
{q:'Через спираль чайника за 30 секунд прошло 300 Кл. Найди ток (А).', ans:10, tol:0.1, why:'$I = 300/30 = 10$ А.'},
{q:'Светодиод работает на токе $I = 20$ мА = $0{,}02$ А. Сколько Кл пройдёт за час?', ans:72, tol:1, why:'$t = 3600$ с, $q = 0{,}02 \\cdot 3600 = 72$ Кл.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p20-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="p20-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p20-task-go">Ответ</button>'
+'<button class="btn" id="p20-task-hint">Подсказка</button>'
+'<button class="btn" id="p20-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p20-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p20-task-fb"></div>';
document.getElementById('p20-task-i').textContent = (i+1);
document.getElementById('p20-task-ok').textContent = ok;
document.getElementById('p20-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p20-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p20-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,'p20-task'); bumpProgress('p20', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p20-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p20-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p20-task-bonus'); bumpProgress('p20', 15); }, 600); }
});
document.getElementById('p20-task-hint').addEventListener('click', ()=>{ document.getElementById('p20-task-hint-txt').classList.toggle('show'); });
document.getElementById('p20-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 3 · WAVE 2 — §21, §22
====================================================================== */
/* ======== §21 — Эл. цепь. Амперметр и вольтметр ======== */
function build_p21(){
const box = document.getElementById('p21-body');
let h = '';
h += makeCard('theory', 'Элементы цепи', '§ 21.1',
'<p>Простейшая электрическая цепь состоит из:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>источника тока</b> (батарея, генератор);</li>'
+'<li><b>потребителя</b> (лампа, нагреватель, мотор);</li>'
+'<li><b>соединительных проводов</b>;</li>'
+'<li><b>ключа</b> (выключателя), который замыкает/размыкает цепь.</li>'
+'</ul>'
+'<p>На схемах элементы рисуют условными значками — это <b>принципиальная схема</b>.</p>'
);
h += makeCard('rule', 'Амперметр и вольтметр', '§ 21.2',
'<p><b>Амперметр</b> измеряет силу тока. Подключается <b>последовательно</b> с измеряемым участком — чтобы весь ток шёл через него.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Имеет очень маленькое сопротивление, чтобы почти не «съедать» напряжение.</li>'
+'<li>Нельзя включать параллельно — сгорит!</li>'
+'</ul>'
+'<p><b>Вольтметр</b> измеряет напряжение. Подключается <b>параллельно</b> участку — чтобы измерить разность потенциалов на его концах.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Имеет очень большое сопротивление, чтобы почти не отбирать ток.</li>'
+'</ul>'
);
h += makeCard('example', 'Простейшая принципиальная схема', '§ 21.3',
'<p>Батарея $\\varepsilon$ — лампа $L$ — амперметр $A$ — ключ $K$ — обратно к батарее. Это всё в одной «петле» — <b>последовательная цепь</b>.</p>'
+'<p>Параллельно лампе подключён вольтметр $V$ — он измеряет напряжение именно на ней.</p>'
);
/* 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>'
+'<svg id="p21-sim" viewBox="0 0 460 260" 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="p21-key">Замкнуть ключ</button><button class="btn" id="p21-reset">Разомкнуть</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Амперметр: <b id="p21-amp">0 А</b></span><span>Вольтметр: <b id="p21-volt">0 В</b></span></div>'
+'</div>';
/* IV2 — викторина «куда включать прибор?» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Как включать прибор?</div></div>'
+'<div class="wg-help">Определи правильное подключение.</div>'
+'<div id="p21-quiz"></div>'
+'<div class="actions"><button class="btn" id="p21-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p21-quiz-r">1</b> / 6</span><span>Правильно: <b id="p21-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="p21-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="t"></div></div>'
+'<div class="drop-box"><h5>Неправильно</h5><div class="drop-items" data-cat="f"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p21-dnd-check">Проверить</button><button class="btn" id="p21-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p21-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="p21-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p21-mcq-i">1</b> / 6</span><span>Правильно: <b id="p21-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p21') + readButton('p21');
renderMath(box);
wireReadBtn('p21');
_initP21_sim();
_initP21_quiz();
_initP21_dnd();
_initP21_mcq();
}
function _initP21_sim(){
const svg = document.getElementById('p21-sim'); if(!svg) return;
let closed = false;
function draw(){
let s = '';
/* схема: батарея слева внизу, по проводам идём: справа от батареи вверх → амперметр → вправо → лампа → вниз → обратно к батарее. Вольтметр параллельно лампе. */
/* батарея */
s += window.PHYS.batteryEMF(80, 200, '4,5 В', 'h');
/* провода */
s += window.PHYS.wire(80, 180, 80, 80); /* левый верт. */
s += window.PHYS.wire(80, 80, 180, 80); /* верх к ам-ру */
s += window.PHYS.wire(220, 80, 280, 80); /* ам-р к лампе */
s += window.PHYS.wire(310, 80, 380, 80); /* после лампы */
s += window.PHYS.wire(380, 80, 380, 200); /* правый верт. */
s += window.PHYS.wire(380, 200, 80, 200);/* низ (через батарею) */
/* амперметр (последовательно) */
s += window.PHYS.ammeterSymbol(200, 80, 18);
/* лампа */
s += window.PHYS.lightbulbSymbol(295, 80, 18);
if(closed){
s += '<circle cx="295" cy="80" r="28" fill="#fde047" opacity="0.45"/>';
s += '<circle cx="295" cy="80" r="40" fill="#fbbf24" opacity="0.18"/>';
}
/* вольтметр (параллельно лампе) */
s += window.PHYS.wire(295, 100, 295, 140);
s += window.PHYS.wire(295, 140, 250, 140);
s += window.PHYS.wire(250, 140, 250, 170);
s += window.PHYS.wire(340, 140, 340, 170);
s += window.PHYS.wire(295, 140, 340, 140);
s += window.PHYS.voltmeterSymbol(295, 155, 18);
/* ключ */
s += '<line x1="160" y1="200" x2="200" y2="200" stroke="#0f172a" stroke-width="2.5"/>';
s += '<circle cx="160" cy="200" r="3" fill="#0f172a"/>';
s += '<circle cx="200" cy="200" r="3" fill="#0f172a"/>';
if(closed){
s += '<line x1="160" y1="200" x2="200" y2="200" stroke="#dc2626" stroke-width="3"/>';
} else {
s += '<line x1="160" y1="200" x2="195" y2="185" stroke="#0f172a" stroke-width="2.5"/>';
}
s += '<text x="180" y="225" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#475569">ключ</text>';
svg.innerHTML = s;
document.getElementById('p21-amp').textContent = closed ? '0,5 А' : '0 А';
document.getElementById('p21-volt').textContent = closed ? '4,5 В' : '0 В';
}
document.getElementById('p21-key').addEventListener('click', ()=>{ closed = !closed; document.getElementById('p21-key').textContent = closed ? 'Разомкнуть' : 'Замкнуть ключ'; draw(); });
document.getElementById('p21-reset').addEventListener('click', ()=>{ closed = false; document.getElementById('p21-key').textContent = 'Замкнуть ключ'; draw(); });
draw();
}
function _initP21_quiz(){
const QS = [
{q:'Как включить <b>амперметр</b> для измерения тока через лампу?', opts:['параллельно лампе','последовательно с лампой','между + и − батареи напрямую','произвольно'], ans:1, why:'Амперметр включается в разрыв цепи (последовательно).'},
{q:'Как включить <b>вольтметр</b> для измерения напряжения на лампе?', opts:['параллельно лампе','последовательно с лампой','до батареи','после ключа'], ans:0, why:'Вольтметр меряет разность потенциалов — параллельно.'},
{q:'У амперметра сопротивление…', opts:['очень малое','очень большое','среднее','равно лампе'], ans:0, why:'Чтобы почти не влиять на ток.'},
{q:'У вольтметра сопротивление…', opts:['очень малое','очень большое','среднее','нулевое'], ans:1, why:'Чтобы не отбирать ток у цепи.'},
{q:'Если подключить амперметр <b>параллельно</b> лампе…', opts:['прибор покажет ток','прибор сгорит — короткое замыкание','напряжение увеличится','ничего не изменится'], ans:1, why:'Через малое R амперметра потечёт огромный ток.'},
{q:'Если подключить вольтметр <b>последовательно</b> с лампой…', opts:['ток будет нормальным','ток будет почти нулевой, лампа не загорится','прибор сгорит','лампа станет ярче'], ans:1, why:'Большое R вольтметра ограничит ток до неощутимого.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p21-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p21-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p21-quiz-r').textContent = (i+1);
document.getElementById('p21-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('p21-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p21-quiz'); bumpProgress('p21', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p21-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p21-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP21_dnd(){
const items = [
{id:'a', cat:'t', html:'амперметр последовательно с лампой'},
{id:'b', cat:'t', html:'вольтметр параллельно лампе'},
{id:'c', cat:'t', html:'амперметр имеет малое $R$'},
{id:'d', cat:'t', html:'вольтметр имеет большое $R$'},
{id:'e', cat:'f', html:'амперметр параллельно батарее'},
{id:'f', cat:'f', html:'вольтметр в разрыв цепи'},
{id:'g', cat:'f', html:'амперметр имеет большое $R$'},
{id:'h', cat:'f', html:'вольтметр меряет силу тока'}
];
const dnd = setupSorter({ poolId:'p21-dnd-pool', scopeSelector:'#sec-p21', cats:['t','f'], items, columnLayout:false });
document.getElementById('p21-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p21-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. A последовательно (мал. R), V параллельно (бол. R).'; addXp(15,'p21-dnd'); bumpProgress('p21', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p21-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p21-dnd-fb'); fb.style.display='none'; });
}
function _initP21_mcq(){
const QS = [
{q:'Что такое замкнутая цепь?', opts:['разрыв провода','круговое соединение элементов','один провод','лампа без батареи'], ans:1, why:'Цепь — петля, по которой может пройти ток.'},
{q:'Что произойдёт, если разомкнуть ключ?', opts:['ток усилится','ток исчезнет','напряжение упадёт','ток пойдёт по новому пути'], ans:1, why:'Цепь разорвана — тока нет.'},
{q:'Какой элемент схемы рисуется как круг с буквой A?', opts:['батарея','амперметр','вольтметр','резистор'], ans:1, why:'A — амперметр.'},
{q:'А с буквой V?', opts:['батарея','вольтметр','лампа','ключ'], ans:1, why:'V — вольтметр.'},
{q:'Все потребители в одной «петле» — это…', opts:['последовательное соединение','параллельное','смешанное','короткое замыкание'], ans:0, why:'В одной петле — последовательно.'},
{q:'Почему амперметр НЕ имеет большого $R$?', opts:['чтобы не сгореть','чтобы не уменьшать ток в цепи','чтобы было дёшево','чтобы измерять напряжение'], ans:1, why:'Большое R снизило бы ток, измерение было бы неточным.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p21-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="p21-mcq-fb"></div><div class="actions"><button class="btn" id="p21-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p21-mcq-i').textContent = (i+1);
document.getElementById('p21-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('p21-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p21-mcq'); bumpProgress('p21', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p21-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p21-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p21-mcq-bonus'); bumpProgress('p21', 15); }, 600); }
});
});
const nb = document.getElementById('p21-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §22 — Закон Ома I = U/R ======== */
function build_p22(){
const box = document.getElementById('p22-body');
let h = '';
h += makeCard('theory', 'Связь $I$ и $U$', '§ 22.1',
'<p>Опыт: подключаем резистор к источнику и меняем напряжение. Что будет с током?</p>'
+'<p>Ток <b>пропорционален</b> напряжению: увеличили $U$ в 2 раза — $I$ тоже в 2 раза. Это и есть <b>закон Ома</b>:</p>'
+'<p style="text-align:center;margin:8px 0">$$I = \\dfrac{U}{R}$$</p>'
+'<p>$R$ — <b>сопротивление</b> участка, измеряется в <b>Омах</b> (Ом). Закон открыт немецким физиком Георгом Симоном Омом в 1826 г.</p>'
);
h += makeCard('rule', 'Из закона Ома', '§ 22.2',
'<p>Закон можно «перевернуть» тремя способами:</p>'
+'<p style="text-align:center;margin:8px 0">$$I = \\dfrac{U}{R}, \\quad U = IR, \\quad R = \\dfrac{U}{I}$$</p>'
+'<p>Эта связка — <b>основа всей электротехники</b>. Зная любые две величины — найдём третью.</p>'
+'<p><b>1 Ом</b> — это сопротивление участка, на котором при напряжении 1 В идёт ток 1 А.</p>'
);
h += makeCard('example', 'ВАХ — вольт-амперная характеристика', '§ 22.3',
'<p>Если на графике отложить $U$ по горизонтали, а $I$ по вертикали, получится <b>ВАХ</b>:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>для металла — прямая через 0;</li>'
+'<li>наклон прямой определяет $R$: круче — меньше $R$;</li>'
+'<li>для лампы с раскалённой нитью ВАХ слегка изогнута (нить нагревается, $R$ растёт).</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">Меняй $R$ — увидь, как меняется наклон прямой $I(U)$. Чем меньше $R$, тем круче растёт ток.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$R$, Ом: <b id="p22-rv">10</b><input type="range" id="p22-r" min="2" max="50" step="1" value="10"></label>'
+'</div>'
+'<svg id="p22-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'</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 class="sliders" style="margin-bottom:10px">'
+'<label>$U$, В: <b id="p22-uv">12</b><input type="range" id="p22-u" min="1" max="220" step="1" value="12"></label>'
+'<label>$R$, Ом: <b id="p22-rv2">10</b><input type="range" id="p22-r2" min="1" max="100" step="1" value="10"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$I = U/R$ = <b id="p22-icur">1,2</b> А</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Для $U = 220$ В и $R = 100$ Ом ток будет 2,2 А — это <b id="p22-bulb">средняя лампа накаливания</b>.</span>'
+'</div>'
+'</div>';
/* IV3 — викторина по графику */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Какой $R$ больше?</div></div>'
+'<div class="wg-help">По наклону ВАХ оцени сопротивление.</div>'
+'<div id="p22-quiz"></div>'
+'<div class="actions"><button class="btn" id="p22-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p22-quiz-r">1</b> / 5</span><span>Правильно: <b id="p22-quiz-ok">0</b></span></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="p22-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p22-task-i">1</b> / 6</span><span>Правильно: <b id="p22-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p22') + readButton('p22');
renderMath(box);
wireReadBtn('p22');
_initP22_vah();
_initP22_calc();
_initP22_quiz();
_initP22_tasks();
}
function _initP22_vah(){
const svg = document.getElementById('p22-sim'); if(!svg) return;
function draw(){
const R = +document.getElementById('p22-r').value;
document.getElementById('p22-rv').textContent = R;
const W = 460, H = 240, pad = 38;
const Umax = 12, Imax = 2;
const toX = u => pad + (W-2*pad) * u / Umax;
const toY = i => H - pad - (H-2*pad) * i / Imax;
let s = '';
/* оси */
s += '<line x1="'+pad+'" y1="'+(H-pad)+'" x2="'+(W-pad)+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
s += '<line x1="'+pad+'" y1="'+pad+'" x2="'+pad+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
/* сетка */
for(let u = 2; u <= 10; u+=2){
s += '<line x1="'+toX(u)+'" y1="'+pad+'" x2="'+toX(u)+'" y2="'+(H-pad)+'" stroke="#e5e7eb" stroke-width="1"/>';
s += '<text x="'+toX(u)+'" y="'+(H-pad+14)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">'+u+'</text>';
}
for(let i = 0.5; i <= 1.5; i+=0.5){
s += '<line x1="'+pad+'" y1="'+toY(i)+'" x2="'+(W-pad)+'" y2="'+toY(i)+'" stroke="#e5e7eb" stroke-width="1"/>';
s += '<text x="'+(pad-6)+'" y="'+(toY(i)+3)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">'+i+'</text>';
}
/* подписи осей */
s += '<text x="'+(W-pad+4)+'" y="'+(H-pad+4)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">U, В</text>';
s += '<text x="'+(pad-4)+'" y="'+(pad-6)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">I, А</text>';
/* прямая I = U/R */
const x0 = toX(0), y0 = toY(0);
const uMaxVisible = Math.min(Umax, Imax * R);
const x1 = toX(uMaxVisible);
const y1 = toY(uMaxVisible / R);
s += '<line x1="'+x0+'" y1="'+y0+'" x2="'+x1+'" y2="'+y1+'" stroke="#d97706" stroke-width="3"/>';
/* подпись наклона */
s += '<text x="'+(W/2)+'" y="'+(pad+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I = U / '+R+' Ом</text>';
svg.innerHTML = s;
}
document.getElementById('p22-r').addEventListener('input', draw);
draw();
}
function _initP22_calc(){
function update(){
const U = +document.getElementById('p22-u').value;
const R = +document.getElementById('p22-r2').value;
document.getElementById('p22-uv').textContent = U;
document.getElementById('p22-rv2').textContent = R;
const I = U/R;
document.getElementById('p22-icur').textContent = I.toFixed(2);
let analogy = 'мало';
if(I > 5) analogy = 'мощный нагреватель';
else if(I > 1) analogy = 'средняя лампа накаливания';
else if(I > 0.2) analogy = 'светодиодная лампа';
else analogy = 'светодиод';
document.getElementById('p22-bulb').textContent = analogy;
}
document.getElementById('p22-u').addEventListener('input', update);
document.getElementById('p22-r2').addEventListener('input', update);
update();
}
function _initP22_quiz(){
const QS = [
{q:'У какого резистора $R$ больше: ВАХ круче или ВАХ положе?', opts:['круче','положе','одинаково','зависит от U'], ans:1, why:'$R$ = $U/I$, при том же $U$ положая прямая даёт меньший $I$ → больше $R$.'},
{q:'Если $U$ постоянно, а $R$ удвоить, что станет с $I$?', opts:['удвоится','уменьшится в 2 раза','не изменится','исчезнет'], ans:1, why:'$I = U/R$, $R$ ↑ → $I$ ↓.'},
{q:'Если $R$ постоянно, а $U$ утроить, что с $I$?', opts:['удвоится','утроится','уменьшится в 3 раза','не изменится'], ans:1, why:'Прямая пропорция.'},
{q:'При $U = 6$ В через резистор течёт $I = 0{,}5$ А. Чему равно $R$?', opts:['3 Ом','12 Ом','6 Ом','0,5 Ом'], ans:1, why:'$R = U/I = 6/0{,}5 = 12$ Ом.'},
{q:'Закон Ома справедлив для…', opts:['только газов','любых веществ','металлических проводников','только сверхпроводников'], ans:2, why:'Для металлов точно, для других — с оговорками.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p22-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p22-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p22-quiz-r').textContent = (i+1);
document.getElementById('p22-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('p22-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p22-quiz'); bumpProgress('p22', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p22-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p22-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP22_tasks(){
const TASKS = [
{q:'$U = 12$ В, $R = 4$ Ом. Найди $I$ (А).', ans:3, tol:0.05, why:'$I = 12/4 = 3$ А.'},
{q:'$I = 0{,}5$ А, $R = 220$ Ом. Найди $U$ (В).', ans:110, tol:1, why:'$U = IR = 0{,}5 \\cdot 220 = 110$ В.'},
{q:'$U = 220$ В, $I = 2$ А. Найди $R$ (Ом).', ans:110, tol:1, why:'$R = 220/2 = 110$ Ом.'},
{q:'Лампа $R = 240$ Ом подключена к 220 В. Какой ток (А, до сотых)?', ans:0.92, tol:0.02, why:'$I = 220/240 \\approx 0{,}917$ А.'},
{q:'$U$ возросло с 5 до 15 В на том же $R$. Во сколько раз возрос ток?', ans:3, tol:0.05, why:'$I$ пропорционально $U$.'},
{q:'При $U = 1{,}5$ В через светодиод течёт 20 мА. Найди $R$ (Ом).', ans:75, tol:1, why:'$R = 1{,}5/0{,}02 = 75$ Ом.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p22-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="p22-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p22-task-go">Ответ</button>'
+'<button class="btn" id="p22-task-hint">Подсказка</button>'
+'<button class="btn" id="p22-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p22-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p22-task-fb"></div>';
document.getElementById('p22-task-i').textContent = (i+1);
document.getElementById('p22-task-ok').textContent = ok;
document.getElementById('p22-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p22-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p22-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,'p22-task'); bumpProgress('p22', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p22-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p22-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p22-task-bonus'); bumpProgress('p22', 15); }, 600); }
});
document.getElementById('p22-task-hint').addEventListener('click', ()=>{ document.getElementById('p22-task-hint-txt').classList.toggle('show'); });
document.getElementById('p22-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 3 · WAVE 3 — §23, §24, §25
====================================================================== */
const MAT_RHO = [
{name:'серебро', rho:0.016},
{name:'медь', rho:0.017},
{name:'алюминий',rho:0.028},
{name:'железо', rho:0.10},
{name:'нихром', rho:1.1},
{name:'константан', rho:0.5}
];
/* ======== §23 — R = ρl/S ======== */
function build_p23(){
const box = document.getElementById('p23-body');
let h = '';
h += makeCard('theory', 'Формула сопротивления', '§ 23.1',
'<p>Сопротивление проводника зависит от трёх параметров:</p>'
+'<p style="text-align:center;margin:8px 0">$$R = \\dfrac{\\rho \\, l}{S}$$</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>$\\rho$ — <b>удельное сопротивление</b>, Ом·мм²/м. Свойство материала.</li>'
+'<li>$l$ — длина проводника, м.</li>'
+'<li>$S$ — площадь поперечного сечения, мм².</li>'
+'</ul>'
+'<p>Чем длиннее — тем больше $R$. Чем толще (больше $S$) — тем меньше $R$. Чем «хуже» материал — тем больше $\\rho$.</p>'
);
h += makeCard('rule', 'Таблица $\\rho$', '§ 23.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">$\\rho$, $\\dfrac{\\text{Ом}\\cdot\\text{мм}^2}{\\text{м}}$</th></tr></thead><tbody>'
+ MAT_RHO.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.rho+'</code></td></tr>').join('')
+'</tbody></table>'
+'<p style="margin-top:8px">Для проводов берут медь или алюминий (малое $\\rho$). Для нагревательных спиралей — нихром (большое $\\rho$ → много тепла при том же токе).</p>'
);
h += makeCard('example', 'Где это применяется', '§ 23.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Электропровода — медные, толстые → малое $R$ → малые потери.</li>'
+'<li>Нагревательная спираль чайника — длинная и тонкая из нихрома.</li>'
+'<li>Линии электропередачи — толстые алюминиевые провода (медь была бы дороже).</li>'
+'<li>Удлинители — чем длиннее, тем больше $R$ → больше потерь.</li>'
+'</ul>'
);
/* IV1 — калькулятор R = ρl/S */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Калькулятор $R = \\rho l / S$</div></div>'
+'<div class="wg-help">Выбери материал, длину и сечение — увидь $R$.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Материал: <select id="p23-mat" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'
+ MAT_RHO.map(m=>'<option value="'+m.rho+'">'+m.name+' ($\\rho='+m.rho+'$)</option>').join('')
+'</select></label>'
+'<label>$l$, м: <b id="p23-lv">10</b><input type="range" id="p23-l" min="0.1" max="100" step="0.5" value="10"></label>'
+'<label>$S$, мм²: <b id="p23-sv">1.0</b><input type="range" id="p23-s" min="0.1" max="10" step="0.1" value="1"></label>'
+'</div>'
+'<svg id="p23-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>$R = \\rho l / S$ = <b id="p23-rv2">0.17</b> Ом</span>'
+'<span style="font-size:.84rem;color:var(--muted)">При $U = 12$ В: $I = U/R = $ <b id="p23-i12">71</b> А.</span>'
+'</div>'
+'</div>';
/* IV2 — «больше/меньше R?» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">У какого провода $R$ больше?</div></div>'
+'<div class="wg-help">Сравни два варианта.</div>'
+'<div id="p23-quiz"></div>'
+'<div class="actions"><button class="btn" id="p23-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p23-quiz-r">1</b> / 6</span><span>Правильно: <b id="p23-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD «как изменится R» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Как изменится $R$?</div></div>'
+'<div class="wg-help">При изменении параметра.</div>'
+'<div id="p23-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">'
+'<div class="drop-box"><h5>$R$ растёт</h5><div class="drop-items" data-cat="up"></div></div>'
+'<div class="drop-box"><h5>$R$ падает</h5><div class="drop-items" data-cat="dn"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p23-dnd-check">Проверить</button><button class="btn" id="p23-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p23-dnd-fb"></div>'
+'</div>';
/* IV4 — задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p23-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p23-task-i">1</b> / 5</span><span>Правильно: <b id="p23-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p23') + readButton('p23');
renderMath(box);
wireReadBtn('p23');
_initP23_calc();
_initP23_quiz();
_initP23_dnd();
_initP23_tasks();
}
function _initP23_calc(){
const svg = document.getElementById('p23-sim'); if(!svg) return;
function update(){
const rho = +document.getElementById('p23-mat').value;
const l = +document.getElementById('p23-l').value;
const S = +document.getElementById('p23-s').value;
document.getElementById('p23-lv').textContent = l.toFixed(1);
document.getElementById('p23-sv').textContent = S.toFixed(1);
const R = rho * l / S;
document.getElementById('p23-rv2').textContent = R.toFixed(2);
document.getElementById('p23-i12').textContent = (12/R).toFixed(1);
/* SVG: провод */
let s = '';
const lenPx = 60 + Math.min(330, l*3);
const wPx = 6 + Math.min(40, S*4);
const cy = 70;
const xS = 30;
s += '<rect x="'+xS+'" y="'+(cy-wPx/2)+'" width="'+lenPx+'" height="'+wPx+'" fill="#cbd5e1" stroke="#0f172a" stroke-width="1.6" rx="3"/>';
/* подписи */
s += '<text x="'+(xS+lenPx/2)+'" y="'+(cy+wPx/2+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">l = '+l.toFixed(1)+' м</text>';
s += '<text x="'+(xS+lenPx+12)+'" y="'+(cy+4)+'" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">S = '+S.toFixed(1)+' мм²</text>';
/* формула */
s += '<text x="'+(xS)+'" y="120" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">R = '+rho+' &middot; '+l.toFixed(1)+' / '+S.toFixed(1)+' = '+R.toFixed(2)+' Ом</text>';
svg.innerHTML = s;
}
document.getElementById('p23-mat').addEventListener('change', update);
document.getElementById('p23-l').addEventListener('input', update);
document.getElementById('p23-s').addEventListener('input', update);
update();
}
function _initP23_quiz(){
const QS = [
{A:'медный провод 10 м', B:'медный провод 20 м', ans:'B', why:'Длиннее — больше $R$.'},
{A:'медный провод $S = 1$ мм²', B:'медный провод $S = 2$ мм²', ans:'A', why:'Толще — меньше $R$.'},
{A:'нихромовый провод 1 м', B:'медный провод 1 м', ans:'A', why:'Нихром в 65 раз хуже меди по $\\rho$.'},
{A:'железный провод', B:'медный провод (тот же)', ans:'A', why:'Железо хуже меди.'},
{A:'алюминиевый провод 50 м', B:'медный провод 50 м', ans:'A', why:'У алюминия $\\rho$ больше.'},
{A:'тонкий нихром', B:'толстая медь', ans:'A', why:'Тонкий и нихром — двойное «увеличение R».'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p23-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="p23-quiz-fb"></div>';
document.getElementById('p23-quiz-r').textContent = (i+1);
document.getElementById('p23-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('p23-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p23-quiz'); bumpProgress('p23', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p23-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p23-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP23_dnd(){
const items = [
{id:'lup', cat:'up', html:'увеличили длину $l$'},
{id:'sdn', cat:'up', html:'уменьшили сечение $S$'},
{id:'mb', cat:'up', html:'заменили медь на нихром'},
{id:'cd', cat:'up', html:'заменили серебро на железо'},
{id:'ldn', cat:'dn', html:'укоротили провод'},
{id:'sup', cat:'dn', html:'утолстили провод'},
{id:'mw', cat:'dn', html:'заменили нихром на медь'},
{id:'cs', cat:'dn', html:'заменили железо на серебро'}
];
const dnd = setupSorter({ poolId:'p23-dnd-pool', scopeSelector:'#sec-p23', cats:['up','dn'], items, columnLayout:false });
document.getElementById('p23-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p23-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. $R$ пропорционально $\\rho l$ и обратно $S$.'; addXp(15,'p23-dnd'); bumpProgress('p23', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p23-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p23-dnd-fb'); fb.style.display='none'; });
}
function _initP23_tasks(){
const TASKS = [
{q:'Медь, $l = 100$ м, $S = 1$ мм². Найди $R$ (Ом).', ans:1.7, tol:0.05, why:'$R = 0{,}017 \\cdot 100 / 1 = 1{,}7$ Ом.'},
{q:'Нихромовая спираль, $l = 5$ м, $S = 0{,}5$ мм². Найди $R$ (Ом).', ans:11, tol:0.3, why:'$R = 1{,}1 \\cdot 5 / 0{,}5 = 11$ Ом.'},
{q:'$R = 4$ Ом, $\\rho = 0{,}4$, $l = 2$ м. Найди $S$ (мм²).', ans:0.2, tol:0.02, why:'$S = \\rho l / R = 0{,}4 \\cdot 2 / 4 = 0{,}2$ мм².'},
{q:'Два медных провода: один длиннее в 3 раза, тоньше в 2 раза. Во сколько раз $R$ больше?', ans:6, tol:0.1, why:'$R$ ∝ $l/S$: $3 \\cdot 2 = 6$ раз.'},
{q:'Алюминиевый провод, $l = 20$ м, $S = 4$ мм². Найди $R$ (Ом).', ans:0.14, tol:0.01, why:'$R = 0{,}028 \\cdot 20 / 4 = 0{,}14$ Ом.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p23-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="p23-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p23-task-go">Ответ</button>'
+'<button class="btn" id="p23-task-hint">Подсказка</button>'
+'<button class="btn" id="p23-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p23-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p23-task-fb"></div>';
document.getElementById('p23-task-i').textContent = (i+1);
document.getElementById('p23-task-ok').textContent = ok;
document.getElementById('p23-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p23-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p23-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,'p23-task'); bumpProgress('p23', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p23-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p23-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p23-task-bonus'); bumpProgress('p23', 15); }, 600); }
});
document.getElementById('p23-task-hint').addEventListener('click', ()=>{ document.getElementById('p23-task-hint-txt').classList.toggle('show'); });
document.getElementById('p23-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §24 — Последовательное соединение. Реостат ======== */
function build_p24(){
const box = document.getElementById('p24-body');
let h = '';
h += makeCard('theory', 'Правила последовательного соединения', '§ 24.1',
'<p>Если резисторы соединены <b>один за другим</b> (в одну «петлю»), это <b>последовательное</b> соединение.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Ток одинаков: $I = I_1 = I_2$ (одна и та же река через все «пороги»).</li>'
+'<li>Напряжения складываются: $U = U_1 + U_2$.</li>'
+'<li>Сопротивления складываются: $R = R_1 + R_2$.</li>'
+'</ul>'
+'<p>Если один из резисторов вышел из строя — <b>вся цепь обесточена</b>.</p>'
);
h += makeCard('rule', 'Реостат', '§ 24.2',
'<p><b>Реостат</b> — переменный резистор. Состоит из проволочного резистивного элемента и подвижного контакта (движка).</p>'
+'<p>Сдвигая движок, мы изменяем длину $l$ включённой части провода. Так как $R = \\rho l/S$, меняется и сопротивление.</p>'
+'<p>Реостат используют для:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>регулировки силы тока (например, яркости лампы);</li>'
+'<li>защиты от перегрузки (последовательно с прибором);</li>'
+'<li>«старой» школы — регуляторов громкости в радиоприёмниках.</li>'
+'</ul>'
);
h += makeCard('example', 'Гирлянда — последовательная цепь', '§ 24.3',
'<p>В старых ёлочных гирляндах все лампочки соединены последовательно. Каждой достаётся $220/N$ Вольт, где $N$ — число лампочек.</p>'
+'<p>Минус: если перегорит одна — погаснут все! Поэтому современные гирлянды чаще делают по секциям с шунтирующими элементами.</p>'
);
/* 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>Положение движка (доля включённой проволоки): <b id="p24-pv">50%</b><input type="range" id="p24-p" min="0" max="100" step="5" value="50"></label>'
+'</div>'
+'<svg id="p24-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;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$R_{реост}$ = <b id="p24-rr">10</b> Ом, $R_{лампы} = 12$ Ом, $R_{общ}$ = <b id="p24-rt">22</b> Ом</span>'
+'<span>$I = U/R$ при $U = 12$ В: <b id="p24-it">0.55</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">Подбирай $R_1$, $R_2$ и $U$ — найди ток и напряжения на каждом.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$U$, В: <b id="p24-uv">12</b><input type="range" id="p24-u" min="1" max="100" step="1" value="12"></label>'
+'<label>$R_1$, Ом: <b id="p24-r1v">4</b><input type="range" id="p24-r1" min="1" max="50" step="1" value="4"></label>'
+'<label>$R_2$, Ом: <b id="p24-r2v">8</b><input type="range" id="p24-r2" min="1" max="50" step="1" value="8"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:3px">'
+'<span>$R$ = $R_1+R_2$ = <b id="p24-rs">12</b> Ом</span>'
+'<span>$I = U/R$ = <b id="p24-is">1</b> А</span>'
+'<span>$U_1 = I R_1$ = <b id="p24-u1">4</b> В</span>'
+'<span>$U_2 = I R_2$ = <b id="p24-u2">8</b> В</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Проверка: $U_1+U_2$ = <b id="p24-check">12</b> В</span>'
+'</div>'
+'</div>';
/* IV3 — DnD «верно/неверно» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Правила послед. соединения</div></div>'
+'<div id="p24-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="t"></div></div>'
+'<div class="drop-box"><h5>Неверно</h5><div class="drop-items" data-cat="f"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p24-dnd-check">Проверить</button><button class="btn" id="p24-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p24-dnd-fb"></div>'
+'</div>';
/* IV4 — задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p24-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p24-task-i">1</b> / 5</span><span>Правильно: <b id="p24-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p24') + readButton('p24');
renderMath(box);
wireReadBtn('p24');
_initP24_rheostat();
_initP24_calc();
_initP24_dnd();
_initP24_tasks();
}
function _initP24_rheostat(){
const svg = document.getElementById('p24-sim'); if(!svg) return;
function draw(){
const p = +document.getElementById('p24-p').value;
document.getElementById('p24-pv').textContent = p+'%';
const Rrh = 20 * p / 100; /* 0..20 Ом */
const Rlamp = 12;
const Rtot = Rrh + Rlamp;
const I = 12 / Rtot;
document.getElementById('p24-rr').textContent = Rrh.toFixed(0);
document.getElementById('p24-rt').textContent = Rtot.toFixed(0);
document.getElementById('p24-it').textContent = I.toFixed(2);
/* SVG */
let s = '';
/* батарея */
s += window.PHYS.batteryEMF(60, 170, '12 В', 'h');
/* провод снизу */
s += window.PHYS.wire(60, 150, 60, 70);
s += window.PHYS.wire(60, 70, 130, 70);
/* реостат — длинная зигзаг полоска */
s += '<rect x="130" y="60" width="180" height="20" fill="#fde68a" stroke="#0f172a" stroke-width="1.5" rx="3"/>';
/* зона до движка (включена) — затемнить */
s += '<rect x="130" y="60" width="'+(p*1.8)+'" height="20" fill="#d97706" opacity="0.4"/>';
/* движок */
s += '<line x1="'+(130+p*1.8)+'" y1="50" x2="'+(130+p*1.8)+'" y2="80" stroke="#0f172a" stroke-width="3"/>';
s += '<polygon points="'+(130+p*1.8-6)+',50 '+(130+p*1.8+6)+',50 '+(130+p*1.8)+',58" fill="#0f172a"/>';
s += '<text x="220" y="100" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#475569">реостат '+Rrh.toFixed(0)+' Ом</text>';
/* провод к лампе */
s += window.PHYS.wire(310, 70, 360, 70);
/* лампа */
s += window.PHYS.lightbulbSymbol(380, 70, 18);
/* яркость зависит от тока */
const glow = Math.min(1, I / 1.2);
s += '<circle cx="380" cy="70" r="'+(24+I*12).toFixed(0)+'" fill="#fde047" opacity="'+(glow*0.5).toFixed(2)+'"/>';
s += '<circle cx="380" cy="70" r="'+(36+I*18).toFixed(0)+'" fill="#fbbf24" opacity="'+(glow*0.18).toFixed(2)+'"/>';
/* провод вниз и обратно */
s += window.PHYS.wire(380, 90, 380, 170);
s += window.PHYS.wire(380, 170, 100, 170);
/* I-метка */
s += '<text x="220" y="195" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I = '+I.toFixed(2)+' А</text>';
svg.innerHTML = s;
}
document.getElementById('p24-p').addEventListener('input', draw);
draw();
}
function _initP24_calc(){
function update(){
const U = +document.getElementById('p24-u').value;
const R1 = +document.getElementById('p24-r1').value;
const R2 = +document.getElementById('p24-r2').value;
document.getElementById('p24-uv').textContent = U;
document.getElementById('p24-r1v').textContent = R1;
document.getElementById('p24-r2v').textContent = R2;
const R = R1+R2;
const I = U/R;
const U1 = I*R1;
const U2 = I*R2;
document.getElementById('p24-rs').textContent = R;
document.getElementById('p24-is').textContent = I.toFixed(2);
document.getElementById('p24-u1').textContent = U1.toFixed(2);
document.getElementById('p24-u2').textContent = U2.toFixed(2);
document.getElementById('p24-check').textContent = (U1+U2).toFixed(2);
}
['p24-u','p24-r1','p24-r2'].forEach(id => document.getElementById(id).addEventListener('input', update));
update();
}
function _initP24_dnd(){
const items = [
{id:'a', cat:'t', html:'$I = I_1 = I_2$'},
{id:'b', cat:'t', html:'$U = U_1 + U_2$'},
{id:'c', cat:'t', html:'$R = R_1 + R_2$'},
{id:'d', cat:'t', html:'обрыв одного $\\to$ нет тока во всей цепи'},
{id:'e', cat:'f', html:'$U = U_1 = U_2$'},
{id:'f', cat:'f', html:'$I = I_1 + I_2$'},
{id:'g', cat:'f', html:'$1/R = 1/R_1 + 1/R_2$'},
{id:'h', cat:'f', html:'обрыв одного $\\to$ ток продолжает идти'}
];
const dnd = setupSorter({ poolId:'p24-dnd-pool', scopeSelector:'#sec-p24', cats:['t','f'], items, columnLayout:false });
document.getElementById('p24-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p24-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. Послед.: одно $I$, складываются $U$ и $R$.'; addXp(15,'p24-dnd'); bumpProgress('p24', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Эти правила — для параллельного соединения.'; renderMath(fb); }
});
document.getElementById('p24-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p24-dnd-fb'); fb.style.display='none'; });
}
function _initP24_tasks(){
const TASKS = [
{q:'$R_1 = 4$, $R_2 = 6$ Ом последовательно, $U = 20$ В. Найди $I$ (А).', ans:2, tol:0.05, why:'$R = 10$, $I = 20/10 = 2$ А.'},
{q:'$R_1 = 3$, $R_2 = 5$ Ом, $I = 2$ А. Найди $U_2$ (В).', ans:10, tol:0.5, why:'$U_2 = I R_2 = 2 \\cdot 5 = 10$ В.'},
{q:'Три одинаковых лампы $R = 50$ Ом каждая, последовательно, $U = 90$ В. Найди $I$ (А).', ans:0.6, tol:0.02, why:'$R = 150$, $I = 90/150 = 0{,}6$ А.'},
{q:'$R_1 = 5$, $R_2 = ?$ последовательно. $U = 30$ В, $I = 2$ А. Найди $R_2$ (Ом).', ans:10, tol:0.5, why:'$R = 30/2 = 15$, $R_2 = 15 - 5 = 10$ Ом.'},
{q:'Реостат $R = 0..50$ Ом + лампа $R_л = 10$ Ом, $U = 12$ В. При каком положении реостата (Ом) ток будет $0{,}2$ А?', ans:50, tol:1, why:'$R_{общ} = U/I = 60$, $R_{реост} = 60 - 10 = 50$ Ом.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p24-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="p24-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p24-task-go">Ответ</button>'
+'<button class="btn" id="p24-task-hint">Подсказка</button>'
+'<button class="btn" id="p24-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p24-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p24-task-fb"></div>';
document.getElementById('p24-task-i').textContent = (i+1);
document.getElementById('p24-task-ok').textContent = ok;
document.getElementById('p24-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p24-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p24-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,'p24-task'); bumpProgress('p24', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p24-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p24-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p24-task-bonus'); bumpProgress('p24', 15); }, 600); }
});
document.getElementById('p24-task-hint').addEventListener('click', ()=>{ document.getElementById('p24-task-hint-txt').classList.toggle('show'); });
document.getElementById('p24-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §25 — Параллельное соединение ======== */
function build_p25(){
const box = document.getElementById('p25-body');
let h = '';
h += makeCard('theory', 'Правила параллельного соединения', '§ 25.1',
'<p>Если резисторы соединены <b>«бок о бок»</b>, между двумя одинаковыми узлами — это <b>параллельное</b> соединение.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Напряжение одинаковое: $U = U_1 = U_2$ (один и тот же «перепад высоты» на всех ветках).</li>'
+'<li>Токи складываются: $I = I_1 + I_2$.</li>'
+'<li>Сопротивления через обратные величины: $\\dfrac{1}{R} = \\dfrac{1}{R_1} + \\dfrac{1}{R_2}$.</li>'
+'</ul>'
+'<p>Поломка одного резистора <b>не отключает</b> остальные.</p>'
);
h += makeCard('rule', 'Полезные формулы', '§ 25.2',
'<p>Для двух резисторов:</p>'
+'<p style="text-align:center;margin:8px 0">$$R = \\dfrac{R_1 R_2}{R_1 + R_2}$$</p>'
+'<p>Для $N$ <b>одинаковых</b> резисторов по $R$:</p>'
+'<p style="text-align:center;margin:8px 0">$$R_{общ} = \\dfrac{R}{N}$$</p>'
+'<p>Общее $R$ <b>меньше</b> любого из частных. Это потому, что параллельные «ветви» дают много путей току.</p>'
);
h += makeCard('example', 'Где встречается', '§ 25.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>В розетках дома: все приборы (свет, ТВ, чайник) подключены параллельно. Поэтому каждый получает 220 В.</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">Параллельная цепь — 2 ветви</div></div>'
+'<div class="wg-help">Меняй $R_1$ и $R_2$ — увидь токи в каждой ветви и общий $R$.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$R_1$, Ом: <b id="p25-r1v">6</b><input type="range" id="p25-r1" min="1" max="40" step="1" value="6"></label>'
+'<label>$R_2$, Ом: <b id="p25-r2v">12</b><input type="range" id="p25-r2" min="1" max="40" step="1" value="12"></label>'
+'</div>'
+'<svg id="p25-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;flex-direction:column;align-items:flex-start;gap:3px">'
+'<span>$R_{общ} = R_1 R_2 / (R_1+R_2)$ = <b id="p25-rt">4</b> Ом</span>'
+'<span>При $U = 12$ В: $I_1 = U/R_1 = $ <b id="p25-i1">2</b> А</span>'
+'<span>$I_2 = U/R_2 = $ <b id="p25-i2">1</b> А</span>'
+'<span>Общий ток $I = I_1 + I_2$ = <b id="p25-it2">3</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="p25-quiz"></div>'
+'<div class="actions"><button class="btn" id="p25-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p25-quiz-r">1</b> / 6</span><span>Правильно: <b id="p25-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="p25-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="ser"></div></div>'
+'<div class="drop-box"><h5>Параллельно</h5><div class="drop-items" data-cat="par"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p25-dnd-check">Проверить</button><button class="btn" id="p25-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p25-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="p25-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p25-task-i">1</b> / 6</span><span>Правильно: <b id="p25-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p25') + readButton('p25');
renderMath(box);
wireReadBtn('p25');
_initP25_calc();
_initP25_quiz();
_initP25_dnd();
_initP25_tasks();
}
function _initP25_calc(){
const svg = document.getElementById('p25-sim'); if(!svg) return;
function update(){
const R1 = +document.getElementById('p25-r1').value;
const R2 = +document.getElementById('p25-r2').value;
document.getElementById('p25-r1v').textContent = R1;
document.getElementById('p25-r2v').textContent = R2;
const Rt = (R1*R2)/(R1+R2);
const U = 12;
const I1 = U/R1;
const I2 = U/R2;
document.getElementById('p25-rt').textContent = Rt.toFixed(2);
document.getElementById('p25-i1').textContent = I1.toFixed(2);
document.getElementById('p25-i2').textContent = I2.toFixed(2);
document.getElementById('p25-it2').textContent = (I1+I2).toFixed(2);
/* SVG */
let s = '';
/* батарея слева */
s += window.PHYS.batteryEMF(60, 110, '12 В', 'h');
/* провод вверх */
s += window.PHYS.wire(60, 92, 60, 60);
s += window.PHYS.wire(60, 60, 380, 60);
/* провод вниз */
s += window.PHYS.wire(60, 130, 60, 170);
s += window.PHYS.wire(60, 170, 380, 170);
/* развилка верх */
s += window.PHYS.wire(150, 60, 150, 80);
s += window.PHYS.wire(270, 60, 270, 80);
/* верхняя ветвь */
s += window.PHYS.resistor(190, 80, R1, 'h');
/* нижняя ветвь */
s += window.PHYS.wire(150, 130, 150, 150);
s += window.PHYS.wire(270, 130, 270, 150);
s += window.PHYS.resistor(190, 130, R2, 'h');
/* подписи токов */
s += '<text x="180" y="65" font-family="JetBrains Mono,monospace" font-size="11" fill="#d97706">I₁ = '+I1.toFixed(2)+' А</text>';
s += '<text x="180" y="170" font-family="JetBrains Mono,monospace" font-size="11" fill="#d97706">I₂ = '+I2.toFixed(2)+' А</text>';
/* провода к развилке снизу */
s += window.PHYS.wire(150, 100, 270, 100);
s += window.PHYS.wire(150, 150, 270, 150);
svg.innerHTML = s;
}
document.getElementById('p25-r1').addEventListener('input', update);
document.getElementById('p25-r2').addEventListener('input', update);
update();
}
function _initP25_quiz(){
const QS = [
{sit:'Все лампы в гирлянде гаснут при поломке одной', ans:'S', why:'Это последовательное соединение.'},
{sit:'В розетке: ТВ и чайник работают независимо', ans:'P', why:'Параллельно — оба под 220 В.'},
{sit:'Лампа, амперметр, батарея — в одной петле', ans:'S', why:'Последовательно.'},
{sit:'Два резистора имеют одно и то же напряжение', ans:'P', why:'Параллельно — общее $U$.'},
{sit:'Через два резистора течёт один и тот же ток', ans:'S', why:'Последовательно — общее $I$.'},
{sit:'Общее $R$ меньше любого из $R_1$, $R_2$', ans:'P', why:'Параллельное соединение.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p25-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="S" style="padding:14px"><b>Последовательно</b></button>'
+'<button class="btn" data-pick="P" style="padding:14px"><b>Параллельно</b></button>'
+'</div>'
+'<div class="feedback" id="p25-quiz-fb"></div>';
document.getElementById('p25-quiz-r').textContent = (i+1);
document.getElementById('p25-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('p25-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p25-quiz'); bumpProgress('p25', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p25-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p25-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP25_dnd(){
const items = [
{id:'a', cat:'ser', html:'$I = I_1 = I_2$'},
{id:'b', cat:'ser', html:'$U = U_1 + U_2$'},
{id:'c', cat:'ser', html:'$R = R_1 + R_2$'},
{id:'d', cat:'ser', html:'обрыв $\\to$ ничего не работает'},
{id:'e', cat:'par', html:'$U = U_1 = U_2$'},
{id:'f', cat:'par', html:'$I = I_1 + I_2$'},
{id:'g', cat:'par', html:'$1/R = 1/R_1 + 1/R_2$'},
{id:'h', cat:'par', html:'обрыв $\\to$ остальные работают'}
];
const dnd = setupSorter({ poolId:'p25-dnd-pool', scopeSelector:'#sec-p25', cats:['ser','par'], items, columnLayout:false });
document.getElementById('p25-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p25-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. Главное отличие: послед.→общий I, паралл.→общее U.'; addXp(15,'p25-dnd'); bumpProgress('p25', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; renderMath(fb); }
});
document.getElementById('p25-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p25-dnd-fb'); fb.style.display='none'; });
}
function _initP25_tasks(){
const TASKS = [
{q:'$R_1 = 6$, $R_2 = 12$ Ом параллельно. Найди $R_{общ}$ (Ом).', ans:4, tol:0.1, why:'$R = 6\\cdot12/(6+12) = 72/18 = 4$ Ом.'},
{q:'Два резистора по $R = 10$ Ом параллельно. $R_{общ}$ (Ом)?', ans:5, tol:0.1, why:'$R/2 = 5$ Ом.'},
{q:'$R_1 = R_2 = R_3 = 9$ Ом, все параллельно. $R_{общ}$ (Ом)?', ans:3, tol:0.1, why:'$R/N = 9/3 = 3$ Ом.'},
{q:'Параллельно $R_1 = 4$, $R_2 = 4$ Ом, $U = 12$ В. Найди ток в одной ветви (А).', ans:3, tol:0.1, why:'$I_1 = U/R_1 = 12/4 = 3$ А.'},
{q:'$R_1 = 6$, $R_2 = ?$ параллельно, $R_{общ} = 4$ Ом. Найди $R_2$.', ans:12, tol:0.3, why:'$1/4 = 1/6 + 1/R_2 \\Rightarrow 1/R_2 = 1/12 \\Rightarrow R_2 = 12$ Ом.'},
{q:'Параллельно к 220 В подключены утюг ($R = 44$ Ом) и лампа ($R = 220$ Ом). Какой общий ток (А)?', ans:6, tol:0.2, why:'$I_1 = 220/44 = 5$, $I_2 = 220/220 = 1$, $I = 6$ А.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p25-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="p25-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p25-task-go">Ответ</button>'
+'<button class="btn" id="p25-task-hint">Подсказка</button>'
+'<button class="btn" id="p25-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p25-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p25-task-fb"></div>';
document.getElementById('p25-task-i').textContent = (i+1);
document.getElementById('p25-task-ok').textContent = ok;
document.getElementById('p25-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p25-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p25-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,'p25-task'); bumpProgress('p25', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p25-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p25-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p25-task-bonus'); bumpProgress('p25', 15); }, 600); }
});
document.getElementById('p25-task-hint').addEventListener('click', ()=>{ document.getElementById('p25-task-hint-txt').classList.toggle('show'); });
document.getElementById('p25-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 3 · WAVE 4 — §26, §27
====================================================================== */
/* ======== §26 — Работа и мощность тока. Джоуль-Ленц ======== */
function build_p26(){
const box = document.getElementById('p26-body');
let h = '';
h += makeCard('theory', 'Работа тока', '§ 26.1',
'<p>Электрическое поле, прогоняя заряд через проводник, совершает работу. За время $t$ через сечение проходит заряд $q = It$. На каждый кулон поле совершает работу $U$ Джоулей. Значит:</p>'
+'<p style="text-align:center;margin:8px 0">$$A = U I t$$</p>'
+'<p>Это <b>работа электрического тока</b> — энергия, которую ток отдаёт прибору. Она превращается в тепло, свет, движение, звук — в зависимости от прибора.</p>'
);
h += makeCard('rule', 'Мощность тока', '§ 26.2',
'<p><b>Мощность</b> $P$ — работа за единицу времени:</p>'
+'<p style="text-align:center;margin:8px 0">$$P = \\dfrac{A}{t} = U I$$</p>'
+'<p>Эквиваленты (используя закон Ома):</p>'
+'<p style="text-align:center;margin:8px 0">$$P = U I = I^2 R = \\dfrac{U^2}{R}$$</p>'
+'<p>Единица: 1 <b>Ватт</b> (Вт) — это 1 Дж/с. Названа в честь Джеймса Уатта.</p>'
);
h += makeCard('example', 'Закон Джоуля — Ленца', '§ 26.3',
'<p>В резисторе вся электрическая энергия превращается в <b>тепло</b>:</p>'
+'<p style="text-align:center;margin:8px 0">$$Q = I^2 R t$$</p>'
+'<p>Это <b>закон Джоуля-Ленца</b>. Поэтому работают спирали чайника, утюга, кипятильника. Чем больше $I$ или $R$, тем больше тепла.</p>'
+'<p>В лампе накаливания нить нагревается до $\\sim 2500$ &#176;C и светится. КПД у такой лампы низкий — 5%; остальное тепло.</p>'
);
/* IV1 — калькулятор P+анимация нагрева */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Мощность и нагрев</div></div>'
+'<div class="wg-help">Меняй $U$ и $R$ — увидь мощность и как нагревается резистор.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$U$, В: <b id="p26-uv">220</b><input type="range" id="p26-u" min="1" max="240" step="1" value="220"></label>'
+'<label>$R$, Ом: <b id="p26-rv">100</b><input type="range" id="p26-r" min="1" max="500" step="1" value="100"></label>'
+'</div>'
+'<svg id="p26-sim" viewBox="0 0 460 140" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="score-display" style="margin-top:10px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$I = U/R$ = <b id="p26-ic">2.2</b> А</span>'
+'<span>$P = UI$ = <b id="p26-pc">484</b> Вт</span>'
+'<span>$Q$ за 1 минуту = <b id="p26-q1">29.0</b> кДж</span>'
+'</div>'
+'</div>';
/* IV2 — выбор формулы */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Какую формулу использовать?</div></div>'
+'<div class="wg-help">Даны величины — выбери удобную формулу для $P$.</div>'
+'<div id="p26-quiz"></div>'
+'<div class="actions"><button class="btn" id="p26-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p26-quiz-r">1</b> / 5</span><span>Правильно: <b id="p26-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD мощности приборов */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Расставь приборы по мощности</div></div>'
+'<div class="wg-help">От самого «слабого» к самому «мощному».</div>'
+'<div id="p26-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin-top:10px">'
+'<div class="drop-box"><h5>1. Меньше</h5><div class="drop-items" data-cat="r1"></div></div>'
+'<div class="drop-box"><h5>2</h5><div class="drop-items" data-cat="r2"></div></div>'
+'<div class="drop-box"><h5>3</h5><div class="drop-items" data-cat="r3"></div></div>'
+'<div class="drop-box"><h5>4</h5><div class="drop-items" data-cat="r4"></div></div>'
+'<div class="drop-box"><h5>5. Больше</h5><div class="drop-items" data-cat="r5"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p26-dnd-check">Проверить</button><button class="btn" id="p26-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p26-dnd-fb"></div>'
+'</div>';
/* IV4 — задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p26-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p26-task-i">1</b> / 6</span><span>Правильно: <b id="p26-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p26') + readButton('p26');
renderMath(box);
wireReadBtn('p26');
_initP26_sim();
_initP26_quiz();
_initP26_dnd();
_initP26_tasks();
}
function _initP26_sim(){
const svg = document.getElementById('p26-sim'); if(!svg) return;
function update(){
const U = +document.getElementById('p26-u').value;
const R = +document.getElementById('p26-r').value;
document.getElementById('p26-uv').textContent = U;
document.getElementById('p26-rv').textContent = R;
const I = U/R;
const P = U*I;
const Q1 = P * 60; /* за 1 мин */
document.getElementById('p26-ic').textContent = I.toFixed(2);
document.getElementById('p26-pc').textContent = P.toFixed(0);
document.getElementById('p26-q1').textContent = (Q1/1000).toFixed(1);
/* SVG: резистор + цвет нагрева */
let s = '';
s += window.PHYS.batteryEMF(60, 70, U+' В', 'h');
s += window.PHYS.wire(60, 52, 60, 30);
s += window.PHYS.wire(60, 30, 200, 30);
/* резистор */
const tCol = window.PHYS.tempColor(Math.min(150, P/8), 20, 150);
s += '<rect x="200" y="20" width="80" height="20" fill="'+tCol+'" stroke="#0f172a" stroke-width="1.8" rx="3"/>';
s += '<text x="240" y="34" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="'+(P>500?'#fff':'#0f172a')+'">R = '+R+' Ом</text>';
/* glow при высокой мощности */
if(P > 100){
const glow = Math.min(1, P/2000);
s += '<rect x="200" y="20" width="80" height="20" fill="#fbbf24" opacity="'+(glow*0.5).toFixed(2)+'" rx="3"/>';
}
s += window.PHYS.wire(280, 30, 400, 30);
s += window.PHYS.wire(400, 30, 400, 70);
s += window.PHYS.wire(80, 70, 400, 70);
/* подпись P */
s += '<text x="240" y="65" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">P = '+P.toFixed(0)+' Вт</text>';
s += '<text x="240" y="100" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">за 1 мин: Q = '+(Q1/1000).toFixed(1)+' кДж</text>';
s += '<text x="240" y="118" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#475569">'+(P<60 ? 'слабый разогрев' : P<500 ? 'тёплый' : P<1500 ? 'горячо' : 'опасно — может оплавиться')+'</text>';
svg.innerHTML = s;
}
document.getElementById('p26-u').addEventListener('input', update);
document.getElementById('p26-r').addEventListener('input', update);
update();
}
function _initP26_quiz(){
const QS = [
{sit:'Дано $U = 220$ В, $I = 5$ А. Какая формула?', opts:['$P = UI$','$P = I^2R$','$P = U^2/R$','любая'], ans:0, why:'Когда дано $U$ и $I$ — прямой подсчёт через $UI$.'},
{sit:'Дано $I = 3$ А, $R = 50$ Ом. Какая?', opts:['$P = UI$','$P = I^2R$','$P = U^2/R$','нельзя'], ans:1, why:'Когда дано $I$ и $R$ — $I^2R$.'},
{sit:'Дано $U = 220$ В, $R = 100$ Ом. Какая?', opts:['$P = UI$','$P = I^2R$','$P = U^2/R$','нельзя'], ans:2, why:'$U^2/R$ удобнее.'},
{sit:'$P$ нагревательного прибора 1500 Вт при $U = 220$ В. Какой ток?', opts:['$I = P/U \\approx 6{,}8$ А','$I = U/R$','$I = UP$','нельзя'], ans:0, why:'$I = P/U = 1500/220 \\approx 6{,}82$ А.'},
{sit:'$Q$ за 10 с в резисторе $R$ при токе $I$. Какая формула?', opts:['$Q = It$','$Q = UI/t$','$Q = I^2 R t$','$Q = U^2/R$'], ans:2, why:'Закон Джоуля-Ленца.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p26-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.sit+'</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p26-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p26-quiz-r').textContent = (i+1);
document.getElementById('p26-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p26-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p26-quiz'); bumpProgress('p26', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p26-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p26-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP26_dnd(){
const items = [
{id:'led', cat:'r1', html:'светодиод (~0,1 Вт)'},
{id:'ph', cat:'r2', html:'телефон-зарядка (~5 Вт)'},
{id:'lamp',cat:'r3', html:'лампа накаливания (60 Вт)'},
{id:'ket', cat:'r4', html:'чайник (2000 Вт)'},
{id:'car', cat:'r5', html:'электромобиль (10 000 Вт)'}
];
const dnd = setupSorter({ poolId:'p26-dnd-pool', scopeSelector:'#sec-p26', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p26-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p26-dnd-fb');
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='&#10003; Идеально! +15 XP. От мВт до кВт — огромный диапазон.'; addXp(15,'p26-dnd'); bumpProgress('p26', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p26-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p26-dnd-fb'); fb.style.display='none'; });
}
function _initP26_tasks(){
const TASKS = [
{q:'$U = 220$ В, $I = 0{,}5$ А. Найди $P$ (Вт).', ans:110, tol:1, why:'$P = UI = 220 \\cdot 0{,}5 = 110$ Вт.'},
{q:'Лампа 60 Вт работает 5 часов. Какая работа $A$ (Дж)?', ans:1080000, tol:5000, why:'$A = Pt = 60 \\cdot 18\\,000 = 1{,}08 \\cdot 10^6$ Дж.'},
{q:'$I = 2$ А, $R = 50$ Ом, $t = 60$ с. Сколько $Q$ выделится (кДж)?', ans:12, tol:0.3, why:'$Q = I^2 R t = 4 \\cdot 50 \\cdot 60 = 12\\,000$ Дж = $12$ кДж.'},
{q:'$P = 2000$ Вт работают 30 мин. Сколько $A$ в кВт·ч?', ans:1, tol:0.05, why:'$A = 2 \\cdot 0{,}5 = 1$ кВт·ч.'},
{q:'Чайник 1500 Вт, $U = 220$ В. Какой ток (А, до сотых)?', ans:6.82, tol:0.1, why:'$I = P/U = 1500/220 \\approx 6{,}82$ А.'},
{q:'$P = 100$ Вт, $R = 484$ Ом. Найди $U$ (В).', ans:220, tol:3, why:'$U = \\sqrt{PR} = \\sqrt{48\\,400} = 220$ В.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p26-task'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Задача '+(i+1)+'.</b> '+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p26-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p26-task-go">Ответ</button>'
+'<button class="btn" id="p26-task-hint">Подсказка</button>'
+'<button class="btn" id="p26-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p26-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p26-task-fb"></div>';
document.getElementById('p26-task-i').textContent = (i+1);
document.getElementById('p26-task-ok').textContent = ok;
document.getElementById('p26-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p26-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p26-task-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
done++;
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(4,'p26-task'); bumpProgress('p26', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p26-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p26-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p26-task-bonus'); bumpProgress('p26', 15); }, 600); }
});
document.getElementById('p26-task-hint').addEventListener('click', ()=>{ document.getElementById('p26-task-hint-txt').classList.toggle('show'); });
document.getElementById('p26-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== §27 — Электроэнергия. Безопасность ======== */
function build_p27(){
const box = document.getElementById('p27-body');
let h = '';
h += makeCard('theory', 'Энергия в быту: кВт·ч', '§ 27.1',
'<p>В квартире электросчётчик измеряет потреблённую энергию в <b>киловатт-часах (кВт·ч)</b>.</p>'
+'<p>$1$ кВт·ч — работа за 1 час прибором мощностью 1 кВт = 1000 Вт.</p>'
+'<p style="text-align:center;margin:8px 0">$$1 \\text{ кВт·ч} = 3{,}6 \\cdot 10^6 \\text{ Дж}$$</p>'
+'<p>Стоимость:</p>'
+'<p style="text-align:center;margin:8px 0">$$\\text{руб} = W (\\text{кВт·ч}) \\times \\text{тариф (руб/кВт·ч)}$$</p>'
);
h += makeCard('rule', 'Как сэкономить', '§ 27.2',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Светодиоды вместо ламп накаливания (60 Вт → 8 Вт при том же свете).</li>'
+'<li>Выключать приборы из розетки (не оставлять в режиме standby).</li>'
+'<li>Энергосберегающие чайники, мультиварки.</li>'
+'<li>Утеплять дом (меньше работает обогреватель).</li>'
+'</ul>'
);
h += makeCard('example', 'Безопасность при работе с током', '§ 27.3',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Не трогать оголённые провода и розетки голыми руками.</li>'
+'<li>Не использовать электроприборы в ванной без УЗО.</li>'
+'<li>При коротком замыкании срабатывает <b>предохранитель</b> или <b>автомат</b> — отключает цепь.</li>'
+'<li><b>Заземление</b> отводит ток на землю при пробое корпуса прибора.</li>'
+'<li>Опасно для жизни: ток &gt; $50$ мА (это $\\sim 12$ В через мокрую кожу).</li>'
+'</ul>'
);
/* IV1 — счётчик электроэнергии за месяц */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Счёт за месяц</div></div>'
+'<div class="wg-help">Сколько часов в день работают приборы — узнай счёт за месяц (30 дней).</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Лампа $60$ Вт, ч/день: <b id="p27-h1">5</b><input type="range" id="p27-h1r" min="0" max="24" step="0.5" value="5"></label>'
+'<label>ТВ $100$ Вт, ч/день: <b id="p27-h2">4</b><input type="range" id="p27-h2r" min="0" max="24" step="0.5" value="4"></label>'
+'<label>Чайник $2000$ Вт, мин/день: <b id="p27-h3">10</b><input type="range" id="p27-h3r" min="0" max="60" step="5" value="10"></label>'
+'<label>Холодильник $150$ Вт, ч/день: <b id="p27-h4">24</b><input type="range" id="p27-h4r" min="0" max="24" step="1" value="24"></label>'
+'<label>Тариф, руб/(кВт·ч): <b id="p27-tar">0.25</b><input type="range" id="p27-tarr" min="0.1" max="2" step="0.05" value="0.25"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:3px">'
+'<span>За месяц израсходуется: <b id="p27-w">186</b> кВт·ч</span>'
+'<span>Стоимость: <b id="p27-cost">46.5</b> руб</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Самый «прожорливый» прибор: <b id="p27-top">холодильник</b>.</span>'
+'</div>'
+'</div>';
/* IV2 — викторина «безопасно ли это?» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Безопасно или нет?</div></div>'
+'<div class="wg-help">Оцени ситуацию с точки зрения электробезопасности.</div>'
+'<div id="p27-quiz"></div>'
+'<div class="actions"><button class="btn" id="p27-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p27-quiz-r">1</b> / 6</span><span>Правильно: <b id="p27-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD энергосбережение */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Экономит или расходует энергию?</div></div>'
+'<div id="p27-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">'
+'<div class="drop-box"><h5>Экономит</h5><div class="drop-items" data-cat="save"></div></div>'
+'<div class="drop-box"><h5>Расходует</h5><div class="drop-items" data-cat="waste"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p27-dnd-check">Проверить</button><button class="btn" id="p27-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p27-dnd-fb"></div>'
+'</div>';
/* IV4 — задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p27-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p27-task-i">1</b> / 5</span><span>Правильно: <b id="p27-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p27') + readButton('p27');
renderMath(box);
wireReadBtn('p27');
_initP27_meter();
_initP27_quiz();
_initP27_dnd();
_initP27_tasks();
}
function _initP27_meter(){
function update(){
const h1 = +document.getElementById('p27-h1r').value;
const h2 = +document.getElementById('p27-h2r').value;
const h3 = +document.getElementById('p27-h3r').value;
const h4 = +document.getElementById('p27-h4r').value;
const tar = +document.getElementById('p27-tarr').value;
document.getElementById('p27-h1').textContent = h1.toFixed(1);
document.getElementById('p27-h2').textContent = h2.toFixed(1);
document.getElementById('p27-h3').textContent = h3.toFixed(0);
document.getElementById('p27-h4').textContent = h4.toFixed(0);
document.getElementById('p27-tar').textContent = tar.toFixed(2);
/* кВт·ч за день */
const w1 = 0.060 * h1;
const w2 = 0.100 * h2;
const w3 = 2.000 * (h3/60);
const w4 = 0.150 * h4;
const wDay = w1+w2+w3+w4;
const wMon = wDay * 30;
const cost = wMon * tar;
document.getElementById('p27-w').textContent = wMon.toFixed(1);
document.getElementById('p27-cost').textContent = cost.toFixed(2);
const all = [['лампа',w1],['ТВ',w2],['чайник',w3],['холодильник',w4]];
all.sort((a,b)=>b[1]-a[1]);
document.getElementById('p27-top').textContent = all[0][0];
}
['p27-h1r','p27-h2r','p27-h3r','p27-h4r','p27-tarr'].forEach(id => document.getElementById(id).addEventListener('input', update));
update();
}
function _initP27_quiz(){
const QS = [
{sit:'Менять лампочку в горячей люстре, не отключив выключатель.', ans:'N', why:'Опасно — можно получить удар током. Сначала выключить.'},
{sit:'Использовать электрочайник с заземлённой розеткой.', ans:'Y', why:'Заземление защищает в случае пробоя корпуса.'},
{sit:'Сушить волосы феном, стоя в ванной с водой.', ans:'N', why:'Очень опасно — вода резко увеличивает риск удара.'},
{sit:'Удлинитель с предохранителем для розеток.', ans:'Y', why:'Предохранитель сработает при перегрузке.'},
{sit:'Сэкономить — обмотать оголённый провод изолентой.', ans:'N', why:'Временный «костыль». Нужно заменить провод.'},
{sit:'Светодиодная лампа вместо лампы накаливания.', ans:'Y', why:'Безопаснее (меньше тепла) и экономичнее.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p27-quiz'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin:8px 0;line-height:1.5">'+q.sit+'</div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
+'<button class="btn" data-pick="Y" style="padding:14px"><b>Безопасно</b></button>'
+'<button class="btn" data-pick="N" style="padding:14px"><b>Опасно</b></button>'
+'</div>'
+'<div class="feedback" id="p27-quiz-fb"></div>';
document.getElementById('p27-quiz-r').textContent = (i+1);
document.getElementById('p27-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
const fb = document.getElementById('p27-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p27-quiz'); bumpProgress('p27', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p27-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p27-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP27_dnd(){
const items = [
{id:'a', cat:'save', html:'светодиодная лампа'},
{id:'b', cat:'save', html:'утеплитель на окнах'},
{id:'c', cat:'save', html:'выключать ТВ из розетки'},
{id:'d', cat:'save', html:'натуральный свет днём'},
{id:'e', cat:'waste', html:'лампа накаливания 100 Вт'},
{id:'f', cat:'waste', html:'оставлять зарядки в розетке'},
{id:'g', cat:'waste', html:'кипятить полный чайник для 1 чашки'},
{id:'h', cat:'waste', html:'обогреватель при открытой двери'}
];
const dnd = setupSorter({ poolId:'p27-dnd-pool', scopeSelector:'#sec-p27', cats:['save','waste'], items, columnLayout:false });
document.getElementById('p27-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p27-dnd-fb');
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='&#10003; Идеально! +15 XP. Экономия = меньше счёт + меньше нагрузка на электростанции.'; addXp(15,'p27-dnd'); bumpProgress('p27', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p27-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p27-dnd-fb'); fb.style.display='none'; });
}
function _initP27_tasks(){
const TASKS = [
{q:'Чайник $P = 2$ кВт работает 30 мин. Сколько кВт·ч?', ans:1, tol:0.05, why:'$W = P \\tau = 2 \\cdot 0{,}5 = 1$ кВт·ч.'},
{q:'Лампа $60$ Вт горит 5 часов. Сколько кВт·ч?', ans:0.3, tol:0.02, why:'$W = 0{,}06 \\cdot 5 = 0{,}3$ кВт·ч.'},
{q:'Тариф $0{,}25$ руб/(кВт·ч). За месяц израсходовали 200 кВт·ч. Стоимость (руб)?', ans:50, tol:1, why:'$200 \\cdot 0{,}25 = 50$ руб.'},
{q:'Холодильник $150$ Вт работает 24 ч/день. Сколько кВт·ч за месяц (30 дней)?', ans:108, tol:2, why:'$W = 0{,}15 \\cdot 24 \\cdot 30 = 108$ кВт·ч.'},
{q:'Электрообогреватель 2 кВт работает 8 ч/день, тариф $0{,}3$ руб. Сколько за месяц (руб)?', ans:144, tol:3, why:'$W = 2 \\cdot 8 \\cdot 30 = 480$ кВт·ч, $480 \\cdot 0{,}3 = 144$ руб.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p27-task'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Задача '+(i+1)+'.</b> '+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p27-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p27-task-go">Ответ</button>'
+'<button class="btn" id="p27-task-hint">Подсказка</button>'
+'<button class="btn" id="p27-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p27-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p27-task-fb"></div>';
document.getElementById('p27-task-i').textContent = (i+1);
document.getElementById('p27-task-ok').textContent = ok;
document.getElementById('p27-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p27-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p27-task-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
done++;
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(4,'p27-task'); bumpProgress('p27', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p27-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p27-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p27-task-bonus'); bumpProgress('p27', 15); }, 600); }
});
document.getElementById('p27-task-hint').addEventListener('click', ()=>{ document.getElementById('p27-task-hint-txt').classList.toggle('show'); });
document.getElementById('p27-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 4 · WAVE 1 — §28, §29
====================================================================== */
/* Хелпер: рисует магнитную полосу N-S */
function _drawMagnet(x, y, w, hMag, flipped){
const halfW = w/2;
const nCol = '#dc2626', sCol = '#2563eb';
const left = flipped ? sCol : nCol;
const right = flipped ? nCol : sCol;
const leftLab = flipped ? 'S' : 'N';
const rightLab = flipped ? 'N' : 'S';
let s = '';
s += '<rect x="'+x+'" y="'+y+'" width="'+halfW+'" height="'+hMag+'" fill="'+left+'" stroke="#0f172a" stroke-width="1.6"/>';
s += '<rect x="'+(x+halfW)+'" y="'+y+'" width="'+halfW+'" height="'+hMag+'" fill="'+right+'" stroke="#0f172a" stroke-width="1.6"/>';
s += '<text x="'+(x+halfW/2)+'" y="'+(y+hMag/2+5)+'" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="14" font-weight="900" fill="#fff">'+leftLab+'</text>';
s += '<text x="'+(x+halfW*1.5)+'" y="'+(y+hMag/2+5)+'" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="14" font-weight="900" fill="#fff">'+rightLab+'</text>';
return s;
}
/* ======== §28 — Постоянные магниты ======== */
function build_p28(){
const box = document.getElementById('p28-body');
let h = '';
h += makeCard('theory', 'Что такое магнит', '§ 28.1',
'<p><b>Постоянный магнит</b> — тело, обладающее магнитными свойствами длительное время. Чаще всего из железа, никеля, кобальта или сплавов (например, «магнитное железо» — ферриты).</p>'
+'<p>У любого магнита есть два <b>полюса</b>: <b>северный (N)</b> и <b>южный (S)</b>. Они расположены на противоположных концах.</p>'
+'<p><b>Сила взаимодействия</b> сосредоточена у полюсов — там магнит «прилипает» к железу сильнее всего.</p>'
);
h += makeCard('rule', 'Закон взаимодействия полюсов', '§ 28.2',
'<p>По аналогии с зарядами:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>Одноимённые</b> полюсы (N-N или S-S) — <b>отталкиваются</b>.</li>'
+'<li><b>Разноимённые</b> (N-S) — <b>притягиваются</b>.</li>'
+'</ul>'
+'<p>В отличие от электрических зарядов, <b>нельзя получить</b> магнит только с одним полюсом. Если разрезать магнит пополам — получатся <b>два новых</b> магнита, у каждого свои N и S.</p>'
);
h += makeCard('example', 'Магнитное поле Земли', '§ 28.3',
'<p>Земля — гигантский магнит. Её магнитные полюсы расположены вблизи географических (но не точно на них).</p>'
+'<p><b>Важно</b>: «северный географический» близок к <b>южному магнитному</b> полюсу Земли. Поэтому северный конец стрелки компаса (он же N) притягивается к географическому северу.</p>'
+'<p>Магнитное поле Земли защищает нас от космических лучей и помогает ориентироваться компасу. Полюсы со временем «дрейфуют».</p>'
);
/* 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>Левый магнит: фиксированный NS</label>'
+'<label>Правый магнит: <select id="p28-flip" class="tinp" style="width:auto;padding:6px 10px"><option value="0">NS (как левый)</option><option value="1">SN (перевёрнут)</option></select></label>'
+'</div>'
+'<svg id="p28-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"><span>Взаимодействие: <b id="p28-act">отталкиваются</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="p28-quiz"></div>'
+'<div class="actions"><button class="btn" id="p28-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p28-quiz-r">1</b> / 5</span><span>Правильно: <b id="p28-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD «факт о магнитах» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Правда или ложь?</div></div>'
+'<div id="p28-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="t"></div></div>'
+'<div class="drop-box"><h5>Ложь</h5><div class="drop-items" data-cat="f"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p28-dnd-check">Проверить</button><button class="btn" id="p28-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p28-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="p28-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p28-mcq-i">1</b> / 6</span><span>Правильно: <b id="p28-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p28') + readButton('p28');
renderMath(box);
wireReadBtn('p28');
_initP28_two();
_initP28_quiz();
_initP28_dnd();
_initP28_mcq();
}
function _initP28_two(){
const svg = document.getElementById('p28-sim'); if(!svg) return;
function draw(){
const flipped = +document.getElementById('p28-flip').value === 1;
let s = '';
/* левый магнит — фиксирован */
s += _drawMagnet(80, 70, 120, 60, false); /* N(left) S(right) */
/* правый магнит */
s += _drawMagnet(260, 70, 120, 60, flipped); /* зависит от флага */
/* Между магнитами: соседствуют правая половина левого (S) и левая половина правого */
/* левая половина правого: flipped ? N : S */
const rightInner = flipped ? 'N' : 'S';
/* левый внутр (правая половина левого) = S */
const leftInner = 'S';
const attract = leftInner !== rightInner;
/* стрелки силы */
if(attract){
/* притяжение — стрелки идут навстречу */
s += window.PHYS.drawArrow(190, 100, 230, 100, '#10b981', 2.5, 10);
s += window.PHYS.drawArrow(270, 100, 230, 100, '#10b981', 2.5, 10);
s += '<text x="230" y="155" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="14" font-weight="800" fill="#10b981">ПРИТЯГИВАЮТСЯ</text>';
} else {
/* отталкивание — стрелки расходятся */
s += window.PHYS.drawArrow(220, 100, 190, 100, '#dc2626', 2.5, 10);
s += window.PHYS.drawArrow(240, 100, 270, 100, '#dc2626', 2.5, 10);
s += '<text x="230" y="155" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="14" font-weight="800" fill="#dc2626">ОТТАЛКИВАЮТСЯ</text>';
}
svg.innerHTML = s;
document.getElementById('p28-act').textContent = attract ? 'притягиваются' : 'отталкиваются';
}
document.getElementById('p28-flip').addEventListener('change', draw);
draw();
}
function _initP28_quiz(){
const QS = [
{sit:'N подносим к N', ans:'R', why:'Одноимённые → отталкивание.'},
{sit:'N подносим к S', ans:'A', why:'Разноимённые → притяжение.'},
{sit:'S подносим к S', ans:'R', why:'Одноимённые → отталкивание.'},
{sit:'S подносим к N', ans:'A', why:'Разноимённые → притяжение.'},
{sit:'Полосовой магнит к стрелке компаса (северный конец)', ans:'A', why:'Северный конец стрелки притягивается к S полюсу магнита, отталкивается от N.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p28-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="A" style="padding:14px"><b>Притягиваются</b></button>'
+'<button class="btn" data-pick="R" style="padding:14px"><b>Отталкиваются</b></button>'
+'</div>'
+'<div class="feedback" id="p28-quiz-fb"></div>';
document.getElementById('p28-quiz-r').textContent = (i+1);
document.getElementById('p28-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('p28-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p28-quiz'); bumpProgress('p28', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p28-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p28-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP28_dnd(){
const items = [
{id:'a', cat:'t', html:'у магнита 2 полюса: N и S'},
{id:'b', cat:'t', html:'N–S притягиваются'},
{id:'c', cat:'t', html:'разрезать магнит → 2 магнита'},
{id:'d', cat:'t', html:'Земля — гигантский магнит'},
{id:'e', cat:'f', html:'можно получить один полюс'},
{id:'f', cat:'f', html:'N–N притягиваются'},
{id:'g', cat:'f', html:'магнит работает только при подключении'},
{id:'h', cat:'f', html:'дерево — магнитный материал'}
];
const dnd = setupSorter({ poolId:'p28-dnd-pool', scopeSelector:'#sec-p28', cats:['t','f'], items, columnLayout:false });
document.getElementById('p28-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p28-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,'p28-dnd'); bumpProgress('p28', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p28-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p28-dnd-fb'); fb.style.display='none'; });
}
function _initP28_mcq(){
const QS = [
{q:'Сколько полюсов у магнита?', opts:['1','2','3','много'], ans:1, why:'У любого магнита всегда 2 полюса.'},
{q:'Какие материалы притягиваются магнитом?', opts:['все','железо, никель, кобальт','медь','стекло'], ans:1, why:'Ферромагнетики.'},
{q:'Что произойдёт при разрезании магнита?', opts:['станет 2 разных магнита N и S','станет ничего','станут 2 полноценных магнита','полюса исчезнут'], ans:2, why:'Каждый «огрызок» снова имеет 2 полюса.'},
{q:'Северный географический полюс Земли — это магнитный…', opts:['северный','южный','никакой','нейтральный'], ans:1, why:'Север географический ≈ южный магнитный (так задумано в физике).'},
{q:'Что показывает северный конец компаса?', opts:['на N магнита','на N полюс магнита','на географический север','куда хочешь'], ans:2, why:'Стрелка компаса север «как бы» указывает на северный географический полюс.'},
{q:'Какая сила действует между магнитами?', opts:['гравитационная','электрическая','магнитная','ядерная'], ans:2, why:'Магнитная.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p28-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="p28-mcq-fb"></div><div class="actions"><button class="btn" id="p28-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p28-mcq-i').textContent = (i+1);
document.getElementById('p28-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('p28-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p28-mcq'); bumpProgress('p28', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p28-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p28-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p28-mcq-bonus'); bumpProgress('p28', 15); }, 600); }
});
});
const nb = document.getElementById('p28-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §29 — Магнитное поле ======== */
function build_p29(){
const box = document.getElementById('p29-body');
let h = '';
h += makeCard('theory', 'Что такое магнитное поле', '§ 29.1',
'<p>Подобно тому как заряды создают электрическое поле, магниты создают вокруг себя <b>магнитное поле</b>.</p>'
+'<p>Силовая характеристика поля — <b>магнитная индукция</b> $\\vec B$. Единица измерения — <b>Тесла (Тл)</b>, в честь Никола Тесла.</p>'
+'<p>Магнитное поле обнаруживается по силе, действующей на:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>другие магниты и магнитные стрелки;</li>'
+'<li>железные предметы;</li>'
+'<li>проводники с током;</li>'
+'<li>движущиеся заряды.</li>'
+'</ul>'
);
h += makeCard('rule', 'Линии магнитной индукции', '§ 29.2',
'<p>Магнитное поле наглядно изображают <b>линиями магнитной индукции</b>:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>выходят из <b>N</b> и входят в <b>S</b> полюс магнита снаружи;</li>'
+'<li>внутри магнита идут от S к N — поэтому они <b>замкнуты</b>;</li>'
+'<li>не пересекаются;</li>'
+'<li>чем гуще линии, тем сильнее поле.</li>'
+'</ul>'
+'<p><b>Важное отличие от электрического поля:</b> линии магн. поля всегда замкнуты (нет «магнитных зарядов»).</p>'
);
h += makeCard('example', 'Опыт с железными опилками', '§ 29.3',
'<p>Если на стекло над магнитом насыпать железные опилки, они выстраиваются вдоль линий магнитной индукции. Получается «фотография» магнитного поля.</p>'
+'<p>Сейчас увидим, как это выглядит.</p>'
);
/* 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">Линии выходят из N и входят в S — снаружи. Внутри магнита идут от S к N, замыкая контур.</div>'
+'<svg id="p29-sim" viewBox="0 0 460 280" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'</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">Правда или ложь о линиях $\\vec B$.</div>'
+'<div id="p29-quiz"></div>'
+'<div class="actions"><button class="btn" id="p29-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p29-quiz-r">1</b> / 5</span><span>Правильно: <b id="p29-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">Электрическое vs магнитное поле</div></div>'
+'<div class="wg-help">К какому полю относится свойство?</div>'
+'<div id="p29-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="e"></div></div>'
+'<div class="drop-box"><h5>Магнитное</h5><div class="drop-items" data-cat="m"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p29-dnd-check">Проверить</button><button class="btn" id="p29-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p29-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="p29-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p29-mcq-i">1</b> / 6</span><span>Правильно: <b id="p29-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p29') + readButton('p29');
renderMath(box);
wireReadBtn('p29');
_initP29_lines();
_initP29_quiz();
_initP29_dnd();
_initP29_mcq();
}
function _initP29_lines(){
const svg = document.getElementById('p29-sim'); if(!svg) return;
/* Полосовой магнит N(слева) S(справа), линии поля изгибаются.
Параметрически: серия эллиптических дуг разного размера, выходящих из N. */
const cx = 230, cy = 140;
const magW = 160, magH = 40;
let s = '';
/* нарисуем магнит */
s += _drawMagnet(cx - magW/2, cy - magH/2, magW, magH, false); /* N(left), S(right) */
/* линии: эллипсы между N(150,140) и S(310,140), радиус растёт */
for(let k = 0; k < 7; k++){
const ry = 30 + k*25;
/* нарисуем верхнюю и нижнюю половину эллипса как path */
const xN = cx - magW/2;
const xS = cx + magW/2;
/* верхняя половина: дуга от xN,cy через (cx,cy-ry) до xS,cy */
const cx1 = (xN+xS)/2;
/* стрелка на верхушке */
s += '<path d="M '+xN+' '+cy+' Q '+cx1+' '+(cy-ry)+' '+xS+' '+cy+'" fill="none" stroke="#7c3aed" stroke-width="1.4" opacity="0.7"/>';
s += '<path d="M '+xN+' '+cy+' Q '+cx1+' '+(cy+ry)+' '+xS+' '+cy+'" fill="none" stroke="#7c3aed" stroke-width="1.4" opacity="0.7"/>';
/* стрелка направления — посередине верхней дуги, направлена вправо */
if(k < 5){
s += window.PHYS.drawArrow(cx-6, cy - ry + 2, cx+6, cy - ry + 2, '#7c3aed', 1.5, 7);
s += window.PHYS.drawArrow(cx-6, cy + ry - 2, cx+6, cy + ry - 2, '#7c3aed', 1.5, 7);
}
}
/* пара прямых линий близко к оси (горизонтально между полюсами) */
s += '<line x1="'+(cx-magW/2-30)+'" y1="'+cy+'" x2="'+(cx-magW/2-4)+'" y2="'+cy+'" stroke="#7c3aed" stroke-width="1.6"/>';
s += '<line x1="'+(cx+magW/2+4)+'" y1="'+cy+'" x2="'+(cx+magW/2+30)+'" y2="'+cy+'" stroke="#7c3aed" stroke-width="1.6"/>';
/* подписи */
s += '<text x="230" y="40" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#7c3aed">линии магнитной индукции $\\vec B$</text>';
s += '<text x="60" y="270" font-family="Inter,sans-serif" font-size="11" fill="#475569">снаружи: N → S</text>';
s += '<text x="280" y="270" font-family="Inter,sans-serif" font-size="11" fill="#475569">внутри: S → N (замыкают)</text>';
svg.innerHTML = s;
}
function _initP29_quiz(){
const QS = [
{st:'Линии магнитного поля выходят из S и входят в N.', ans:'F', why:'Наоборот: из N в S.'},
{st:'Линии магнитного поля замкнуты.', ans:'T', why:'Внутри магнита они идут от S к N, замыкая контур.'},
{st:'Линии магнитного поля могут пересекаться.', ans:'F', why:'В каждой точке вектор $\\vec B$ имеет одно направление.'},
{st:'Чем гуще линии, тем сильнее поле.', ans:'T', why:'Густота показывает $|B|$.'},
{st:'Магнитное поле действует на неподвижный заряд.', ans:'F', why:'Только на движущийся заряд и на ток!'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p29-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="p29-quiz-fb"></div>';
document.getElementById('p29-quiz-r').textContent = (i+1);
document.getElementById('p29-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('p29-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p29-quiz'); bumpProgress('p29', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p29-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p29-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP29_dnd(){
const items = [
{id:'a', cat:'e', html:'линии начинаются на +, кончаются на &minus;'},
{id:'b', cat:'e', html:'действует на любой заряд'},
{id:'c', cat:'e', html:'источники — заряды'},
{id:'d', cat:'e', html:'$U = A/q$'},
{id:'e', cat:'m', html:'линии замкнуты (без начала и конца)'},
{id:'f', cat:'m', html:'действует на движ. заряды и ток'},
{id:'g', cat:'m', html:'источники — магниты и токи'},
{id:'h', cat:'m', html:'$\\vec B$ — индукция, [Тл]'}
];
const dnd = setupSorter({ poolId:'p29-dnd-pool', scopeSelector:'#sec-p29', cats:['e','m'], items, columnLayout:false });
document.getElementById('p29-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p29-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,'p29-dnd'); bumpProgress('p29', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; renderMath(fb); }
});
document.getElementById('p29-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p29-dnd-fb'); fb.style.display='none'; });
}
function _initP29_mcq(){
const QS = [
{q:'Что обозначают $\\vec B$?', opts:['напряжённость','индукцию','потенциал','работу'], ans:1, why:'Магнитная индукция.'},
{q:'Единица измерения $|B|$?', opts:['Вольт','Ампер','Тесла','Ом'], ans:2, why:'Тесла (Тл).'},
{q:'Куда направлены линии вне магнита?', opts:['от N к S','от S к N','внутри по кругу','хаос'], ans:0, why:'Снаружи — от N к S.'},
{q:'Линии $\\vec B$ обладают свойством…', opts:['открытости','замкнутости','пересечения','исчезновения'], ans:1, why:'Магнитное поле — соленоидальное.'},
{q:'Что отклоняет стрелку компаса?', opts:['заряд','свет','магнитное поле Земли','гравитация'], ans:2, why:'Земля — гигантский магнит.'},
{q:'Как «увидеть» магнитное поле?', opts:['опилками','спиртом','краской','углём'], ans:0, why:'Железные опилки выстраиваются вдоль линий.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p29-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="p29-mcq-fb"></div><div class="actions"><button class="btn" id="p29-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p29-mcq-i').textContent = (i+1);
document.getElementById('p29-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('p29-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p29-mcq'); bumpProgress('p29', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p29-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p29-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p29-mcq-bonus'); bumpProgress('p29', 15); }, 600); }
});
});
const nb = document.getElementById('p29-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======================================================================
PHASE 4 · WAVE 2 — §30, §31, FINAL 2
====================================================================== */
/* ======== §30 — Магнитное поле тока. Опыт Эрстеда ======== */
function build_p30(){
const box = document.getElementById('p30-body');
let h = '';
h += makeCard('theory', 'Опыт Эрстеда', '§ 30.1',
'<p>В 1820 году датский физик Ханс Эрстед сделал революционное открытие. Он расположил магнитную стрелку под прямым проводником и включил ток.</p>'
+'<p>Стрелка <b>повернулась</b>! Это значило: вокруг проводника с током есть <b>магнитное поле</b>.</p>'
+'<p>До этого считалось, что электричество и магнетизм — совершенно независимы. Опыт Эрстеда показал: <b>это одно явление</b>.</p>'
);
h += makeCard('rule', 'Что говорит этот опыт', '§ 30.2',
'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Ток (движущиеся заряды) создаёт магнитное поле.</li>'
+'<li>Чем больше $I$, тем сильнее поле.</li>'
+'<li>Без тока (ключ разомкнут) — поля нет, стрелка стоит как обычно (по полю Земли).</li>'
+'<li>Если поменять направление тока — стрелка повернётся в обратную сторону.</li>'
+'</ul>'
+'<p>Это положило начало <b>электромагнетизму</b> — теории, объединяющей две силы природы.</p>'
);
h += makeCard('example', 'Применения', '§ 30.3',
'<ul style="padding-left:20px;margin:6px 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">Включи ток — стрелка отклонится. Поменяй направление — стрелка повернётся в другую сторону.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>Ключ: <select id="p30-key" class="tinp" style="width:auto;padding:6px 10px"><option value="0">разомкнут (тока нет)</option><option value="1">замкнут</option></select></label>'
+'<label>Направление тока: <select id="p30-dir" class="tinp" style="width:auto;padding:6px 10px"><option value="1">→ (вправо)</option><option value="-1">← (влево)</option></select></label>'
+'</div>'
+'<svg id="p30-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"><span>Стрелка отклонена на: <b id="p30-ang">0&#176;</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="p30-quiz"></div>'
+'<div class="actions"><button class="btn" id="p30-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p30-quiz-r">1</b> / 5</span><span>Правильно: <b id="p30-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="p30-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="y"></div></div>'
+'<div class="drop-box"><h5>Нет поля</h5><div class="drop-items" data-cat="n"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p30-dnd-check">Проверить</button><button class="btn" id="p30-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p30-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="p30-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p30-mcq-i">1</b> / 6</span><span>Правильно: <b id="p30-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p30') + readButton('p30');
renderMath(box);
wireReadBtn('p30');
_initP30_oersted();
_initP30_quiz();
_initP30_dnd();
_initP30_mcq();
}
function _initP30_oersted(){
const svg = document.getElementById('p30-sim'); if(!svg) return;
function draw(){
const key = +document.getElementById('p30-key').value === 1;
const dir = +document.getElementById('p30-dir').value;
let s = '';
/* провод горизонтальный */
s += '<line x1="60" y1="60" x2="400" y2="60" stroke="#374151" stroke-width="4" stroke-linecap="round"/>';
/* батарея слева, ключ */
s += window.PHYS.batteryEMF(60, 130, '6 В', 'h');
s += window.PHYS.wire(60, 112, 60, 60);
s += window.PHYS.wire(60, 148, 60, 170);
s += window.PHYS.wire(60, 170, 400, 170);
s += window.PHYS.wire(400, 170, 400, 60);
/* ключ на нижнем проводе */
s += '<circle cx="200" cy="170" r="3" fill="#0f172a"/>';
s += '<circle cx="240" cy="170" r="3" fill="#0f172a"/>';
if(key) s += '<line x1="200" y1="170" x2="240" y2="170" stroke="#0f172a" stroke-width="3"/>';
else s += '<line x1="200" y1="170" x2="235" y2="155" stroke="#0f172a" stroke-width="3"/>';
/* стрелка тока на проводе */
if(key){
if(dir > 0) s += window.PHYS.drawArrow(200, 50, 260, 50, '#d97706', 2.2, 10);
else s += window.PHYS.drawArrow(260, 50, 200, 50, '#d97706', 2.2, 10);
s += '<text x="230" y="40" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#d97706">I</text>';
}
/* магнитная стрелка под проводом */
const cx = 230, cy = 100;
/* угол отклонения */
let angle = 0; /* 0 — стрелка вдоль провода (на запад/восток... условно вверх в нашем рисунке: будет указывать на N=верх) */
/* без тока: стрелка указывает «на север» — то есть направлена вверх (в нашей рисовке север = верх) */
/* с током: отклоняется перпендикулярно проводу.
Эрстед: если ток слева направо (запад → восток), стрелка отклоняется по часовой стрелке (вид сверху, стрелка кажется поворачивается вправо). */
if(key){
angle = dir > 0 ? 60 : -60; /* 60° от исходного направления */
}
/* нарисуем стрелку: длина 36, поворот на angle */
const rad = angle * Math.PI / 180;
/* без поворота стрелка идёт вверх (от cx,cy+18 до cx,cy-18) */
const lenH = 18;
const ex1 = cx + lenH * Math.sin(rad);
const ey1 = cy - lenH * Math.cos(rad); /* северный конец */
const ex2 = cx - lenH * Math.sin(rad);
const ey2 = cy + lenH * Math.cos(rad);
s += '<circle cx="'+cx+'" cy="'+cy+'" r="4" fill="#0f172a"/>';
s += '<line x1="'+cx+'" y1="'+cy+'" x2="'+ex1.toFixed(1)+'" y2="'+ey1.toFixed(1)+'" stroke="#dc2626" stroke-width="3"/>';
s += '<polygon points="'+ex1.toFixed(1)+','+ey1.toFixed(1)+' '+(ex1-4*Math.cos(rad)).toFixed(1)+','+(ey1-4*Math.sin(rad)).toFixed(1)+' '+(ex1+4*Math.cos(rad)).toFixed(1)+','+(ey1+4*Math.sin(rad)).toFixed(1)+'" fill="#dc2626"/>';
s += '<line x1="'+cx+'" y1="'+cy+'" x2="'+ex2.toFixed(1)+'" y2="'+ey2.toFixed(1)+'" stroke="#2563eb" stroke-width="3"/>';
/* подпись стрелки */
s += '<text x="'+(ex1+8*Math.sin(rad)).toFixed(1)+'" y="'+(ey1-2).toFixed(1)+'" font-family="Inter,sans-serif" font-size="11" font-weight="800" fill="#dc2626">N</text>';
/* отклонение */
document.getElementById('p30-ang').textContent = Math.abs(angle).toFixed(0)+'&#176;';
svg.innerHTML = s;
}
document.getElementById('p30-key').addEventListener('change', draw);
document.getElementById('p30-dir').addEventListener('change', draw);
draw();
}
function _initP30_quiz(){
const QS = [
{q:'Что заметил Эрстед в опыте?', opts:['тёплый провод','отклонение стрелки около проводника с током','свечение проводника','ничего'], ans:1, why:'Магнитная стрелка отклонилась.'},
{q:'Что доказывает опыт?', opts:['ток нагревает','ток создаёт магн. поле','магнит проводит ток','ничего'], ans:1, why:'Ток создаёт магнитное поле.'},
{q:'В каком году провёл опыт?', opts:['1750','1820','1900','1950'], ans:1, why:'1820 год.'},
{q:'Что произойдёт, если поменять направление тока?', opts:['стрелка не среагирует','стрелка повернётся в противоположную сторону','стрелка сломается','стрелка сделает оборот'], ans:1, why:'Направление поля зависит от направления тока.'},
{q:'Без тока (ключ разомкнут) стрелка…', opts:['отклонена','указывает на север','вращается','не работает'], ans:1, why:'Под действием магнитного поля Земли указывает на N.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p30-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p30-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p30-quiz-r').textContent = (i+1);
document.getElementById('p30-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('p30-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p30-quiz'); bumpProgress('p30', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p30-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p30-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP30_dnd(){
const items = [
{id:'a', cat:'y', html:'постоянный магнит'},
{id:'b', cat:'y', html:'провод с током'},
{id:'c', cat:'y', html:'движущийся электрон'},
{id:'d', cat:'y', html:'катушка под током'},
{id:'e', cat:'n', html:'нейтральный кусок железа'},
{id:'f', cat:'n', html:'разомкнутая цепь'},
{id:'g', cat:'n', html:'покоящийся заряд'},
{id:'h', cat:'n', html:'батарейка без провода'}
];
const dnd = setupSorter({ poolId:'p30-dnd-pool', scopeSelector:'#sec-p30', cats:['y','n'], items, columnLayout:false });
document.getElementById('p30-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p30-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,'p30-dnd'); bumpProgress('p30', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p30-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p30-dnd-fb'); fb.style.display='none'; });
}
function _initP30_mcq(){
const QS = [
{q:'Кто открыл связь электричества и магнетизма?', opts:['Вольт','Ампер','Эрстед','Ом'], ans:2, why:'Х. Эрстед, 1820 г.'},
{q:'Какое поле создаёт ток?', opts:['электрическое','магнитное','гравитационное','тепловое'], ans:1, why:'Магнитное.'},
{q:'От чего зависит сила магн. поля у провода?', opts:['от U','от I','от R','от температуры'], ans:1, why:'Чем больше $I$, тем сильнее поле.'},
{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('p30-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="p30-mcq-fb"></div><div class="actions"><button class="btn" id="p30-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p30-mcq-i').textContent = (i+1);
document.getElementById('p30-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('p30-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p30-mcq'); bumpProgress('p30', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p30-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p30-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p30-mcq-bonus'); bumpProgress('p30', 15); }, 600); }
});
});
const nb = document.getElementById('p30-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §31 — Поле прямого провода и катушки. Электромагнит ======== */
function build_p31(){
const box = document.getElementById('p31-body');
let h = '';
h += makeCard('theory', 'Поле прямого провода', '§ 31.1',
'<p>Опыт с железными опилками показывает: линии магнитного поля вокруг прямого провода с током — <b>концентрические окружности</b> в плоскости, перпендикулярной проводу.</p>'
+'<p><b>Правило правой руки</b>: если большой палец правой руки направить вдоль тока, то остальные пальцы покажут направление линий $\\vec B$.</p>'
);
h += makeCard('rule', 'Катушка с током (соленоид)', '§ 31.2',
'<p>Если намотать провод спиралью и пропустить через него ток, получится <b>соленоид</b>. Его магнитное поле очень похоже на поле полосового магнита:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>один торец становится «N» полюсом, другой — «S»;</li>'
+'<li>линии $\\vec B$ выходят из N, входят в S, замыкаются;</li>'
+'<li>сила поля растёт с током $I$ и числом витков $N$: $|B| \\propto I N$.</li>'
+'</ul>'
+'<p>Направление полюсов снова определяется правилом правой руки: пальцы — по витку с током, большой палец — в сторону северного полюса.</p>'
);
h += makeCard('example', 'Электромагнит', '§ 31.3',
'<p>Если вставить внутрь катушки железный <b>сердечник</b>, поле резко усиливается (в 1000+ раз). Получается <b>электромагнит</b>.</p>'
+'<p>Его преимущества:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>включается и выключается по желанию (через ключ);</li>'
+'<li>сила управляется током $I$;</li>'
+'<li>можно сделать очень сильным.</li>'
+'</ul>'
+'<p>Применения: дверные звонки, реле, грузоподъёмные краны, левитирующие поезда.</p>'
);
/* 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>$I$, А: <b id="p31-iv">1.0</b><input type="range" id="p31-i" min="0" max="3" step="0.1" value="1"></label>'
+'<label>Число витков $N$: <b id="p31-nv">50</b><input type="range" id="p31-n" min="0" max="200" step="10" value="50"></label>'
+'<label>Сердечник: <select id="p31-core" class="tinp" style="width:auto;padding:6px 10px"><option value="1">воздух (без)</option><option value="500">железо</option></select></label>'
+'</div>'
+'<svg id="p31-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;flex-direction:column;align-items:flex-start;gap:3px">'
+'<span>$|B| \\propto I \\cdot N \\cdot \\mu$: <b id="p31-bval">50</b> у.е.</span>'
+'<span>Поднимет скрепок: <b id="p31-clips">2</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="p31-quiz"></div>'
+'<div class="actions"><button class="btn" id="p31-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p31-quiz-r">1</b> / 5</span><span>Правильно: <b id="p31-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="p31-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="p31-dnd-check">Проверить</button><button class="btn" id="p31-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p31-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="p31-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p31-mcq-i">1</b> / 6</span><span>Правильно: <b id="p31-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p31') + readButton('p31');
renderMath(box);
wireReadBtn('p31');
_initP31_emagnet();
_initP31_quiz();
_initP31_dnd();
_initP31_mcq();
}
function _initP31_emagnet(){
const svg = document.getElementById('p31-sim'); if(!svg) return;
function update(){
const I = +document.getElementById('p31-i').value;
const N = +document.getElementById('p31-n').value;
const mu = +document.getElementById('p31-core').value;
document.getElementById('p31-iv').textContent = I.toFixed(1);
document.getElementById('p31-nv').textContent = N;
const B = I * N * mu / 100; /* условная единица */
document.getElementById('p31-bval').textContent = B.toFixed(0);
const clips = Math.floor(B / 25);
document.getElementById('p31-clips').textContent = clips;
/* SVG: катушка + сердечник + поднятые скрепки */
let s = '';
/* батарея */
s += window.PHYS.batteryEMF(50, 180, I.toFixed(1)+' А', 'h');
/* провода */
s += window.PHYS.wire(50, 162, 50, 100);
s += window.PHYS.wire(50, 100, 130, 100);
s += window.PHYS.wire(50, 198, 50, 200);
s += window.PHYS.wire(50, 200, 360, 200);
s += window.PHYS.wire(360, 200, 360, 100);
s += window.PHYS.wire(360, 100, 320, 100);
/* катушка — серия наклонных полудуг */
const wireCount = Math.max(3, Math.min(20, Math.floor(N/10)));
const coilX1 = 130, coilX2 = 320;
const coilSpan = coilX2 - coilX1;
const coilCy = 100;
/* стержень-сердечник (если железо — заметнее) */
if(mu > 1){
s += '<rect x="'+(coilX1-10)+'" y="'+(coilCy-12)+'" width="'+(coilSpan+20)+'" height="24" fill="#cbd5e1" stroke="#0f172a" stroke-width="1.5" rx="3"/>';
}
for(let k=0; k<wireCount; k++){
const cx = coilX1 + (k+0.5) * coilSpan/wireCount;
s += '<ellipse cx="'+cx.toFixed(1)+'" cy="'+coilCy+'" rx="6" ry="18" fill="none" stroke="#d97706" stroke-width="2"/>';
}
/* N и S */
s += '<text x="'+(coilX1-22)+'" y="'+(coilCy+5)+'" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="16" font-weight="900" fill="#dc2626">N</text>';
s += '<text x="'+(coilX2+22)+'" y="'+(coilCy+5)+'" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="16" font-weight="900" fill="#2563eb">S</text>';
/* скрепки притянуты к N полюсу */
for(let k=0;k<clips && k<8;k++){
const yC = 130 + k*10;
s += '<path d="M '+(coilX1-30-k*4)+' '+yC+' Q '+(coilX1-15)+' '+(yC+3)+' '+(coilX1-15)+' '+yC+' Q '+(coilX1-15)+' '+(yC-3)+' '+(coilX1-30-k*4)+' '+yC+'" fill="none" stroke="#475569" stroke-width="2"/>';
}
/* подпись */
s += '<text x="'+(coilX1+coilSpan/2)+'" y="50" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#7c3aed">|B| = '+B.toFixed(0)+', клипс: '+clips+'</text>';
svg.innerHTML = s;
}
['p31-i','p31-n','p31-core'].forEach(id => document.getElementById(id).addEventListener('input', update));
document.getElementById('p31-core').addEventListener('change', update);
update();
}
function _initP31_quiz(){
const QS = [
{q:'Большой палец правой руки = ток. Куда направлены остальные пальцы?', opts:['от N','вдоль линий $\\vec B$','к S','перпендикулярно'], ans:1, why:'Правило правой руки: пальцы по $\\vec B$.'},
{q:'У катушки — какой полюс там, куда «выходят» линии?', opts:['N','S','оба','никакой'], ans:0, why:'Линии выходят из N.'},
{q:'Что усилит поле катушки сильнее всего?', opts:['+ток','+витки','+железный сердечник','любое'], ans:2, why:'Сердечник усиливает поле в сотни раз.'},
{q:'Электромагнит работает на постоянном…', opts:['напряжении','токе','тепле','магните'], ans:1, why:'Нужен постоянный ток.'},
{q:'Что произойдёт, если выключить ток?', opts:['поле остаётся','поле исчезает','поле растёт','полюса меняются'], ans:1, why:'Поле существует только при токе.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p31-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p31-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p31-quiz-r').textContent = (i+1);
document.getElementById('p31-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('p31-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p31-quiz'); bumpProgress('p31', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p31-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p31-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP31_dnd(){
const items = [
{id:'i', cat:'up', html:'увеличить ток $I$'},
{id:'n', cat:'up', html:'добавить витков'},
{id:'c', cat:'up', html:'вставить железный сердечник'},
{id:'t', cat:'up', html:'намотать туже'},
{id:'ki',cat:'dn', html:'уменьшить ток'},
{id:'nn',cat:'dn', html:'убрать половину витков'},
{id:'ne',cat:'dn', html:'вынуть сердечник'},
{id:'op',cat:'dn', html:'разомкнуть цепь'}
];
const dnd = setupSorter({ poolId:'p31-dnd-pool', scopeSelector:'#sec-p31', cats:['up','dn'], items, columnLayout:false });
document.getElementById('p31-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p31-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. $|B| \\propto I N$ + сердечник усиливает в сотни раз.'; addXp(15,'p31-dnd'); bumpProgress('p31', 20); renderMath(fb); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p31-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p31-dnd-fb'); fb.style.display='none'; });
}
function _initP31_mcq(){
const QS = [
{q:'Линии $\\vec B$ вокруг прямого провода — это…', opts:['прямые','окружности','эллипсы','зигзаги'], ans:1, why:'Концентрические окружности перпендикулярно проводу.'},
{q:'Что определяет правило правой руки?', opts:['знак заряда','направление $\\vec B$','напряжение','частоту'], ans:1, why:'Направление магнитного поля.'},
{q:'Соленоид с током похож на…', opts:['прямой магнит','полосовой магнит','подковообразный','ничего'], ans:1, why:'Поле как у полосового магнита.'},
{q:'Электромагнит = …', opts:['обычный магнит','катушка + сердечник','батарейка + лампа','любой провод'], ans:1, why:'Катушка с сердечником.'},
{q:'Зачем сердечник?', opts:['для красоты','для прочности','для усиления магнитного поля','чтобы катушка не сгорала'], ans:2, why:'Железо концентрирует магнитное поле — усиливает в сотни раз.'},
{q:'Где применяют электромагнит?', opts:['в МРТ','в кранах','в звонках, реле','везде перечисленное'], ans:3, why:'Все эти устройства!'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p31-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="p31-mcq-fb"></div><div class="actions"><button class="btn" id="p31-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p31-mcq-i').textContent = (i+1);
document.getElementById('p31-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('p31-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p31-mcq'); bumpProgress('p31', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p31-mcq-ok').textContent = ok;
renderMath(wrap);
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p31-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p31-mcq-bonus'); bumpProgress('p31', 15); }, 600); }
});
});
const nb = document.getElementById('p31-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
renderMath(wrap);
}
render();
}
/* ======== ФИНАЛ ГЛАВЫ 2 — 10 интегрированных боссов ======== */
function build_final2(){
const box = document.getElementById('final2-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">Шпаргалка главы 2</div></div>'
+'<div class="card-body" style="display:grid;grid-template-columns:1fr 1fr;gap:14px">'
+'<div><b>Заряд:</b> $q = Ne$, $e = 1{,}6\\cdot10^{-19}$ Кл</div>'
+'<div><b>Поле:</b> $\\vec E$, линии от + к &minus;</div>'
+'<div><b>Напряжение:</b> $U = A/q$, $A = qU$</div>'
+'<div><b>Ток:</b> $I = q/t$, [А]</div>'
+'<div><b>Ом:</b> $I = U/R$</div>'
+'<div><b>Сопротивление:</b> $R = \\rho l/S$</div>'
+'<div><b>Последов.:</b> $R = R_1+R_2$, $I=$const, $U=U_1+U_2$</div>'
+'<div><b>Паралл.:</b> $1/R = 1/R_1+1/R_2$, $U=$const, $I=I_1+I_2$</div>'
+'<div><b>Мощность:</b> $P = UI = I^2R = U^2/R$</div>'
+'<div><b>Джоуль-Ленц:</b> $Q = I^2Rt$</div>'
+'<div><b>Энергия:</b> $W = Pt$ кВт·ч; $1$ кВт·ч $= 3{,}6\\cdot10^6$ Дж</div>'
+'<div><b>Магниты:</b> N, S; одноим. отталк., разноим. притяг.</div>'
+'</div></div>';
/* 10 интегрированных боссов */
const BOSSES = [
{n:1, title:'Закон Ома базовый', q:'$U = 36$ В, $R = 12$ Ом. Найди $I$ (А).', hint:'$I = U/R = 3$ А.', ans:3, tol:0.05, step:'0.01'},
{n:2, title:'Сопротивление провода', q:'Медь $l = 50$ м, $S = 0{,}5$ мм². Найди $R$ (Ом). ($\\rho = 0{,}017$)', hint:'$R = \\rho l/S = 0{,}017\\cdot50/0{,}5 = 1{,}7$ Ом.', ans:1.7, tol:0.05, step:'0.01'},
{n:3, title:'Послед. цепь', q:'$R_1 = 4$, $R_2 = 6$, $R_3 = 10$ Ом последовательно, $U = 40$ В. Найди $I$ (А).', hint:'$R = 20$, $I = 40/20 = 2$ А.', ans:2, tol:0.05, step:'0.01'},
{n:4, title:'Паралл. цепь', q:'Два резистора $R_1 = R_2 = 12$ Ом параллельно. Найди $R_{общ}$ (Ом).', hint:'$R/2 = 6$ Ом.', ans:6, tol:0.1, step:'0.1'},
{n:5, title:'Смешанная цепь', q:'$R_1 = 5$ Ом последовательно с параллельным блоком ($R_2 = R_3 = 10$ Ом каждый). $U = 20$ В. Найди общий $I$ (А).', hint:'$R_{пар} = 5$, $R = 10$, $I = 20/10 = 2$ А.', ans:2, tol:0.05, step:'0.01'},
{n:6, title:'Мощность', q:'Чайник 1100 Вт работает при $U = 220$ В. Найди $I$ (А) и $R$ (Ом). Введи $R$.', hint:'$I = P/U = 5$ А, $R = U/I = 44$ Ом.', ans:44, tol:1, step:'1'},
{n:7, title:'Джоуль-Ленц', q:'Спираль $R = 50$ Ом, $I = 4$ А, $t = 1$ мин. Сколько кДж выделится?', hint:'$Q = I^2Rt = 16\\cdot50\\cdot60 = 48\\,000$ Дж = $48$ кДж.', ans:48, tol:1, step:'1'},
{n:8, title:'кВт·ч за месяц', q:'Холодильник 200 Вт работает 24 ч/день, 30 дней. Сколько кВт·ч за месяц?', hint:'$W = 0{,}2 \\cdot 24 \\cdot 30 = 144$ кВт·ч.', ans:144, tol:2, step:'1'},
{n:9, title:'Стоимость энергии', q:'За месяц израсходовано 250 кВт·ч, тариф $0{,}25$ руб/(кВт·ч). Сколько руб?', hint:'$250 \\cdot 0{,}25 = 62{,}5$ руб.', ans:62.5, tol:0.5, step:'0.1'},
{n:10, title:'Магистр электромагнетизма', q:'Электромагнит с $N = 200$ витками и $I = 1{,}5$ А. Поле в 10 раз сильнее, чем при $N = 50$ и $I = 0{,}6$ А. Во сколько раз поле сильнее в обычной катушке без сердечника, чем с железным сердечником (предположим, сердечник усиливает в 500 раз)? Введи число.', hint:'Сердечник увеличивает поле в 500 раз. Без сердечника поле в 500 раз слабее.', ans:500, tol:5, step:'1'}
];
h += '<div class="card" style="margin-top:14px"><div class="card-header"><div class="card-icon example">'+ICONS.example+'</div><div class="card-title">10 боссов главы 2</div></div><div class="card-body">'
+'<div class="boss-overall-bar" style="background:linear-gradient(135deg,rgba(15,23,42,.04),rgba(217,119,6,.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="f2-won">0</b> / 10</span>'
+'<div style="flex:1;min-width:160px;height:8px;background:rgba(0,0,0,.08);border-radius:4px;overflow:hidden">'
+'<div id="f2-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="f2-bosses"></div>'
+'</div></div>';
box.innerHTML = h + secNavFor('final2') + readButton('final2');
renderMath(box);
wireReadBtn('final2');
_initFinal2_bosses(BOSSES);
}
function _initFinal2_bosses(BOSSES){
const KEY = 'physics8_ch2_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('f2-won').textContent = won;
document.getElementById('f2-bar').style.width = Math.round(won*100/BOSSES.length)+'%';
if(won >= BOSSES.length && !STATE.achievements.has('em_master')){
addXp(50, 'em-master');
achievement('em_master');
}
return won;
}
function renderAll(){
const cont = document.getElementById('f2-bosses');
const state = loadState();
let html = '';
BOSSES.forEach(b=>{
const solved = state[b.n];
html += '<div class="boss-card'+(solved?' solved':'')+'" id="f2-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="f2-b'+b.n+'-inp" placeholder="число" style="width:140px"'+(solved?' value="'+b.ans+'" disabled':'')+'>'
+'<button class="btn primary" id="f2-b'+b.n+'-go"'+(solved?' disabled':'')+'>Атаковать</button>'
+'<button class="btn" id="f2-b'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="boss-hint-txt" id="f2-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="f2-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('f2-b'+b.n+'-go');
const inp = document.getElementById('f2-b'+b.n+'-inp');
const fb = document.getElementById('f2-b'+b.n+'-fb');
const ht = document.getElementById('f2-b'+b.n+'-ht');
const hintBtn = document.getElementById('f2-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('f2-boss-'+b.n).classList.add('solved');
const s = loadState();
if(!s[b.n]){ s[b.n]=true; saveState(s); addXp(10,'f2-boss-'+b.n); bumpProgress('final2', 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>