Files
Learn_System/frontend/textbooks/physics_10_ch3.html
Maxim Dolgolyov 9d5a2959e1 fix(textbooks): кнопка «Шпаргалка» не открывала контент на desktop
На десктопе (>980px) .col-side уже видна как sticky-колонка справа в grid 1fr 280px.
Клик по кнопке #sidebar-btn добавлял .col-side-backdrop.show — backdrop с
z-index:9990 затемнял всю страницу, перекрывая sticky-aside. Со стороны
выглядело как «ничего не открылось» — на самом деле появлялась чёрная вуаль.

Фикс: @media(min-width:981px) скрывает #sidebar-btn и подавляет показ backdrop.
На мобайле (≤980px) кнопка и overlay работают как раньше.

Применено в 51 файле: physics 8/9/10 chN, algebra 7/9/10/11 chN + 8 ch2-3,
geometry 7/8/9/11 chN, geometry_10 r1-4.
2026-05-30 09:51:04 +03:00

4230 lines
291 KiB
HTML
Raw Permalink 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>Физика 10 · Глава 3 · «Электростатика»</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>
<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:#fafafa; --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:#6d28d9; --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,#3b0764 0%,#7c3aed 55%,#a78bfa 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(255,255,255,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 3';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,255,255,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
.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::before{content:'+q';position:absolute;right:0;top:-30px;font-size:clamp(2rem,12vw,8rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
.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-p16"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p17"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p18"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p19"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p20"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p21"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p22"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p23"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p24"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-final3"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.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::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.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)}
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(0,0,0,.10);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.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(min-width:981px){#sidebar-btn{display:none}.col-side-backdrop.show{display:none}}
@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}
/* === PHYS10 POLISH (visual + micro-interactions) === */
@keyframes wgFadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec.active .wg{animation:wgFadeIn .35s cubic-bezier(.16,1,.3,1) backwards}
.sec.active .wg:nth-of-type(1){animation-delay:.02s}
.sec.active .wg:nth-of-type(2){animation-delay:.08s}
.sec.active .wg:nth-of-type(3){animation-delay:.14s}
.sec.active .wg:nth-of-type(4){animation-delay:.20s}
.sec.active .wg:nth-of-type(5){animation-delay:.26s}
.sec.active .wg:nth-of-type(6){animation-delay:.32s}
.wg svg{transition:filter .25s ease}
.wg:hover svg{filter:drop-shadow(0 4px 16px rgba(0,0,0,.10))}
input[type=range]:active{box-shadow:0 0 0 4px var(--pri-soft);border-radius:8px}
.wg input[type=range]{cursor:ew-resize}
.score-display b{transition:transform .22s cubic-bezier(.16,1,.3,1),color .22s;display:inline-block;transform-origin:center}
.score-display b.bump{transform:scale(1.28);color:var(--pri)}
.katex{transition:color .2s}
.wg-help .katex:hover,.card-body .katex:hover{color:var(--pri2,var(--pri));cursor:help}
.hp-fill,.psel-prog-fill,.xp-fill,[id$="-overall-fill"]{transition:width .6s cubic-bezier(.16,1,.3,1)!important}
.boss-card,.btn.primary,.btn-primary{position:relative;overflow:hidden}
.btn.primary::after,.btn-primary::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at center,rgba(255,255,255,.42) 0%,transparent 60%);opacity:0;transition:opacity .25s;pointer-events:none}
.btn.primary:hover::after,.btn-primary:hover::after{opacity:1}
.psel-card{position:relative}
.psel-card .psel-done{position:absolute;top:6px;right:6px;width:18px;height:18px;border-radius:50%;background:#10b981;display:none;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(16,185,129,.45);z-index:2}
.psel-card .psel-done svg{width:11px;height:11px;stroke:#fff;fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.psel-card.done .psel-done{display:flex}
.boss-card{transition:border-color .35s,box-shadow .6s,background .3s,transform .2s}
.boss-card.glow{box-shadow:0 0 24px rgba(16,185,129,.6),0 0 0 2px rgba(16,185,129,.45)!important}
@keyframes bossPulse{0%{box-shadow:0 0 0 0 rgba(16,185,129,.55)}70%{box-shadow:0 0 0 14px rgba(16,185,129,0)}100%{box-shadow:0 0 0 0 rgba(16,185,129,0)}}
.boss-card.pulse{animation:bossPulse .8s ease-out}
.sec{transition:opacity .25s}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Физика 10 · Глава 3</h1>
<div class="hdr-sub">Заряд · Кулон · поле · потенциал · напряжение · конденсаторы</div>
</div>
<div class="hdr-side">
<a href="/textbook/physics-10" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К физике 10</a>
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Электростатика — поле неподвижных зарядов</h2>
<p>Электрический заряд создаёт поле. Изучаем закон Кулона, напряжённость и потенциал поля, конденсаторы и их энергию.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p16')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 16</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-p16" class="sec" data-watermark="q"><div class="sec-header"><span class="sec-num">§ 16</span><h2 class="sec-h">Электрический заряд</h2></div><div id="p16-body"></div></section>
<section id="sec-p17" class="sec" data-watermark="Кулон"><div class="sec-header"><span class="sec-num">§ 17</span><h2 class="sec-h">Закон Кулона</h2></div><div id="p17-body"></div></section>
<section id="sec-p18" class="sec" data-watermark="&vec;E"><div class="sec-header"><span class="sec-num">§ 18</span><h2 class="sec-h">Электростатическое поле</h2></div><div id="p18-body"></div></section>
<section id="sec-p19" class="sec" data-watermark="E"><div class="sec-header"><span class="sec-num">§ 19</span><h2 class="sec-h">Напряжённость поля. Принцип суперпозиции</h2></div><div id="p19-body"></div></section>
<section id="sec-p20" class="sec" data-watermark="&rarr;"><div class="sec-header"><span class="sec-num">§ 20</span><h2 class="sec-h">Линии напряжённости</h2></div><div id="p20-body"></div></section>
<section id="sec-p21" class="sec" data-watermark="A=qU"><div class="sec-header"><span class="sec-num">§ 21</span><h2 class="sec-h">Работа поля. Потенциал</h2></div><div id="p21-body"></div></section>
<section id="sec-p22" class="sec" data-watermark="U"><div class="sec-header"><span class="sec-num">§ 22</span><h2 class="sec-h">Разность потенциалов. Напряжение</h2></div><div id="p22-body"></div></section>
<section id="sec-p23" class="sec" data-watermark="C"><div class="sec-header"><span class="sec-num">§ 23</span><h2 class="sec-h">Конденсаторы</h2></div><div id="p23-body"></div></section>
<section id="sec-p24" class="sec" data-watermark="CU&sup2;"><div class="sec-header"><span class="sec-num">§ 24</span><h2 class="sec-h">Энергия поля конденсатора</h2></div><div id="p24-body"></div></section>
<section id="sec-final3" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#7c3aed,#a78bfa)"></span><h2 class="sec-h">Финал главы</h2></div><div id="final3-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">Интерактивный учебник «Физика 10» · Глава 3 · «Электростатика» · 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:'p16', progress:{}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 10;
const _TB_SLUG = 'physics-10-ch3';
const PARAS = [
{ id:'p16', num:'\u00a7 16', name:"Электрический заряд", sub:'$q = ne$' },
{ id:'p17', num:'\u00a7 17', name:"Закон Кулона", sub:'$F = k\\dfrac{q_1 q_2}{r^2}$' },
{ id:'p18', num:'\u00a7 18', name:"Электростатическое поле", sub:'$\\vec{E}$' },
{ id:'p19', num:'\u00a7 19', name:"Напряжённость поля. Принцип суперпозиции", sub:'$\\vec{E} = \\sum \\vec{E_i}$' },
{ id:'p20', num:'\u00a7 20', name:"Линии напряжённости", sub:'Силовые линии' },
{ id:'p21', num:'\u00a7 21', name:"Работа поля. Потенциал", sub:'$A = qU$' },
{ id:'p22', num:'\u00a7 22', name:"Разность потенциалов. Напряжение", sub:'$U = E \\cdot d$' },
{ id:'p23', num:'\u00a7 23', name:"Конденсаторы", sub:'$C = q/U$' },
{ id:'p24', num:'\u00a7 24', name:"Энергия поля конденсатора", sub:'$W = CU^2/2$' },
{ id:'final3', num:'\u2605', name:'Финал главы', sub:'Итоги \u00b7 боссы главы 3', final:true }
];
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
const ACH_LABELS = {
start:"Начало главы 3!",
p16_done:"Электрический заряд освоен!",
p17_done:"Закон Кулона освоен!",
p18_done:"Электростатическое поле освоен!",
p19_done:"Напряжённость поля. Принцип суперпозиции освоен!",
p20_done:"Линии напряжённости освоен!",
p21_done:"Работа поля. Потенциал освоен!",
p22_done:"Разность потенциалов. Напряжение освоен!",
p23_done:"Конденсаторы освоен!",
p24_done:"Энергия поля конденсатора освоен!",
ch3_done:"Глава 3 пройдена!"
};
function loadProgress(){
try{
const s=localStorage.getItem('physics10_ch3_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('physics10_ch3_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('physics10_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('physics10_ch3_progress', JSON.stringify(STATE.progress));
localStorage.setItem('physics10_ch3_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('physics10_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,'physics10-ch3-'+(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();
const BUILDERS = { 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(), final3:()=>build_final3() };
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);
}
const SIDEBARS = {
p16:{title:"Шпаргалка §16",rows:[["Заряд","$q$ — Кл"],["Электрон","$e = 1{,}6 \\cdot 10^{-19}$ Кл"],["Закон сохр.","$\\sum q = \\text{const}$ в замкн. системе"],["$q = ne$","дискретность"]]},
p17:{title:"Шпаргалка §17",rows:[["Закон","$F = k\\dfrac{|q_1 q_2|}{r^2}$"],["$k$","$k = 9 \\cdot 10^9$ Н·м²/Кл²"],["$\\varepsilon_0$","$\\varepsilon_0 = 8{,}85 \\cdot 10^{-12}$ Ф/м"]]},
p18:{title:"Шпаргалка §18",rows:[["Поле","посредник взаимодействия"],["Свойства","действует на заряд силой $\\vec{F}$"],["Источник","$+q$ или $-q$"]]},
p19:{title:"Шпаргалка §19",rows:[["$\\vec{E}$","$\\vec{E} = \\vec{F}/q_0$"],["Точ. заряд","$E = k|q|/r^2$"],["Суперпоз.","$\\vec{E} = \\sum \\vec{E_i}$"]]},
p20:{title:"Шпаргалка §20",rows:[["Линии","касат. — $\\vec{E}$"],["Густота","$E$ велико — линии чаще"],["$+ \\to -$","начало на $+$, конец на $-$ или $\\infty$"]]},
p21:{title:"Шпаргалка §21",rows:[["Работа поля","$A_{поля} = qU$ — не зависит от пути"],["Потенц.","$\\varphi = W_p/q$"],["Знак","от $+$ к $-$ ток. зар. — $A > 0$"]]},
p22:{title:"Шпаргалка §22",rows:[["Разность","$U = \\varphi_1 - \\varphi_2$"],["Однор. поле","$U = Ed$"],["Эквипот.","$\\bot$ линиям $\\vec{E}$"]]},
p23:{title:"Шпаргалка §23",rows:[["$C = q/U$","Ф (фарад)"],["Плоский","$C = \\varepsilon\\varepsilon_0 S/d$"],["Парал.","$C = \\sum C_i$"],["Послед.","$1/C = \\sum 1/C_i$"]]},
p24:{title:"Шпаргалка §24",rows:[["Энергия","$W = \\tfrac{CU^2}{2} = \\tfrac{q^2}{2C} = \\tfrac{qU}{2}$"],["Плотн.","$w = \\tfrac{\\varepsilon\\varepsilon_0 E^2}{2}$"]]},
final3:{title:"Финал главы 3",rows:[["§§1624","теория главы 3"],["Награда","+50 XP"]]}
};
const TIPS=[
{sec:'p16',html:"Заряд квантуется: $q = ne$, где $e = 1{,}6 \\cdot 10^{-19}$ Кл. Закон сохранения заряда — фундаментальный."},
{sec:'p17',html:"$F = k\\dfrac{|q_1 q_2|}{r^2}$, $k = 9 \\cdot 10^9$ Н·м²/Кл². Аналог закона всемирного тяготения."},
{sec:'p18',html:"Поле — посредник взаимодействия. Действует на заряд силой $\\vec{F} = q\\vec{E}$."},
{sec:'p19',html:"$\\vec{E} = \\vec{F}/q_0$ — векторная характеристика поля. Принцип суперпозиции: $\\vec{E} = \\sum \\vec{E_i}$."},
{sec:'p20',html:"Линии напряжённости — касательные к $\\vec{E}$. Начинаются на «+» зарядах, заканчиваются на «−» или в $\\infty$."},
{sec:'p21',html:"Работа поля не зависит от пути: $A_{поля} = qU = q(\\varphi_1 - \\varphi_2)$. Поле потенциально."},
{sec:'p22',html:"В однородном поле: $U = E \\cdot d$. Эквипотенциальные поверхности перпендикулярны линиям $\\vec{E}$."},
{sec:'p23',html:"$C = q/U$ — Ф. Плоский: $C = \\dfrac{\\varepsilon\\varepsilon_0 S}{d}$. Параллельно — $C_\\Sigma = \\sum C_i$."},
{sec:'p24',html:"$W = \\dfrac{CU^2}{2} = \\dfrac{q^2}{2C} = \\dfrac{qU}{2}$ — три эквивалентные формулы."},
{sec:'final3',html:"Финал главы 3 — интегрированные задачи по §§16–24. В разработке (Phase 3+)."}
];
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('physics10_ch3_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('physics10_ch3_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>';
}
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(); }};
}
/* === SVG-хелперы (axes2D, plotFunc, pointWithDrop, asymptote, snapToValue, геом.) === */
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){
const NAMES = {p16:'\xA716',p17:'\xA717',p18:'\xA718',p19:'\xA719',p20:'\xA720',p21:'\xA721',p22:'\xA722',p23:'\xA723',p24:'\xA724',final3:'Финал'};
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> '+NAMES[prev]+'</button>':'<span></span>';
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[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 : '\xA7?');
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>'
+' Я прочитал — '+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);
});
}
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
function build_p16(){
const box = document.getElementById('p16-body');
let html = '';
/* THEORY 1 — Электрический заряд и виды */
html += makeCard('theory', "Электрический заряд и виды", "§16", `
<p><b>Электрический заряд</b> $q$ — физическая величина, характеризующая способность тел и частиц участвовать в электромагнитных взаимодействиях.</p>
<p style="margin-top:8px">Бывает <b>двух видов</b>: положительный (+) и отрицательный (&minus;).</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b style="color:#dc2626">Одноимённые</b> заряды <b>отталкиваются</b>.</li>
<li><b style="color:#2563eb">Разноимённые</b> заряды <b>притягиваются</b>.</li>
</ul>
<p><b>Элементарный заряд</b> — наименьший существующий в природе модуль заряда:</p>
<p style="text-align:center;margin:10px 0">$$e = 1{,}6 \\cdot 10^{-19} \\text{ Кл}$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Электрон</b>: заряд $-e$, масса $\\sim 9{,}1 \\cdot 10^{-31}$ кг.</li>
<li><b>Протон</b>: заряд $+e$, масса $\\sim 1{,}67 \\cdot 10^{-27}$ кг (в 1836 раз больше электрона).</li>
<li><b>Нейтрон</b>: заряд $0$.</li>
</ul>
<p>Любой макроскопический заряд <b>кратен элементарному</b>: $q = ne$, где $n$ — целое число.</p>
`);
/* THEORY 2 — Закон сохранения заряда */
html += makeCard('rule', "Закон сохранения заряда", "§16", `
<p><b>Закон сохранения электрического заряда</b>: в любой замкнутой (изолированной) системе алгебраическая сумма электрических зарядов сохраняется:</p>
<p style="text-align:center;margin:10px 0">$$\\sum q_i = \\text{const}$$</p>
<p>Заряды могут <b>перераспределяться</b> между телами, но общий заряд не меняется.</p>
<p style="margin-top:8px"><b>Способы электризации</b> (передачи заряда):</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Трением</b>: например, расчёска из пластика трением о волосы.</li>
<li><b>Прикосновением</b>: заряженное тело передаёт часть заряда нейтральному.</li>
<li><b>Через индукцию</b>: заряженное тело наводит противоположный заряд на близлежащее проводящее тело (без касания).</li>
</ul>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0"><b>Пример:</b> два одинаковых металлических шара. Один заряжен зарядом $q$, другой нейтрален. После соприкосновения заряд распределится поровну: на каждом по $q/2$.</p>
`);
/* THEORY 3 — Единица заряда */
html += makeCard('example', "Единица заряда и примеры", "§16", `
<p><b>Единица заряда</b> в СИ — <b>Кулон</b> (Кл). Назван в честь Шарля Кулона.</p>
<p style="margin-top:8px">$1$ Кл — заряд, эквивалентный $1/e \\approx 6{,}25 \\cdot 10^{18}$ элементарных зарядов.</p>
<p>Это <b>очень большой заряд</b>! В реальных задачах часто используются:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>мкКл</b> (микроКулон) $= 10^{-6}$ Кл</li>
<li><b>нКл</b> (наноКулон) $= 10^{-9}$ Кл</li>
<li><b>пКл</b> (пикоКулон) $= 10^{-12}$ Кл</li>
</ul>
<p><b>Примеры зарядов</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Заряд электрона: $-1{,}6 \\cdot 10^{-19}$ Кл.</li>
<li>Заряд натёртой стеклянной палочки: $\\sim 10^{-7}-10^{-9}$ Кл.</li>
<li>Заряд молнии: $\\sim 10-100$ Кл.</li>
<li>Заряд при касании дверной ручки в сухом помещении: пара нКл, но напряжение огромно!</li>
</ul>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0"><b>Точечный заряд</b> — модель: заряженное тело, размерами которого можно пренебречь по сравнению с расстояниями до других объектов.</p>
`);
/* INTERACTIVE 1 — Симуляция передачи заряда */
html += `<div class="wg" id="p16-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Закон сохранения заряда: симуляция</div></div>
<div class="wg-help">Два одинаковых металлических шара. Задай заряды и нажми «Прикоснуть» — заряды распределятся поровну. Сумма $q_1 + q_2$ сохранится!</div>
<div class="sliders">
<label>$q_1$: <b id="p16-iv1-q1L">+5</b> нКл <input type="range" id="p16-iv1-q1" min="-10" max="10" value="5" step="1"></label>
<label>$q_2$: <b id="p16-iv1-q2L">-3</b> нКл <input type="range" id="p16-iv1-q2" min="-10" max="10" value="-3" step="1"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p16-iv1-svg" viewBox="0 0 380 220" width="100%" style="height:auto"></svg>
</div>
<div class="actions" style="justify-content:center;margin-top:10px">
<button class="btn primary" id="p16-iv1-touch">Прикоснуть шары</button>
<button class="btn" id="p16-iv1-reset">Сначала</button>
</div>
<div id="p16-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
<div class="feedback" id="p16-iv1-fb"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор электронов */
html += `<div class="wg" id="p16-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор числа электронов</div></div>
<div class="wg-help">Введи заряд $q$ — получи число электронов $|n| = |q|/e$ (избыток при $q<0$, недостаток при $q>0$).</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<label>$q =$ <input type="number" id="p16-iv2-q" class="tinp" style="width:120px;text-align:center" value="-1" step="any"></label>
<select id="p16-iv2-unit" class="tinp" style="padding:8px 10px">
<option value="1e-9">нКл</option>
<option value="1e-6">мкКл</option>
<option value="1e-12">пКл</option>
<option value="1">Кл</option>
</select>
<button class="btn primary" id="p16-iv2-go">Вычислить</button>
</div>
<div id="p16-iv2-out" style="padding:12px 14px;background:var(--card);border:1px solid var(--border);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p16-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — квикфайр «Притяжение или отталкивание?» */
html += `<div class="wg" id="p16-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Притяжение или отталкивание?</div></div>
<div class="wg-help">6 ситуаций — выбери, что произойдёт между телами.</div>
<div class="score-display"><span>Задача <b id="p16-iv3-i">1</b> / 6</span><span>Очки: <b id="p16-iv3-s">0</b> / 6</span></div>
<div id="p16-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p16-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p16-iv3-fb"></div>
<div class="actions"><button class="btn" id="p16-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр заряда */
html += `<div class="wg" id="p16-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр заряда</div></div>
<div class="wg-help">5 задач. $e = 1{,}6 \\cdot 10^{-19}$ Кл. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p16-iv4-i">1</b> / 5</span><span>Очки: <b id="p16-iv4-s">0</b> / 5</span></div>
<div id="p16-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p16-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p16-iv4-go">Проверить</button>
<button class="btn" id="p16-iv4-start">Заново</button>
</div>
<div class="feedback" id="p16-iv4-fb"></div>
</div>`;
html += secNav(null, 'p17');
html += readButton('p16');
box.innerHTML = html;
renderMath(box);
/* IV1 — Симуляция передачи заряда */
(function(){
const svg = document.getElementById('p16-iv1-svg');
const q1S = document.getElementById('p16-iv1-q1');
const q2S = document.getElementById('p16-iv1-q2');
const q1L = document.getElementById('p16-iv1-q1L');
const q2L = document.getElementById('p16-iv1-q2L');
const out = document.getElementById('p16-iv1-out');
const fb = document.getElementById('p16-iv1-fb');
let q1 = +q1S.value, q2 = +q2S.value;
const seen = new Set();
let _done = false;
function fmtQ(q){ return (q>=0?'+':'') + q; }
function render(){
const W=380, H=220;
// Координаты шаров
const cx1 = 110, cx2 = 270, cy = 120;
const r = 30;
let g = '';
// Подставка
g += '<line x1="60" y1="180" x2="320" y2="180" stroke="#94a3b8" stroke-width="2"/>';
g += '<line x1="' + cx1 + '" y1="' + (cy + r) + '" x2="' + cx1 + '" y2="180" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="3 3"/>';
g += '<line x1="' + cx2 + '" y1="' + (cy + r) + '" x2="' + cx2 + '" y2="180" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="3 3"/>';
// Шары
g += PHYS.chargeMark(cx1, cy, q1 === 0 ? 1 : (q1 > 0 ? 1 : -1), r, '');
if(q1 === 0){
// overlay серый круг для нейтрального
g += '<circle cx="' + cx1 + '" cy="' + cy + '" r="' + r + '" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="' + cx1 + '" y="' + (cy + 4) + '" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
}
g += PHYS.chargeMark(cx2, cy, q2 === 0 ? 1 : (q2 > 0 ? 1 : -1), r, '');
if(q2 === 0){
g += '<circle cx="' + cx2 + '" cy="' + cy + '" r="' + r + '" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="' + cx2 + '" y="' + (cy + 4) + '" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
}
// Подписи
g += '<text x="' + cx1 + '" y="' + (cy + r + 22) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">q₁ = ' + fmtQ(q1) + ' нКл</text>';
g += '<text x="' + cx2 + '" y="' + (cy + r + 22) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">q₂ = ' + fmtQ(q2) + ' нКл</text>';
// Заголовок
g += '<text x="190" y="30" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Два металлических шара</text>';
svg.innerHTML = g;
const sum = q1 + q2;
out.innerHTML = '<b>Сумма зарядов:</b> $q_1 + q_2 = ' + fmtQ(q1) + ' + (' + fmtQ(q2) + ') = ' + fmtQ(sum) + '$ нКл (сохраняется!)';
renderMath(out);
}
function sync(){
q1 = +q1S.value; q2 = +q2S.value;
q1L.textContent = fmtQ(q1); q2L.textContent = fmtQ(q2);
render();
}
q1S.addEventListener('input', sync);
q2S.addEventListener('input', sync);
document.getElementById('p16-iv1-touch').addEventListener('click', () => {
const sum = q1 + q2;
const avg = sum / 2;
q1 = avg; q2 = avg;
q1S.value = q1; q2S.value = q2;
q1L.textContent = fmtQ(q1); q2L.textContent = fmtQ(q2);
render();
feedback(fb, true, '&#10003; После касания: $q_1 = q_2 = (q_1 + q_2)/2 = ' + fmtQ(avg) + '$ нКл. Сумма сохранилась!');
const key = (+q1S.value) + ':' + (+q2S.value);
seen.add(key);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p16-iv1'); bumpProgress('p16', 15); }
});
document.getElementById('p16-iv1-reset').addEventListener('click', () => {
q1 = 5; q2 = -3; q1S.value = 5; q2S.value = -3;
q1L.textContent = '+5'; q2L.textContent = '-3';
fb.style.display = 'none';
render();
});
sync();
})();
/* IV2 — Калькулятор электронов */
(function(){
const qI = document.getElementById('p16-iv2-q');
const uI = document.getElementById('p16-iv2-unit');
const out = document.getElementById('p16-iv2-out');
const fb = document.getElementById('p16-iv2-fb');
const seen = new Set();
let _done = false;
const e = PHYS.CONST.e;
function calc(){
const qv = parseFloat(qI.value);
if(!isFinite(qv)){ feedback(fb, false, '&#10007; Введи число.'); return; }
const unit = parseFloat(uI.value);
const qC = qv * unit;
const n = Math.abs(qC) / e;
const exp = Math.floor(Math.log10(n));
const coef = n / Math.pow(10, exp);
const sign = qC < 0 ? '<b style="color:#2563eb">избыток</b>' : (qC > 0 ? '<b style="color:#dc2626">недостаток</b>' : '<b>нет</b>');
const qExp = qC === 0 ? 0 : Math.floor(Math.log10(Math.abs(qC)));
const qCoef = qC === 0 ? 0 : qC / Math.pow(10, qExp);
out.innerHTML = '<div style="margin-bottom:6px"><b>Заряд в Кл:</b> $q = ' + (qC === 0 ? '0' : (+qCoef.toFixed(3) + ' \\cdot 10^{' + qExp + '}')) + '$ Кл</div>'
+ '<div style="margin-bottom:6px"><b>Число электронов:</b> $|n| = \\dfrac{|q|}{e} = \\dfrac{' + Math.abs(qC).toExponential(2).replace('e','\\cdot 10^{') + '}}{1{,}6 \\cdot 10^{-19}} \\approx ' + (+coef.toFixed(2)) + ' \\cdot 10^{' + exp + '}$</div>'
+ '<div>' + sign + ' электронов (' + (qC < 0 ? 'заряд отрицательный' : (qC > 0 ? 'заряд положительный' : 'нейтрально')) + ').</div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
seen.add((+qv.toFixed(3)) + ':' + uI.value);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p16-iv2'); bumpProgress('p16', 15); }
}
document.getElementById('p16-iv2-go').addEventListener('click', calc);
qI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
})();
/* IV3 — квикфайр Притяжение/Отталкивание */
(function(){
const Q = [
{ q:'Два положительно заряженных шарика рядом. Что происходит?', a:'Притяжение', b:'Отталкивание', ans:1, why:'Одноимённые ($+$ и $+$) отталкиваются.' },
{ q:'Положительный заряд и отрицательный заряд рядом.', a:'Притяжение', b:'Отталкивание', ans:0, why:'Разноимённые ($+$ и $-$) притягиваются.' },
{ q:'Два отрицательно заряженных тела рядом.', a:'Притяжение', b:'Отталкивание', ans:1, why:'Одноимённые ($-$ и $-$) отталкиваются.' },
{ q:'Заряженная стеклянная палочка подносится к нейтральному бумажному шарику (без касания).', a:'Притяжение (через индукцию)', b:'Отталкивание', ans:0, why:'На нейтральном шарике наводится противоположный заряд — индукция вызывает притяжение.' },
{ q:'Электрон и протон.', a:'Притяжение', b:'Отталкивание', ans:0, why:'Электрон $-e$, протон $+e$ — разноимённые, притягиваются.' },
{ q:'Два электрона.', a:'Притяжение', b:'Отталкивание', ans:1, why:'Оба отрицательны ($-e$, $-e$) — отталкиваются.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p16-iv3-q');
const oEl = document.getElementById('p16-iv3-opts');
const fb = document.getElementById('p16-iv3-fb');
const iEl = document.getElementById('p16-iv3-i');
const sEl = document.getElementById('p16-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p16-iv3'); bumpProgress('p16', 25); }
else if(score >= 4){ addXp(8, 'p16-iv3'); bumpProgress('p16', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = '<button class="btn primary" data-v="0">' + item.a + '</button><button class="btn primary" data-v="1">' + item.b + '</button>';
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1600);
});
});
}
document.getElementById('p16-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр заряда */
(function(){
const Q = [
{ q:'Сколько электронов нужно для заряда $-1$ нКл? Введи мантиссу при $10^9$.', ans:6.25, tol:0.3, hint:'$n = 10^{-9}/(1{,}6\\cdot 10^{-19}) = 6{,}25 \\cdot 10^9$.' },
{ q:'Два одинаковых шара с зарядами $+4$ нКл и $-6$ нКл соприкоснулись. Заряд каждого после (в нКл)?', ans:-1, tol:0.1, hint:'$(q_1+q_2)/2 = (4-6)/2 = -1$ нКл.' },
{ q:'Заряд $q = +3{,}2 \\cdot 10^{-19}$ Кл соответствует скольким элементарным?', ans:2, tol:0.1, hint:'$n = 3{,}2/1{,}6 = 2$.' },
{ q:'Заряженный шар отдал половину своего заряда. Если был $+10$ нКл, сколько осталось (в нКл)?', ans:5, tol:0.2, hint:'$10/2 = 5$ нКл.' },
{ q:'Заряд молнии $\\approx 30$ Кл. Сколько это электронов? Введи мантиссу при $10^{20}$.', ans:1.9, tol:0.3, hint:'$n = 30/(1{,}6 \\cdot 10^{-19}) \\approx 1{,}9 \\cdot 10^{20}$.' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p16-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p16-iv4'); bumpProgress('p16', 25); }
else if(score >= 3){ addXp(8, 'p16-iv4'); bumpProgress('p16', 15); }
return;
}
document.getElementById('p16-iv4-i').textContent = (i+1);
document.getElementById('p16-iv4-s').textContent = score;
document.getElementById('p16-iv4-q').innerHTML = Q[i].q;
document.getElementById('p16-iv4-ans').value = '';
renderMath(document.getElementById('p16-iv4-q'));
document.getElementById('p16-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p16-iv4-fb');
const raw = document.getElementById('p16-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p16-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p16-iv4-go').addEventListener('click', go);
document.getElementById('p16-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p16-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p16');
}
function build_p17(){
const box = document.getElementById('p17-body');
let html = '';
/* THEORY 1 — Закон Кулона */
html += makeCard('theory', "Закон Кулона", "§17", `
<p><b>Закон Кулона</b>: сила взаимодействия двух точечных зарядов прямо пропорциональна произведению модулей зарядов и обратно пропорциональна квадрату расстояния между ними:</p>
<p style="text-align:center;margin:10px 0">$$F = k\\,\\dfrac{|q_1 q_2|}{r^2}$$</p>
<p>где:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$F$ — сила (Н);</li>
<li>$q_1, q_2$ — заряды (Кл);</li>
<li>$r$ — расстояние между зарядами (м);</li>
<li>$k = 9 \\cdot 10^9$ Н$\\cdot$м$^2$/Кл$^2$ — коэффициент пропорциональности.</li>
</ul>
<p>Альтернативная форма:</p>
<p style="text-align:center;margin:10px 0">$$F = \\dfrac{1}{4\\pi\\varepsilon_0} \\cdot \\dfrac{|q_1 q_2|}{r^2}$$</p>
<p>где $\\varepsilon_0 = 8{,}85 \\cdot 10^{-12}$ Ф/м — <b>электрическая постоянная</b>.</p>
<p style="margin-top:8px"><b>Направление</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Одноимённые ($q_1 q_2 > 0$): силы направлены <b>от</b> другого заряда (отталкивание).</li>
<li>Разноимённые ($q_1 q_2 < 0$): силы направлены <b>к</b> другому заряду (притяжение).</li>
</ul>
<p>По 3-му закону Ньютона $\\vec{F}_{12} = -\\vec{F}_{21}$.</p>
`);
/* THEORY 2 — Закон Кулона в среде */
html += makeCard('rule', "Закон Кулона в среде", "§17", `
<p>В диэлектрической среде сила Кулона уменьшается в $\\varepsilon$ раз:</p>
<p style="text-align:center;margin:10px 0">$$F = k\\,\\dfrac{|q_1 q_2|}{\\varepsilon\\, r^2}$$</p>
<p>где $\\varepsilon$ — <b>относительная диэлектрическая проницаемость</b> среды (безразмерная).</p>
<p style="margin-top:8px"><b>Примеры</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Вакуум, воздух</b>: $\\varepsilon \\approx 1$.</li>
<li><b>Бензин</b>: $\\varepsilon \\approx 2$.</li>
<li><b>Стекло</b>: $\\varepsilon \\approx 5-10$.</li>
<li><b>Вода</b>: $\\varepsilon \\approx 81$ (сильное ослабление сил!).</li>
<li><b>Сегнетоэлектрики</b>: $\\varepsilon$ может достигать сотен и тысяч.</li>
</ul>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0">Это объясняет, почему соли и кислоты легко диссоциируют в воде: силы между ионами в воде в $81$ раз слабее, чем в вакууме.</p>
`);
/* THEORY 3 — Принцип суперпозиции */
html += makeCard('example', "Принцип суперпозиции и пример", "§17", `
<p><b>Принцип суперпозиции</b>: если на заряд $q$ действуют несколько других зарядов, результирующая сила равна <b>векторной сумме</b> сил от каждого заряда отдельно:</p>
<p style="text-align:center;margin:10px 0">$$\\vec{F} = \\vec{F_1} + \\vec{F_2} + \\vec{F_3} + \\ldots$$</p>
<p>Силы складываются как векторы (учитывая направления!).</p>
<p style="margin-top:10px"><b>Пример.</b> Два заряда по $+1$ мкКл расположены на расстоянии $0{,}1$ м. Найти силу.</p>
<p style="text-align:center;margin:10px 0">$$F = 9 \\cdot 10^9 \\cdot \\dfrac{(10^{-6})^2}{(0{,}1)^2} = 9 \\cdot 10^9 \\cdot \\dfrac{10^{-12}}{0{,}01} = 0{,}9 \\text{ Н}$$</p>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0"><b>Сравнение с гравитацией</b>: кулоновская сила между двумя протонами в $\\sim 10^{36}$ раз сильнее гравитации. В макромире доминирует электромагнетизм; гравитация важна только потому, что положительные и отрицательные заряды в обычных телах в среднем уравновешиваются.</p>
`);
/* INTERACTIVE 1 — Визуализатор закона Кулона (главный) */
html += `<div class="wg" id="p17-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Визуализатор закона Кулона</div></div>
<div class="wg-help">Меняй заряды и расстояние — стрелки покажут силы, формула $F = k|q_1 q_2|/r^2$ пересчитается.</div>
<div class="sliders">
<label>$q_1$: <b id="p17-iv1-q1L">+5</b> нКл <input type="range" id="p17-iv1-q1" min="-10" max="10" value="5" step="0.5"></label>
<label>$q_2$: <b id="p17-iv1-q2L">+5</b> нКл <input type="range" id="p17-iv1-q2" min="-10" max="10" value="5" step="0.5"></label>
<label>$r$: <b id="p17-iv1-rL">0.20</b> м <input type="range" id="p17-iv1-r" min="0.05" max="0.5" value="0.2" step="0.01"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p17-iv1-svg" viewBox="0 0 480 260" width="100%" style="height:auto"></svg>
</div>
<div id="p17-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор силы Кулона */
html += `<div class="wg" id="p17-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор силы Кулона</div></div>
<div class="wg-help">Введи заряды, расстояние, выбери среду. $F = k|q_1 q_2|/(\\varepsilon r^2)$, $k = 9 \\cdot 10^9$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-bottom:10px">
<label style="display:flex;flex-direction:column;gap:4px;font-size:.86rem">$q_1$
<div style="display:flex;gap:4px">
<input type="number" id="p17-iv2-q1" class="tinp" style="width:80px;text-align:center" value="1" step="any">
<select id="p17-iv2-u1" class="tinp" style="padding:4px"><option value="1e-9">нКл</option><option value="1e-6" selected>мкКл</option><option value="1">Кл</option></select>
</div>
</label>
<label style="display:flex;flex-direction:column;gap:4px;font-size:.86rem">$q_2$
<div style="display:flex;gap:4px">
<input type="number" id="p17-iv2-q2" class="tinp" style="width:80px;text-align:center" value="1" step="any">
<select id="p17-iv2-u2" class="tinp" style="padding:4px"><option value="1e-9">нКл</option><option value="1e-6" selected>мкКл</option><option value="1">Кл</option></select>
</div>
</label>
<label style="display:flex;flex-direction:column;gap:4px;font-size:.86rem">$r$
<div style="display:flex;gap:4px">
<input type="number" id="p17-iv2-r" class="tinp" style="width:80px;text-align:center" value="10" step="any">
<select id="p17-iv2-ur" class="tinp" style="padding:4px"><option value="0.01">см</option><option value="1" selected>м</option></select>
</div>
</label>
<label style="display:flex;flex-direction:column;gap:4px;font-size:.86rem">$\\varepsilon$
<select id="p17-iv2-eps" class="tinp" style="padding:6px">
<option value="1" selected>1 (воздух)</option>
<option value="2">2 (бензин)</option>
<option value="7">7 (стекло)</option>
<option value="81">81 (вода)</option>
</select>
</label>
</div>
<div class="actions" style="justify-content:center"><button class="btn primary" id="p17-iv2-go">Вычислить $F$</button></div>
<div id="p17-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border:1px solid var(--border);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p17-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — квикфайр «Как изменится сила?» */
html += `<div class="wg" id="p17-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Как изменится сила?</div></div>
<div class="wg-help">6 ситуаций — как изменится сила Кулона? 4 варианта ответа.</div>
<div class="score-display"><span>Задача <b id="p17-iv3-i">1</b> / 6</span><span>Очки: <b id="p17-iv3-s">0</b> / 6</span></div>
<div id="p17-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p17-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p17-iv3-fb"></div>
<div class="actions"><button class="btn" id="p17-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр Кулона */
html += `<div class="wg" id="p17-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр закона Кулона</div></div>
<div class="wg-help">5 задач. $k = 9 \\cdot 10^9$ Н$\\cdot$м$^2$/Кл$^2$. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p17-iv4-i">1</b> / 5</span><span>Очки: <b id="p17-iv4-s">0</b> / 5</span></div>
<div id="p17-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p17-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p17-iv4-go">Проверить</button>
<button class="btn" id="p17-iv4-start">Заново</button>
</div>
<div class="feedback" id="p17-iv4-fb"></div>
</div>`;
html += secNav('p16', 'p18');
html += readButton('p17');
box.innerHTML = html;
renderMath(box);
/* IV1 — Главный визуализатор закона Кулона */
(function(){
const svg = document.getElementById('p17-iv1-svg');
const q1S = document.getElementById('p17-iv1-q1');
const q2S = document.getElementById('p17-iv1-q2');
const rS = document.getElementById('p17-iv1-r');
const q1L = document.getElementById('p17-iv1-q1L');
const q2L = document.getElementById('p17-iv1-q2L');
const rL = document.getElementById('p17-iv1-rL');
const out = document.getElementById('p17-iv1-out');
const seen = new Set();
let _done = false;
const k = PHYS.CONST.k;
function fmtQ(q){ return (q>=0?'+':'') + q.toFixed(1); }
function render(){
const q1 = +q1S.value, q2 = +q2S.value, r = +rS.value;
q1L.textContent = fmtQ(q1); q2L.textContent = fmtQ(q2); rL.textContent = r.toFixed(2);
const W = 480, H = 260;
const cy = 130;
// Картина: q1 слева, q2 справа. Минимальное расстояние пикселями для наглядности
const minPx = 90, maxPx = 360;
// Линейно: r=0.05 -> minPx, r=0.5 -> maxPx
const pxR = minPx + (r - 0.05) / (0.5 - 0.05) * (maxPx - minPx);
const cx1 = (W - pxR) / 2;
const cx2 = cx1 + pxR;
const radius = 22;
let g = '';
// Линия расстояния
g += '<line x1="' + cx1 + '" y1="' + (cy - 50) + '" x2="' + cx2 + '" y2="' + (cy - 50) + '" stroke="#94a3b8" stroke-width="1.3" stroke-dasharray="4 3"/>';
g += '<line x1="' + cx1 + '" y1="' + (cy - 55) + '" x2="' + cx1 + '" y2="' + (cy - 45) + '" stroke="#94a3b8" stroke-width="1.5"/>';
g += '<line x1="' + cx2 + '" y1="' + (cy - 55) + '" x2="' + cx2 + '" y2="' + (cy - 45) + '" stroke="#94a3b8" stroke-width="1.5"/>';
g += '<text x="' + ((cx1+cx2)/2) + '" y="' + (cy - 58) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0f172a">r = ' + r.toFixed(2) + ' м</text>';
// Заряды
g += PHYS.chargeMark(cx1, cy, q1 === 0 ? 1 : (q1 > 0 ? 1 : -1), radius, '');
if(q1 === 0){
g += '<circle cx="' + cx1 + '" cy="' + cy + '" r="' + radius + '" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="' + cx1 + '" y="' + (cy + 4) + '" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
}
g += PHYS.chargeMark(cx2, cy, q2 === 0 ? 1 : (q2 > 0 ? 1 : -1), radius, '');
if(q2 === 0){
g += '<circle cx="' + cx2 + '" cy="' + cy + '" r="' + radius + '" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="' + cx2 + '" y="' + (cy + 4) + '" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
}
// Подписи зарядов
g += '<text x="' + cx1 + '" y="' + (cy + radius + 22) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">q₁ = ' + fmtQ(q1) + ' нКл</text>';
g += '<text x="' + cx2 + '" y="' + (cy + radius + 22) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">q₂ = ' + fmtQ(q2) + ' нКл</text>';
// Расчёт силы (в Н). Заряды в нКл -> Кл
const qC1 = q1 * 1e-9, qC2 = q2 * 1e-9;
const F = k * Math.abs(qC1 * qC2) / (r * r);
// Преобразование в мН/мкН
let Fdisp, Funit;
if(F >= 1e-3){ Fdisp = (F * 1000).toFixed(2); Funit = 'мН'; }
else if(F >= 1e-6){ Fdisp = (F * 1e6).toFixed(2); Funit = 'мкН'; }
else { Fdisp = F.toExponential(2); Funit = 'Н'; }
// Стрелки силы
const prod = q1 * q2;
let kind;
if(prod === 0){
kind = 'Один из зарядов = 0: сила отсутствует';
} else {
const repulse = prod > 0;
kind = repulse ? '<b style="color:#dc2626">Одноимённые: отталкиваются</b>' : '<b style="color:#10b981">Разноимённые: притягиваются</b>';
// Длина стрелки пропорциональна F (нормируем)
const maxArrowLen = 60;
// Нормировка относительно F при q1=q2=10 нКл, r=0.05м
const Fref = k * (10e-9 * 10e-9) / (0.05 * 0.05);
let arrowLen = Math.min(maxArrowLen, Math.sqrt(F / Fref) * maxArrowLen);
arrowLen = Math.max(15, arrowLen);
if(repulse){
// q1 толкается влево, q2 — вправо
g += PHYS.drawArrow(cx1 - 4, cy, cx1 - 4 - arrowLen, cy, '#dc2626', 3, 11);
g += PHYS.drawArrow(cx2 + 4, cy, cx2 + 4 + arrowLen, cy, '#dc2626', 3, 11);
g += '<text x="' + (cx1 - 4 - arrowLen - 6) + '" y="' + (cy - 8) + '" text-anchor="end" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#dc2626">F</text>';
g += '<text x="' + (cx2 + 4 + arrowLen + 6) + '" y="' + (cy - 8) + '" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#dc2626">F</text>';
} else {
// q1 тянется вправо, q2 — влево
g += PHYS.drawArrow(cx1 + 4, cy, cx1 + 4 + arrowLen, cy, '#10b981', 3, 11);
g += PHYS.drawArrow(cx2 - 4, cy, cx2 - 4 - arrowLen, cy, '#10b981', 3, 11);
g += '<text x="' + (cx1 + 4 + arrowLen + 6) + '" y="' + (cy - 8) + '" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#10b981">F</text>';
g += '<text x="' + (cx2 - 4 - arrowLen - 6) + '" y="' + (cy - 8) + '" text-anchor="end" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#10b981">F</text>';
}
}
// Заголовок
g += '<text x="240" y="28" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Закон Кулона: F = k|q₁q₂|/r²</text>';
svg.innerHTML = g;
out.innerHTML = '<div>' + kind + '</div>'
+ '<div style="margin-top:6px"><b>Сила:</b> $F = k\\dfrac{|q_1 q_2|}{r^2} = ' + Fdisp + '$ ' + Funit + '</div>';
renderMath(out);
seen.add(q1.toFixed(1) + ':' + q2.toFixed(1) + ':' + r.toFixed(2));
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p17-iv1'); bumpProgress('p17', 15); }
}
q1S.addEventListener('input', render);
q2S.addEventListener('input', render);
rS.addEventListener('input', render);
render();
})();
/* IV2 — Калькулятор силы Кулона */
(function(){
const out = document.getElementById('p17-iv2-out');
const fb = document.getElementById('p17-iv2-fb');
const seen = new Set();
let _done = false;
const k = PHYS.CONST.k;
function calc(){
const q1 = parseFloat(document.getElementById('p17-iv2-q1').value);
const q2 = parseFloat(document.getElementById('p17-iv2-q2').value);
const r = parseFloat(document.getElementById('p17-iv2-r').value);
const u1 = parseFloat(document.getElementById('p17-iv2-u1').value);
const u2 = parseFloat(document.getElementById('p17-iv2-u2').value);
const ur = parseFloat(document.getElementById('p17-iv2-ur').value);
const eps = parseFloat(document.getElementById('p17-iv2-eps').value);
if(!isFinite(q1) || !isFinite(q2)){ feedback(fb, false, '&#10007; Введи заряды.'); return; }
if(!isFinite(r) || r <= 0){ feedback(fb, false, '&#10007; Расстояние должно быть положительным.'); return; }
const qC1 = q1 * u1, qC2 = q2 * u2, rM = r * ur;
const F = k * Math.abs(qC1 * qC2) / (eps * rM * rM);
// подача в подходящих единицах
let Fdisp, Funit;
if(F >= 1){ Fdisp = (+F.toFixed(3)); Funit = 'Н'; }
else if(F >= 1e-3){ Fdisp = (+(F*1000).toFixed(3)); Funit = 'мН'; }
else if(F >= 1e-6){ Fdisp = (+(F*1e6).toFixed(3)); Funit = 'мкН'; }
else { Fdisp = F.toExponential(3); Funit = 'Н'; }
const epsStr = eps === 1 ? '' : '\\varepsilon';
const epsForm = eps === 1 ? '' : (' \\cdot \\dfrac{1}{' + eps + '}');
out.innerHTML = '<div style="margin-bottom:6px"><b>В СИ:</b> $q_1 = ' + qC1.toExponential(2) + '$ Кл, $q_2 = ' + qC2.toExponential(2) + '$ Кл, $r = ' + rM + '$ м'+(eps===1?'':', $\\varepsilon = '+eps+'$')+'</div>'
+ '<div style="margin-bottom:6px"><b>Подстановка:</b> $F = \\dfrac{9 \\cdot 10^9 \\cdot |' + qC1.toExponential(1) + ' \\cdot ' + qC2.toExponential(1) + '|}{' + (eps===1?'':eps+' \\cdot ') + (rM*rM).toExponential(2) + '}$</div>'
+ '<div><b>Сила:</b> $F \\approx ' + Fdisp + '$ ' + Funit + '</div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
seen.add(qC1.toExponential(1) + ':' + qC2.toExponential(1) + ':' + rM + ':' + eps);
if(!_done && seen.size >= 3){ _done = true; addXp(10, 'p17-iv2'); bumpProgress('p17', 15); }
}
document.getElementById('p17-iv2-go').addEventListener('click', calc);
})();
/* IV3 — квикфайр «Как изменится сила?» */
(function(){
// 4 опции на каждый вопрос
const OPTS = ['Увеличится в 2 раза', 'Уменьшится в 2 раза', 'Увеличится в 4 раза', 'Уменьшится в 4 раза'];
const Q = [
{ q:'Если расстояние $r$ удвоить (заряды без изменений) — как изменится $F$?', ans:3, why:'$F \\propto 1/r^2$; при $r\\to 2r$ сила уменьшается в $4$ раза.' },
{ q:'Если заряд $q_1$ удвоить (остальное без изменений) — как изменится $F$?', ans:0, why:'$F \\propto q_1$; сила увеличится в $2$ раза.' },
{ q:'Если оба заряда $q_1, q_2$ удвоить (расстояние без изменений) — как изменится $F$?', ans:2, why:'$F \\propto q_1 q_2$; при $q_1\\to 2q_1$ и $q_2\\to 2q_2$ сила увеличится в $4$ раза.' },
{ q:'Если расстояние $r$ уменьшить в $2$ раза — как изменится $F$?', ans:2, why:'$F \\propto 1/r^2$; при $r\\to r/2$ сила увеличится в $4$ раза.' },
{ q:'Если заряды поместить в среду с $\\varepsilon = 2$ (вместо вакуума) — как изменится $F$?', ans:1, why:'$F = k|q_1q_2|/(\\varepsilon r^2)$; при $\\varepsilon = 2$ сила уменьшится в $2$ раза.' },
{ q:'Если один из зарядов удвоить И расстояние удвоить — как изменится $F$?', ans:1, why:'$F \\propto q/r^2$; множитель $2/4 = 1/2$ — сила уменьшится в $2$ раза.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p17-iv3-q');
const oEl = document.getElementById('p17-iv3-opts');
const fb = document.getElementById('p17-iv3-fb');
const iEl = document.getElementById('p17-iv3-i');
const sEl = document.getElementById('p17-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p17-iv3'); bumpProgress('p17', 25); }
else if(score >= 4){ addXp(8, 'p17-iv3'); bumpProgress('p17', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p17-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр Кулона */
(function(){
const Q = [
{ q:'Два заряда по $+1$ мкКл на расстоянии $0{,}1$ м (в вакууме). Сила в Н?', ans:0.9, tol:0.05, hint:'$F = 9\\cdot10^9 \\cdot (10^{-6})^2/(0{,}1)^2 = 0{,}9$ Н.' },
{ q:'Заряды $+2$ нКл и $-3$ нКл на $r = 0{,}06$ м. Сила в мН?', ans:15, tol:1, hint:'$F = 9\\cdot10^9 \\cdot 6\\cdot10^{-18}/0{,}0036 = 0{,}015$ Н $= 15$ мН.' },
{ q:'При $r = 1$ м сила Кулона $F_0$. При каком $r$ (м) сила станет в $4$ раза меньше?', ans:2, tol:0.1, hint:'$F \\propto 1/r^2$; чтобы $F\\to F_0/4$, нужно $r \\to 2r_0 = 2$ м.' },
{ q:'В воде ($\\varepsilon = 81$) сила Кулона во сколько раз меньше, чем в вакууме (при тех же зарядах и $r$)?', ans:81, tol:2, hint:'$F = k|q_1q_2|/(\\varepsilon r^2)$ — делится на $\\varepsilon = 81$.' },
{ q:'Два электрона на $r = 10^{-10}$ м. Сила в Н? Введи мантиссу при $10^{-8}$.', ans:2.3, tol:0.3, hint:'$F = 9\\cdot10^9 \\cdot (1{,}6\\cdot10^{-19})^2/(10^{-10})^2 \\approx 2{,}3 \\cdot 10^{-8}$ Н.' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p17-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p17-iv4'); bumpProgress('p17', 25); }
else if(score >= 3){ addXp(8, 'p17-iv4'); bumpProgress('p17', 15); }
return;
}
document.getElementById('p17-iv4-i').textContent = (i+1);
document.getElementById('p17-iv4-s').textContent = score;
document.getElementById('p17-iv4-q').innerHTML = Q[i].q;
document.getElementById('p17-iv4-ans').value = '';
renderMath(document.getElementById('p17-iv4-q'));
document.getElementById('p17-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p17-iv4-fb');
const raw = document.getElementById('p17-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p17-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p17-iv4-go').addEventListener('click', go);
document.getElementById('p17-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p17-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p17');
}
function build_p18(){
const box = document.getElementById('p18-body');
let html = '';
/* THEORY 1 — Понятие поля */
html += makeCard('theory', "Электростатическое поле", "§18", `
<p><b>Электростатическое поле</b> — особый вид материи, который существует вокруг электрических зарядов и проявляется в действии на другие заряды.</p>
<p>До открытия поля физики верили в <b>«дальнодействие»</b> — мгновенное действие на расстоянии. Однако опыты с движущимися зарядами показали, что взаимодействие распространяется с <b>конечной скоростью</b> (скоростью света). Носителем этого взаимодействия является <b>электромагнитное поле</b>.</p>
<p><b>Электростатическое</b> поле создаётся <b>неподвижными</b> зарядами и не меняется со временем.</p>
<p>Подобно тому, как магнит создаёт магнитное поле, а Земля создаёт гравитационное поле, электрический заряд создаёт <b>электростатическое поле</b>.</p>
`);
/* THEORY 2 — Свойства поля */
html += makeCard('rule', "Свойства электростатического поля", "§18", `
<p><b>Свойства электростатического поля</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Создаётся</b> электрическим зарядом (источником).</li>
<li><b>Действует</b> только на электрические заряды (помещённые в поле).</li>
<li><b>Невидимо</b>, обнаруживается по силам, действующим на заряды.</li>
<li><b>Передаётся</b> с конечной скоростью (в пустоте — со скоростью света $c = 3 \\cdot 10^8$ м/с).</li>
<li><b>Подчиняется принципу суперпозиции</b>: поля складываются векторно.</li>
</ul>
<p>Поля могут создаваться:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Одним точечным зарядом.</li>
<li>Системой точечных зарядов.</li>
<li>Заряженными телами (заряд распределён по поверхности или объёму).</li>
</ul>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0">В этом параграфе мы изучим <b>электростатическое</b> поле — поле <b>неподвижных</b> зарядов. Его характеристики не зависят от времени.</p>
`);
/* THEORY 3 — Пробный заряд и источник */
html += makeCard('example', "Пробный заряд и источник", "§18", `
<p><b>Пробный заряд</b> $q_{пр}$ — это очень маленький положительный заряд, который вносится в исследуемую точку поля для измерения характеристик поля.</p>
<p><b>Условия для пробного заряда</b>:</p>
<ol style="margin:6px 0 8px 22px;line-height:1.75">
<li>Должен быть <b>малым</b>, чтобы своим полем не искажать исходное поле.</li>
<li>По общему соглашению — <b>положительным</b>, чтобы направление силы совпадало с направлением поля.</li>
</ol>
<p><b>Источник поля</b> — заряд (или система зарядов), создающий поле.</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0"><b>Аналогия</b>: чтобы измерить силу ветра, мы выставляем флюгер (пробный объект). Так же, чтобы измерить поле, мы помещаем в точку пробный заряд и смотрим, какая сила на него действует.</p>
<p>Поле существует <b>независимо</b> от того, есть в точке пробный заряд или нет. Поле — это <b>объективная физическая реальность</b>.</p>
`);
/* INTERACTIVE 1 — Визуализатор электростатического поля */
html += `<div class="wg" id="p18-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Визуализатор электростатического поля</div></div>
<div class="wg-help">Меняй знак и величину заряда — увидишь линии поля. Добавь пробный заряд — увидишь силу, действующую на него.</div>
<div class="sliders">
<label>$q$: <b id="p18-iv1-qL">+5</b> нКл <input type="range" id="p18-iv1-q" min="-10" max="10" value="5" step="1"></label>
<label>пробных зарядов: <b id="p18-iv1-nL">2</b> <input type="range" id="p18-iv1-n" min="0" max="8" value="2" step="1"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p18-iv1-svg" viewBox="0 0 380 260" width="100%" style="height:auto"></svg>
</div>
<div id="p18-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Понятие поля */
html += `<div class="wg" id="p18-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Понятие поля</div></div>
<div class="wg-help">5 вопросов о природе и свойствах электростатического поля.</div>
<div class="score-display"><span>Вопрос <b id="p18-iv2-i">1</b> / 5</span><span>Очки: <b id="p18-iv2-s">0</b> / 5</span></div>
<div id="p18-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p18-iv2-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p18-iv2-fb"></div>
<div class="actions"><button class="btn" id="p18-iv2-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 3 — Существует ли поле? */
html += `<div class="wg" id="p18-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Существует ли поле?</div></div>
<div class="wg-help">6 ситуаций — есть ли в указанной точке электростатическое поле?</div>
<div class="score-display"><span>Задача <b id="p18-iv3-i">1</b> / 6</span><span>Очки: <b id="p18-iv3-s">0</b> / 6</span></div>
<div id="p18-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p18-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p18-iv3-fb"></div>
<div class="actions"><button class="btn" id="p18-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Кратко: что такое поле */
html += `<div class="wg" id="p18-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Кратко: что такое поле</div></div>
<div class="wg-help">4 быстрых вопроса. Выбери номер правильного варианта.</div>
<div class="score-display"><span>Вопрос <b id="p18-iv4-i">1</b> / 4</span><span>Очки: <b id="p18-iv4-s">0</b> / 4</span></div>
<div id="p18-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p18-iv4-opts" style="display:grid;grid-template-columns:1fr;gap:6px"></div>
<div class="feedback" id="p18-iv4-fb"></div>
<div class="actions"><button class="btn" id="p18-iv4-restart">Начать заново</button></div>
</div>`;
html += secNav('p17', 'p19');
html += readButton('p18');
box.innerHTML = html;
renderMath(box);
/* IV1 — Визуализатор электростатического поля */
(function(){
const svg = document.getElementById('p18-iv1-svg');
const qS = document.getElementById('p18-iv1-q');
const nS = document.getElementById('p18-iv1-n');
const qL = document.getElementById('p18-iv1-qL');
const nL = document.getElementById('p18-iv1-nL');
const out = document.getElementById('p18-iv1-out');
const seen = new Set();
let _done = false;
const k = PHYS.CONST.k;
function fmtQ(q){ return (q>=0?'+':'') + q; }
function render(){
const q = +qS.value, n = +nS.value;
qL.textContent = fmtQ(q); nL.textContent = n;
const W = 380, H = 260, cx = W/2, cy = H/2;
let g = '';
// Заголовок
g += '<text x="190" y="20" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Электростатическое поле точечного заряда</text>';
// Линии поля (если q != 0)
if(q !== 0){
g += PHYS.fieldLinesPointCharge(cx, cy, q > 0 ? 1 : -1, 95, 16);
}
// Сам заряд
if(q === 0){
g += '<circle cx="'+cx+'" cy="'+cy+'" r="20" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="'+cx+'" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
} else {
g += PHYS.chargeMark(cx, cy, q > 0 ? 1 : -1, 20, '');
}
// Подпись
g += '<text x="'+cx+'" y="'+(cy+44)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">q = '+fmtQ(q)+' нКл</text>';
// Пробные заряды (по окружности r=130)
if(n > 0 && q !== 0){
const Rp = 115;
const qpr = 1e-9; // 1 нКл пробный
for(let i = 0; i < n; i++){
const a = 2*Math.PI*i/n + 0.15;
const px = cx + Rp*Math.cos(a);
const py = cy + Rp*Math.sin(a);
// Пробный заряд: маленький красный крестик
g += '<circle cx="'+px.toFixed(1)+'" cy="'+py.toFixed(1)+'" r="5" fill="#fecaca" stroke="#dc2626" stroke-width="1.5"/>';
g += '<line x1="'+(px-3).toFixed(1)+'" y1="'+py.toFixed(1)+'" x2="'+(px+3).toFixed(1)+'" y2="'+py.toFixed(1)+'" stroke="#dc2626" stroke-width="1.5"/>';
g += '<line x1="'+px.toFixed(1)+'" y1="'+(py-3).toFixed(1)+'" x2="'+px.toFixed(1)+'" y2="'+(py+3).toFixed(1)+'" stroke="#dc2626" stroke-width="1.5"/>';
// Стрелка силы: от заряда (если q>0) или к заряду (если q<0)
const dirx = Math.cos(a), diry = Math.sin(a);
const L = 22;
const sign = q > 0 ? 1 : -1;
// F = qE. Если q>0 (источник), пробный отталкивается — стрелка от центра
const sx = px + sign * 9 * dirx;
const sy = py + sign * 9 * diry;
const ex = px + sign * (9 + L) * dirx;
const ey = py + sign * (9 + L) * diry;
g += PHYS.drawArrow(sx, sy, ex, ey, '#ea580c', 2, 8);
}
}
svg.innerHTML = g;
// Описание
let txt = '';
if(q === 0){
txt = '<b>Заряд = 0</b>: поля нет.';
} else if(q > 0){
txt = '<b>Положительный заряд</b>: линии поля направлены <b>от заряда</b>. На пробный $+$ заряд действует сила <b>отталкивания</b>.';
} else {
txt = '<b>Отрицательный заряд</b>: линии поля направлены <b>к заряду</b>. На пробный $+$ заряд действует сила <b>притяжения</b>.';
}
out.innerHTML = txt;
renderMath(out);
seen.add(q+':'+n);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p18-iv1'); bumpProgress('p18', 15); }
}
qS.addEventListener('input', render);
nS.addEventListener('input', render);
render();
})();
/* IV2 — Понятие поля */
(function(){
const Q = [
{ q:'Электростатическое поле существует, только когда в точке есть пробный заряд?', opts:['Да','Нет'], ans:1, why:'Поле существует независимо от пробного заряда — это объективная физическая реальность.' },
{ q:'Источник электростатического поля — это:', opts:['Электрический заряд','Магнит'], ans:0, why:'Электростатическое поле создаётся электрическими зарядами. Магнит создаёт магнитное поле.' },
{ q:'Пробный заряд по соглашению — какого знака?', opts:['Положительный','Отрицательный'], ans:0, why:'Пробный заряд берут положительным, чтобы направление силы совпадало с направлением поля.' },
{ q:'Электростатическое поле создаётся:', opts:['Неподвижными зарядами','Движущимися зарядами'], ans:0, why:'Именно неподвижные заряды создают <b>электростатическое</b> поле. Движущиеся создают магнитное.' },
{ q:'Через что передаётся действие заряда на другой заряд?', opts:['Через пустоту','Через поле'], ans:1, why:'Носителем взаимодействия является поле. «Дальнодействие через пустоту» — устаревшее представление.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p18-iv2-q');
const oEl = document.getElementById('p18-iv2-opts');
const fb = document.getElementById('p18-iv2-fb');
const iEl = document.getElementById('p18-iv2-i');
const sEl = document.getElementById('p18-iv2-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p18-iv2'); bumpProgress('p18', 25); }
else if(score >= 3){ addXp(8, 'p18-iv2'); bumpProgress('p18', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = item.opts.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p18-iv2-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV3 — Существует ли поле? */
(function(){
const OPTS = ['Существует', 'Не существует (E = 0)'];
const Q = [
{ q:'В точке возле положительно заряженного шара.', ans:0, why:'Заряженное тело создаёт поле вокруг себя.' },
{ q:'Вокруг нейтрального тела (суммарный заряд = 0).', ans:1, why:'Нет заряда — нет поля. (В реальности есть микрополе, но в школьной модели — нет.)' },
{ q:'Внутри однородно заряженной сферической оболочки.', ans:1, why:'По теореме Гаусса: внутри полой однородно заряженной сферы поле равно нулю.' },
{ q:'В точке возле отдельного электрона.', ans:0, why:'Электрон — заряженная частица, вокруг него всегда есть поле.' },
{ q:'В точке посередине между двумя равными одноимёнными зарядами.', ans:1, why:'Поля от двух зарядов равны по модулю и противоположны по направлению — взаимно компенсируются.' },
{ q:'В вакууме вдали от любых зарядов.', ans:1, why:'Без источников электростатического поля нет.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p18-iv3-q');
const oEl = document.getElementById('p18-iv3-opts');
const fb = document.getElementById('p18-iv3-fb');
const iEl = document.getElementById('p18-iv3-i');
const sEl = document.getElementById('p18-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p18-iv3'); bumpProgress('p18', 25); }
else if(score >= 4){ addXp(8, 'p18-iv3'); bumpProgress('p18', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p18-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Кратко: что такое поле */
(function(){
const Q = [
{ q:'Электростатическое поле создаётся:', opts:['1. Электрическим зарядом', '2. Массой тела', '3. Течением времени'], ans:0, why:'Источник поля — электрический заряд.' },
{ q:'Электростатическое поле создают:', opts:['1. Неподвижные заряды', '2. Движущиеся заряды', '3. Любые заряды'], ans:0, why:'Именно <b>неподвижные</b> заряды создают электростатическое поле.' },
{ q:'Пробный заряд — это:', opts:['1. Положительный и большой', '2. Положительный и малый', '3. Любого знака и любой величины'], ans:1, why:'Малый — чтобы не искажать поле; положительный — для соответствия направления силы и поля.' },
{ q:'Действие электростатического поля передаётся:', opts:['1. Мгновенно', '2. Со скоростью света', '3. Со скоростью звука'], ans:1, why:'В вакууме электромагнитное взаимодействие распространяется со скоростью света $c \\approx 3 \\cdot 10^8$ м/с.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p18-iv4-q');
const oEl = document.getElementById('p18-iv4-opts');
const fb = document.getElementById('p18-iv4-fb');
const iEl = document.getElementById('p18-iv4-i');
const sEl = document.getElementById('p18-iv4-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p18-iv4'); bumpProgress('p18', 25); }
else if(score >= 2){ addXp(8, 'p18-iv4'); bumpProgress('p18', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = item.opts.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p18-iv4-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p18');
}
function build_p19(){
const box = document.getElementById('p19-body');
let html = '';
/* THEORY 1 — Определение напряжённости */
html += makeCard('theory', "Напряжённость электростатического поля", "§19", `
<p><b>Напряжённость электростатического поля</b> $\\vec{E}$ — векторная физическая величина, равная отношению силы, действующей на пробный заряд, к величине этого заряда:</p>
<p style="text-align:center;margin:10px 0">$$\\vec{E} = \\dfrac{\\vec{F}}{q_{пр}}$$</p>
<p>где:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$\\vec{E}$ — напряжённость поля в данной точке (Н/Кл);</li>
<li>$\\vec{F}$ — сила, действующая на пробный заряд (Н);</li>
<li>$q_{пр}$ — пробный заряд (Кл).</li>
</ul>
<p><b>Единица измерения</b>: $1$ Н/Кл $= 1$ В/м (вольт на метр).</p>
<p style="margin-top:10px"><b>Сила на заряд</b> в поле напряжённостью $\\vec{E}$:</p>
<p style="text-align:center;margin:10px 0">$$\\vec{F} = q \\vec{E}$$</p>
<p><b>Направление</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Для $q > 0$: $\\vec{F}$ совпадает с $\\vec{E}$ по направлению.</li>
<li>Для $q < 0$: $\\vec{F}$ противоположна $\\vec{E}$.</li>
</ul>
`);
/* THEORY 2 — Поле точечного заряда */
html += makeCard('rule', "Напряжённость поля точечного заряда", "§19", `
<p>Модуль напряжённости поля <b>точечного заряда</b> $q$ на расстоянии $r$ от него:</p>
<p style="text-align:center;margin:10px 0">$$E = k\\,\\dfrac{|q|}{r^2}$$</p>
<p>где $k = 9 \\cdot 10^9$ Н$\\cdot$м$^2$/Кл$^2$ — постоянная Кулона.</p>
<p><b>Направление</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Поле положительного заряда направлено <b>от</b> заряда (наружу).</li>
<li>Поле отрицательного заряда направлено <b>к</b> заряду (внутрь).</li>
</ul>
<p>В среде с диэлектрической проницаемостью $\\varepsilon$:</p>
<p style="text-align:center;margin:10px 0">$$E = k\\,\\dfrac{|q|}{\\varepsilon\\, r^2}$$</p>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0"><b>Пример</b>. Точечный заряд $q = 1$ нКл. Напряжённость на расстоянии $r = 0{,}1$ м:
$E = 9 \\cdot 10^9 \\cdot \\dfrac{10^{-9}}{(0{,}1)^2} = \\dfrac{9}{0{,}01} = 900$ В/м.</p>
`);
/* THEORY 3 — Принцип суперпозиции */
html += makeCard('example', "Принцип суперпозиции полей", "§19", `
<p><b>Принцип суперпозиции</b>: если поле создано несколькими зарядами, то напряжённость суммарного поля равна <b>векторной сумме</b> напряжённостей полей от каждого заряда отдельно:</p>
<p style="text-align:center;margin:10px 0">$$\\vec{E} = \\vec{E_1} + \\vec{E_2} + \\vec{E_3} + \\ldots$$</p>
<p>Это <b>векторная</b> сумма — учитываем направления каждого $\\vec{E_i}$.</p>
<p style="margin-top:12px"><b>Пример 1.</b> Два заряда по $+q$ на расстоянии $2d$. Найти $E$ в точке посередине.</p>
<p>$E_1$ от первого и $E_2$ от второго равны по модулю, но направлены <b>в разные стороны</b> $\\Rightarrow$ $E = 0$. Это <b>точка равновесия</b>.</p>
<p style="margin-top:10px"><b>Пример 2.</b> Два заряда $+q$ и $-q$ на расстоянии $2d$ (диполь). В точке посередине $E_1$ и $E_2$ направлены <b>в одну сторону</b> (от $+q$ к $-q$):</p>
<p style="text-align:center;margin:10px 0">$$E = 2 \\cdot k\\dfrac{|q|}{d^2}$$</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0"><b>Однородное поле</b> — поле, в котором $\\vec{E}$ одинакова во всех точках (по модулю и направлению). Создаётся, например, между параллельными пластинами заряженного конденсатора. Сила $\\vec{F} = q\\vec{E}$ на заряд везде одинакова.</p>
`);
/* INTERACTIVE 1 — Напряжённость поля точечного заряда */
html += `<div class="wg" id="p19-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Напряжённость поля точечного заряда</div></div>
<div class="wg-help">Меняй $q$ и $r$ — стрелки $\\vec{E}$ в 8 точках вокруг заряда покажут направление и величину поля.</div>
<div class="sliders">
<label>$q$: <b id="p19-iv1-qL">+5</b> нКл <input type="range" id="p19-iv1-q" min="-10" max="10" value="5" step="1"></label>
<label>$r$: <b id="p19-iv1-rL">0.20</b> м <input type="range" id="p19-iv1-r" min="0.05" max="0.5" value="0.2" step="0.01"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p19-iv1-svg" viewBox="0 0 480 320" width="100%" style="height:auto"></svg>
</div>
<div id="p19-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Принцип суперпозиции: 2 заряда */
html += `<div class="wg" id="p19-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Принцип суперпозиции: 2 заряда</div></div>
<div class="wg-help">Заряды $q_1$ слева, $q_2$ справа на расстоянии $0{,}3$ м. Пробная точка между ними. $\\vec{E}$ от каждого — коллинеарны.</div>
<div class="sliders">
<label>$q_1$: <b id="p19-iv2-q1L">+5</b> нКл <input type="range" id="p19-iv2-q1" min="-10" max="10" value="5" step="1"></label>
<label>$q_2$: <b id="p19-iv2-q2L">+5</b> нКл <input type="range" id="p19-iv2-q2" min="-10" max="10" value="5" step="1"></label>
<label>$x$ от $q_1$: <b id="p19-iv2-xL">0.15</b> м <input type="range" id="p19-iv2-x" min="0.05" max="0.25" value="0.15" step="0.01"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p19-iv2-svg" viewBox="0 0 480 320" width="100%" style="height:auto"></svg>
</div>
<div id="p19-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — Какое направление E? */
html += `<div class="wg" id="p19-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какое направление $\\vec{E}$?</div></div>
<div class="wg-help">6 ситуаций — куда направлен вектор напряжённости?</div>
<div class="score-display"><span>Задача <b id="p19-iv3-i">1</b> / 6</span><span>Очки: <b id="p19-iv3-s">0</b> / 6</span></div>
<div id="p19-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p19-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px"></div>
<div class="feedback" id="p19-iv3-fb"></div>
<div class="actions"><button class="btn" id="p19-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр напряжённости */
html += `<div class="wg" id="p19-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр напряжённости</div></div>
<div class="wg-help">5 задач. $k = 9 \\cdot 10^9$ Н$\\cdot$м$^2$/Кл$^2$. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p19-iv4-i">1</b> / 5</span><span>Очки: <b id="p19-iv4-s">0</b> / 5</span></div>
<div id="p19-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p19-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p19-iv4-go">Проверить</button>
<button class="btn" id="p19-iv4-start">Заново</button>
</div>
<div class="feedback" id="p19-iv4-fb"></div>
</div>`;
html += secNav('p18', 'p20');
html += readButton('p19');
box.innerHTML = html;
renderMath(box);
/* IV1 — Напряжённость точечного заряда (8 стрелок) */
(function(){
const svg = document.getElementById('p19-iv1-svg');
const qS = document.getElementById('p19-iv1-q');
const rS = document.getElementById('p19-iv1-r');
const qL = document.getElementById('p19-iv1-qL');
const rL = document.getElementById('p19-iv1-rL');
const out = document.getElementById('p19-iv1-out');
const seen = new Set();
let _done = false;
const k = PHYS.CONST.k;
function fmtQ(q){ return (q>=0?'+':'') + q; }
function render(){
const q = +qS.value, r = +rS.value;
qL.textContent = fmtQ(q); rL.textContent = r.toFixed(2);
const W = 480, H = 320, cx = W/2, cy = H/2;
let g = '';
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">E = k|q|/r² — поле точечного заряда</text>';
// Расчёт E
const qC = q * 1e-9;
const E = (q === 0) ? 0 : k * Math.abs(qC) / (r * r);
// 8 точек вокруг заряда на пиксельном радиусе Rpx
const Rpx = 110;
// Длина стрелки пропорциональна E (нормируем). Erefer = k*10e-9/0.05²
const Eref = k * 10e-9 / (0.05*0.05);
let arrowLen = (q === 0) ? 0 : Math.min(55, Math.max(12, Math.sqrt(E / Eref) * 55));
if(q !== 0){
const sign = q > 0 ? 1 : -1;
for(let i = 0; i < 8; i++){
const a = 2*Math.PI*i/8;
const dx = Math.cos(a), dy = Math.sin(a);
const px = cx + Rpx * dx, py = cy + Rpx * dy;
// Если q > 0: от заряда (наружу). Если q < 0: к заряду (внутрь).
let sx, sy, ex, ey;
if(sign > 0){
sx = px; sy = py;
ex = px + arrowLen * dx; ey = py + arrowLen * dy;
} else {
sx = px + arrowLen * dx; sy = py + arrowLen * dy;
ex = px; ey = py;
}
g += PHYS.drawArrow(sx, sy, ex, ey, '#ea580c', 2.4, 9);
}
}
// Сам заряд
if(q === 0){
g += '<circle cx="'+cx+'" cy="'+cy+'" r="22" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="'+cx+'" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
} else {
g += PHYS.chargeMark(cx, cy, q > 0 ? 1 : -1, 22, '');
}
g += '<text x="'+cx+'" y="'+(cy+44)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#0f172a">q = '+fmtQ(q)+' нКл</text>';
// Окружность радиуса r (визуально Rpx)
g += '<circle cx="'+cx+'" cy="'+cy+'" r="'+Rpx+'" fill="none" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 4"/>';
g += '<text x="'+(cx + Rpx + 8)+'" y="'+(cy - 6)+'" font-family="JetBrains Mono,monospace" font-size="12" fill="#64748b">r = '+r.toFixed(2)+' м</text>';
svg.innerHTML = g;
// Описание
let txt;
if(q === 0){
txt = '$E = 0$ — заряда нет.';
} else {
const Estr = E.toFixed(0);
const dir = q > 0 ? '<b>от заряда</b>' : '<b>к заряду</b>';
txt = '<b>$E = k\\dfrac{|q|}{r^2} = 9\\cdot10^9 \\cdot \\dfrac{'+Math.abs(q)+'\\cdot 10^{-9}}{('+r.toFixed(2)+')^2} \\approx '+Estr+'$ В/м</b><br>'
+ 'Направление: '+dir+'.';
}
out.innerHTML = txt;
renderMath(out);
seen.add(q+':'+r.toFixed(2));
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p19-iv1'); bumpProgress('p19', 15); }
}
qS.addEventListener('input', render);
rS.addEventListener('input', render);
render();
})();
/* IV2 — Принцип суперпозиции: 2 заряда */
(function(){
const svg = document.getElementById('p19-iv2-svg');
const q1S = document.getElementById('p19-iv2-q1');
const q2S = document.getElementById('p19-iv2-q2');
const xS = document.getElementById('p19-iv2-x');
const q1L = document.getElementById('p19-iv2-q1L');
const q2L = document.getElementById('p19-iv2-q2L');
const xL = document.getElementById('p19-iv2-xL');
const out = document.getElementById('p19-iv2-out');
const seen = new Set();
let _done = false;
const k = PHYS.CONST.k;
const D = 0.3; // м, фикс расстояние
function fmtQ(q){ return (q>=0?'+':'') + q; }
function render(){
const q1 = +q1S.value, q2 = +q2S.value, x = +xS.value;
q1L.textContent = fmtQ(q1); q2L.textContent = fmtQ(q2); xL.textContent = x.toFixed(2);
const W = 480, H = 320, cy = H/2;
// Заряды по горизонтали: q1 в xpx1, q2 в xpx2
const xpx1 = 70, xpx2 = 410;
const dpx = xpx2 - xpx1;
// Пробная точка
const ppx = xpx1 + (x / D) * dpx;
let g = '';
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Суперпозиция: E = E₁ + E₂ (векторно)</text>';
// Линия
g += '<line x1="'+xpx1+'" y1="'+cy+'" x2="'+xpx2+'" y2="'+cy+'" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3 3"/>';
// Заряды
if(q1 === 0){
g += '<circle cx="'+xpx1+'" cy="'+cy+'" r="20" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="'+xpx1+'" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
} else g += PHYS.chargeMark(xpx1, cy, q1 > 0 ? 1 : -1, 20, '');
if(q2 === 0){
g += '<circle cx="'+xpx2+'" cy="'+cy+'" r="20" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="'+xpx2+'" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#64748b">0</text>';
} else g += PHYS.chargeMark(xpx2, cy, q2 > 0 ? 1 : -1, 20, '');
// Подписи зарядов
g += '<text x="'+xpx1+'" y="'+(cy+44)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0f172a">q₁ = '+fmtQ(q1)+' нКл</text>';
g += '<text x="'+xpx2+'" y="'+(cy+44)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0f172a">q₂ = '+fmtQ(q2)+' нКл</text>';
// Пробная точка
g += '<circle cx="'+ppx.toFixed(1)+'" cy="'+cy+'" r="4.5" fill="#fde047" stroke="#a16207" stroke-width="1.5"/>';
g += '<text x="'+ppx.toFixed(1)+'" y="'+(cy-20)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#a16207">P</text>';
// Расчёт E1, E2 в точке P. Направление по оси x.
// r1 = x, r2 = D - x
const r1 = x, r2 = D - x;
const qC1 = q1 * 1e-9, qC2 = q2 * 1e-9;
// E1: знак — куда направлено поле от q1 в точке P.
// Точка P справа от q1. Если q1>0, E1 направлено вправо (+). Если q1<0, влево (-).
const E1 = (r1 > 1e-6) ? k * qC1 / (r1*r1) : 0; // знак сохраняем
// E2: точка P слева от q2. Если q2>0, поле от q2 направлено влево (-). Если q2<0, вправо (+).
const E2 = (r2 > 1e-6) ? -k * qC2 / (r2*r2) : 0;
const Esum = E1 + E2;
// Стрелки E1, E2, Esum в точке P. Длина — по модулю (с насыщением).
function arr(E, color, yOffset, label){
if(Math.abs(E) < 1e-3) return '';
const Emax = 5000;
const L = Math.min(70, Math.max(12, Math.abs(E)/Emax * 70));
const dir = E > 0 ? 1 : -1;
const sx = ppx, sy = cy + yOffset;
const ex = ppx + dir * L, ey = cy + yOffset;
let s = PHYS.drawArrow(sx, sy, ex, ey, color, 2.4, 9);
s += '<text x="'+(ex + dir*8)+'" y="'+(ey + 4)+'" text-anchor="'+(dir>0?'start':'end')+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+color+'">'+label+'</text>';
return s;
}
g += arr(E1, '#2563eb', -60, 'E₁');
g += arr(E2, '#10b981', -90, 'E₂');
g += arr(Esum, '#dc2626', -30, 'E');
// Расстояния
g += '<text x="'+((xpx1+ppx)/2).toFixed(1)+'" y="'+(cy+62)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">r₁ = '+r1.toFixed(2)+' м</text>';
g += '<text x="'+((ppx+xpx2)/2).toFixed(1)+'" y="'+(cy+62)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">r₂ = '+r2.toFixed(2)+' м</text>';
svg.innerHTML = g;
function s(E){ return (E>=0?'+':'') + E.toFixed(0); }
out.innerHTML = '<div><span style="color:#2563eb"><b>$E_1 = '+s(E1)+'$ В/м</b></span> &nbsp;&nbsp; '
+ '<span style="color:#10b981"><b>$E_2 = '+s(E2)+'$ В/м</b></span> &nbsp;&nbsp; '
+ '<span style="color:#dc2626"><b>$E = '+s(Esum)+'$ В/м</b></span></div>'
+ '<div style="margin-top:6px;font-size:.88rem;color:#64748b">Знак $+$ — поле направлено вправо, $-$ — влево.</div>';
renderMath(out);
seen.add(q1+':'+q2+':'+x.toFixed(2));
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p19-iv2'); bumpProgress('p19', 15); }
}
q1S.addEventListener('input', render);
q2S.addEventListener('input', render);
xS.addEventListener('input', render);
render();
})();
/* IV3 — Какое направление E? */
(function(){
const OPTS = ['От заряда (от +)', 'К заряду (к )', '$E = 0$'];
const Q = [
{ q:'Поле положительного точечного заряда. Куда направлено $\\vec{E}$?', ans:0, why:'$\\vec{E}$ положительного заряда направлено наружу (от заряда).' },
{ q:'Поле отрицательного точечного заряда. Куда направлено $\\vec{E}$?', ans:1, why:'$\\vec{E}$ отрицательного заряда направлено к заряду (внутрь).' },
{ q:'Середина между двумя одинаковыми $+q$ зарядами.', ans:2, why:'Поля равны по модулю, противоположны по направлению — сумма ноль.' },
{ q:'Середина между двумя одинаковыми $-q$ зарядами.', ans:2, why:'Аналогично: оба поля направлены к своим зарядам, в середине компенсируются.' },
{ q:'Точка возле положительного протона ($+$).', ans:0, why:'От любого положительного заряда поле направлено наружу.' },
{ q:'Точка возле отрицательного электрона ($-$).', ans:1, why:'К любому отрицательному заряду поле направлено внутрь.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p19-iv3-q');
const oEl = document.getElementById('p19-iv3-opts');
const fb = document.getElementById('p19-iv3-fb');
const iEl = document.getElementById('p19-iv3-i');
const sEl = document.getElementById('p19-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p19-iv3'); bumpProgress('p19', 25); }
else if(score >= 4){ addXp(8, 'p19-iv3'); bumpProgress('p19', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p19-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр напряжённости */
(function(){
const Q = [
{ q:'Точечный заряд $+1$ нКл. Напряжённость $E$ (В/м) на $r = 0{,}1$ м?', ans:900, tol:30, hint:'$E = 9\\cdot10^9 \\cdot 10^{-9}/(0{,}1)^2 = 900$ В/м.' },
{ q:'Поле $E = 1000$ В/м действует на заряд $q = 2$ мкКл. Найди силу в мН.', ans:2, tol:0.1, hint:'$F = qE = 2\\cdot10^{-6} \\cdot 1000 = 2\\cdot10^{-3}$ Н $= 2$ мН.' },
{ q:'Точечный заряд $-2$ нКл. $E$ (В/м) на $r = 0{,}05$ м?', ans:7200, tol:100, hint:'$E = 9\\cdot10^9 \\cdot 2\\cdot10^{-9}/(0{,}05)^2 = 18/0{,}0025 = 7200$ В/м.' },
{ q:'Два заряда $+2$ нКл и $+2$ нКл на расстоянии $0{,}2$ м. $E$ (В/м) в середине отрезка?', ans:0, tol:0.1, hint:'Поля от одинаковых зарядов в середине направлены навстречу — суммарное $E = 0$.' },
{ q:'Два заряда $+2$ нКл и $-2$ нКл на расстоянии $0{,}2$ м. $E$ (В/м) в середине? (Поля складываются.)', ans:3600, tol:50, hint:'$E_1 = E_2 = 9\\cdot10^9 \\cdot 2\\cdot10^{-9}/(0{,}1)^2 = 1800$ В/м. Направлены в одну сторону (от $+$ к $-$) $\\Rightarrow E = 3600$ В/м.' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p19-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p19-iv4'); bumpProgress('p19', 25); }
else if(score >= 3){ addXp(8, 'p19-iv4'); bumpProgress('p19', 15); }
return;
}
document.getElementById('p19-iv4-i').textContent = (i+1);
document.getElementById('p19-iv4-s').textContent = score;
document.getElementById('p19-iv4-q').innerHTML = Q[i].q;
document.getElementById('p19-iv4-ans').value = '';
renderMath(document.getElementById('p19-iv4-q'));
document.getElementById('p19-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p19-iv4-fb');
const raw = document.getElementById('p19-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p19-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p19-iv4-go').addEventListener('click', go);
document.getElementById('p19-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p19-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p19');
}
function build_p20(){
const box = document.getElementById('p20-body');
let html = '';
/* THEORY 1 — Линии напряжённости */
html += makeCard('theory', "Линии напряжённости", "§20", `
<p><b>Силовая линия</b> (линия напряжённости) — это линия, касательная к которой в каждой точке совпадает с направлением вектора $\\vec{E}$.</p>
<p style="margin-top:10px"><b>Свойства силовых линий:</b></p>
<ol style="margin:6px 0 8px 22px;line-height:1.75">
<li>Линии <b>начинаются на положительных</b> зарядах (или на бесконечности) и <b>заканчиваются на отрицательных</b> (или уходят на бесконечность).</li>
<li>Силовые линии <b>не пересекаются</b> между собой — в каждой точке поле имеет только одно направление.</li>
<li><b>Густота линий</b> пропорциональна величине напряжённости $E$: где линии гуще — поле сильнее.</li>
<li>Линии <b>перпендикулярны</b> заряженной поверхности проводника.</li>
</ol>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px">Картина силовых линий — это <b>способ визуализации</b> невидимого поля. Чтобы «увидеть» $\\vec{E}$, мы рисуем «карту» из линий.</p>
`);
/* THEORY 2 — Поле точечного заряда и системы зарядов */
html += makeCard('rule', "Поле точечного заряда и системы зарядов", "§20", `
<p><b>Поле точечного заряда:</b> линии напряжённости — <b>радиальные</b>.</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>От $+q$ — расходятся <b>наружу</b>.</li>
<li>К $-q$ — сходятся <b>внутрь</b>.</li>
<li>В точке самого заряда линии расходятся бесконечно, плотность бесконечна.</li>
</ul>
<p style="margin-top:10px"><b>Поле двух одноимённых зарядов $+q$ и $+q$:</b> линии расходятся от обоих зарядов и <b>отталкиваются</b> друг от друга. В середине между зарядами — <b>точка равновесия</b> ($E = 0$, поля компенсируются).</p>
<p style="margin-top:10px"><b>Поле двух разноимённых зарядов $+q$ и $-q$ (диполь):</b> линии идут от $+q$ к $-q$, образуя характерную картину (похожую на поле магнита!). В центре между зарядами поле направлено от $+q$ к $-q$.</p>
`);
/* THEORY 3 — Однородное и неоднородное поле */
html += makeCard('example', "Однородное и неоднородное поле", "§20", `
<p><b>Однородное поле</b> — поле, в котором $\\vec{E}$ одинакова во всех точках:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>по модулю,</li>
<li>по направлению.</li>
</ul>
<p>Силовые линии однородного поля — <b>параллельные и одинаково расположенные</b>.</p>
<p style="margin-top:10px"><b>Где встречается:</b></p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Между пластинами параллельного плоского конденсатора (вдали от краёв).</li>
<li>В небольшой области вблизи поверхности большого равномерно заряженного тела.</li>
</ul>
<p style="margin-top:10px">В однородном поле сила $\\vec{F} = q\\vec{E}$ везде одинакова, а поле описывается одним вектором $\\vec{E}$.</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0"><b>Неоднородное поле</b> — поле, где $\\vec{E}$ меняется от точки к точке. Примеры: поле точечного заряда, поле системы зарядов, поле заряженной сферы снаружи.</p>
`);
/* INTERACTIVE 1 — Картины полей */
html += `<div class="wg" id="p20-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Картины полей — 5 конфигураций</div></div>
<div class="wg-help">Переключай тип конфигурации и наблюдай характерную картину силовых линий. Это главный визуализатор электростатики!</div>
<div class="sliders">
<label>Тип: <b id="p20-iv1-tL">1</b> <input type="range" id="p20-iv1-t" min="1" max="5" value="1" step="1"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p20-iv1-svg" viewBox="0 0 480 360" width="100%" style="height:auto"></svg>
</div>
<div id="p20-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.7"></div>
</div>`;
/* INTERACTIVE 2 — Свойства силовых линий (квикфайр) */
html += `<div class="wg" id="p20-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Свойства силовых линий</div></div>
<div class="wg-help">6 вопросов на понимание свойств линий поля.</div>
<div class="score-display"><span>Вопрос <b id="p20-iv2-i">1</b> / 6</span><span>Очки: <b id="p20-iv2-s">0</b> / 6</span></div>
<div id="p20-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p20-iv2-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p20-iv2-fb"></div>
<div class="actions"><button class="btn" id="p20-iv2-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 3 — Какая картина соответствует конфигурации? */
html += `<div class="wg" id="p20-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какая картина соответствует конфигурации?</div></div>
<div class="wg-help">Выбери тип картины силовых линий для каждой конфигурации зарядов.</div>
<div class="score-display"><span>Задача <b id="p20-iv3-i">1</b> / 6</span><span>Очки: <b id="p20-iv3-s">0</b> / 6</span></div>
<div id="p20-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p20-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p20-iv3-fb"></div>
<div class="actions"><button class="btn" id="p20-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр поля */
html += `<div class="wg" id="p20-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр поля</div></div>
<div class="wg-help">5 задач на свойства поля и силовых линий.</div>
<div class="score-display"><span>Задача <b id="p20-iv4-i">1</b> / 5</span><span>Очки: <b id="p20-iv4-s">0</b> / 5</span></div>
<div id="p20-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p20-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p20-iv4-go">Проверить</button>
<button class="btn" id="p20-iv4-start">Заново</button>
</div>
<div class="feedback" id="p20-iv4-fb"></div>
</div>`;
html += secNav('p19', 'p21');
html += readButton('p20');
box.innerHTML = html;
renderMath(box);
/* IV1 — Картины полей (5 конфигураций) */
(function(){
const svg = document.getElementById('p20-iv1-svg');
const tS = document.getElementById('p20-iv1-t');
const tL = document.getElementById('p20-iv1-tL');
const out = document.getElementById('p20-iv1-out');
const seen = new Set();
let _done = false;
const NAMES = {
1: 'Точечный заряд $+q$',
2: 'Точечный заряд $-q$',
3: 'Два одноимённых $+q$, $+q$',
4: 'Диполь $+q$, $-q$',
5: 'Однородное поле'
};
const DESC = {
1: 'Радиальные линии расходятся <b>от заряда</b> наружу. $E$ максимально у заряда, убывает как $1/r^2$.',
2: 'Радиальные линии сходятся <b>к заряду</b>. Картина симметрична относительно центра.',
3: 'Линии расходятся от каждого заряда и <b>отталкиваются</b> друг от друга. В середине — <b>точка равновесия</b> $E = 0$.',
4: 'Линии идут <b>от $+q$ к $-q$</b>. В центре между зарядами поле максимально и направлено от $+$ к $-$.',
5: 'Линии <b>параллельны и одинаково расположены</b>. $\\vec{E}$ одинакова во всех точках. Создаётся между пластинами конденсатора.'
};
function render(){
const t = +tS.value;
tL.textContent = t;
const W = 480, H = 360, cx = W/2, cy = H/2;
let g = '';
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Конфигурация '+t+': '+(t===1?'точечный +q':t===2?'точечный q':t===3?'два одноимённых ++':t===4?'диполь +':'однородное поле')+'</text>';
if(t === 1){
// Точечный +q — радиальные линии наружу
g += PHYS.fieldLinesPointCharge(cx, cy, +1, 140, 16);
g += PHYS.chargeMark(cx, cy, +1, 22, '');
} else if(t === 2){
// Точечный -q — радиальные линии внутрь
g += PHYS.fieldLinesPointCharge(cx, cy, -1, 140, 16);
g += PHYS.chargeMark(cx, cy, -1, 22, '');
} else if(t === 3){
// Два одноимённых +q +q
const x1 = cx - 90, x2 = cx + 90;
// Радиальные линии от каждого, но с пропуском внутреннего сектора (где линии бы отталкивались)
function radial(xC, yC, sign, skipAngleRange){
let s = '';
const N = 14;
const color = sign > 0 ? '#dc2626' : '#2563eb';
for(let i=0;i<N;i++){
const a = 2*Math.PI*i/N;
// пропустить углы, направленные к другому заряду
if(skipAngleRange){
let aDeg = (a*180/Math.PI + 360) % 360;
if(aDeg > skipAngleRange[0] && aDeg < skipAngleRange[1]) continue;
}
const r1 = 18, r2 = 95;
const x1 = xC + r1*Math.cos(a), y1 = yC + r1*Math.sin(a);
const x2 = xC + r2*Math.cos(a), y2 = yC + r2*Math.sin(a);
if(sign > 0) s += PHYS.drawArrow(x1, y1, x2, y2, color, 1.3, 6);
else s += PHYS.drawArrow(x2, y2, x1, y1, color, 1.3, 6);
}
return s;
}
// Левый заряд: пропускаем углы вправо (направление к другому заряду)
g += radial(x1, cy, +1, [340, 360]);
g += radial(x1, cy, +1, [0, 20]);
g += radial(x2, cy, +1, [160, 200]);
// Точка равновесия
g += '<circle cx="'+cx+'" cy="'+cy+'" r="6" fill="none" stroke="#a16207" stroke-width="2" stroke-dasharray="3 2"/>';
g += '<text x="'+cx+'" y="'+(cy-12)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#a16207">E=0</text>';
g += PHYS.chargeMark(x1, cy, +1, 22, '');
g += PHYS.chargeMark(x2, cy, +1, 22, '');
} else if(t === 4){
// Диполь +q, -q
const x1 = cx - 90, x2 = cx + 90;
// Прямая линия между зарядами
g += PHYS.drawArrow(x1+22, cy, x2-22, cy, '#7c3aed', 2.2, 9);
// Дуги: от +q к -q сверху и снизу
function dipoleArc(yOffset, color){
// Кривая Безье из (x1, cy) в (x2, cy) через (cx, cy+yOffset)
// Превратим в полилинию из стрелок
const N = 8;
let prevX = x1+22, prevY = cy;
let s = '';
for(let i=1;i<=N;i++){
const t = i/N;
// Quadratic Bezier
const bx = (1-t)*(1-t)*(x1+22) + 2*(1-t)*t*cx + t*t*(x2-22);
const by = (1-t)*(1-t)*cy + 2*(1-t)*t*(cy+yOffset) + t*t*cy;
if(i === N || i % 2 === 0){
s += PHYS.drawArrow(prevX, prevY, bx, by, color, 1.4, (i===N?7:0));
} else {
s += '<line x1="'+prevX.toFixed(1)+'" y1="'+prevY.toFixed(1)+'" x2="'+bx.toFixed(1)+'" y2="'+by.toFixed(1)+'" stroke="'+color+'" stroke-width="1.4" stroke-linecap="round"/>';
}
prevX = bx; prevY = by;
}
return s;
}
g += dipoleArc(-60, '#7c3aed');
g += dipoleArc(+60, '#7c3aed');
g += dipoleArc(-110, '#7c3aed');
g += dipoleArc(+110, '#7c3aed');
// Линии, уходящие за пределы (от +q налево, от -q направо — наоборот, к -q справа)
// Линия влево от +q
g += PHYS.drawArrow(x1-22, cy, x1-90, cy, '#7c3aed', 1.4, 7);
// Линия вправо к -q (приходит)
g += PHYS.drawArrow(x2+90, cy, x2+22, cy, '#7c3aed', 1.4, 7);
g += PHYS.chargeMark(x1, cy, +1, 22, '');
g += PHYS.chargeMark(x2, cy, -1, 22, '');
} else {
// Однородное поле: параллельные стрелки
// Условные пластины слева и справа
g += '<line x1="40" y1="60" x2="40" y2="300" stroke="#dc2626" stroke-width="4"/>';
g += '<line x1="440" y1="60" x2="440" y2="300" stroke="#2563eb" stroke-width="4"/>';
g += '<text x="40" y="50" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">+</text>';
g += '<text x="440" y="50" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#2563eb"></text>';
// 8 параллельных стрелок
const rows = 7;
for(let i = 0; i < rows; i++){
const y = 90 + i * 32;
g += PHYS.drawArrow(70, y, 410, y, '#7c3aed', 2.0, 9);
}
g += '<text x="240" y="335" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#7c3aed">$\\vec{E}$ = const во всех точках</text>';
}
svg.innerHTML = g;
out.innerHTML = '<b>'+NAMES[t]+'.</b> '+DESC[t];
renderMath(out);
seen.add(t);
if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p20-iv1'); bumpProgress('p20', 15); }
}
tS.addEventListener('input', render);
render();
})();
/* IV2 — Свойства силовых линий (квикфайр да/нет с вариантами) */
(function(){
const Q = [
{ q:'Могут ли две силовые линии пересекаться?', opts:['Да', 'Нет'], ans:1, why:'Нет: в точке пересечения было бы два направления поля одновременно — противоречие.' },
{ q:'Где линии плотнее (гуще)?', opts:['Возле заряда', 'Вдали от заряда'], ans:0, why:'Возле заряда: там $E$ больше, плотность линий пропорциональна $E$.' },
{ q:'Куда направлены линии от положительного заряда $+q$?', opts:['От заряда (наружу)', 'К заряду (внутрь)'], ans:0, why:'От $+q$ линии расходятся наружу.' },
{ q:'Куда направлены линии к отрицательному заряду $-q$?', opts:['От заряда', 'К заряду (внутрь)'], ans:1, why:'К $-q$ линии сходятся внутрь.' },
{ q:'Что показывает густота силовых линий?', opts:['Величину $E$', 'Знак заряда'], ans:0, why:'Густота пропорциональна $E$: где гуще — поле сильнее.' },
{ q:'Как направлены линии в однородном поле?', opts:['Радиально', 'Параллельно друг другу'], ans:1, why:'Параллельно: $\\vec{E}$ одинакова во всех точках по модулю и направлению.' }
];
let i = 0, score = 0;
const qEl = document.getElementById('p20-iv2-q');
const oEl = document.getElementById('p20-iv2-opts');
const fb = document.getElementById('p20-iv2-fb');
const iEl = document.getElementById('p20-iv2-i');
const sEl = document.getElementById('p20-iv2-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p20-iv2'); bumpProgress('p20', 25); }
else if(score >= 4){ addXp(8, 'p20-iv2'); bumpProgress('p20', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = item.opts.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p20-iv2-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV3 — Какая картина соответствует конфигурации? */
(function(){
const OPTS = ['Радиальные от центра', 'Радиальные к центру', 'Диполь (от + к )', 'Параллельные'];
const Q = [
{ q:'Точечный заряд $+q$ в центре.', ans:0, why:'Линии расходятся радиально от $+q$ наружу.' },
{ q:'Точечный заряд $-q$ в центре.', ans:1, why:'Линии сходятся радиально к $-q$ внутрь.' },
{ q:'Два заряда: $+q$ и $-q$.', ans:2, why:'Это классическая картина диполя: линии идут от $+q$ к $-q$.' },
{ q:'Между пластинами заряженного конденсатора.', ans:3, why:'В однородном поле линии параллельны и одинаково расположены.' },
{ q:'Заряженная положительно сфера, точка снаружи.', ans:0, why:'Снаружи сфера действует как точечный заряд: линии радиально от центра.' },
{ q:'Два одноимённых заряда $+q$ и $+q$.', ans:0, why:'От каждого заряда линии расходятся радиально (ближе всего к этой картине). В центре между ними — точка $E = 0$.' }
];
let i = 0, score = 0;
const qEl = document.getElementById('p20-iv3-q');
const oEl = document.getElementById('p20-iv3-opts');
const fb = document.getElementById('p20-iv3-fb');
const iEl = document.getElementById('p20-iv3-i');
const sEl = document.getElementById('p20-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p20-iv3'); bumpProgress('p20', 25); }
else if(score >= 4){ addXp(8, 'p20-iv3'); bumpProgress('p20', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p20-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр поля */
(function(){
const Q = [
{ q:'В однородном поле линии параллельны? Введи $1$ (да) или $2$ (нет).', ans:1, tol:0.1, hint:'Да: однородное поле — параллельные линии.' },
{ q:'В точке между двумя одинаковыми $+q$ зарядами $E = ?$ В/м (для симметрии).', ans:0, tol:0.1, hint:'Поля от одинаковых зарядов компенсируются в середине — $E = 0$.' },
{ q:'$E \\propto 1/r^2$. На каком $r$ (м) напряжённость уменьшится в $9$ раз по сравнению с $r_0 = 1$ м?', ans:3, tol:0.1, hint:'$E \\propto 1/r^2$: в $9$ раз меньше $\\Rightarrow r^2 = 9 \\Rightarrow r = 3$ м.' },
{ q:'Густота линий в точке означает: $1$ = величину $E$, $2$ = направление $E$, $3$ = знак заряда.', ans:1, tol:0.1, hint:'Густота линий пропорциональна модулю $E$.' },
{ q:'Силовая линия в точке указывает направление: $1$ = на источник, $2$ = вектора $\\vec{E}$, $3$ = случайно.', ans:2, tol:0.1, hint:'По определению, касательная к силовой линии в каждой точке совпадает с $\\vec{E}$.' }
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p20-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p20-iv4'); bumpProgress('p20', 25); }
else if(score >= 3){ addXp(8, 'p20-iv4'); bumpProgress('p20', 15); }
return;
}
document.getElementById('p20-iv4-i').textContent = (i+1);
document.getElementById('p20-iv4-s').textContent = score;
document.getElementById('p20-iv4-q').innerHTML = Q[i].q;
document.getElementById('p20-iv4-ans').value = '';
renderMath(document.getElementById('p20-iv4-q'));
document.getElementById('p20-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p20-iv4-fb');
const raw = document.getElementById('p20-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p20-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p20-iv4-go').addEventListener('click', go);
document.getElementById('p20-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p20-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p20');
}
function build_p21(){
const box = document.getElementById('p21-body');
let html = '';
/* THEORY 1 — Работа однородного электростатического поля */
html += makeCard('theory', "Работа однородного электростатического поля", "§21", `
<p>В однородном поле напряжённостью $\\vec{E}$ на заряд $q$ действует сила $\\vec{F} = q\\vec{E}$. При перемещении заряда на расстояние $d$ под углом $\\alpha$ к силе:</p>
<p style="text-align:center;margin:10px 0">$$A = Fd\\cos\\alpha = qEd\\cos\\alpha$$</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Очень важно:</b> работа электростатического поля <b>не зависит от формы траектории</b> — только от <b>начального</b> и <b>конечного</b> положений заряда.</p>
<p style="margin-top:10px">Поле, обладающее этим свойством, называется <b>потенциальным</b> (= консервативным).</p>
<p style="margin-top:10px"><b>Следствие:</b> работа по замкнутой траектории равна нулю — $A_{замкн} = 0$.</p>
`);
/* THEORY 2 — Потенциал */
html += makeCard('rule', "Потенциал электростатического поля", "§21", `
<p><b>Потенциал</b> $\\varphi$ — скалярная энергетическая характеристика электростатического поля в данной точке. Определяется как отношение потенциальной энергии пробного заряда $W_p$ к величине этого заряда:</p>
<p style="text-align:center;margin:10px 0">$$\\varphi = \\dfrac{W_p}{q_{пр}}$$</p>
<p><b>Единица измерения:</b> <b>Вольт</b> (В) $= 1$ Дж / Кл.</p>
<p style="margin-top:10px"><b>Потенциал точечного заряда</b> $q$ на расстоянии $r$:</p>
<p style="text-align:center;margin:10px 0">$$\\varphi = k\\,\\dfrac{q}{r}$$</p>
<p>Знак потенциала <b>совпадает со знаком заряда</b> (для точечного заряда). На бесконечности $\\varphi = 0$.</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Принцип суперпозиции для потенциала:</b> $\\varphi = \\varphi_1 + \\varphi_2 + \\ldots$ — потенциалы складываются <b>алгебраически</b> (со знаками!), не векторно.</p>
`);
/* THEORY 3 — Работа через потенциал */
html += makeCard('example', "Связь работы с потенциалом", "§21", `
<p><b>Работа поля</b> при перемещении заряда $q$ из точки 1 в точку 2:</p>
<p style="text-align:center;margin:10px 0">$$A_{12} = q(\\varphi_1 - \\varphi_2)$$</p>
<p>Это <b>разность потенциалов</b>, умноженная на заряд.</p>
<p style="margin-top:10px"><b>В однородном поле</b>:</p>
<p style="text-align:center;margin:10px 0">$$E = \\dfrac{\\varphi_1 - \\varphi_2}{d} = \\dfrac{U}{d}$$</p>
<p>где $d$ — расстояние между точками вдоль $\\vec{E}$.</p>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0"><b>Пример.</b> Заряд $q = +2$ нКл перенесли из точки с $\\varphi_1 = 100$ В в точку с $\\varphi_2 = 40$ В. Работа поля?<br>
$A = q(\\varphi_1 - \\varphi_2) = 2\\cdot10^{-9} \\cdot 60 = 1{,}2 \\cdot 10^{-7}$ Дж $= 120$ нДж.</p>
<p>$A > 0$ — поле совершило положительную работу: заряд перемещался по направлению $\\vec{E}$, от большего $\\varphi$ к меньшему.</p>
`);
/* INTERACTIVE 1 — Однородное поле и работа */
html += `<div class="wg" id="p21-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Однородное поле: $A = qEd\\cos\\alpha$</div></div>
<div class="wg-help">Меняй $q$, $E$, $d$, угол $\\alpha$ — наблюдай работу поля и направление силы.</div>
<div class="sliders">
<label>$q$: <b id="p21-iv1-qL">+5</b> нКл <input type="range" id="p21-iv1-q" min="-10" max="10" value="5" step="1"></label>
<label>$E$: <b id="p21-iv1-EL">500</b> В/м <input type="range" id="p21-iv1-E" min="100" max="1000" value="500" step="50"></label>
<label>$d$: <b id="p21-iv1-dL">0.10</b> м <input type="range" id="p21-iv1-d" min="0.01" max="0.20" value="0.10" step="0.01"></label>
<label>$\\alpha$: <b id="p21-iv1-aL">0</b>° <input type="range" id="p21-iv1-a" min="0" max="180" value="0" step="15"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p21-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
</div>
<div id="p21-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор потенциала */
html += `<div class="wg" id="p21-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор потенциала точечного заряда</div></div>
<div class="wg-help">$\\varphi = k\\,q / r$. Для двух зарядов: $\\varphi_{сум} = \\varphi_1 + \\varphi_2$ (алгебраически!).</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:end;margin-bottom:10px">
<label>$q_1$ (нКл): <input type="number" id="p21-iv2-q1" class="tinp" value="1" step="0.1" style="width:100%"></label>
<label>$r_1$ (м): <input type="number" id="p21-iv2-r1" class="tinp" value="0.1" step="0.01" min="0.01" style="width:100%"></label>
<label>$q_2$ (нКл, опц.): <input type="number" id="p21-iv2-q2" class="tinp" value="0" step="0.1" style="width:100%"></label>
<label>$r_2$ (м): <input type="number" id="p21-iv2-r2" class="tinp" value="0.1" step="0.01" min="0.01" style="width:100%"></label>
</div>
<div style="display:flex;justify-content:center;margin-bottom:10px">
<button class="btn primary" id="p21-iv2-go">Вычислить $\\varphi$</button>
</div>
<div id="p21-iv2-out" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.95rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — Работа поля + или − ? */
html += `<div class="wg" id="p21-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Работа поля: $+$, $-$ или $0$?</div></div>
<div class="wg-help">Определи знак работы электростатического поля в каждой ситуации.</div>
<div class="score-display"><span>Задача <b id="p21-iv3-i">1</b> / 6</span><span>Очки: <b id="p21-iv3-s">0</b> / 6</span></div>
<div id="p21-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p21-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px"></div>
<div class="feedback" id="p21-iv3-fb"></div>
<div class="actions"><button class="btn" id="p21-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр работы и потенциала */
html += `<div class="wg" id="p21-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр работы и потенциала</div></div>
<div class="wg-help">5 задач. $k = 9 \\cdot 10^9$. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p21-iv4-i">1</b> / 5</span><span>Очки: <b id="p21-iv4-s">0</b> / 5</span></div>
<div id="p21-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p21-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p21-iv4-go">Проверить</button>
<button class="btn" id="p21-iv4-start">Заново</button>
</div>
<div class="feedback" id="p21-iv4-fb"></div>
</div>`;
html += secNav('p20', 'p22');
html += readButton('p21');
box.innerHTML = html;
renderMath(box);
/* IV1 — Однородное поле: A = qEd cos α */
(function(){
const svg = document.getElementById('p21-iv1-svg');
const qS = document.getElementById('p21-iv1-q');
const ES = document.getElementById('p21-iv1-E');
const dS = document.getElementById('p21-iv1-d');
const aS = document.getElementById('p21-iv1-a');
const qL = document.getElementById('p21-iv1-qL');
const EL = document.getElementById('p21-iv1-EL');
const dL = document.getElementById('p21-iv1-dL');
const aL = document.getElementById('p21-iv1-aL');
const out = document.getElementById('p21-iv1-out');
const seen = new Set();
let _done = false;
function fmtQ(q){ return (q>=0?'+':'') + q; }
function render(){
const q = +qS.value, E = +ES.value, d = +dS.value, aDeg = +aS.value;
qL.textContent = fmtQ(q); EL.textContent = E; dL.textContent = d.toFixed(2); aL.textContent = aDeg;
const W = 480, H = 280, cy = H/2;
let g = '';
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Однородное поле: $A = qEd\\cos\\alpha$</text>';
// Поле — 5 параллельных стрелок (горизонтально вправо)
const fieldRows = [70, 110, 150, 190, 230];
for(let i = 0; i < fieldRows.length; i++){
g += PHYS.drawArrow(40, fieldRows[i], 440, fieldRows[i], '#a78bfa', 1.4, 7);
}
g += '<text x="44" y="60" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#7c3aed">$\\vec{E}$</text>';
// Начальная и конечная точки заряда
const x0 = 180, y0 = cy;
const Dpx = 130 * (d / 0.2); // длина в px, max 130
const aRad = aDeg * Math.PI / 180;
const x1 = x0 + Dpx * Math.cos(aRad);
const y1 = y0 - Dpx * Math.sin(aRad); // y инвертирована
// Стрелка перемещения d (зелёная)
g += PHYS.drawArrow(x0, y0, x1, y1, '#10b981', 2.6, 10);
// Подпись d
const midX = (x0+x1)/2, midY = (y0+y1)/2;
g += '<text x="'+midX+'" y="'+(midY-10)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#10b981">d</text>';
// Сила F = qE (если q>0 — вправо, если q<0 — влево)
const Fdir = q > 0 ? 1 : (q < 0 ? -1 : 0);
if(Fdir !== 0){
const Flen = 70;
const fx0 = x0, fy0 = y0;
const fx1 = x0 + Fdir * Flen, fy1 = y0;
g += PHYS.drawArrow(fx0, fy0, fx1, fy1, '#dc2626', 2.6, 10);
g += '<text x="'+(fx1 + Fdir*8)+'" y="'+(fy1+4)+'" text-anchor="'+(Fdir>0?'start':'end')+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#dc2626">$\\vec{F}$</text>';
}
// Заряд в начальной точке
if(q === 0){
g += '<circle cx="'+x0+'" cy="'+y0+'" r="14" fill="#e5e7eb" stroke="#94a3b8" stroke-width="2"/>';
g += '<text x="'+x0+'" y="'+(y0+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#64748b">0</text>';
} else {
g += PHYS.chargeMark(x0, y0, q > 0 ? 1 : -1, 14, '');
}
// Подпись заряда
g += '<text x="'+x0+'" y="'+(y0+30)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#0f172a">q = '+fmtQ(q)+' нКл</text>';
// Угол α — дуга
if(aDeg > 0 && aDeg < 180 && Dpx > 30){
const Rarc = 28;
const startA = 0, endA = -aRad;
const lx1 = x0 + Rarc, ly1 = y0;
const lx2 = x0 + Rarc*Math.cos(aRad), ly2 = y0 - Rarc*Math.sin(aRad);
g += '<path d="M '+lx1+' '+ly1+' A '+Rarc+' '+Rarc+' 0 0 0 '+lx2+' '+ly2+'" fill="none" stroke="#7c3aed" stroke-width="1.5"/>';
const midA = aRad/2;
g += '<text x="'+(x0 + (Rarc+8)*Math.cos(midA))+'" y="'+(y0 - (Rarc+8)*Math.sin(midA) + 4)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#7c3aed">α</text>';
}
svg.innerHTML = g;
// Расчёт
const qC = q * 1e-9;
const F = Math.abs(qC) * E;
// A = qEd cos α (сохраняем знак q)
const A = qC * E * d * Math.cos(aRad);
const Annano = A * 1e9; // нДж
let sign = '';
if(Math.abs(A) < 1e-15){ sign = ' (заряд $0$ или $\\alpha = 90°$)'; }
else if(A > 0){ sign = ' &nbsp;<span style="color:#10b981">($A > 0$, поле совершает работу)</span>'; }
else { sign = ' &nbsp;<span style="color:#dc2626">($A < 0$, против поля)</span>'; }
out.innerHTML = '<b>$F = |q|E = '+F.toExponential(2)+'$ Н</b> &nbsp; '
+ '<b>$A = qEd\\cos\\alpha = '+Annano.toFixed(1)+'$ нДж</b>' + sign;
renderMath(out);
seen.add(q+':'+E+':'+d.toFixed(2)+':'+aDeg);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p21-iv1'); bumpProgress('p21', 15); }
}
qS.addEventListener('input', render);
ES.addEventListener('input', render);
dS.addEventListener('input', render);
aS.addEventListener('input', render);
render();
})();
/* IV2 — Калькулятор потенциала */
(function(){
const k = PHYS.CONST.k;
const out = document.getElementById('p21-iv2-out');
const seen = new Set();
let _done = false;
function calc(){
const q1 = parseFloat(document.getElementById('p21-iv2-q1').value) || 0;
const r1 = parseFloat(document.getElementById('p21-iv2-r1').value) || 0.01;
const q2 = parseFloat(document.getElementById('p21-iv2-q2').value) || 0;
const r2 = parseFloat(document.getElementById('p21-iv2-r2').value) || 0.01;
const phi1 = k * (q1 * 1e-9) / r1;
const phi2 = (q2 !== 0) ? k * (q2 * 1e-9) / r2 : 0;
const phiSum = phi1 + phi2;
let html = '';
html += '<div><b>$\\varphi_1 = k\\,q_1/r_1 = 9\\cdot10^9 \\cdot ('+q1+'\\cdot10^{-9})/'+r1+' = '+phi1.toFixed(1)+'$ В</b></div>';
if(q2 !== 0){
html += '<div style="margin-top:6px"><b>$\\varphi_2 = k\\,q_2/r_2 = '+phi2.toFixed(1)+'$ В</b></div>';
html += '<div style="margin-top:8px;font-size:1.05rem;color:#7c3aed"><b>$\\varphi = \\varphi_1 + \\varphi_2 = '+phiSum.toFixed(1)+'$ В</b></div>';
} else {
html += '<div style="margin-top:6px;font-size:.88rem;color:#64748b">Введи $q_2 \\ne 0$ для суперпозиции потенциалов.</div>';
}
out.innerHTML = html;
renderMath(out);
seen.add(q1+':'+r1+':'+q2+':'+r2);
if(!_done && seen.size >= 3){ _done = true; addXp(10, 'p21-iv2'); bumpProgress('p21', 15); }
}
document.getElementById('p21-iv2-go').addEventListener('click', calc);
['p21-iv2-q1','p21-iv2-r1','p21-iv2-q2','p21-iv2-r2'].forEach(id => {
document.getElementById(id).addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
});
calc();
})();
/* IV3 — Работа поля + или − ? */
(function(){
const OPTS = ['$A > 0$', '$A < 0$', '$A = 0$'];
const Q = [
{ q:'Заряд $+q$ перемещается по направлению $\\vec{E}$.', ans:0, why:'Сила $\\vec{F} = q\\vec{E}$ сонаправлена с перемещением $\\Rightarrow A > 0$.' },
{ q:'Заряд $+q$ перемещается против $\\vec{E}$.', ans:1, why:'$\\vec{F}$ противоположна перемещению $\\Rightarrow A < 0$.' },
{ q:'Заряд $-q$ перемещается по направлению $\\vec{E}$.', ans:1, why:'У $-q$ сила $\\vec{F} = q\\vec{E}$ направлена против $\\vec{E}$, значит против перемещения $\\Rightarrow A < 0$.' },
{ q:'Заряд $-q$ перемещается против $\\vec{E}$.', ans:0, why:'У $-q$ сила направлена против $\\vec{E}$, то есть по направлению перемещения $\\Rightarrow A > 0$.' },
{ q:'Заряд перемещается перпендикулярно $\\vec{E}$ ($\\alpha = 90°$).', ans:2, why:'$A = qEd\\cos 90° = 0$.' },
{ q:'Заряд вернулся в исходную точку по замкнутой траектории.', ans:2, why:'Поле потенциально: $A_{замкн} = 0$ для любой замкнутой траектории.' }
];
let i = 0, score = 0;
const qEl = document.getElementById('p21-iv3-q');
const oEl = document.getElementById('p21-iv3-opts');
const fb = document.getElementById('p21-iv3-fb');
const iEl = document.getElementById('p21-iv3-i');
const sEl = document.getElementById('p21-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p21-iv3'); bumpProgress('p21', 25); }
else if(score >= 4){ addXp(8, 'p21-iv3'); bumpProgress('p21', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1800);
});
});
}
document.getElementById('p21-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр работы и потенциала */
(function(){
const Q = [
{ q:'Заряд $q = +1$ мкКл перенесли из точки с $\\varphi_1 = 50$ В в точку с $\\varphi_2 = 20$ В. Работа в мкДж?', ans:30, tol:1.5, hint:'$A = q(\\varphi_1 - \\varphi_2) = 10^{-6}\\cdot 30 = 30$ мкДж.' },
{ q:'Однородное поле $E = 500$ В/м. Заряд $+2$ нКл перемещают на $10$ см по направлению поля. Работа в нДж?', ans:100, tol:5, hint:'$A = qEd = 2\\cdot10^{-9}\\cdot 500\\cdot 0{,}1 = 10^{-7}$ Дж $= 100$ нДж.' },
{ q:'Потенциал поля точечного заряда $+1$ нКл на расстоянии $r = 0{,}1$ м? (В)', ans:90, tol:5, hint:'$\\varphi = kq/r = 9\\cdot10^9\\cdot 10^{-9}/0{,}1 = 90$ В.' },
{ q:'Заряд $-2$ нКл. Потенциал на расстоянии $0{,}05$ м? (В)', ans:-360, tol:20, hint:'$\\varphi = kq/r = 9\\cdot10^9\\cdot(-2\\cdot10^{-9})/0{,}05 = -360$ В.' },
{ q:'Однородное поле $E = 1000$ В/м между пластинами на расстоянии $d = 0{,}05$ м. Разность потенциалов $U$ в В?', ans:50, tol:3, hint:'$U = Ed = 1000\\cdot 0{,}05 = 50$ В.' }
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p21-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p21-iv4'); bumpProgress('p21', 25); }
else if(score >= 3){ addXp(8, 'p21-iv4'); bumpProgress('p21', 15); }
return;
}
document.getElementById('p21-iv4-i').textContent = (i+1);
document.getElementById('p21-iv4-s').textContent = score;
document.getElementById('p21-iv4-q').innerHTML = Q[i].q;
document.getElementById('p21-iv4-ans').value = '';
renderMath(document.getElementById('p21-iv4-q'));
document.getElementById('p21-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p21-iv4-fb');
const raw = document.getElementById('p21-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p21-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p21-iv4-go').addEventListener('click', go);
document.getElementById('p21-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p21-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p21');
}
function build_p22(){
const box = document.getElementById('p22-body');
let html = '';
/* THEORY 1 — Разность потенциалов и напряжение */
html += makeCard('theory', "Разность потенциалов и напряжение", "§22", `
<p><b>Напряжение</b> $U$ между двумя точками 1 и 2 электростатического поля — это <b>разность потенциалов</b> этих точек:</p>
<p style="text-align:center;margin:10px 0">$$U_{12} = \\varphi_1 - \\varphi_2$$</p>
<p><b>Единица измерения:</b> <b>Вольт</b> (В) $= 1$ Дж / 1 Кл.</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px">$1$ В — это напряжение, при котором поле совершает работу $1$ Дж при переносе заряда $1$ Кл из одной точки в другую.</p>
<p style="margin-top:10px"><b>Работа поля</b> при перемещении заряда $q$ из точки 1 в точку 2:</p>
<p style="text-align:center;margin:10px 0">$$A_{12} = q U_{12} = q(\\varphi_1 - \\varphi_2)$$</p>
<p>Если $A > 0$ — поле помогает перемещению. Если $A < 0$ — поле сопротивляется (нужно работать против поля).</p>
<p style="margin-top:10px">Напряжение измеряется <b>вольтметром</b> (подключается между двумя точками).</p>
`);
/* THEORY 2 — Связь U и E в однородном поле */
html += makeCard('rule', "Связь напряжения с напряжённостью", "§22", `
<p>В <b>однородном поле</b> (например, между обкладок плоского конденсатора) напряжение и напряжённость связаны формулой:</p>
<p style="text-align:center;margin:10px 0">$$U = E \\cdot d$$</p>
<p>где:</p>
<ul style="margin:6px 0 6px 18px">
<li>$U$ — напряжение в В</li>
<li>$E$ — напряжённость в В/м</li>
<li>$d$ — расстояние между точками <b>вдоль направления $\\vec{E}$</b>, в м</li>
</ul>
<p>Если перемещение перпендикулярно $\\vec{E}$ — напряжение между точками равно нулю.</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px">Эта формула — главный инструмент для расчётов в однородных полях: конденсаторы, поле над поверхностью, поле между параллельными пластинами.</p>
<p style="margin-top:10px">Отсюда удобная форма для напряжённости: $E = U/d$ — поэтому единица $\\vec{E}$ — <b>В/м</b>.</p>
`);
/* THEORY 3 — Эквипотенциальные поверхности */
html += makeCard('example', "Эквипотенциальные поверхности", "§22", `
<p><b>Эквипотенциальная поверхность</b> — поверхность, на которой потенциал во всех точках одинаков ($\\varphi = $ const).</p>
<p><b>Свойства:</b></p>
<ul style="margin:6px 0 6px 18px">
<li>Силовые линии $\\vec{E}$ <b>перпендикулярны</b> эквипотенциальным поверхностям.</li>
<li>Работа поля при перемещении вдоль эквипотенциальной поверхности <b>равна нулю</b>: $\\Delta\\varphi = 0 \\Rightarrow A = q\\Delta\\varphi = 0$.</li>
<li>В <b>однородном поле</b> эквипотенциальные поверхности — параллельные плоскости.</li>
<li>Вокруг <b>точечного заряда</b> — концентрические сферы.</li>
<li>Поверхность <b>проводника</b> — эквипотенциальная (в электростатике).</li>
</ul>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0"><b>Пример.</b> Между параллельными пластинами заряженного конденсатора эквипотенциальные поверхности — плоскости, параллельные пластинам. Чем ближе к положительной пластине — тем выше $\\varphi$.</p>
`);
/* INTERACTIVE 1 — Связь U и E в однородном поле */
html += `<div class="wg" id="p22-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Связь $U$ и $E$ в однородном поле</div></div>
<div class="wg-help">Меняй напряжение $U$ и расстояние $d$ между пластинами. Наблюдай $E = U/d$ — ширина стрелок $\\vec{E}$ пропорциональна напряжённости.</div>
<div class="sliders">
<label>$U$: <b id="p22-iv1-UL">100</b> В <input type="range" id="p22-iv1-U" min="10" max="500" value="100" step="10"></label>
<label>$d$: <b id="p22-iv1-dL">0.05</b> м <input type="range" id="p22-iv1-d" min="0.01" max="0.20" value="0.05" step="0.01"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p22-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
</div>
<div id="p22-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор работы поля */
html += `<div class="wg" id="p22-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор работы поля</div></div>
<div class="wg-help">Режим A: $A = q(\\varphi_1 - \\varphi_2)$. Режим B: $A = qEd$ в однородном поле.</div>
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px">
<button class="btn primary" id="p22-iv2-mA">Режим A: $\\varphi_1, \\varphi_2$</button>
<button class="btn" id="p22-iv2-mB">Режим B: $E, d$</button>
</div>
<div id="p22-iv2-inA" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;align-items:end;margin-bottom:10px">
<label>$q$ (нКл): <input type="number" id="p22-iv2-q" class="tinp" value="2" step="0.5" style="width:100%"></label>
<label>$\\varphi_1$ (В): <input type="number" id="p22-iv2-f1" class="tinp" value="100" step="10" style="width:100%"></label>
<label>$\\varphi_2$ (В): <input type="number" id="p22-iv2-f2" class="tinp" value="40" step="10" style="width:100%"></label>
</div>
<div id="p22-iv2-inB" style="display:none;grid-template-columns:1fr 1fr 1fr;gap:10px;align-items:end;margin-bottom:10px">
<label>$q$ (нКл): <input type="number" id="p22-iv2-qB" class="tinp" value="2" step="0.5" style="width:100%"></label>
<label>$E$ (В/м): <input type="number" id="p22-iv2-E" class="tinp" value="500" step="50" style="width:100%"></label>
<label>$d$ (м): <input type="number" id="p22-iv2-d" class="tinp" value="0.1" step="0.01" min="0.001" style="width:100%"></label>
</div>
<div style="display:flex;justify-content:center;margin-bottom:10px">
<button class="btn primary" id="p22-iv2-go">Вычислить $A$</button>
</div>
<div id="p22-iv2-out" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.95rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — Знак напряжения */
html += `<div class="wg" id="p22-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">$U$ положительно, отрицательно или ноль?</div></div>
<div class="wg-help">Определи знак напряжения $U_{12} = \\varphi_1 - \\varphi_2$.</div>
<div class="score-display"><span>Задача <b id="p22-iv3-i">1</b> / 6</span><span>Очки: <b id="p22-iv3-s">0</b> / 6</span></div>
<div id="p22-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p22-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px"></div>
<div class="feedback" id="p22-iv3-fb"></div>
<div class="actions"><button class="btn" id="p22-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр напряжения */
html += `<div class="wg" id="p22-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр напряжения</div></div>
<div class="wg-help">5 задач. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p22-iv4-i">1</b> / 5</span><span>Очки: <b id="p22-iv4-s">0</b> / 5</span></div>
<div id="p22-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p22-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p22-iv4-go">Проверить</button>
<button class="btn" id="p22-iv4-start">Заново</button>
</div>
<div class="feedback" id="p22-iv4-fb"></div>
</div>`;
html += secNav('p21', 'p23');
html += readButton('p22');
box.innerHTML = html;
renderMath(box);
/* IV1 — Связь U и E в однородном поле */
(function(){
const svg = document.getElementById('p22-iv1-svg');
const US = document.getElementById('p22-iv1-U');
const dS = document.getElementById('p22-iv1-d');
const UL = document.getElementById('p22-iv1-UL');
const dL = document.getElementById('p22-iv1-dL');
const out = document.getElementById('p22-iv1-out');
const seen = new Set();
let _done = false;
function render(){
const U = +US.value, d = +dS.value;
UL.textContent = U; dL.textContent = d.toFixed(2);
const E = U / d;
const W = 480, H = 280;
// Две пластины: верхняя "+", нижняя "". Расстояние px зависит от d.
const yMid = 140;
const platePx = Math.max(30, Math.min(180, d * 900)); // визуальное расстояние
const yTop = yMid - platePx/2;
const yBot = yMid + platePx/2;
const xL = 80, xR = 400;
let g = '';
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Однородное поле: $U = E\\cdot d$</text>';
// Верхняя пластина (+)
g += '<line x1="'+xL+'" y1="'+yTop+'" x2="'+xR+'" y2="'+yTop+'" stroke="#dc2626" stroke-width="4"/>';
g += '<text x="'+(xL-12)+'" y="'+(yTop+5)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#dc2626">+</text>';
// Нижняя пластина (−)
g += '<line x1="'+xL+'" y1="'+yBot+'" x2="'+xR+'" y2="'+yBot+'" stroke="#2563eb" stroke-width="4"/>';
g += '<text x="'+(xL-12)+'" y="'+(yBot+5)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#2563eb"></text>';
// Линии E между пластинами — толщина ~ E (от 0.6 до 4)
const Emin = 10/0.20, Emax = 500/0.01;
const wE = 0.6 + 3.4 * (Math.min(E, Emax) - Emin) / (Emax - Emin);
const sw = Math.max(0.8, Math.min(4, wE));
const arrowCount = 6;
for(let i = 0; i < arrowCount; i++){
const x = xL + 40 + (xR - xL - 80) * i / (arrowCount-1);
g += PHYS.drawArrow(x, yTop+6, x, yBot-6, '#a78bfa', sw, 7);
}
g += '<text x="'+(xR+12)+'" y="'+yMid+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#7c3aed">$\\vec{E}$</text>';
// Метка d (вертикальная)
g += '<line x1="'+(xR+45)+'" y1="'+yTop+'" x2="'+(xR+45)+'" y2="'+yBot+'" stroke="#10b981" stroke-width="1.6" stroke-dasharray="3,3"/>';
g += '<line x1="'+(xR+40)+'" y1="'+yTop+'" x2="'+(xR+50)+'" y2="'+yTop+'" stroke="#10b981" stroke-width="1.6"/>';
g += '<line x1="'+(xR+40)+'" y1="'+yBot+'" x2="'+(xR+50)+'" y2="'+yBot+'" stroke="#10b981" stroke-width="1.6"/>';
g += '<text x="'+(xR+58)+'" y="'+(yMid+4)+'" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#10b981">d</text>';
// Эквипотенциали — несколько горизонтальных пунктирных
const equipCount = 3;
for(let i = 1; i <= equipCount; i++){
const yE = yTop + (yBot-yTop) * i / (equipCount+1);
g += '<line x1="'+(xL+10)+'" y1="'+yE+'" x2="'+(xR-10)+'" y2="'+yE+'" stroke="#94a3b8" stroke-width="0.8" stroke-dasharray="4,4"/>';
}
g += '<text x="'+(xL+8)+'" y="'+(yBot+22)+'" font-family="Inter,sans-serif" font-size="10" fill="#64748b">эквипотенциали ⊥ $\\vec{E}$</text>';
svg.innerHTML = g;
out.innerHTML = '<b>$E = U/d = '+U+'/'+d.toFixed(2)+' = '+E.toFixed(0)+'$ В/м</b> &nbsp; '
+ '<span style="color:#64748b">(чем больше $U$ или меньше $d$ — тем сильнее поле)</span>';
renderMath(out);
seen.add(U+':'+d.toFixed(2));
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p22-iv1'); bumpProgress('p22', 15); }
}
US.addEventListener('input', render);
dS.addEventListener('input', render);
render();
})();
/* IV2 — Калькулятор работы поля */
(function(){
let mode = 'A';
const inA = document.getElementById('p22-iv2-inA');
const inB = document.getElementById('p22-iv2-inB');
const mA = document.getElementById('p22-iv2-mA');
const mB = document.getElementById('p22-iv2-mB');
const out = document.getElementById('p22-iv2-out');
const seen = new Set();
let _done = false;
function setMode(m){
mode = m;
if(m === 'A'){
inA.style.display = 'grid'; inB.style.display = 'none';
mA.classList.add('primary'); mB.classList.remove('primary');
} else {
inA.style.display = 'none'; inB.style.display = 'grid';
mB.classList.add('primary'); mA.classList.remove('primary');
}
calc();
}
function calc(){
let html = '';
if(mode === 'A'){
const q = parseFloat(document.getElementById('p22-iv2-q').value) || 0;
const f1 = parseFloat(document.getElementById('p22-iv2-f1').value) || 0;
const f2 = parseFloat(document.getElementById('p22-iv2-f2').value) || 0;
const U = f1 - f2;
const A = (q * 1e-9) * U;
const Annano = A * 1e9;
html += '<div><b>$U = \\varphi_1 - \\varphi_2 = '+f1+' - '+f2+' = '+U.toFixed(1)+'$ В</b></div>';
html += '<div style="margin-top:6px"><b>$A = qU = '+q+'\\cdot 10^{-9} \\cdot '+U.toFixed(1)+' = '+Annano.toFixed(2)+'$ нДж</b></div>';
if(A > 0) html += '<div style="margin-top:6px;color:#10b981">$A > 0$: поле совершает положительную работу.</div>';
else if(A < 0) html += '<div style="margin-top:6px;color:#dc2626">$A < 0$: поле сопротивляется — нужно работать против поля.</div>';
else html += '<div style="margin-top:6px;color:#64748b">$A = 0$: точки на одной эквипотенциали.</div>';
seen.add('A:'+q+':'+f1+':'+f2);
} else {
const q = parseFloat(document.getElementById('p22-iv2-qB').value) || 0;
const E = parseFloat(document.getElementById('p22-iv2-E').value) || 0;
const d = parseFloat(document.getElementById('p22-iv2-d').value) || 0;
const U = E * d;
const A = (q * 1e-9) * U;
const Annano = A * 1e9;
html += '<div><b>$U = Ed = '+E+'\\cdot '+d+' = '+U.toFixed(2)+'$ В</b></div>';
html += '<div style="margin-top:6px"><b>$A = qEd = '+q+'\\cdot 10^{-9} \\cdot '+E+' \\cdot '+d+' = '+Annano.toFixed(2)+'$ нДж</b></div>';
seen.add('B:'+q+':'+E+':'+d);
}
out.innerHTML = html;
renderMath(out);
if(!_done && seen.size >= 3){ _done = true; addXp(10, 'p22-iv2'); bumpProgress('p22', 15); }
}
mA.addEventListener('click', () => setMode('A'));
mB.addEventListener('click', () => setMode('B'));
document.getElementById('p22-iv2-go').addEventListener('click', calc);
['p22-iv2-q','p22-iv2-f1','p22-iv2-f2','p22-iv2-qB','p22-iv2-E','p22-iv2-d'].forEach(id => {
const el = document.getElementById(id);
if(el) el.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
});
setMode('A');
})();
/* IV3 — Знак напряжения */
(function(){
const OPTS = ['$U > 0$', '$U < 0$', '$U = 0$'];
const Q = [
{ q:'$\\varphi_1 = 100$ В, $\\varphi_2 = 50$ В. Чему равно $U_{12}$?', ans:0, why:'$U = 100 - 50 = 50 > 0$.' },
{ q:'$\\varphi_1 = 30$ В, $\\varphi_2 = 80$ В.', ans:1, why:'$U = 30 - 80 = -50 < 0$.' },
{ q:'$\\varphi_1 = 50$ В, $\\varphi_2 = 50$ В.', ans:2, why:'$U = 50 - 50 = 0$ — на одной эквипотенциали.' },
{ q:'Точки 1 и 2 лежат на одной эквипотенциальной поверхности.', ans:2, why:'$\\varphi_1 = \\varphi_2 \\Rightarrow U = 0$.' },
{ q:'В направлении $\\vec{E}$ на расстоянии $d = 0{,}1$ м при $E = 1000$ В/м.', ans:0, why:'$U = Ed = 100$ В $> 0$ (потенциал падает вдоль $\\vec{E}$).' },
{ q:'Перемещение между двумя точками — строго перпендикулярно $\\vec{E}$.', ans:2, why:'Это вдоль эквипотенциали $\\Rightarrow U = 0$.' }
];
let i = 0, score = 0;
const qEl = document.getElementById('p22-iv3-q');
const oEl = document.getElementById('p22-iv3-opts');
const fb = document.getElementById('p22-iv3-fb');
const iEl = document.getElementById('p22-iv3-i');
const sEl = document.getElementById('p22-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p22-iv3'); bumpProgress('p22', 25); }
else if(score >= 4){ addXp(8, 'p22-iv3'); bumpProgress('p22', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1800);
});
});
}
document.getElementById('p22-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр напряжения */
(function(){
const Q = [
{ q:'$\\varphi_1 = 100$ В, $\\varphi_2 = 40$ В. Напряжение $U_{12}$ в В?', ans:60, tol:3, hint:'$U = \\varphi_1 - \\varphi_2 = 100 - 40 = 60$ В.' },
{ q:'Однородное поле $E = 500$ В/м между пластинами $d = 0{,}02$ м. Напряжение $U$ в В?', ans:10, tol:0.5, hint:'$U = Ed = 500\\cdot 0{,}02 = 10$ В.' },
{ q:'Заряд $+2$ мкКл перенесли при $U = 50$ В. Работа поля в мкДж?', ans:100, tol:5, hint:'$A = qU = 2\\cdot 10^{-6}\\cdot 50 = 10^{-4}$ Дж $= 100$ мкДж.' },
{ q:'$U = 1000$ В между пластинами на $d = 0{,}05$ м. Напряжённость $E$ в В/м?', ans:20000, tol:500, hint:'$E = U/d = 1000/0{,}05 = 20\\,000$ В/м.' },
{ q:'Какова работа поля при перемещении заряда вдоль эквипотенциальной поверхности? (Дж)', ans:0, tol:0.0001, hint:'$\\Delta\\varphi = 0 \\Rightarrow A = q\\Delta\\varphi = 0$.' }
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p22-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p22-iv4'); bumpProgress('p22', 25); }
else if(score >= 3){ addXp(8, 'p22-iv4'); bumpProgress('p22', 15); }
return;
}
document.getElementById('p22-iv4-i').textContent = (i+1);
document.getElementById('p22-iv4-s').textContent = score;
document.getElementById('p22-iv4-q').innerHTML = Q[i].q;
document.getElementById('p22-iv4-ans').value = '';
renderMath(document.getElementById('p22-iv4-q'));
document.getElementById('p22-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p22-iv4-fb');
const raw = document.getElementById('p22-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p22-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p22-iv4-go').addEventListener('click', go);
document.getElementById('p22-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p22-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p22');
}
function build_p23(){
const box = document.getElementById('p23-body');
let html = '';
/* THEORY 1 — Конденсатор и электроёмкость */
html += makeCard('theory', "Конденсатор и электроёмкость", "§23", `
<p><b>Конденсатор</b> — устройство из двух проводников (обкладок), разделённых диэлектриком, предназначенное для накопления электрического заряда и энергии.</p>
<p>Заряд конденсатора $q$ — это <b>модуль</b> заряда на одной обкладке (на другой обкладке заряд $-q$). Между обкладками возникает напряжение $U$.</p>
<p style="margin-top:10px"><b>Электроёмкость</b> $C$ — отношение заряда обкладки к напряжению между обкладками:</p>
<p style="text-align:center;margin:10px 0">$$C = \\dfrac{q}{U}$$</p>
<p>$C$ — характеристика самого конденсатора (не зависит от $q$ и $U$, только от геометрии и диэлектрика).</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Единица:</b> <b>Фарад</b> (Ф) $= 1$ Кл / 1 В. Это очень большая ёмкость! На практике:</p>
<ul style="margin:6px 0 6px 18px">
<li>мкФ ($\\mu$Ф) $= 10^{-6}$ Ф</li>
<li>нФ $= 10^{-9}$ Ф</li>
<li>пФ $= 10^{-12}$ Ф</li>
</ul>
`);
/* THEORY 2 — Плоский конденсатор */
html += makeCard('rule', "Плоский конденсатор", "§23", `
<p><b>Плоский конденсатор</b> состоит из двух параллельных пластин площадью $S$, разделённых диэлектриком толщины $d$.</p>
<p style="text-align:center;margin:10px 0">$$C = \\dfrac{\\varepsilon \\varepsilon_0 S}{d}$$</p>
<p>где:</p>
<ul style="margin:6px 0 6px 18px">
<li>$\\varepsilon_0 = 8{,}85 \\cdot 10^{-12}$ Ф/м — электрическая постоянная</li>
<li>$\\varepsilon$ — относительная диэлектрическая проницаемость диэлектрика (для воздуха $\\approx 1$, для слюды $\\approx 6$, керамики до 1000)</li>
<li>$S$ — площадь пластины в м²</li>
<li>$d$ — расстояние между пластинами в м</li>
</ul>
<p style="margin-top:10px;padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Способы увеличить $C$:</b></p>
<ul style="margin:6px 0 6px 18px">
<li>Увеличить площадь $S$.</li>
<li>Уменьшить расстояние $d$.</li>
<li>Использовать диэлектрик с большим $\\varepsilon$.</li>
</ul>
`);
/* THEORY 3 — Соединения конденсаторов */
html += makeCard('example', "Соединения конденсаторов", "§23", `
<p><b>Параллельное соединение:</b></p>
<ul style="margin:6px 0 6px 18px">
<li>Напряжение одинаково: $U = U_1 = U_2 = \\ldots$</li>
<li>Заряды складываются: $q = q_1 + q_2 + \\ldots$</li>
<li><b>Ёмкости складываются:</b> $C_{общ} = C_1 + C_2 + \\ldots$</li>
</ul>
<p style="margin-top:10px"><b>Последовательное соединение:</b></p>
<ul style="margin:6px 0 6px 18px">
<li>Заряд одинаков: $q = q_1 = q_2 = \\ldots$</li>
<li>Напряжения складываются: $U = U_1 + U_2 + \\ldots$</li>
<li><b>Складываются обратные ёмкости:</b></li>
</ul>
<p style="text-align:center;margin:10px 0">$$\\dfrac{1}{C_{общ}} = \\dfrac{1}{C_1} + \\dfrac{1}{C_2} + \\ldots$$</p>
<p>Для двух последовательно соединённых конденсаторов: $C_{общ} = \\dfrac{C_1 C_2}{C_1 + C_2}$.</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Применение:</b> в радио и электронике для фильтрации и сглаживания сигналов, в импульсных устройствах (фотовспышка), в фильтрах источников питания, в компьютерной памяти.</p>
`);
/* INTERACTIVE 1 — Конструктор плоского конденсатора */
html += `<div class="wg" id="p23-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор плоского конденсатора</div></div>
<div class="wg-help">Меняй площадь пластин $S$, расстояние $d$ и диэлектрик. Наблюдай $C = \\varepsilon\\varepsilon_0 S/d$.</div>
<div class="sliders">
<label>$S$: <b id="p23-iv1-SL">0.010</b> м² <input type="range" id="p23-iv1-S" min="0.001" max="0.100" value="0.010" step="0.001"></label>
<label>$d$: <b id="p23-iv1-dL">0.005</b> м <input type="range" id="p23-iv1-d" min="0.001" max="0.010" value="0.005" step="0.0005"></label>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin:8px 0">
<button class="btn primary" id="p23-iv1-eA">Воздух $\\varepsilon = 1$</button>
<button class="btn" id="p23-iv1-eM">Слюда $\\varepsilon = 6$</button>
<button class="btn" id="p23-iv1-eC">Керамика $\\varepsilon = 100$</button>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p23-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
</div>
<div id="p23-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор соединений */
html += `<div class="wg" id="p23-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор соединений конденсаторов</div></div>
<div class="wg-help">Параллельно: $C = C_1 + C_2$. Последовательно: $1/C = 1/C_1 + 1/C_2$.</div>
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px">
<button class="btn primary" id="p23-iv2-mP">Параллельно</button>
<button class="btn" id="p23-iv2-mS">Последовательно</button>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:end;margin-bottom:10px">
<label>$C_1$ (мкФ): <input type="number" id="p23-iv2-C1" class="tinp" value="2" step="0.5" min="0.1" style="width:100%"></label>
<label>$C_2$ (мкФ): <input type="number" id="p23-iv2-C2" class="tinp" value="6" step="0.5" min="0.1" style="width:100%"></label>
</div>
<div style="display:flex;justify-content:center;margin-bottom:10px">
<button class="btn primary" id="p23-iv2-go">Вычислить $C_{общ}$</button>
</div>
<div id="p23-iv2-out" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.95rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — Что увеличит ёмкость? */
html += `<div class="wg" id="p23-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Что увеличит ёмкость конденсатора?</div></div>
<div class="wg-help">Опираясь на $C = \\varepsilon\\varepsilon_0 S/d$, выбери: действие <b>увеличит</b> или <b>уменьшит</b> $C$.</div>
<div class="score-display"><span>Задача <b id="p23-iv3-i">1</b> / 6</span><span>Очки: <b id="p23-iv3-s">0</b> / 6</span></div>
<div id="p23-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p23-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p23-iv3-fb"></div>
<div class="actions"><button class="btn" id="p23-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр конденсаторов */
html += `<div class="wg" id="p23-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр конденсаторов</div></div>
<div class="wg-help">5 задач. $\\varepsilon_0 = 8{,}85 \\cdot 10^{-12}$ Ф/м. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p23-iv4-i">1</b> / 5</span><span>Очки: <b id="p23-iv4-s">0</b> / 5</span></div>
<div id="p23-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p23-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p23-iv4-go">Проверить</button>
<button class="btn" id="p23-iv4-start">Заново</button>
</div>
<div class="feedback" id="p23-iv4-fb"></div>
</div>`;
html += secNav('p22', 'p24');
html += readButton('p23');
box.innerHTML = html;
renderMath(box);
/* IV1 — Конструктор плоского конденсатора */
(function(){
const eps0 = PHYS.CONST.eps0;
const svg = document.getElementById('p23-iv1-svg');
const SS = document.getElementById('p23-iv1-S');
const dS = document.getElementById('p23-iv1-d');
const SL = document.getElementById('p23-iv1-SL');
const dL = document.getElementById('p23-iv1-dL');
const out = document.getElementById('p23-iv1-out');
const bA = document.getElementById('p23-iv1-eA');
const bM = document.getElementById('p23-iv1-eM');
const bC = document.getElementById('p23-iv1-eC');
const seen = new Set();
let _done = false;
let eps = 1, epsName = 'воздух', epsColor = '#f8fafc';
function setEps(e, name, color, btn){
eps = e; epsName = name; epsColor = color;
[bA, bM, bC].forEach(x => x.classList.remove('primary'));
btn.classList.add('primary');
render();
}
function render(){
const S = +SS.value, d = +dS.value;
SL.textContent = S.toFixed(3);
dL.textContent = d.toFixed(3);
const C = eps * eps0 * S / d; // Ф
const Cnf = C * 1e9; // нФ
const Cmkf = C * 1e6; // мкФ
const W = 480, H = 280, cx = W/2, cy = H/2;
// Длина пластин пропорциональна sqrt(S), макс ширина ~280
const plateLen = 80 + 200 * Math.sqrt(S / 0.1);
// Расстояние между пластинами пропорционально d
const platesGap = 20 + 120 * (d / 0.01);
const xL = cx - plateLen/2, xR = cx + plateLen/2;
const yTop = cy - platesGap/2;
const yBot = cy + platesGap/2;
let g = '';
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Плоский конденсатор</text>';
// Диэлектрик между пластинами
g += '<rect x="'+xL+'" y="'+yTop+'" width="'+(xR-xL)+'" height="'+(yBot-yTop)+'" fill="'+epsColor+'" stroke="#cbd5e1" stroke-width="0.8"/>';
// Верхняя пластина (+)
g += '<line x1="'+xL+'" y1="'+yTop+'" x2="'+xR+'" y2="'+yTop+'" stroke="#dc2626" stroke-width="5"/>';
// Нижняя пластина (−)
g += '<line x1="'+xL+'" y1="'+yBot+'" x2="'+xR+'" y2="'+yBot+'" stroke="#2563eb" stroke-width="5"/>';
// Метки + и
g += '<text x="'+(xL-10)+'" y="'+(yTop+5)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#dc2626">+</text>';
g += '<text x="'+(xL-10)+'" y="'+(yBot+5)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#2563eb"></text>';
// Подпись диэлектрика по центру
g += '<text x="'+cx+'" y="'+(cy+4)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">'+epsName+' (ε = '+eps+')</text>';
// Размер d справа
g += '<line x1="'+(xR+25)+'" y1="'+yTop+'" x2="'+(xR+25)+'" y2="'+yBot+'" stroke="#10b981" stroke-width="1.6" stroke-dasharray="3,3"/>';
g += '<line x1="'+(xR+20)+'" y1="'+yTop+'" x2="'+(xR+30)+'" y2="'+yTop+'" stroke="#10b981" stroke-width="1.6"/>';
g += '<line x1="'+(xR+20)+'" y1="'+yBot+'" x2="'+(xR+30)+'" y2="'+yBot+'" stroke="#10b981" stroke-width="1.6"/>';
g += '<text x="'+(xR+38)+'" y="'+(cy+4)+'" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#10b981">d</text>';
// Размер плакеты сверху (S = площадь, длина — проекция)
g += '<text x="'+cx+'" y="'+(yTop-8)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#7c3aed">площадь S</text>';
svg.innerHTML = g;
// Вывод значения
let CStr;
if(Cmkf >= 1) CStr = Cmkf.toFixed(2) + ' мкФ';
else if(Cnf >= 1) CStr = Cnf.toFixed(2) + ' нФ';
else CStr = (C*1e12).toFixed(2) + ' пФ';
out.innerHTML = '<b>$C = \\dfrac{\\varepsilon\\varepsilon_0 S}{d} = \\dfrac{'+eps+'\\cdot 8{,}85\\cdot 10^{-12}\\cdot '+S.toFixed(3)+'}{'+d.toFixed(3)+'} = '+C.toExponential(2)+'$ Ф $\\approx$ '+CStr+'</b>';
renderMath(out);
seen.add(S.toFixed(3)+':'+d.toFixed(3)+':'+eps);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p23-iv1'); bumpProgress('p23', 15); }
}
bA.addEventListener('click', () => setEps(1, 'воздух', '#f8fafc', bA));
bM.addEventListener('click', () => setEps(6, 'слюда', '#e2e8f0', bM));
bC.addEventListener('click', () => setEps(100, 'керамика', '#fed7aa', bC));
SS.addEventListener('input', render);
dS.addEventListener('input', render);
render();
})();
/* IV2 — Калькулятор соединений */
(function(){
let mode = 'P';
const mP = document.getElementById('p23-iv2-mP');
const mS = document.getElementById('p23-iv2-mS');
const out = document.getElementById('p23-iv2-out');
const seen = new Set();
let _done = false;
function setMode(m){
mode = m;
if(m === 'P'){ mP.classList.add('primary'); mS.classList.remove('primary'); }
else { mS.classList.add('primary'); mP.classList.remove('primary'); }
calc();
}
function calc(){
const C1 = parseFloat(document.getElementById('p23-iv2-C1').value) || 0;
const C2 = parseFloat(document.getElementById('p23-iv2-C2').value) || 0;
let html = '';
if(mode === 'P'){
const Cob = C1 + C2;
html += '<div><b>Параллельное соединение:</b></div>';
html += '<div style="margin-top:6px"><b>$C_{общ} = C_1 + C_2 = '+C1+' + '+C2+' = '+Cob.toFixed(2)+'$ мкФ</b></div>';
} else {
const inv = 1/C1 + 1/C2;
const Cob = (C1*C2)/(C1+C2);
html += '<div><b>Последовательное соединение:</b></div>';
html += '<div style="margin-top:6px"><b>$\\dfrac{1}{C_{общ}} = \\dfrac{1}{C_1} + \\dfrac{1}{C_2} = \\dfrac{1}{'+C1+'} + \\dfrac{1}{'+C2+'} = '+inv.toFixed(3)+'$ 1/мкФ</b></div>';
html += '<div style="margin-top:6px"><b>$C_{общ} = \\dfrac{C_1 C_2}{C_1 + C_2} = \\dfrac{'+C1+'\\cdot '+C2+'}{'+(C1+C2)+'} = '+Cob.toFixed(3)+'$ мкФ</b></div>';
}
out.innerHTML = html;
renderMath(out);
seen.add(mode+':'+C1+':'+C2);
if(!_done && seen.size >= 3){ _done = true; addXp(10, 'p23-iv2'); bumpProgress('p23', 15); }
}
mP.addEventListener('click', () => setMode('P'));
mS.addEventListener('click', () => setMode('S'));
document.getElementById('p23-iv2-go').addEventListener('click', calc);
['p23-iv2-C1','p23-iv2-C2'].forEach(id => {
document.getElementById(id).addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
});
setMode('P');
})();
/* IV3 — Что увеличит ёмкость? */
(function(){
const OPTS = ['Увеличит', 'Уменьшит'];
const Q = [
{ q:'Увеличить площадь пластин $S$ (при прочих равных).', ans:0, why:'$C \\propto S$ — рост $S$ увеличивает $C$.' },
{ q:'Увеличить расстояние $d$ между пластинами.', ans:1, why:'$C \\propto 1/d$ — рост $d$ уменьшает $C$.' },
{ q:'Заменить воздух на слюду ($\\varepsilon = 6$) между пластинами.', ans:0, why:'$C \\propto \\varepsilon$ — больший $\\varepsilon$ увеличивает $C$.' },
{ q:'Уменьшить площадь пластин $S$ вдвое.', ans:1, why:'$C \\propto S$ — уменьшение $S$ уменьшает $C$.' },
{ q:'Уменьшить расстояние $d$ между пластинами.', ans:0, why:'$C \\propto 1/d$ — уменьшение $d$ увеличивает $C$.' },
{ q:'Заменить слюду ($\\varepsilon = 6$) на вакуум ($\\varepsilon = 1$).', ans:1, why:'$\\varepsilon$ уменьшится в 6 раз $\\Rightarrow C$ уменьшится.' }
];
let i = 0, score = 0;
const qEl = document.getElementById('p23-iv3-q');
const oEl = document.getElementById('p23-iv3-opts');
const fb = document.getElementById('p23-iv3-fb');
const iEl = document.getElementById('p23-iv3-i');
const sEl = document.getElementById('p23-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p23-iv3'); bumpProgress('p23', 25); }
else if(score >= 4){ addXp(8, 'p23-iv3'); bumpProgress('p23', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1800);
});
});
}
document.getElementById('p23-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр конденсаторов */
(function(){
const Q = [
{ q:'$S = 0{,}01$ м², $d = 0{,}001$ м, воздух ($\\varepsilon = 1$). Ёмкость $C$ в пФ?', ans:88.5, tol:2, hint:'$C = \\varepsilon\\varepsilon_0 S/d = 8{,}85\\cdot 10^{-12}\\cdot 0{,}01/0{,}001 \\approx 88{,}5$ пФ.' },
{ q:'$C = 100$ мкФ, $U = 50$ В. Заряд $q$ в мКл?', ans:5, tol:0.3, hint:'$q = CU = 100\\cdot 10^{-6}\\cdot 50 = 5\\cdot 10^{-3}$ Кл $= 5$ мКл.' },
{ q:'$C_1 = 2$ мкФ, $C_2 = 6$ мкФ, соединены параллельно. $C_{общ}$ в мкФ?', ans:8, tol:0.5, hint:'$C_{общ} = C_1 + C_2 = 2 + 6 = 8$ мкФ.' },
{ q:'$C_1 = 2$ мкФ, $C_2 = 6$ мкФ, соединены последовательно. $C_{общ}$ в мкФ?', ans:1.5, tol:0.1, hint:'$C_{общ} = C_1 C_2 /(C_1+C_2) = 12/8 = 1{,}5$ мкФ.' },
{ q:'$C = 200$ нФ, $U = 100$ В. Заряд конденсатора $q$ в мкКл?', ans:20, tol:1, hint:'$q = CU = 200\\cdot 10^{-9}\\cdot 100 = 2\\cdot 10^{-5}$ Кл $= 20$ мкКл.' }
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p23-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p23-iv4'); bumpProgress('p23', 25); }
else if(score >= 3){ addXp(8, 'p23-iv4'); bumpProgress('p23', 15); }
return;
}
document.getElementById('p23-iv4-i').textContent = (i+1);
document.getElementById('p23-iv4-s').textContent = score;
document.getElementById('p23-iv4-q').innerHTML = Q[i].q;
document.getElementById('p23-iv4-ans').value = '';
renderMath(document.getElementById('p23-iv4-q'));
document.getElementById('p23-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p23-iv4-fb');
const raw = document.getElementById('p23-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p23-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p23-iv4-go').addEventListener('click', go);
document.getElementById('p23-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p23-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p23');
}
function build_p24(){
const box = document.getElementById('p24-body');
let html = '';
/* THEORY 1 — Энергия заряженного конденсатора */
html += makeCard('theory', "Энергия заряженного конденсатора", "§24", `
<p>Заряженный конденсатор обладает <b>запасённой энергией</b>. Чтобы зарядить конденсатор, источник тока совершает работу — переносит заряды с одной обкладки на другую против сил электрического поля.</p>
<p>Если рассчитать эту работу через интегрирование, получим, что <b>энергия конденсатора</b> равна:</p>
<p style="text-align:center;margin:10px 0">$$W = \\dfrac{CU^2}{2}$$</p>
<p>Используя соотношение $q = CU$ ($C = q/U$, $U = q/C$), эту формулу можно переписать ещё двумя способами:</p>
<p style="text-align:center;margin:10px 0">$$W = \\dfrac{q^2}{2C} = \\dfrac{qU}{2}$$</p>
<p>Все три формулы <b>эквивалентны</b> — выбирай удобную в зависимости от того, какие величины даны в задаче:</p>
<ul style="margin:6px 0 6px 18px">
<li>Известны $C$ и $U$ → $W = CU^2/2$.</li>
<li>Известны $q$ и $C$ → $W = q^2/(2C)$.</li>
<li>Известны $q$ и $U$ → $W = qU/2$.</li>
</ul>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Единица энергии:</b> Джоуль (Дж). На практике в электронике: мДж $= 10^{-3}$ Дж, мкДж $= 10^{-6}$ Дж.</p>
`);
/* THEORY 2 — Где запасена энергия */
html += makeCard('rule', "Где запасена энергия. Плотность энергии поля", "§24", `
<p>Где же находится энергия конденсатора? Не на пластинах и не в зарядах. <b>Энергия запасена в электрическом поле</b> между обкладками.</p>
<p>Это глубокий вывод: <b>электрическое поле — носитель энергии</b>, как и материя. Энергию можно вычислить через характеристики самого поля.</p>
<p style="margin-top:10px"><b>Объёмная плотность энергии</b> — энергия в единице объёма поля:</p>
<p style="text-align:center;margin:10px 0">$$w = \\dfrac{\\varepsilon \\varepsilon_0 E^2}{2}$$</p>
<ul style="margin:6px 0 6px 18px">
<li>$w$ — плотность энергии в Дж/м³</li>
<li>$E$ — напряжённость поля в В/м</li>
<li>$\\varepsilon$ — относительная диэлектрическая проницаемость среды</li>
<li>$\\varepsilon_0 = 8{,}85 \\cdot 10^{-12}$ Ф/м — электрическая постоянная</li>
</ul>
<p style="margin-top:10px">Полная энергия поля в объёме $V$:</p>
<p style="text-align:center;margin:10px 0">$$W = w \\cdot V$$</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Проверка:</b> для плоского конденсатора $E = U/d$, $V = Sd$, $C = \\varepsilon\\varepsilon_0 S/d$. Подставив, получаем $W = \\varepsilon\\varepsilon_0 (U/d)^2 \\cdot Sd / 2 = (\\varepsilon\\varepsilon_0 S/d)\\cdot U^2/2 = CU^2/2$. То же самое!</p>
`);
/* THEORY 3 — Применение */
html += makeCard('example', "Применение энергии конденсатора", "§24", `
<p>Запасание энергии в конденсаторах используется в технике и медицине очень широко:</p>
<ul style="margin:6px 0 6px 18px">
<li><b>Фотовспышка:</b> конденсатор медленно заряжается от батареи, затем за миллисекунды разряжается через ксеноновую лампу → яркая вспышка света.</li>
<li><b>Импульсные лазеры:</b> накопленная энергия за миллисекунды отдаётся в активную среду для накачки.</li>
<li><b>Дефибрилляторы:</b> короткий мощный импульс (сотни Дж) восстанавливает ритм сердца — спасает жизни.</li>
<li><b>Источники бесперебойного питания, фильтры:</b> сглаживают пульсации тока и поддерживают питание при просадках.</li>
<li><b>Суперконденсаторы в электромобилях:</b> быстрое накопление/отдача энергии при разгоне и торможении.</li>
</ul>
<p style="margin-top:12px;padding:12px 14px;background:var(--sec-acc-soft);border-radius:10px"><b>Пример (фотовспышка).</b> $C = 1000$ мкФ $= 10^{-3}$ Ф, $U = 300$ В.</p>
<p style="text-align:center;margin:10px 0">$$W = \\dfrac{CU^2}{2} = \\dfrac{10^{-3} \\cdot 90\\,000}{2} = 45 \\text{ Дж}$$</p>
<p>Это много! Если разряд происходит за $\\Delta t = 1$ мс $= 10^{-3}$ с, мощность вспышки:</p>
<p style="text-align:center;margin:10px 0">$$P = \\dfrac{W}{\\Delta t} = \\dfrac{45}{10^{-3}} = 45\\,000 \\text{ Вт} = 45 \\text{ кВт}$$</p>
<p>Поэтому фотовспышка такая яркая.</p>
`);
/* INTERACTIVE 1 — Энергия от C и U */
html += `<div class="wg" id="p24-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Энергия конденсатора от $C$ и $U$</div></div>
<div class="wg-help">Меняй ёмкость $C$ и напряжение $U$. Наблюдай $W = CU^2/2$ и заряд $q = CU$. «Заряжённость» обкладок видна по числу значков «+» и «−».</div>
<div class="sliders">
<label>$C$: <b id="p24-iv1-CL">100</b> мкФ <input type="range" id="p24-iv1-C" min="1" max="1000" value="100" step="1"></label>
<label>$U$: <b id="p24-iv1-UL">100</b> В <input type="range" id="p24-iv1-U" min="10" max="500" value="100" step="5"></label>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p24-iv1-svg" viewBox="0 0 480 260" width="100%" style="height:auto"></svg>
</div>
<div id="p24-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор энергии */
html += `<div class="wg" id="p24-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор энергии: 3 формулы</div></div>
<div class="wg-help">Выбери формулу, подставь данные, получи $W$. Попробуй все три варианта.</div>
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px;flex-wrap:wrap">
<button class="btn primary" id="p24-iv2-mCU">через $C, U$</button>
<button class="btn" id="p24-iv2-mqC">через $q, C$</button>
<button class="btn" id="p24-iv2-mqU">через $q, U$</button>
</div>
<div id="p24-iv2-inputs" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:end;margin-bottom:10px"></div>
<div style="display:flex;justify-content:center;margin-bottom:10px">
<button class="btn primary" id="p24-iv2-go">Вычислить $W$</button>
</div>
<div id="p24-iv2-out" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.95rem;line-height:1.75;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — Как изменится энергия? */
html += `<div class="wg" id="p24-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Как изменится энергия конденсатора?</div></div>
<div class="wg-help">Опираясь на формулы $W = CU^2/2 = q^2/(2C) = qU/2$, выбери, как изменится энергия.</div>
<div class="score-display"><span>Задача <b id="p24-iv3-i">1</b> / 6</span><span>Очки: <b id="p24-iv3-s">0</b> / 6</span></div>
<div id="p24-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p24-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
<div class="feedback" id="p24-iv3-fb"></div>
<div class="actions"><button class="btn" id="p24-iv3-restart">Начать заново</button></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр энергии */
html += `<div class="wg" id="p24-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр энергии конденсатора</div></div>
<div class="wg-help">5 задач. Допуск $\\pm 5\\%$. Внимательно следи за единицами (мкФ, нФ, мкКл, мДж).</div>
<div class="score-display"><span>Задача <b id="p24-iv4-i">1</b> / 5</span><span>Очки: <b id="p24-iv4-s">0</b> / 5</span></div>
<div id="p24-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p24-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p24-iv4-go">Проверить</button>
<button class="btn" id="p24-iv4-start">Заново</button>
</div>
<div class="feedback" id="p24-iv4-fb"></div>
</div>`;
html += secNav('p23', 'final3');
html += readButton('p24');
box.innerHTML = html;
renderMath(box);
/* IV1 — Энергия конденсатора (C, U) */
(function(){
const CS = document.getElementById('p24-iv1-C');
const US = document.getElementById('p24-iv1-U');
const CL = document.getElementById('p24-iv1-CL');
const UL = document.getElementById('p24-iv1-UL');
const svg = document.getElementById('p24-iv1-svg');
const out = document.getElementById('p24-iv1-out');
const seen = new Set();
let _done = false;
function render(){
const C_uF = +CS.value; // мкФ
const U = +US.value; // В
const C = C_uF * 1e-6; // Ф
const W = C * U * U / 2; // Дж
const q = C * U; // Кл
CL.textContent = C_uF;
UL.textContent = U;
// SVG: два прямоугольника-обкладки с зарядами
const W_svg = 480, H_svg = 260, cx = W_svg/2, cy = H_svg/2;
const plateW = 280, plateH = 14;
const gap = 90;
const xL = cx - plateW/2;
const yTop = cy - gap/2 - plateH;
const yBot = cy + gap/2;
// Число значков пропорционально q (норм. к диапазону ~ q_max = 1000e-6 * 500 = 0.5 Кл; logarithmically scale)
const qMax = 1000e-6 * 500;
const ratio = Math.min(1, Math.sqrt(q / qMax));
const nSign = Math.max(3, Math.round(3 + ratio * 17)); // 3..20
let g = '';
g += '<rect x="0" y="0" width="'+W_svg+'" height="'+H_svg+'" fill="#fafafa"/>';
g += '<text x="'+cx+'" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Заряд на обкладках</text>';
// Поле (стрелки между пластинами)
const yMid = (yTop+plateH + yBot)/2;
for(let i = 0; i < 5; i++){
const x = xL + 30 + i*(plateW-60)/4;
g += '<line x1="'+x+'" y1="'+(yTop+plateH+4)+'" x2="'+x+'" y2="'+(yBot-4)+'" stroke="#0ea5e9" stroke-width="1.4" stroke-dasharray="3,3" opacity="0.6"/>';
g += '<polygon points="'+(x-4)+','+(yBot-8)+' '+(x+4)+','+(yBot-8)+' '+x+','+(yBot-2)+'" fill="#0ea5e9" opacity="0.6"/>';
}
// Верхняя обкладка (+)
g += '<rect x="'+xL+'" y="'+yTop+'" width="'+plateW+'" height="'+plateH+'" fill="#fecaca" stroke="#dc2626" stroke-width="2"/>';
// Нижняя обкладка (−)
g += '<rect x="'+xL+'" y="'+yBot+'" width="'+plateW+'" height="'+plateH+'" fill="#bfdbfe" stroke="#2563eb" stroke-width="2"/>';
// Значки + сверху
for(let i = 0; i < nSign; i++){
const x = xL + 14 + i*(plateW-28)/(nSign-1 || 1);
g += '<text x="'+x+'" y="'+(yTop+plateH-2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">+</text>';
}
// Значки снизу
for(let i = 0; i < nSign; i++){
const x = xL + 14 + i*(plateW-28)/(nSign-1 || 1);
g += '<text x="'+x+'" y="'+(yBot+plateH-2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#2563eb"></text>';
}
// Подписи U и q
g += '<text x="'+(xL-12)+'" y="'+(yTop+plateH+4)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#dc2626">+q</text>';
g += '<text x="'+(xL-12)+'" y="'+(yBot+plateH-2)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#2563eb">q</text>';
g += '<text x="'+(xL+plateW+12)+'" y="'+(yMid+4)+'" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0ea5e9">U</text>';
svg.innerHTML = g;
// Вывод
let WStr;
if(W >= 1) WStr = W.toFixed(2) + ' Дж';
else if(W >= 1e-3) WStr = (W*1e3).toFixed(2) + ' мДж';
else WStr = (W*1e6).toFixed(2) + ' мкДж';
let qStr;
if(q >= 1e-3) qStr = (q*1e3).toFixed(2) + ' мКл';
else qStr = (q*1e6).toFixed(2) + ' мкКл';
out.innerHTML = '<b>$W = \\dfrac{CU^2}{2} = \\dfrac{'+C_uF+'\\cdot 10^{-6} \\cdot '+U+'^2}{2} \\approx$ '+WStr+'</b><br><span style="color:var(--muted);font-size:.9rem">$q = CU = $ '+qStr+'</span>';
renderMath(out);
seen.add(C_uF+':'+U);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p24-iv1'); bumpProgress('p24', 15); }
}
CS.addEventListener('input', render);
US.addEventListener('input', render);
render();
})();
/* IV2 — Калькулятор энергии (3 формулы) */
(function(){
let mode = 'CU';
const mCU = document.getElementById('p24-iv2-mCU');
const mqC = document.getElementById('p24-iv2-mqC');
const mqU = document.getElementById('p24-iv2-mqU');
const inputs = document.getElementById('p24-iv2-inputs');
const out = document.getElementById('p24-iv2-out');
const seen = new Set();
let _done = false;
function setMode(m){
mode = m;
[mCU,mqC,mqU].forEach(b => b.classList.remove('primary'));
if(m === 'CU') mCU.classList.add('primary');
else if(m === 'qC') mqC.classList.add('primary');
else mqU.classList.add('primary');
render();
}
function render(){
if(mode === 'CU'){
inputs.innerHTML = '<label>$C$ (мкФ): <input type="number" id="p24-iv2-C" class="tinp" value="100" step="1" min="0.1" style="width:100%"></label>'
+ '<label>$U$ (В): <input type="number" id="p24-iv2-U" class="tinp" value="100" step="1" min="0.1" style="width:100%"></label>';
} else if(mode === 'qC'){
inputs.innerHTML = '<label>$q$ (мкКл): <input type="number" id="p24-iv2-q" class="tinp" value="20" step="0.1" min="0.001" style="width:100%"></label>'
+ '<label>$C$ (мкФ): <input type="number" id="p24-iv2-C" class="tinp" value="10" step="0.1" min="0.001" style="width:100%"></label>';
} else {
inputs.innerHTML = '<label>$q$ (мкКл): <input type="number" id="p24-iv2-q" class="tinp" value="20" step="0.1" min="0.001" style="width:100%"></label>'
+ '<label>$U$ (В): <input type="number" id="p24-iv2-U" class="tinp" value="100" step="1" min="0.1" style="width:100%"></label>';
}
renderMath(inputs);
out.innerHTML = '<span style="color:var(--muted)">Введи значения и нажми «Вычислить $W$».</span>';
renderMath(out);
}
function calc(){
let W = 0, html = '';
if(mode === 'CU'){
const Cuf = parseFloat(document.getElementById('p24-iv2-C').value) || 0;
const U = parseFloat(document.getElementById('p24-iv2-U').value) || 0;
const C = Cuf * 1e-6;
W = C * U * U / 2;
html = '<b>$W = \\dfrac{CU^2}{2} = \\dfrac{'+Cuf+'\\cdot 10^{-6} \\cdot '+U+'^2}{2} = '+W.toExponential(3)+'$ Дж</b>';
} else if(mode === 'qC'){
const quc = parseFloat(document.getElementById('p24-iv2-q').value) || 0;
const Cuf = parseFloat(document.getElementById('p24-iv2-C').value) || 0;
const q = quc * 1e-6, C = Cuf * 1e-6;
W = (C > 0) ? (q*q / (2*C)) : 0;
html = '<b>$W = \\dfrac{q^2}{2C} = \\dfrac{('+quc+'\\cdot 10^{-6})^2}{2 \\cdot '+Cuf+'\\cdot 10^{-6}} = '+W.toExponential(3)+'$ Дж</b>';
} else {
const quc = parseFloat(document.getElementById('p24-iv2-q').value) || 0;
const U = parseFloat(document.getElementById('p24-iv2-U').value) || 0;
const q = quc * 1e-6;
W = q * U / 2;
html = '<b>$W = \\dfrac{qU}{2} = \\dfrac{'+quc+'\\cdot 10^{-6} \\cdot '+U+'}{2} = '+W.toExponential(3)+'$ Дж</b>';
}
// Дополнительный перевод в удобные единицы
let extra = '';
if(W >= 1) extra = '$\\approx '+W.toFixed(3)+'$ Дж';
else if(W >= 1e-3) extra = '$\\approx '+(W*1e3).toFixed(3)+'$ мДж';
else if(W > 0) extra = '$\\approx '+(W*1e6).toFixed(3)+'$ мкДж';
out.innerHTML = html + (extra ? '<br><span style="font-size:.92rem">'+extra+'</span>' : '');
renderMath(out);
seen.add(mode);
if(!_done && seen.size >= 3){ _done = true; addXp(10, 'p24-iv2'); bumpProgress('p24', 15); }
}
mCU.addEventListener('click', () => setMode('CU'));
mqC.addEventListener('click', () => setMode('qC'));
mqU.addEventListener('click', () => setMode('qU'));
document.getElementById('p24-iv2-go').addEventListener('click', calc);
setMode('CU');
})();
/* IV3 — Как изменится энергия? (6 заданий, 4 варианта) */
(function(){
const OPTS = ['увеличится в 2 раза', 'уменьшится в 2 раза', 'увеличится в 4 раза', 'не изменится'];
// ans: 0 = увеличится в 2 раза, 1 = уменьшится в 2 раза, 2 = увеличится в 4 раза, 3 = не изменится
const Q = [
{ q:'Удвоили $U$ при том же $C$. Как изменится $W$?', ans:2, why:'$W = CU^2/2$: $U$ удваивается $\\Rightarrow W$ растёт в $2^2 = 4$ раза.' },
{ q:'Удвоили $C$ при том же $U$. Как изменится $W$?', ans:0, why:'$W = CU^2/2$: $C$ удваивается $\\Rightarrow W$ растёт в 2 раза.' },
{ q:'Удвоили заряд $q$ при том же $C$. Как изменится $W$?', ans:2, why:'$W = q^2/(2C)$: $q$ удваивается $\\Rightarrow W$ растёт в $2^2 = 4$ раза.' },
{ q:'Уменьшили $C$ в 2 раза при том же заряде $q$. Как изменится $W$?', ans:0, why:'$W = q^2/(2C)$: $C$ в знаменателе уменьшается в 2 раза $\\Rightarrow W$ растёт в 2 раза.' },
{ q:'Уменьшили $U$ в 2 раза, а $C$ увеличили в 4 раза. Как изменится $W$?', ans:3, why:'$W = CU^2/2$: $U^2$ упало в 4 раза, $C$ выросло в 4 раза $\\Rightarrow$ $W$ не меняется.' },
{ q:'Зарядили конденсатор с 100 В до 200 В при том же $C$. Как изменилась $W$?', ans:2, why:'$W \\propto U^2$: $(200/100)^2 = 4$ — $W$ увеличилась в 4 раза.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p24-iv3-q');
const oEl = document.getElementById('p24-iv3-opts');
const fb = document.getElementById('p24-iv3-fb');
const iEl = document.getElementById('p24-iv3-i');
const sEl = document.getElementById('p24-iv3-s');
// Update score-display total to actual length
const total = Q.length;
document.querySelector('#p24-iv3 .score-display').innerHTML =
'<span>Задача <b id="p24-iv3-i">1</b> / '+total+'</span><span>Очки: <b id="p24-iv3-s">0</b> / '+total+'</span>';
const iEl2 = document.getElementById('p24-iv3-i');
const sEl2 = document.getElementById('p24-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p24-iv3'); bumpProgress('p24', 25); }
else if(score >= 4){ addXp(8, 'p24-iv3'); bumpProgress('p24', 15); }
return;
}
iEl2.textContent = (i+1);
sEl2.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
fb.style.display = 'none';
renderMath(qEl); renderMath(oEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const v = +b.dataset.v;
if(v === item.ans){ score++; feedback(fb, true, '&#10003; Верно! ' + item.why + ' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. ' + item.why + ' Дальше ▶');
sEl2.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 2000);
});
});
}
document.getElementById('p24-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* IV4 — Тренажёр энергии */
(function(){
const Q = [
{ q:'$C = 100$ мкФ, $U = 100$ В. Найди $W$ в Дж (до сотых).', ans:0.5, tol:0.03, hint:'$W = CU^2/2 = 10^{-4}\\cdot 10^4/2 = 0{,}5$ Дж.' },
{ q:'$C = 1000$ мкФ, $U = 300$ В (фотовспышка). Найди $W$ в Дж.', ans:45, tol:2.5, hint:'$W = CU^2/2 = 10^{-3}\\cdot 9\\cdot 10^4/2 = 45$ Дж.' },
{ q:'$q = 10$ мкКл, $U = 200$ В. Найди $W$ в мДж.', ans:1, tol:0.06, hint:'$W = qU/2 = 10^{-5}\\cdot 200/2 = 10^{-3}$ Дж $= 1$ мДж.' },
{ q:'$q = 20$ мкКл, $C = 200$ нФ. Найди $W$ в мДж.', ans:1, tol:0.06, hint:'$W = q^2/(2C) = (2\\cdot 10^{-5})^2/(2\\cdot 2\\cdot 10^{-7}) = 4\\cdot 10^{-10}/(4\\cdot 10^{-7}) = 10^{-3}$ Дж $= 1$ мДж.' },
{ q:'Если удвоить $U$ конденсатора при $C = $ const, во сколько раз вырастет $W$?', ans:4, tol:0.1, hint:'$W \\propto U^2$: $(2U)^2/U^2 = 4$.' }
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p24-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p24-iv4'); bumpProgress('p24', 25); }
else if(score >= 3){ addXp(8, 'p24-iv4'); bumpProgress('p24', 15); }
return;
}
document.getElementById('p24-iv4-i').textContent = (i+1);
document.getElementById('p24-iv4-s').textContent = score;
document.getElementById('p24-iv4-q').innerHTML = Q[i].q;
document.getElementById('p24-iv4-ans').value = '';
renderMath(document.getElementById('p24-iv4-q'));
document.getElementById('p24-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p24-iv4-fb');
const raw = document.getElementById('p24-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
document.getElementById('p24-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p24-iv4-go').addEventListener('click', go);
document.getElementById('p24-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p24-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p24');
}
function build_final3(){
const box = document.getElementById('final3-body');
let html = '';
/* Часть А — Шпаргалка главы (9 mini-карточек) */
const SHEET = [
{ t:'§ 16 · Заряд', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><text x="12" y="16" text-anchor="middle" font-size="12" font-weight="700" fill="#dc2626">+</text></svg>', body:'$q = ne$, $e = 1{,}6 \\cdot 10^{-19}$ Кл. Закон сохранения: $\\sum q = $ const.' },
{ t:'§ 17 · Кулон', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" style="width:18px;height:18px"><circle cx="6" cy="12" r="3"/><circle cx="18" cy="12" r="3"/><path d="M9 12h6"/></svg>', body:'$F = k\\dfrac{q_1 q_2}{r^2}$, $k = 9\\cdot 10^9$ Н·м²/Кл². В среде: $F = F_0/\\varepsilon$.' },
{ t:'§ 18 · Поле', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#0ea5e9" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="3"/><path d="M12 5v2"/><path d="M12 17v2"/><path d="M5 12h2"/><path d="M17 12h2"/></svg>', body:'Создаётся неподвижными зарядами, действует на заряды. Пробный заряд $q_0$ — для измерения.' },
{ t:'§ 19 · Напряжённость', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" style="width:18px;height:18px"><path d="M4 12h14"/><polyline points="14 7 19 12 14 17"/></svg>', body:'$\\vec{E} = \\vec{F}/q$. Точечный: $E = kq/r^2$. Принцип суперпозиции — векторно.' },
{ t:'§ 20 · Линии', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2" style="width:18px;height:18px"><path d="M5 4c4 4 10 4 14 0"/><path d="M5 12c4 4 10 4 14 0"/><path d="M5 20c4 4 10 4 14 0"/></svg>', body:'Касательны к $\\vec{E}$. От + к −, не пересекаются. Густота = $E$. Однородное поле — параллельные линии.' },
{ t:'§ 21 · Работа · потенциал', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" stroke-width="2" style="width:18px;height:18px"><path d="M4 18l8-12 8 12"/></svg>', body:'$A = qEd\\cos\\alpha = q(\\varphi_1-\\varphi_2)$. $\\varphi = kq/r$. Работа не зависит от траектории.' },
{ t:'§ 22 · Напряжение', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="2" style="width:18px;height:18px"><path d="M4 6h16"/><path d="M4 12h16"/><path d="M4 18h10"/></svg>', body:'$U = \\varphi_1 - \\varphi_2$ [В]. В однородном: $U = Ed$. Эквипотенциальные поверхности $\\perp \\vec{E}$.' },
{ t:'§ 23 · Конденсаторы', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#0891b2" stroke-width="2" style="width:18px;height:18px"><path d="M4 8v8"/><path d="M9 6v12"/><path d="M15 6v12"/><path d="M20 8v8"/></svg>', body:'$C = q/U$ [Ф]. Плоский: $C = \\varepsilon\\varepsilon_0 S/d$. $\\parallel$: $\\sum C$. Последов.: $\\sum 1/C$.' },
{ t:'§ 24 · Энергия', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" style="width:18px;height:18px"><polygon points="13,2 4,14 12,14 11,22 20,10 12,10"/></svg>', body:'$W = \\dfrac{CU^2}{2} = \\dfrac{q^2}{2C} = \\dfrac{qU}{2}$. Хранится в поле.' },
];
html += `<div class="card">
<div class="card-header">
<span class="card-icon theory">${ICONS.theory}</span>
<span class="card-title">Шпаргалка главы 3 — Электростатика</span>
<span class="card-num">Итог</span>
</div>
<div class="card-body">
<p>Ключевые формулы и идеи всех 9 параграфов главы — повтори перед битвой с боссами.</p>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-top:10px">
${SHEET.map(s => `<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
${s.icon}
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.88rem">${s.t}</div>
</div>
<div style="font-size:.92rem;line-height:1.55">${s.body}</div>
</div>`).join('')}
</div>
</div>
</div>`;
/* Часть Б — 7 боссов intro */
html += `<div class="card">
<div class="card-header">
<span class="card-icon rule">${ICONS.rule}</span>
<span class="card-title">Боссы главы 3</span>
<span class="card-num">7</span>
</div>
<div class="card-body">
<p>7 интегрированных задач по §§16–24. За каждого побеждённого босса: <b>+10 XP, +14% к прогрессу</b>. Победишь всех — ачивка <b>«Мастер электростатики»</b> и <b>+50 XP бонус</b>.</p>
<p style="margin-top:6px;font-size:.92rem;color:var(--muted)">Константы: $k = 9 \\cdot 10^9$ Н·м²/Кл², $e = 1{,}6 \\cdot 10^{-19}$ Кл, $\\varepsilon_0 = 8{,}85 \\cdot 10^{-12}$ Ф/м. Допуск $\\pm 5\\%$.</p>
</div>
</div>`;
html += '<div id="ch3-bosses-container"></div>';
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch3-final-summary">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
<div id="ch3-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 7 боссов побеждено</div>
<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">
<div id="ch3-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#7c3aed,#a78bfa);transition:width .35s"></div>
</div>
<div id="ch3-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid #7c3aed">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:#5b21b6;font-size:1.05rem;margin-bottom:6px">Мастер электростатики</div>
<div style="font-size:.92rem;margin-bottom:10px">Глава 3 пройдена! Все 7 боссов повержены. +50 XP бонус.</div>
<a class="btn primary" href="/textbook/physics-10-ch4" style="text-decoration:none">Дальше: Глава 4 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
</div>
</div>`;
html += secNav('p24', null);
box.innerHTML = html;
renderMath(box);
/* Боссы главы 3 */
const BOSSES = [
{
n:1, color:'#dc2626',
title:'Хранитель Заряда',
tag:'§ 16',
q:'Сколько электронов в заряде модулем $|q| = 3{,}2$ нКл? Введи коэффициент $a$ в записи $n = a \\cdot 10^{10}$.',
ans:2, tol:0.1,
hint:'$n = |q|/e = 3{,}2 \\cdot 10^{-9} / (1{,}6 \\cdot 10^{-19}) = 2 \\cdot 10^{10}$.'
},
{
n:2, color:'#7c3aed',
title:'Дракон Кулона',
tag:'§ 17',
q:'Два заряда по $+2$ мкКл в воздухе на $r = 0{,}3$ м. Найди модуль силы Кулона $F$ в Н (до сотых).',
ans:0.4, tol:0.025,
hint:'$F = k\\dfrac{q_1 q_2}{r^2} = 9\\cdot 10^9 \\cdot \\dfrac{4 \\cdot 10^{-12}}{0{,}09} = 0{,}4$ Н.'
},
{
n:3, color:'#f59e0b',
title:'Стражник Напряжённости',
tag:'§ 19',
q:'$E$ на расстоянии $r = 0{,}2$ м от точечного заряда $q = +5$ нКл. Ответ в В/м.',
ans:1125, tol:60,
hint:'$E = kq/r^2 = 9\\cdot 10^9 \\cdot 5\\cdot 10^{-9}/0{,}04 = 1125$ В/м.'
},
{
n:4, color:'#8b5cf6',
title:'Сторож Потенциала',
tag:'§ 21 + § 22',
q:'Заряд $q = +2$ мкКл перенесли между точками с $\\varphi_1 = 200$ В и $\\varphi_2 = 50$ В. Работа поля $A$ в мкДж?',
ans:300, tol:15,
hint:'$A = q(\\varphi_1 - \\varphi_2) = 2\\cdot 10^{-6} \\cdot 150 = 3\\cdot 10^{-4}$ Дж $= 300$ мкДж.'
},
{
n:5, color:'#0891b2',
title:'Капитан Конденсатор',
tag:'§ 23',
q:'Плоский конденсатор: $S = 0{,}02$ м², $d = 0{,}001$ м, воздух ($\\varepsilon = 1$). Найди $C$ в пФ.',
ans:177, tol:9,
hint:'$C = \\varepsilon\\varepsilon_0 S/d = 8{,}85\\cdot 10^{-12}\\cdot 0{,}02/0{,}001 = 1{,}77\\cdot 10^{-10}$ Ф $= 177$ пФ.'
},
{
n:6, color:'#dc2626',
title:'Энергетический Левиафан',
tag:'§ 24',
q:'$C = 500$ мкФ, $U = 100$ В. Найди энергию $W$ конденсатора в Дж (до десятых).',
ans:2.5, tol:0.15,
hint:'$W = CU^2/2 = 5\\cdot 10^{-4}\\cdot 10^4/2 = 2{,}5$ Дж.'
},
{
n:7, color:'#a78bfa',
title:'Магистр Электростатики',
tag:'§ 16 + § 17',
q:'Два одинаковых металлических шара с зарядами $+8$ нКл и $-4$ нКл соприкоснулись и были разведены на $r = 0{,}2$ м. Найди модуль силы между ними в мкН (до десятых).',
ans:0.9, tol:0.06,
hint:'После касания: $(8 + (-4))/2 = +2$ нКл на каждом. $F = k q^2/r^2 = 9\\cdot 10^9 \\cdot (2\\cdot 10^{-9})^2/0{,}04 = 9\\cdot 10^{-7}$ Н $= 0{,}9$ мкН.'
},
];
const cont = document.getElementById('ch3-bosses-container');
const STATE_KEY = 'physics10_ch3_bosses';
const BOSS_STATE = (function(){
try{ const s = localStorage.getItem(STATE_KEY); if(s){ const p = JSON.parse(s); if(Array.isArray(p) && p.length === BOSSES.length) return p; } }catch(e){}
return BOSSES.map(()=>({defeated:false}));
})();
function saveBosses(){ try{ localStorage.setItem(STATE_KEY, JSON.stringify(BOSS_STATE)); }catch(e){} }
cont.innerHTML = BOSSES.map((b)=>{
return '<div class="boss-card" id="boss3-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
+'<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
+'</div>'
+'<div class="boss-q" id="boss3-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
+'<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
+'<input type="number" id="boss3-'+b.n+'-ans" class="tinp" style="width:130px;text-align:center" step="0.01" placeholder="число">'
+'<button class="btn primary" id="boss3-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
+'<button class="btn" id="boss3-'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="feedback" id="boss3-'+b.n+'-fb"></div>'
+'</div>';
}).join('');
renderMath(cont);
function refreshOverall(){
const won = BOSS_STATE.filter(s => s.defeated).length;
const txt = document.getElementById('ch3-boss-overall');
const fill = document.getElementById('ch3-boss-overall-fill');
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
if(won >= BOSSES.length){
const reward = document.getElementById('ch3-final-reward');
if(reward && reward.style.display === 'none'){
reward.style.display = 'block';
if(!STATE.achievements.has('ch3_done')){
achievement('ch3_done','Мастер электростатики');
addXp(50, 'ch3-bonus');
bumpProgress('final3', 30);
if(window.confetti){ try{ confetti(); }catch(e){} }
}
}
}
}
BOSSES.forEach((b, idx)=>{
const card = document.getElementById('boss3-'+b.n+'-card');
const goBtn = document.getElementById('boss3-'+b.n+'-go');
const hintBtn = document.getElementById('boss3-'+b.n+'-hint');
const ansInp = document.getElementById('boss3-'+b.n+'-ans');
if(BOSS_STATE[idx].defeated){
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow');
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
ansInp.disabled = true;
}
goBtn.addEventListener('click', ()=>{
if(BOSS_STATE[idx].defeated) return;
const fb = document.getElementById('boss3-'+b.n+'-fb');
const raw = ansInp.value.replace(',', '.');
const val = parseFloat(raw);
if(isNaN(val)){ feedback(fb, false, '&#10007; Введи число.'); return; }
const tol = (typeof b.tol === 'number') ? b.tol : Math.max(0.05 * Math.abs(b.ans), 0.05);
if(Math.abs(val - b.ans) < tol + 0.001){
BOSS_STATE[idx].defeated = true; saveBosses();
feedback(fb, true, '&#10003; Босс '+b.n+' повержен! +10 XP. '+b.hint);
addXp(10, 'boss-ch3-'+b.n);
bumpProgress('final3', 14);
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
ansInp.disabled = true;
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow','pulse');
setTimeout(()=>card.classList.remove('pulse'), 900);
refreshOverall();
} else {
feedback(fb, false, '&#10007; Промах. Попробуй ещё. Подсказка доступна.');
}
});
hintBtn.addEventListener('click', ()=>{
const fb = document.getElementById('boss3-'+b.n+'-fb');
fb.className = 'feedback ok';
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
fb.style.display = 'block';
fb.style.background = 'var(--warn-bg)';
fb.style.color = '#92400e';
fb.style.borderLeftColor = 'var(--warn)';
renderMath(fb);
});
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
});
refreshOverall();
}
/* ===== 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)+'…':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(); });
}
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);
/* === PHYS10 POLISH JS === */
(function(){
function bumpScore(el){
if(!el) return;
el.classList.remove('bump');
void el.offsetWidth;
el.classList.add('bump');
setTimeout(function(){ try{ el.classList.remove('bump'); }catch(e){} }, 270);
}
window.__phys10BumpScore = bumpScore;
function observeScores(root){
root = root || document;
var nodes = root.querySelectorAll('.score-display b');
nodes.forEach(function(b){
if(b.__scoreObs) return;
b.__scoreObs = true;
var last = b.textContent;
try{
var mo = new MutationObserver(function(){
var nv = b.textContent;
if(nv !== last){ last = nv; bumpScore(b); }
});
mo.observe(b, {childList:true, characterData:true, subtree:true});
}catch(e){}
});
}
function rescanScores(){ try{ observeScores(document); }catch(e){} }
if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', rescanScores);
else rescanScores();
try{
var rootObs = new MutationObserver(function(muts){
var need = false;
for(var i=0;i<muts.length && !need;i++){
var m = muts[i];
for(var j=0;j<m.addedNodes.length;j++){
var n = m.addedNodes[j];
if(n.nodeType===1){
if(n.classList && n.classList.contains('score-display')) { need = true; break; }
if(n.querySelector && n.querySelector('.score-display b')) { need = true; break; }
}
}
}
if(need) rescanScores();
});
rootObs.observe(document.body, {childList:true, subtree:true});
}catch(e){}
function refreshDoneMarks(){
try{
if(typeof STATE === 'undefined' || !STATE.progress) return;
document.querySelectorAll('.psel-card').forEach(function(c){
var id = c.dataset.id || c.dataset.progCard;
if(!id) return;
var pct = +STATE.progress[id] || 0;
if(!c.querySelector('.psel-done')){
var s = document.createElement('span');
s.className = 'psel-done';
s.setAttribute('title','Прочитано');
s.innerHTML = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
c.appendChild(s);
}
c.classList.toggle('done', pct >= 50);
});
}catch(e){}
}
try{
if(typeof window.refreshProgressUI === 'function'){
var _origRP = window.refreshProgressUI;
window.refreshProgressUI = function(){ var r = _origRP.apply(this, arguments); setTimeout(refreshDoneMarks, 0); return r; };
}
}catch(e){}
setTimeout(refreshDoneMarks, 600);
setTimeout(refreshDoneMarks, 1800);
window.addEventListener('focus', function(){ setTimeout(refreshDoneMarks, 200); });
document.addEventListener('click', function(e){
var card = e.target.closest && e.target.closest('.psel-card');
if(!card) return;
var id = card.dataset.id;
if(!id) return;
setTimeout(function(){
var sec = document.getElementById('sec-' + id);
if(sec) try{ sec.scrollIntoView({behavior:'smooth', block:'start'}); }catch(e){}
}, 60);
});
})();
</script>
</body>
</html>