Files
Learn_System/frontend/textbooks/physics_11_ch1.html
T
Maxim Dolgolyov f2a1c6e24d feat(phys11 W1): Глава 1 §1-§3 + расширение phys-fx.js (EnergyView)
phys-fx.js (+EnergyView):
- PHYS.EnergyView — график 3 кривых: W_к (красный), W_п (зелёный), W_мех=const (фиолетовый пунктир)
- Использует кинетическую/потенциальную энергию для гарм. колеб.: cos², sin², сумма = 1
- Легенда в правом верхнем углу

physics_11_ch1.html (~63 КБ):
Архитектура geom_10_r1 (geom11-стиль):
- 2-кол layout с col-side (XP card + cheat sheet + tip)
- Hero cyan-градиент + кнопка 'Начать §1'
- psel-grid: 6 параграфов + Финал; §1-§3 активны, §4-§6 и Финал locked
- sec секции с watermark (∿, маятник, E, ☰, ∿, муз. нота, ★)
- card теории + wg workshops + opt-btn кнопки

§1 Колебательное движение. Гарм. колебания:
- 3 теор. карточки (определение, T/ν/ω, гарм. колеб. x=Acos(ωt+φ₀))
- Инт. 1: Oscillogram с ползунками A, ω, φ (live-анимация)
- Инт. 2: Расчёт T,ν,ω (5 задач input)
- Инт. 3: Свойства колеб. (5 MC)
- Босс §1: 5 этапов, +65 XP

§2 Маятники:
- 2 теор. карточки (пружинный T=2π√(m/k), матем. T=2π√(l/g))
- Инт. 1: SpringMass + Pendulum side-by-side с 4 ползунками (m,k,l,g)
- Инт. 2: Расчёт T (5 input)
- Инт. 3: Как изменится T (5 MC)
- Босс §2: 5 этапов, +70 XP

§3 Превращения энергии:
- 2 теор. карточки (формулы W_к, W_п; закон сохранения W_мех=kA²/2)
- Инт. 1: EnergyView с ползунками A, ω (3 кривые в реал. времени)
- Инт. 2: Расчёт энергии (5 input)
- Инт. 3: Превращения энергии (5 MC)
- Босс §3: 5 этапов, +65 XP

§4-§6 и Финал — stub-карточки 'в разработке (W2)'.

LocalStorage: physics11_ch1_*, physics11_xp (общий со всем курсом)
Server sync: /api/textbooks/physics-11-ch1/progress
2026-05-29 17:52:47 +03:00

900 lines
67 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>Физика 11 · Глава 1 · «Механические колебания и волны»</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/phys-fx.js?v=1"></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:#0891b2; --pri2:#0e7490; --pri-soft:#cffafe;
--acc:#06b6d4; --acc2:#0891b2; --acc-soft:#a5f3fc;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#06181a; --card:#0a2225; --card-soft:#0d2a2e; --text:#cffafe; --ink:#cffafe; --muted:#67e8f9; --border:#0f4750}
*{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,#155e75 0%,#0891b2 55%,#a5f3fc 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(165,243,252,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 1';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:'\223F';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(160px,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:.84rem;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(--warn-bg),var(--pri-soft))}
.psel-card.final .psel-num{color:var(--warn)}
.psel-card.locked{opacity:.55}
.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}
.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(--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(--pri-soft);position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--pri),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.5rem;font-weight:800;color: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(--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}
.card-body ul{margin:6px 0 6px 22px;line-height:1.7}
.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(--pri-soft);border-color:var(--pri)}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--pri);color:#fff;border-color:var(--pri)}
.btn.primary:hover{background:var(--pri2);border-color: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(--pri-soft));border:1.5px solid 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(--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(--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),var(--pri-soft));border-left:4px solid var(--warn);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;width:140px}
.tinp:focus{outline:0;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-soft)}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px;align-items:center}
.opts-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
.opt-btn{padding:8px 14px;background:var(--card);border:1.5px solid var(--border);border-radius:9px;font-weight:700;font-size:.88rem;color:var(--text);cursor:pointer;transition:all .15s}
.opt-btn:hover{background:var(--pri-soft);border-color:var(--pri)}
.opt-btn.correct{background:var(--ok-bg);border-color:var(--ok);color:#065f46}
.opt-btn.wrong{background:var(--fail-bg);border-color:var(--fail);color:#991b1b}
.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:.84rem;line-height:1.55}
.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(--warn-bg),var(--pri-soft));border:1.5px solid var(--warn);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--warn);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:#92400e;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(--warn),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(--warn));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}
.fx-holder{margin:10px 0;padding:0;text-align:center}
.fx-sliders{margin-top:10px;display:flex;flex-wrap:wrap;gap:4px;background:rgba(255,255,255,.5);border-radius:9px;padding:6px 4px}
.fx-row{display:flex;gap:14px;flex-wrap:wrap;align-items:flex-start;justify-content:center}
.fx-cell{flex:1 1 280px;max-width:340px}
.fx-cell-label{font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:6px}
.boss-card{background:var(--card);border:2px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;transition:border-color .35s,box-shadow .35s,transform .2s}
.boss-card.solved{border-color:#10b981;box-shadow:0 0 0 3px rgba(16,185,129,.18)}
.boss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap}
.boss-tag{font-size:.7rem;font-weight:800;padding:3px 9px;border-radius:99px;background:var(--pri-soft);color:var(--pri2);letter-spacing:.04em;text-transform:uppercase}
.boss-title{font-family:'Unbounded',sans-serif;font-weight:800;color:var(--text);font-size:1.02rem;flex:1;min-width:0}
.boss-q{padding:12px 14px;background:var(--pri-soft);border-radius:10px;font-size:.96rem;line-height:1.55;margin-bottom:10px;color:var(--text)}
.boss-input{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);font-family:'JetBrains Mono',monospace;width:140px;text-align:center;font-size:.95rem}
.boss-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:6px}
.boss-fb{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none;line-height:1.45}
.boss-fb.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.boss-fb.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
.stub-note{padding:18px 22px;background:linear-gradient(135deg,var(--pri-soft),var(--warn-bg));border:1.5px dashed var(--pri);border-radius:13px;text-align:center;color:var(--text);margin-bottom:14px}
.stub-note h3{font-family:'Unbounded',sans-serif;color:var(--pri2);margin-bottom:8px;font-size:1.05rem}
.stub-note p{color:var(--muted);font-size:.9rem;line-height:1.55}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Физика 11 · Глава 1</h1>
<div class="hdr-sub">Механические колебания и волны · гармонические колебания, маятники, энергия, резонанс, волны</div>
</div>
<div class="hdr-side">
<a href="/textbook/physics-11" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К физике 11</a>
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Механические колебания и волны</h2>
<p>Колебания — повторяющиеся движения вокруг положения равновесия. Изучаем гармонические колебания, маятники, превращения энергии, резонанс и распространение колебаний в виде волн (включая звук).</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 1</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge"></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="&#8767;"><div class="sec-header"><span class="sec-num">§ 1</span><h2 class="sec-h">Колебательное движение. Гармонические колебания</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="&#9701;"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Пружинный и математический маятники</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="E"><div class="sec-header"><span class="sec-num">§ 3</span><h2 class="sec-h">Превращения энергии при гарм. колебаниях</h2></div><div id="p3-body"></div></section>
<section id="sec-p4" class="sec" data-watermark="&#9776;"><div class="sec-header"><span class="sec-num">§ 4</span><h2 class="sec-h">Свободные и вынужденные колебания. Резонанс</h2></div><div id="p4-body"></div></section>
<section id="sec-p5" class="sec" data-watermark="&#8767;"><div class="sec-header"><span class="sec-num">§ 5</span><h2 class="sec-h">Распр. колебаний в упругой среде</h2></div><div id="p5-body"></div></section>
<section id="sec-p6" class="sec" data-watermark="&#119082;"><div class="sec-header"><span class="sec-num">§ 6</span><h2 class="sec-h">Звуковые волны</h2></div><div id="p6-body"></div></section>
<section id="sec-final" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#d97706,#f59e0b)">&#9733;</span><h2 class="sec-h">Финал главы 1</h2></div><div id="final-body"></div></section>
</div>
<aside class="col-side"><div id="sidebar-content"></div></aside>
</main>
<footer class="foot">Интерактивный учебник «Физика 11» · Глава 1 · «Механические колебания и волны» · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<script>
'use strict';
const STATE = { current:'p1', progress:{}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 6;
const _TB_SLUG = 'physics-11-ch1';
const PARAS = [
{ id:'p1', num:'§ 1', name:'Гармонические колебания', sub:'$T, \\nu, \\omega$', built:true },
{ id:'p2', num:'§ 2', name:'Маятники', sub:'$T = 2\\pi\\sqrt{m/k}$', built:true },
{ id:'p3', num:'§ 3', name:'Превращения энергии', sub:'$W_{мех} = \\text{const}$', built:true },
{ id:'p4', num:'§ 4', name:'Резонанс', sub:'Будет в W2', built:false },
{ id:'p5', num:'§ 5', name:'Волны', sub:'Будет в W2', built:false },
{ id:'p6', num:'§ 6', name:'Звук', sub:'Будет в W2', built:false },
{ id:'final', num:'★', name:'Финал главы', sub:'4 босса · W2', final:true, built:false }
];
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 = {
p1_done:'§1 — гармонические колебания освоены',
p2_done:'§2 — маятники освоены',
p3_done:'§3 — превращения энергии освоены',
start:'Начало главы 1!'
};
function loadProgress(){
try{
const s=localStorage.getItem('physics11_ch1_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('physics11_ch1_achievements');
if(a){ const p=JSON.parse(a); 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('physics11_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('physics11_ch1_progress', JSON.stringify(STATE.progress));
localStorage.setItem('physics11_ch1_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('physics11_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 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,'physics11-ch1-'+(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)+'%'; if((STATE.progress[k]||0)>=100) el.classList.add('done'); });
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':'')+(p.built?'':' locked');
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><div class="psel-done"><svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg></div>';
card.addEventListener('click', ()=>goTo(p.id));
g.appendChild(card);
});
if(window.renderMathInElement) try{ renderMath(g); }catch(e){}
}
const BUILT=new Set();
const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), p3:()=>buildP3(), p4:()=>buildStubP('p4','§ 4','§4 в разработке (W2)'), p5:()=>buildStubP('p5','§ 5','§5 в разработке (W2)'), p6:()=>buildStubP('p6','§ 6','§6 в разработке (W2)'), final:()=>buildStubP('final','Финал','Финал главы 1 — в волне W2') };
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 = {
p1:{title:'Шпаргалка § 1', rows:[['Период','$T = \\Delta t / N$'],['Частота','$\\nu = 1/T$'],['Цикл. частота','$\\omega = 2\\pi\\nu = 2\\pi/T$'],['Уравнение','$x = A\\cos(\\omega t + \\varphi_0)$'],['Размерности','$[T]=$ с, $[\\nu]=$ Гц, $[\\omega]=$ рад/с']]},
p2:{title:'Шпаргалка § 2', rows:[['Пружинный','$T = 2\\pi\\sqrt{m/k}$'],['Математ.','$T = 2\\pi\\sqrt{l/g}$'],['Условие','малые амплитуды'],['Не зависит от','амплитуды (для гарм.)']]},
p3:{title:'Шпаргалка § 3', rows:[['$W_{кинет}$','$\\dfrac{mv^2}{2}$'],['$W_{потенц}$','$\\dfrac{kx^2}{2}$'],['$W_{мех}$','$\\dfrac{kA^2}{2} = $ const'],['Связь','$k = m\\omega^2$']]},
p4:{title:'§ 4', rows:[['Тема','Резонанс'],['Статус','В разработке (W2)']]},
p5:{title:'§ 5', rows:[['Тема','Волны'],['Статус','В разработке (W2)']]},
p6:{title:'§ 6', rows:[['Тема','Звук'],['Статус','В разработке (W2)']]},
final:{title:'Финал главы 1', rows:[['Статус','В разработке (W2)'],['Боссов','4 интегрированных'],['Награда','+100 XP + ачивка']]}
};
const TIPS=[
{sec:'p1',html:'§ 1 — крути ползунки осциллограммы. Главное: $\\omega = 2\\pi/T$ связывает все 3 величины.'},
{sec:'p2',html:'§ 2 — для пружины период зависит от $m, k$, для матем. от $l, g$. <b>Амплитуда не входит</b>!'},
{sec:'p3',html:'§ 3 — пока $W_к$ растёт, $W_п$ убывает. Сумма постоянна: $W_{мех} = kA^2/2$.'},
{sec:'p4',html:'§ 4 — в разработке (W2).'},
{sec:'p5',html:'§ 5 — в разработке (W2).'},
{sec:'p6',html:'§ 6 — в разработке (W2).'},
{sec:'final',html:'Финал главы 1 — в разработке (W2).'}
];
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"><div class="xp-card-title"><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?' — '+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),var(--pri-soft));border-color:var(--warn)"><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:.82rem;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('physics11_theme')||localStorage.getItem('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('physics11_theme', dark?'dark':'light');
localStorage.setItem('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){} } }
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>'
};
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример'};
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 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;
const NAMES = {p1:'\xA71',p2:'\xA72',p3:'\xA73',p4:'\xA74',p5:'\xA75',p6:'\xA76',final:'Финал'};
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);
});
}
function normalizeAns(s){
return String(s||'').toLowerCase().replace(/\s+/g,'').replace(/°/g,'').replace(/sqrt/g,'√').replace(/корень/g,'√').replace(/,/g,'.');
}
function makeBoss(id, def){
const stage = (typeof def.stage === 'number') ? def.stage : 0;
const solved = !!def.solved;
const total = def.stages.length;
const stageObj = def.stages[Math.min(stage, total-1)];
let optsHtml = '';
if(solved){
return '<div class="boss-card solved" id="boss-'+id+'"><div class="boss-head"><span class="boss-tag">'+(def.tag||'Босс')+'</span><span class="boss-title">'+def.title+'</span></div><div class="boss-q">Побеждён! +'+def.xp+' XP получены.</div></div>';
}
if(stageObj.type === 'mc'){
optsHtml = '<div class="opts-row">';
stageObj.opts.forEach((o,i)=>{ optsHtml += '<button class="opt-btn" data-i="'+i+'">'+o+'</button>'; });
optsHtml += '</div>';
} else {
optsHtml = '<div class="boss-row"><input class="boss-input" id="boss-'+id+'-inp" placeholder="ответ"><button class="btn primary" id="boss-'+id+'-go">Атака</button></div>';
}
return '<div class="boss-card" id="boss-'+id+'"><div class="boss-head"><span class="boss-tag">'+(def.tag||'Босс')+'</span><span class="boss-title">'+def.title+' — этап '+(stage+1)+' / '+total+'</span></div><div class="boss-q">'+stageObj.q+'</div>'+optsHtml+'<div class="boss-fb" id="boss-'+id+'-fb"></div></div>';
}
function bindBoss(id, def, state, save, onWin){
const card = document.getElementById('boss-'+id);
if(!card || state.solved) return;
const stageObj = def.stages[state.stage];
const fb = document.getElementById('boss-'+id+'-fb');
function advance(){
state.stage++;
if(state.stage >= def.stages.length){
state.solved = true; save(); addXp(def.xp, 'boss-'+id);
if(onWin) onWin();
} else { save(); }
rebuildBoss(id, def, state, save, onWin);
}
if(stageObj.type === 'mc'){
card.querySelectorAll('.opt-btn').forEach(btn=>{
btn.addEventListener('click', ()=>{
const i = +btn.dataset.i;
if(i === stageObj.correct){
btn.classList.add('correct');
fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+(stageObj.explain||''); fb.style.display='block'; renderMath(fb);
setTimeout(advance, 700);
} else {
btn.classList.add('wrong');
fb.className='feedback fail'; fb.innerHTML='&#10007; Не так. '+(stageObj.explain||''); fb.style.display='block'; renderMath(fb);
}
});
});
} else {
const inp = document.getElementById('boss-'+id+'-inp');
const go = document.getElementById('boss-'+id+'-go');
function attack(){
const v = normalizeAns(inp.value);
const ans = Array.isArray(stageObj.a) ? stageObj.a.map(normalizeAns) : [normalizeAns(stageObj.a)];
if(ans.indexOf(v) >= 0){
fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+(stageObj.explain||''); fb.style.display='block'; renderMath(fb);
setTimeout(advance, 600);
} else {
fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+(stageObj.explain||''); fb.style.display='block'; renderMath(fb);
}
}
go.addEventListener('click', attack);
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); attack(); } });
}
}
function rebuildBoss(id, def, state, save, onWin){
const card = document.getElementById('boss-'+id);
if(!card) return;
card.outerHTML = makeBoss(id, Object.assign({}, def, state));
bindBoss(id, def, state, save, onWin);
renderMath(document.getElementById('boss-'+id));
}
function makeAndBindBoss(slotId, id, def, state, save, onWin){
const slot = document.getElementById(slotId); if(!slot) return;
slot.innerHTML = makeBoss(id, Object.assign({}, def, state));
bindBoss(id, def, state, save, onWin);
renderMath(slot);
}
function ensureFx(cb){ if(window.PHYS) return cb(); setTimeout(()=>ensureFx(cb), 60); }
/* ===== §1 ===== */
function buildP1(){
const box = document.getElementById('p1-body'); if(!box) return;
let html = '';
html += makeCard('theory', 'Колебательное движение', '§ 1.1',
'<p><b>Колебательное движение</b> — повторяющееся (периодическое) движение тела вокруг положения равновесия.</p>'
+ '<p>Примеры: маятник часов, груз на пружине, биение сердца, листья на ветру, струна гитары.</p>'
+ '<p>Если значения физических величин повторяются через равные промежутки времени, колебания называются <b>периодическими</b>.</p>');
html += makeCard('rule', 'Период, частота, цикл. частота', '§ 1.2',
'<p><b>Период $T$</b> — минимальный промежуток времени, за который повторяются значения всех физических величин:</p>'
+ '<p style="text-align:center;margin:6px 0">$$T = \\dfrac{\\Delta t}{N}$$</p>'
+ '<p>где $\\Delta t$ — промежуток времени, за который произошло $N$ колебаний.</p>'
+ '<p><b>Частота $\\nu$</b> — число колебаний в единицу времени:</p>'
+ '<p style="text-align:center;margin:6px 0">$$\\nu = \\dfrac{N}{\\Delta t} = \\dfrac{1}{T}$$</p>'
+ '<p>Единица частоты — <b>герц (Гц)</b>: $1$ Гц $= 1$ кол/с.</p>'
+ '<p><b>Циклическая (угловая) частота $\\omega$</b> — число колебаний за $2\\pi$ секунд:</p>'
+ '<p style="text-align:center;margin:6px 0">$$\\omega = 2\\pi\\nu = \\dfrac{2\\pi}{T}$$</p>'
+ '<p>Единица: рад/с.</p>');
html += makeCard('rule', 'Гармонические колебания', '§ 1.3',
'<p>Колебания, при которых координата меняется по закону косинуса (или синуса), называются <b>гармоническими</b>:</p>'
+ '<p style="text-align:center;margin:8px 0">$$x(t) = A\\cos(\\omega t + \\varphi_0)$$</p>'
+ '<p>где:</p>'
+ '<ul>'
+ '<li>$A$ — <b>амплитуда</b> (максимальное отклонение от равновесия);</li>'
+ '<li>$\\omega$ — циклическая частота;</li>'
+ '<li>$\\varphi_0$ — <b>начальная фаза</b> (в момент $t=0$);</li>'
+ '<li>$\\omega t + \\varphi_0$ — <b>фаза колебаний</b>.</li>'
+ '</ul>'
+ '<p>График $x(t)$ — синусоида (косинусоида), которая называется <b>осциллограммой</b>.</p>');
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><span class="wg-title">Осциллограмма — поиграй с амплитудой, частотой, фазой</span></div>'
+ '<div class="wg-help">Двигай ползунки и наблюдай за изменением графика $x(t) = A\\cos(\\omega t + \\varphi_0)$.</div>'
+ '<div class="fx-holder" id="fx-osc1"></div>'
+ '<div class="fx-sliders" id="fx-osc1-sl"></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 2</span><span class="wg-title">Подсчёт $T, \\nu, \\omega$</span></div>'
+ '<div class="wg-help">Решено: <b id="i1-calc-score">0</b> / 5.</div>'
+ '<div id="i1-calc-q" style="margin:8px 0"></div><div class="actions"><input class="tinp" id="i1-calc-inp" placeholder="число"><button class="btn primary" id="i1-calc-go">Проверить</button></div><div class="feedback" id="i1-calc-fb"></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 3</span><span class="wg-title">Свойства колебаний</span></div>'
+ '<div class="wg-help">Решено: <b id="i1-prop-score">0</b> / 5.</div>'
+ '<div id="i1-prop-q" style="margin:8px 0"></div><div class="opts-row" id="i1-prop-opts"></div><div class="feedback" id="i1-prop-fb"></div></div>';
html += '<div id="boss-1-slot"></div>';
html += readButton('p1');
html += secNavFor('p1');
box.innerHTML = html;
/* Mount Oscillogram + sliders */
ensureFx(()=>{
const oscEl = document.getElementById('fx-osc1');
const osc = new PHYS.Oscillogram(oscEl, {width:560, height:200, A:0.7, omega:2*Math.PI, phi0:0, label:'x(t)'});
const slBox = document.getElementById('fx-osc1-sl');
const slA = PHYS.util.slider({label:'A (амплитуда)', min:0.1, max:1.0, step:0.05, value:0.7, fmt:v=>v.toFixed(2), onChange:v=>osc.setA(v)});
const slW = PHYS.util.slider({label:'ω (рад/с)', min:1, max:12, step:0.2, value:Math.round(2*Math.PI*10)/10, fmt:v=>v.toFixed(1), onChange:v=>osc.setOmega(v)});
const slPhi = PHYS.util.slider({label:'φ₀ (рад)', min:-Math.PI, max:Math.PI, step:0.1, value:0, fmt:v=>v.toFixed(1), onChange:v=>osc.setPhi(v)});
slBox.innerHTML = slA.html + slW.html + slPhi.html;
slA.wire(slBox); slW.wire(slBox); slPhi.wire(slBox);
});
runQuizInput('i1-calc', I1_CALC_ITEMS, 14);
runQuizMC('i1-prop', I1_PROP_ITEMS, 12);
const bs = loadBossState('boss-1') || { stage:0, solved:false };
makeAndBindBoss('boss-1-slot', '1', BOSS_DEFS.b1, bs,
()=>saveBossState('boss-1', bs),
()=>{ bumpProgress('p1', 40); achievement('p1_done'); });
wireReadBtn('p1');
renderMath(box);
}
/* ===== §2 ===== */
function buildP2(){
const box = document.getElementById('p2-body'); if(!box) return;
let html = '';
html += makeCard('theory', 'Пружинный маятник', '§ 2.1',
'<p><b>Пружинный маятник</b> — груз массой $m$, прикреплённый к пружине жёсткости $k$.</p>'
+ '<p>Сила упругости пропорциональна смещению: $F = -kx$. По 2-му закону Ньютона: $ma = -kx$, откуда:</p>'
+ '<p style="text-align:center;margin:6px 0">$$\\omega = \\sqrt{\\dfrac{k}{m}}, \\quad T = 2\\pi\\sqrt{\\dfrac{m}{k}}$$</p>'
+ '<p>Период <b>не зависит</b> от амплитуды (для гармонических колебаний). С ростом массы $T$ увеличивается, с ростом жёсткости — уменьшается.</p>');
html += makeCard('theory', 'Математический маятник', '§ 2.2',
'<p><b>Математический маятник</b> — точечная масса $m$ на невесомой нерастяжимой нити длиной $l$.</p>'
+ '<p>При малых углах ($\\alpha \\lesssim 10°$) период:</p>'
+ '<p style="text-align:center;margin:6px 0">$$T = 2\\pi\\sqrt{\\dfrac{l}{g}}$$</p>'
+ '<p>Период не зависит от массы груза! Зависит только от длины нити $l$ и ускорения свободного падения $g$.</p>'
+ '<p><b>Пример:</b> при $l = 1$ м, $g = 9{,}81$ м/с² период $T \\approx 2{,}01$ с — секундный маятник.</p>');
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><span class="wg-title">Пружинный и математический маятники side-by-side</span></div>'
+ '<div class="wg-help">Сравни оба маятника. Двигай ползунки $m, k, l$ — наблюдай, как меняется период.</div>'
+ '<div class="fx-row"><div class="fx-cell"><div class="fx-cell-label">Пружинный (T = 2π√(m/k))</div><div class="fx-holder" id="fx-spring"></div><div class="fx-sliders" id="fx-spring-sl"></div></div>'
+ '<div class="fx-cell"><div class="fx-cell-label">Математический (T = 2π√(l/g))</div><div class="fx-holder" id="fx-pendulum"></div><div class="fx-sliders" id="fx-pendulum-sl"></div></div></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 2</span><span class="wg-title">Расчёт периода</span></div>'
+ '<div class="wg-help">Используй $T = 2\\pi\\sqrt{m/k}$ или $T = 2\\pi\\sqrt{l/g}$. Округляй до 2 знаков. Решено: <b id="i2-calc-score">0</b> / 5.</div>'
+ '<div id="i2-calc-q" style="margin:8px 0"></div><div class="actions"><input class="tinp" id="i2-calc-inp" placeholder="ответ"><button class="btn primary" id="i2-calc-go">Проверить</button></div><div class="feedback" id="i2-calc-fb"></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 3</span><span class="wg-title">Как изменится период?</span></div>'
+ '<div class="wg-help">Решено: <b id="i2-change-score">0</b> / 5.</div>'
+ '<div id="i2-change-q" style="margin:8px 0"></div><div class="opts-row" id="i2-change-opts"></div><div class="feedback" id="i2-change-fb"></div></div>';
html += '<div id="boss-2-slot"></div>';
html += readButton('p2');
html += secNavFor('p2');
box.innerHTML = html;
ensureFx(()=>{
/* Spring */
const spEl = document.getElementById('fx-spring');
const sp = new PHYS.SpringMass(spEl, {width:220, height:280, m:0.5, k:20, A:0.06});
const spSl = document.getElementById('fx-spring-sl');
const slM = PHYS.util.slider({label:'m (кг)', min:0.1, max:2.0, step:0.05, value:0.5, fmt:v=>v.toFixed(2), onChange:v=>sp.setMass(v)});
const slK = PHYS.util.slider({label:'k (Н/м)', min:5, max:80, step:1, value:20, fmt:v=>v.toFixed(0), onChange:v=>sp.setStiffness(v)});
spSl.innerHTML = slM.html + slK.html;
slM.wire(spSl); slK.wire(spSl);
/* Pendulum */
const peEl = document.getElementById('fx-pendulum');
const pe = new PHYS.Pendulum(peEl, {width:220, height:280, l:1.0, g:9.81, theta0:Math.PI/12});
const peSl = document.getElementById('fx-pendulum-sl');
const slL = PHYS.util.slider({label:'l (м)', min:0.2, max:3.0, step:0.1, value:1.0, fmt:v=>v.toFixed(1), onChange:v=>pe.setLength(v)});
const slG = PHYS.util.slider({label:'g (м/с²)', min:1.6, max:24, step:0.1, value:9.81, fmt:v=>v.toFixed(2), onChange:v=>pe.setG(v)});
peSl.innerHTML = slL.html + slG.html;
slL.wire(peSl); slG.wire(peSl);
});
runQuizInput('i2-calc', I2_CALC_ITEMS, 14);
runQuizMC('i2-change', I2_CHANGE_ITEMS, 12);
const bs = loadBossState('boss-2') || { stage:0, solved:false };
makeAndBindBoss('boss-2-slot', '2', BOSS_DEFS.b2, bs,
()=>saveBossState('boss-2', bs),
()=>{ bumpProgress('p2', 40); achievement('p2_done'); });
wireReadBtn('p2');
renderMath(box);
}
/* ===== §3 ===== */
function buildP3(){
const box = document.getElementById('p3-body'); if(!box) return;
let html = '';
html += makeCard('theory', 'Энергии при гармонических колебаниях', '§ 3.1',
'<p>При колебаниях тело обладает <b>кинетической</b> и <b>потенциальной</b> энергией. При $x = A\\cos(\\omega t + \\varphi_0)$:</p>'
+ '<p>$v = -A\\omega\\sin(\\omega t + \\varphi_0)$, поэтому:</p>'
+ '<p style="text-align:center;margin:6px 0">$$W_к = \\dfrac{mv^2}{2} = \\dfrac{1}{2}mA^2\\omega^2\\sin^2(\\omega t + \\varphi_0)$$</p>'
+ '<p>Для пружины $k = m\\omega^2$, и потенциальная энергия:</p>'
+ '<p style="text-align:center;margin:6px 0">$$W_п = \\dfrac{kx^2}{2} = \\dfrac{1}{2}mA^2\\omega^2\\cos^2(\\omega t + \\varphi_0)$$</p>');
html += makeCard('rule', 'Закон сохранения механической энергии', '§ 3.2',
'<p>Используя $\\sin^2 + \\cos^2 = 1$:</p>'
+ '<p style="text-align:center;margin:8px 0">$$W_{мех} = W_к + W_п = \\dfrac{1}{2}mA^2\\omega^2 = \\dfrac{kA^2}{2} = \\text{const}$$</p>'
+ '<p>Полная механическая энергия гармонических колебаний <b>постоянна</b> и пропорциональна <b>квадрату амплитуды</b>.</p>'
+ '<p>В положении равновесия ($x=0$): $W_к = W_{мех}$, $W_п = 0$.</p>'
+ '<p>В точках max отклонения ($x = \\pm A$): $W_к = 0$, $W_п = W_{мех}$.</p>');
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><span class="wg-title">Превращения энергии в реальном времени</span></div>'
+ '<div class="wg-help">График показывает 3 кривые: красная — $W_к$, зелёная — $W_п$, фиолетовая пунктирная — $W_{мех} = $ const. Двигай ползунки $A, \\omega$.</div>'
+ '<div class="fx-holder" id="fx-energy"></div>'
+ '<div class="fx-sliders" id="fx-energy-sl"></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 2</span><span class="wg-title">Расчёт энергии</span></div>'
+ '<div class="wg-help">Используй $W_{мех} = kA^2/2$. Решено: <b id="i3-calc-score">0</b> / 5.</div>'
+ '<div id="i3-calc-q" style="margin:8px 0"></div><div class="actions"><input class="tinp" id="i3-calc-inp" placeholder="ответ (Дж)"><button class="btn primary" id="i3-calc-go">Проверить</button></div><div class="feedback" id="i3-calc-fb"></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 3</span><span class="wg-title">Превращения энергии</span></div>'
+ '<div class="wg-help">Решено: <b id="i3-trans-score">0</b> / 5.</div>'
+ '<div id="i3-trans-q" style="margin:8px 0"></div><div class="opts-row" id="i3-trans-opts"></div><div class="feedback" id="i3-trans-fb"></div></div>';
html += '<div id="boss-3-slot"></div>';
html += readButton('p3');
html += secNavFor('p3');
box.innerHTML = html;
ensureFx(()=>{
const evEl = document.getElementById('fx-energy');
const ev = new PHYS.EnergyView(evEl, {width:560, height:230, A:1, omega:2*Math.PI});
const slBox = document.getElementById('fx-energy-sl');
const slA = PHYS.util.slider({label:'A (отн.)', min:0.3, max:1.0, step:0.05, value:1.0, fmt:v=>v.toFixed(2), onChange:v=>ev.setA(v)});
const slW = PHYS.util.slider({label:'ω (рад/с)', min:1, max:10, step:0.2, value:Math.round(2*Math.PI*10)/10, fmt:v=>v.toFixed(1), onChange:v=>ev.setOmega(v)});
slBox.innerHTML = slA.html + slW.html;
slA.wire(slBox); slW.wire(slBox);
});
runQuizInput('i3-calc', I3_CALC_ITEMS, 14);
runQuizMC('i3-trans', I3_TRANS_ITEMS, 12);
const bs = loadBossState('boss-3') || { stage:0, solved:false };
makeAndBindBoss('boss-3-slot', '3', BOSS_DEFS.b3, bs,
()=>saveBossState('boss-3', bs),
()=>{ bumpProgress('p3', 40); achievement('p3_done'); });
wireReadBtn('p3');
renderMath(box);
}
/* ===== Stubs §4-§6, Final ===== */
function buildStubP(id, label, message){
const box = document.getElementById(id + '-body'); if(!box) return;
let html = '<div class="stub-note"><h3>' + label + ' — в разработке</h3><p>' + message + '</p></div>';
html += secNavFor(id);
box.innerHTML = html;
}
/* ===== Boss state ===== */
function loadBossState(key){ try{ return JSON.parse(localStorage.getItem('physics11_ch1_'+key)||'null'); }catch(e){ return null; } }
function saveBossState(key, state){ try{ localStorage.setItem('physics11_ch1_'+key, JSON.stringify(state)); }catch(e){} }
/* ===== Quiz engines ===== */
function runQuizMC(id, items, xpReward){
const state = JSON.parse(localStorage.getItem('physics11_ch1_quiz_'+id)||'null') || { idx:0, solved:0, awarded:false };
const qEl = document.getElementById(id+'-q');
const optsEl = document.getElementById(id+'-opts');
const fbEl = document.getElementById(id+'-fb');
const scoreEl = document.getElementById(id+'-score');
function save(){ localStorage.setItem('physics11_ch1_quiz_'+id, JSON.stringify(state)); }
function render(){
if(state.solved >= items.length){
qEl.innerHTML = '<b style="color:var(--ok)">Все задания решены!</b> +'+xpReward+' XP.';
optsEl.innerHTML = ''; fbEl.style.display='none';
if(scoreEl) scoreEl.textContent = state.solved;
if(!state.awarded){ state.awarded = true; save(); addXp(xpReward, 'quiz-'+id); }
return;
}
const it = items[state.idx % items.length];
qEl.innerHTML = it.q; optsEl.innerHTML = ''; fbEl.style.display='none';
if(scoreEl) scoreEl.textContent = state.solved;
it.opts.forEach((o,i)=>{
const b = document.createElement('button'); b.className = 'opt-btn'; b.innerHTML = o;
b.addEventListener('click', ()=>{
if(i === it.correct){
b.classList.add('correct'); state.solved++; state.idx++; save();
if(scoreEl) scoreEl.textContent = state.solved;
fbEl.className='feedback ok'; fbEl.innerHTML='&#10003; Верно. '+(it.explain||''); fbEl.style.display='block'; renderMath(fbEl);
setTimeout(render, 850);
} else {
b.classList.add('wrong');
fbEl.className='feedback fail'; fbEl.innerHTML='&#10007; Не так. '+(it.explain||''); fbEl.style.display='block'; renderMath(fbEl);
}
});
optsEl.appendChild(b);
});
renderMath(qEl);
}
render();
}
function runQuizInput(id, items, xpReward){
const state = JSON.parse(localStorage.getItem('physics11_ch1_quiz_'+id)||'null') || { idx:0, solved:0, awarded:false };
const qEl = document.getElementById(id+'-q');
const inp = document.getElementById(id+'-inp');
const go = document.getElementById(id+'-go');
const fbEl = document.getElementById(id+'-fb');
const scoreEl = document.getElementById(id+'-score');
function save(){ localStorage.setItem('physics11_ch1_quiz_'+id, JSON.stringify(state)); }
function render(){
if(state.solved >= items.length){
qEl.innerHTML = '<b style="color:var(--ok)">Все задания решены!</b> +'+xpReward+' XP.';
inp.value=''; inp.disabled=true; go.disabled=true;
if(scoreEl) scoreEl.textContent = state.solved;
if(!state.awarded){ state.awarded = true; save(); addXp(xpReward, 'quiz-'+id); }
return;
}
const it = items[state.idx % items.length];
qEl.innerHTML = it.q; inp.value=''; inp.disabled=false; go.disabled=false; fbEl.style.display='none';
if(scoreEl) scoreEl.textContent = state.solved;
renderMath(qEl);
}
function check(){
const it = items[state.idx % items.length];
const v = normalizeAns(inp.value);
const ans = Array.isArray(it.answer) ? it.answer.map(normalizeAns) : [normalizeAns(it.answer)];
if(ans.indexOf(v) >= 0){
state.solved++; state.idx++; save();
if(scoreEl) scoreEl.textContent = state.solved;
fbEl.className='feedback ok'; fbEl.innerHTML='&#10003; Верно. '+(it.explain||''); fbEl.style.display='block'; renderMath(fbEl);
setTimeout(render, 850);
} else {
fbEl.className='feedback fail'; fbEl.innerHTML='&#10007; Не так. '+(it.explain||''); fbEl.style.display='block'; renderMath(fbEl);
}
}
go.addEventListener('click', check);
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); check(); } });
render();
}
/* ===== Quiz items ===== */
const I1_CALC_ITEMS = [
{ q:'$T = 0{,}5$ с. Найди $\\nu$ (Гц).', answer:'2', explain:'$\\nu = 1/T = 1/0{,}5 = 2$ Гц.' },
{ q:'$\\nu = 4$ Гц. Найди $T$ (с).', answer:['0.25','1/4'], explain:'$T = 1/\\nu = 0{,}25$ с.' },
{ q:'За $\\Delta t = 10$ с тело совершило $N = 20$ колебаний. Период $T$ (с)?', answer:'0.5', explain:'$T = \\Delta t / N = 10/20 = 0{,}5$ с.' },
{ q:'$T = 2$ с. Цикл. частота $\\omega$ (рад/с)?', answer:['π','pi','3.14','3.1416'], explain:'$\\omega = 2\\pi/T = \\pi$ рад/с.' },
{ q:'$\\nu = 50$ Гц (промышленная частота). $\\omega$ (рад/с)?', answer:['100π','100pi','314','314.16'], explain:'$\\omega = 2\\pi\\nu = 100\\pi \\approx 314$ рад/с.' }
];
const I1_PROP_ITEMS = [
{ q:'Что такое амплитуда колебаний?', opts:['Период','Максимальное отклонение от равновесия','Число колебаний','Скорость'], correct:1, explain:'Амплитуда $A$ — максимальное отклонение от положения равновесия.' },
{ q:'Единица измерения частоты?', opts:['с','рад/с','Гц','м'], correct:2, explain:'Герц = 1/секунду.' },
{ q:'Связь $\\omega$ и $\\nu$:', opts:['$\\omega = \\nu$','$\\omega = 2\\pi\\nu$','$\\omega = \\nu/(2\\pi)$','$\\omega = \\nu^2$'], correct:1, explain:'$\\omega = 2\\pi\\nu$.' },
{ q:'При гарм. колебаниях $x(t) = $:', opts:['$A\\cos(\\omega t)$','$At$','$Ae^{-\\omega t}$','$A/t$'], correct:0, explain:'Косинусоидальный закон.' },
{ q:'Если $A$ удвоить, период $T$:', opts:['Удвоится','Уполовинится','Не изменится','Учетверится'], correct:2, explain:'Период гарм. колебаний не зависит от амплитуды!' }
];
const I2_CALC_ITEMS = [
{ q:'$l = 1$ м, $g = 9{,}81$ м/с². $T$ матем. маятника (с)?', answer:['2','2.01','2.0'], explain:'$T = 2\\pi\\sqrt{1/9{,}81} \\approx 2{,}0$ с.' },
{ q:'Пружина $k = 100$ Н/м, $m = 1$ кг. $T$ (с)?', answer:['0.63','0.628','0.6'], explain:'$T = 2\\pi\\sqrt{1/100} = 0{,}2\\pi \\approx 0{,}63$ с.' },
{ q:'$m = 2$ кг, $k = 200$ Н/м. $T$ (с)?', answer:['0.63','0.628','0.6'], explain:'$T = 2\\pi\\sqrt{2/200} = 0{,}2\\pi \\approx 0{,}63$ с.' },
{ q:'$l = 4$ м, $g = 10$ м/с². $T$ (с)?', answer:['4','4.0','3.97'], explain:'$T = 2\\pi\\sqrt{4/10} = 2\\pi\\cdot 0{,}632 \\approx 4{,}0$ с.' },
{ q:'$l = 0{,}25$ м, $g = 9{,}81$ м/с². $T$ (с)?', answer:['1','1.0','1.004'], explain:'$T = 2\\pi\\sqrt{0{,}25/9{,}81} \\approx 1$ с.' }
];
const I2_CHANGE_ITEMS = [
{ q:'Длину матем. маятника увеличили в 4 раза. $T$:', opts:['Увеличится в 4 раза','Увеличится в 2 раза','Уменьшится в 2 раза','Не изменится'], correct:1, explain:'$T \\propto \\sqrt{l}$, $\\sqrt{4} = 2$.' },
{ q:'Жёсткость пружины увеличили в 9 раз. $T$:', opts:['Увеличится в 9','Уменьшится в 9','Увеличится в 3','Уменьшится в 3'], correct:3, explain:'$T \\propto 1/\\sqrt{k}$, $\\sqrt{9} = 3$.' },
{ q:'Массу груза удвоили (пружина та же). $T$:', opts:['Увеличится в 2','Уменьшится в 2','Увеличится в $\\sqrt{2}$','Не изменится'], correct:2, explain:'$T \\propto \\sqrt{m}$.' },
{ q:'Период матем. маятника зависит от массы?', opts:['Да, линейно','Да, как $\\sqrt{m}$','Нет, не зависит','Да, как $m^2$'], correct:2, explain:'$T = 2\\pi\\sqrt{l/g}$ — массы нет.' },
{ q:'Маятник перенесли с Земли ($g=9{,}81$) на Луну ($g=1{,}62$). $T$:', opts:['Увеличится в $\\approx 2{,}5$ раза','Уменьшится','Не изменится','Увеличится в 6'], correct:0, explain:'$T \\propto 1/\\sqrt{g}$, $\\sqrt{9{,}81/1{,}62} \\approx 2{,}46$.' }
];
const I3_CALC_ITEMS = [
{ q:'$k = 200$ Н/м, $A = 0{,}1$ м. $W_{мех}$ (Дж)?', answer:'1', explain:'$W = kA^2/2 = 200 \\cdot 0{,}01 / 2 = 1$ Дж.' },
{ q:'$m = 0{,}5$ кг, $\\omega = 4$ рад/с, $A = 0{,}2$ м. $W_{мех}$ (Дж)?', answer:'0.16', explain:'$W = m\\omega^2 A^2/2 = 0{,}5 \\cdot 16 \\cdot 0{,}04 / 2 = 0{,}16$ Дж.' },
{ q:'$k = 50$ Н/м, $A = 0{,}2$ м. $W_{мех}$ (Дж)?', answer:'1', explain:'$50 \\cdot 0{,}04 / 2 = 1$ Дж.' },
{ q:'Удвоили амплитуду. Энергия станет:', answer:'4', explain:'$W \\propto A^2$, $2^2 = 4$. Энергия увеличилась в 4 раза.' },
{ q:'$W_{мех} = 2$ Дж. В точке max отклонения $W_п$ (Дж)?', answer:'2', explain:'В $x = \\pm A$: $W_к=0$, $W_п = W_{мех} = 2$ Дж.' }
];
const I3_TRANS_ITEMS = [
{ q:'В положении равновесия $x = 0$:', opts:['$W_к = 0$','$W_к = W_{мех}$','$W_п = W_{мех}$','$W_{мех} = 0$'], correct:1, explain:'$x=0$ ⇒ $W_п = 0$ ⇒ $W_к = W_{мех}$.' },
{ q:'В точке max отклонения $x = A$:', opts:['$W_к = W_{мех}$','$W_п = 0$','$W_к = 0$','$v = $ max'], correct:2, explain:'$v = 0$ в крайних точках, $W_к = 0$.' },
{ q:'$W_{мех} = $ ?', opts:['$kA/2$','$kA^2/2$','$kx^2/2$','$mv^2$'], correct:1, explain:'$W_{мех} = kA^2/2$ — пропорциональна квадрату амплитуды.' },
{ q:'При гарм. колебаниях $W_{мех}$:', opts:['Растёт со временем','Убывает','Постоянна','Колеблется'], correct:2, explain:'Без затухания $W_{мех}$ = const.' },
{ q:'$W_к$ и $W_п$:', opts:['В фазе','В противофазе','Не связаны','Растут вместе'], correct:1, explain:'Когда $W_к$ max, $W_п$ = 0 (и наоборот). Сумма = const.' }
];
/* ===== Boss defs ===== */
const BOSS_DEFS = {
b1: { title:'Босс §1 — Гарм. колебания', tag:'§1', xp:65, stages:[
{ q:'$T = 0{,}1$ с. $\\nu$ (Гц)?', type:'input', a:'10', explain:'$\\nu = 1/T = 10$ Гц.' },
{ q:'$\\omega = 4\\pi$ рад/с. $T$ (с)?', type:'input', a:'0.5', explain:'$T = 2\\pi/\\omega = 0{,}5$ с.' },
{ q:'Уравнение гарм. колеб. — это:', type:'mc', opts:['$x = At$','$x = A\\cos(\\omega t + \\varphi_0)$','$x = Ae^{-t}$','$x = A/t$'], correct:1, explain:'Косинусоидальный закон.' },
{ q:'За 5 с тело совершило 25 колебаний. $T$ (с)?', type:'input', a:'0.2', explain:'$T = 5/25 = 0{,}2$ с.' },
{ q:'Если $A$ утроить, период:', type:'mc', opts:['Утроится','Уменьшится в 3','Не изменится','Увеличится в 9'], correct:2, explain:'Период не зависит от $A$.' }
]},
b2: { title:'Босс §2 — Маятники', tag:'§2', xp:70, stages:[
{ q:'$l = 1{,}0$ м, $g = 9{,}81$ м/с². $T$ (с)?', type:'input', a:['2','2.0','2.01'], explain:'$T \\approx 2{,}0$ с.' },
{ q:'$m = 0{,}1$ кг, $k = 10$ Н/м. $T$ (с)?', type:'input', a:['0.63','0.628','0.6'], explain:'$T = 2\\pi\\sqrt{0{,}01} = 0{,}2\\pi \\approx 0{,}63$ с.' },
{ q:'$T$ матем. маятника зависит от:', type:'mc', opts:['Массы','Амплитуды','Длины и $g$','Жёсткости'], correct:2, explain:'$T = 2\\pi\\sqrt{l/g}$.' },
{ q:'$T$ пружинного зависит от:', type:'mc', opts:['$m, k$','$l, g$','$m, l$','Амплитуды'], correct:0, explain:'$T = 2\\pi\\sqrt{m/k}$.' },
{ q:'Длину матем. маятника увеличили в 9 раз. $T$:', type:'mc', opts:['Увеличится в 9','Увеличится в 3','Не изменится','Уменьшится в 3'], correct:1, explain:'$\\sqrt{9} = 3$.' }
]},
b3: { title:'Босс §3 — Превращения энергии', tag:'§3', xp:65, stages:[
{ q:'$k = 100$ Н/м, $A = 0{,}2$ м. $W_{мех}$ (Дж)?', type:'input', a:'2', explain:'$W = kA^2/2 = 100 \\cdot 0{,}04 / 2 = 2$ Дж.' },
{ q:'$W_{мех} = $ ?', type:'mc', opts:['$kA/2$','$kA^2/2$','$mv$','$\\omega A$'], correct:1, explain:'$kA^2/2$.' },
{ q:'В точке $x = 0$:', type:'mc', opts:['$W_п = $ max','$W_к = $ max','$v = 0$','$W_{мех} = 0$'], correct:1, explain:'$x=0 \\Rightarrow W_п=0 \\Rightarrow W_к = $ max.' },
{ q:'Удвоили $A$. Во сколько раз изменится $W_{мех}$?', type:'input', a:'4', explain:'$W \\propto A^2$.' },
{ q:'При гарм. колебаниях $W_к + W_п = $ ?', type:'mc', opts:['$0$','$\\text{const}$','Зависит от $t$','$kA$'], correct:1, explain:'Сохранение полной механической энергии.' }
]}
};
/* ===== Init ===== */
function init(){
loadProgress();
initTheme();
buildParaSelector();
goTo('p1');
refreshProgressUI();
if(!STATE.achievements.has('start')) achievement('start');
}
if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
</script>
</body>
</html>