Files
Learn_System/frontend/textbooks/physics_11_ch4.html
T
Maxim Dolgolyov 660e7e2747 feat(gamification): Phase 1 — full kill-switch + textbook XP wrapping
Until now the 'gamification' feature flag did nothing: it had no row in
app_settings, the admin couldn't toggle it, awardXP/awardCoins ignored
it, and the CSS only hid three dashboard widgets — XP bars in textbooks
stayed visible regardless.

Phase 1 closes every hole.

Backend (source of truth):
  • migration 029 seeds feature_gamification_enabled=1
  • new isGamificationEnabled() helper in gamification/_shared.js with a
    30s cache + invalidateGamificationCache() for instant admin toggles
  • awardXP / awardCoins / updateStreak / unlockAchievement /
    checkAchievements all bail out when the flag is off
  • /api/gamification/* and /api/shop/* (user routes) return 404 when
    disabled; admin routes remain open so the switch itself is reachable
  • adminController.updateFeatures gains 'gamification' in the allow-list
    and invalidates the cache on flip

Frontend:
  • LS.isGamificationEnabled() (synchronous, populated by loadFeatures)
    so xp.js + applyCosmetics can bail without a round-trip
  • xp.js load/add/flush become no-ops when the flag is off
  • applyCosmetics skips the round-trip when off
  • CSS .no-gamification rule expanded to cover .hero-xp-badge, .po-xp,
    .xp-card, .xp-bar, #frames-section, and a universal [data-gamified]
    hook for future blocks

Textbooks (Variant 2 of the plan):
  • backend/scripts/wrap_textbook_xp.py — idempotent script that adds
    data-gamified to 167 XP tags across 63 textbook files (chapters +
    hubs, all subjects/grades). Single CSS rule now hides everything.

Verified end-to-end: with the flag off, awardXP/awardCoins write nothing;
flipping back restores normal behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 19:43:24 +03:00

963 lines
76 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">
<title>Физика 11 · Глава 4 · «Основы СТО»</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:#2563eb; --pri2:#1d4ed8; --pri-soft:#dbeafe;
--acc:#3b82f6; --acc2:#1e40af; --acc-soft:#bfdbfe;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#020617; --card:#0a1929; --card-soft:#0f1c30; --text:#dbeafe; --ink:#dbeafe; --muted:#93c5fd; --border:#1e3a8a}
*{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,#1e3a8a 0%,#2563eb 55%,#93c5fd 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(147,197,253,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 4';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:'c';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(150px,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:.8rem;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:.45}
.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 .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,.card-body ol{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}
.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)}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Физика 11 · Глава 4</h1>
<div class="hdr-sub">Основы СТО · принцип относительности Эйнштейна, замедление времени, сокращение длин, E = mc²</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>Глава о том, как в начале XX века Альберт Эйнштейн пересмотрел понятия пространства и времени. 3 параграфа + финал с 3 интегральными боссами.</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> Начать § 24</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" data-gamified></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="c"><div class="sec-header"><span class="sec-num">§ 24</span><h2 class="sec-h">Принцип относ. Галилея и ЭМ явления</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="&#947;"><div class="sec-header"><span class="sec-num">§ 25</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">§ 26</span><h2 class="sec-h">Релятивистская динамика. E = mc²</h2></div><div id="p3-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,#2563eb,#1d4ed8)">&#9733;</span><h2 class="sec-h">Финал главы 4</h2></div><div id="final-body"></div></section>
</div>
<aside class="col-side"><div id="sidebar-content"></div></aside>
</main>
<footer class="foot">Интерактивный учебник «Физика 11» · Глава 4 · «Основы СТО» · 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 = 3;
const _TB_SLUG = 'physics-11-ch4';
const PARAS = [
{ id:'p1', num:'§ 24', name:'Принцип относ. Галилея', sub:'Опыт Майкельсона – Морли', built:true },
{ id:'p2', num:'§ 25', name:'Постулаты СТО', sub:'$\\Delta t = \\gamma\\Delta t_0$, $L = L_0/\\gamma$', built:true },
{ id:'p3', num:'§ 26', name:'Релятивистская динамика', sub:'$E = mc^2$', built:true },
{ id:'final', num:'★', name:'Финал главы 4', sub:'Интегральные боссы', final:true, built: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 = {
p1_done:'§24 — принцип относительности освоен',
p2_done:'§25 — постулаты СТО освоены',
p3_done:'§26 — релятивистская динамика освоена',
ch4_master:'Магистр СТО — пройден финал главы 4!',
start:'Начало главы 4!',
ch4_done:'Глава 4 пройдена — Основы СТО!'
};
function loadProgress(){
try{
const s=localStorage.getItem('physics11_ch4_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('physics11_ch4_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_ch4_progress', JSON.stringify(STATE.progress));
localStorage.setItem('physics11_ch4_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-ch4-'+(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(),
final:()=>buildFinal()
};
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current=id; markLastPara(id);
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
ensureBuilt(id);
const sec=document.getElementById('sec-'+id); if(sec) sec.classList.add('active');
buildSidebar(id);
window.scrollTo({top:0, behavior:'smooth'});
}
const SIDEBARS = {
p1:{title:'§ 24 — Принцип относительности', rows:[['Класс. принцип','Галилей: $v = v\' + V$'],['Эфир','опровергнут (Майкельсон)'],['Кризис','$c$ не зависит от ИСО']]},
p2:{title:'§ 25 — Постулаты СТО', rows:[['I постулат','Все законы инвариантны в ИСО'],['II постулат','$c = const$'],['Время','$\\Delta t = \\gamma\\Delta t_0$'],['Длина','$L = L_0/\\gamma$']]},
p3:{title:'§ 26 — Динамика', rows:[['Энергия покоя','$E_0 = mc^2$'],['Полная энергия','$E = \\gamma mc^2$'],['Импульс','$p = \\gamma mv$'],['Связь','$E^2 = (pc)^2 + (mc^2)^2$']]},
final:{title:'Финал главы 4', rows:[['Боссов','3 интегральных'],['Покрытие','§24-§26'],['Награда','+150 XP + Магистр СТО']]}
};
const TIPS=[
{sec:'p1',html:'§ 24 — главное: в механике принцип Галилея работает, но при попытке применить его к электромагнитным явлениям возник парадокс. Опыт Майкельсона показал, что $c$ одинакова во всех ИСО.'},
{sec:'p2',html:'§ 25 — два постулата: (1) принцип относительности для всех законов физики, (2) $c = const$. Из них вытекают: замедление времени и сокращение длин.'},
{sec:'p3',html:'§ 26 — $E = mc^2$: масса и энергия эквивалентны. Запомни: $E^2 = (pc)^2 + (mc^2)^2$ — релятивистский «теорема Пифагора».'},
{sec:'final',html:'Финал главы 4: 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?' — '+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:'\xA724',p2:'\xA725',p3:'\xA726',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); }
/* ===== §24 Принцип отн. Галилея и ЭМ явления ===== */
function buildP1(){
const box = document.getElementById('p1-body'); if(!box) return;
let html = '';
html += makeCard('repeat', 'Классический принцип относительности Галилея', '§ 24.1',
'<p>В классической механике (Ньютон, Галилей) <b>принцип относительности</b> гласит:</p>'
+ '<p style="text-align:center;margin:8px 0;font-weight:700;color:var(--pri)">«Все механические явления протекают одинаково во всех инерциальных системах отсчёта (ИСО).»</p>'
+ '<p><b>Преобразования Галилея</b> для перехода из одной ИСО в другую, движущуюся со скоростью $V$:</p>'
+ '<p style="text-align:center;margin:6px 0">$$x\' = x - Vt, \\quad t\' = t, \\quad y\' = y, \\quad z\' = z$$</p>'
+ '<p>Из них следует <b>классический закон сложения скоростей</b>:</p>'
+ '<p style="text-align:center;margin:6px 0">$$v = v\' + V$$</p>'
+ '<p>В механике принцип Галилея отлично работает: сидя в равномерно движущемся поезде, ты не отличишь его от стоящего без обращения к окну.</p>');
html += makeCard('theory', 'Кризис классической физики — конец XIX в.', '§ 24.2',
'<p>К концу XIX в. <b>Дж. Максвелл</b> завершил теорию электромагнетизма. Из уравнений Максвелла следовало: ЭМ волна распространяется в вакууме со скоростью</p>'
+ '<p style="text-align:center;margin:6px 0">$$c = \\dfrac{1}{\\sqrt{\\varepsilon_0\\mu_0}} \\approx 3 \\cdot 10^8 \\text{ м/с}$$</p>'
+ '<p><b>Но в какой ИСО?</b> Если принцип Галилея распространить на электромагнетизм, то в разных ИСО $c$ должна быть разной (по $v = v\' + V$).</p>'
+ '<p>Физики предположили: существует особая среда — <b>эфир</b>, заполняющая всё пространство. В нём $c$ — это «истинная» скорость, а в других ИСО — обычная по Галилею.</p>');
html += makeCard('example', 'Опыт Майкельсона – Морли (1887)', '§ 24.3',
'<p>А. Майкельсон и Э. Морли построили <b>интерферометр</b>, чтобы измерить «эфирный ветер» — относительную скорость Земли в эфире.</p>'
+ '<p>Идея: Земля летит вокруг Солнца со скоростью $\\approx 30$ км/с. Если эфир есть, скорость света вдоль движения и поперёк должна отличаться. Интерферометр должен показать сдвиг интерференционной картины.</p>'
+ '<p><b>Результат:</b> сдвига <b>не обнаружено</b>. Эксперимент повторяли с возрастающей точностью — результат тот же.</p>'
+ '<p>Вывод: <b>скорость света одинакова во всех направлениях независимо от движения Земли</b>. Гипотеза эфира опровергнута. Принцип Галилея не работает для электромагнитных явлений.</p>'
+ '<p>Этот «отрицательный результат» оказался одним из важнейших экспериментов в истории физики — он подготовил почву для СТО.</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">Классическая формула: $v = v\' + V$. Решено: <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">Инт. 2</span><span class="wg-title">История и теория</span></div>'
+ '<div class="wg-help">Решено: <b id="i1-th-score">0</b> / 5.</div>'
+ '<div id="i1-th-q" style="margin:8px 0"></div><div class="opts-row" id="i1-th-opts"></div><div class="feedback" id="i1-th-fb"></div></div>';
html += '<div id="boss-1-slot"></div>';
html += readButton('p1');
html += secNavFor('p1');
box.innerHTML = html;
runQuizInput('i1-calc', I1_CALC_ITEMS, 16);
runQuizMC('i1-th', I1_TH_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);
}
/* ===== §25 Постулаты СТО ===== */
function buildP2(){
const box = document.getElementById('p2-body'); if(!box) return;
let html = '';
html += makeCard('theory', 'Два постулата Эйнштейна (1905)', '§ 25.1',
'<p>В <b>1905 г.</b> 26-летний А. Эйнштейн опубликовал статью «К электродинамике движущихся тел», в которой постулировал:</p>'
+ '<p><b>I постулат (принцип относительности):</b></p>'
+ '<p style="text-align:center;margin:6px 0;font-weight:700;color:var(--pri)">«<b>Все</b> законы физики (не только механики!) одинаковы во всех ИСО.»</p>'
+ '<p><b>II постулат (инвариантность $c$):</b></p>'
+ '<p style="text-align:center;margin:6px 0;font-weight:700;color:var(--pri)">«Скорость света в вакууме одинакова во всех ИСО и не зависит от движения источника и наблюдателя.»</p>'
+ '<p>Из этих постулатов вытекают все эффекты СТО.</p>');
html += makeCard('rule', 'Замедление времени (дилатация)', '§ 25.2',
'<p>Время, измеренное по часам, движущимся вместе с событиями, называется <b>собственным временем</b> $\\tau_0$.</p>'
+ '<p>Для наблюдателя в другой ИСО (относительно которой часы движутся со скоростью $v$) время идёт <b>медленнее</b>:</p>'
+ '<p style="text-align:center;margin:8px 0">$$\\Delta\\tau = \\dfrac{\\Delta\\tau_0}{\\sqrt{1 - v^2/c^2}} = \\gamma \\cdot \\Delta\\tau_0$$</p>'
+ '<p>где $\\gamma = 1/\\sqrt{1 - \\beta^2}$ — <b>гамма-фактор</b> Лоренца, $\\beta = v/c$.</p>'
+ '<p><b>Пример:</b> «парадокс близнецов» — астронавт, летящий с $v = 0{,}99c$, через 10 лет полёта вернётся, постаревший на 1,4 года, а его брат на Земле — на 10 лет.</p>'
+ '<p><b>Подтверждение:</b> мюоны космических лучей — нестабильные частицы с $\\tau_0 \\approx 2{,}2$ мкс. Они летят со скоростью $\\sim 0{,}99c$ и долетают до поверхности Земли (расстояние требует $\\sim 70$ мкс), благодаря замедлению времени в их собственной ИСО.</p>');
html += makeCard('rule', 'Сокращение длин (Лоренца)', '§ 25.3',
'<p>Длина тела, измеренная в его собственной ИСО (где оно покоится), называется <b>собственной длиной</b> $L_0$.</p>'
+ '<p>Для наблюдателя, относительно которого тело движется со скоростью $v$ вдоль своей длины, оно становится <b>короче</b>:</p>'
+ '<p style="text-align:center;margin:8px 0">$$L = L_0 \\sqrt{1 - v^2/c^2} = \\dfrac{L_0}{\\gamma}$$</p>'
+ '<p>Поперечные размеры не меняются. Сокращение — релятивистский эффект, никаких механических сил тут нет.</p>'
+ '<p><b>Пример:</b> ракета длиной $L_0 = 100$ м, летящая с $\\beta = 0{,}8$ ($\\gamma = 5/3$), кажется неподвижному наблюдателю длиной $100 \\cdot 3/5 = 60$ м.</p>'
+ '<p><b>Симметрия:</b> в ИСО ракеты неподвижный наблюдатель сам сжат и идёт замедленно. СТО симметрична: «движется тот, кого мы наблюдаем».</p>');
html += makeCard('rule', 'Релятивистский закон сложения скоростей', '§ 25.4',
'<p>Классическая формула $v = v\' + V$ нарушает II постулат: скорость света получалась бы разной в разных ИСО.</p>'
+ '<p><b>Правильная (Эйнштейновская) формула</b> для скоростей вдоль одной оси:</p>'
+ '<p style="text-align:center;margin:8px 0">$$v = \\dfrac{v\' + V}{1 + v\'V/c^2}$$</p>'
+ '<p><b>Проверка:</b> если $v\' = c$ (в одной ИСО луч идёт со скоростью $c$), то $v = (c + V)/(1 + V/c) = c$ — в любой ИСО тоже $c$. Постулат соблюдён!</p>'
+ '<p>При малых скоростях ($v, V \\ll c$) формула переходит в Галилееву $v \\approx v\' + V$.</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">График $\\gamma = 1/\\sqrt{1 - \\beta^2}$. Двигай ползунок $\\beta = v/c$ — наблюдай, как растут эффекты замедления и сокращения. При $\\beta \\to 1$ $\\gamma \\to \\infty$.</div>'
+ '<div class="fx-holder" id="fx-gamma"></div>'
+ '<div class="fx-sliders" id="fx-gamma-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">Чёрные часы — неподвижного наблюдателя, красные — движущейся ИСО. Чем быстрее движение, тем медленнее идут красные часы.</div>'
+ '<div class="fx-holder" id="fx-time"></div>'
+ '<div class="fx-sliders" id="fx-time-sl"></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">Синий стержень — в покое, красный — тот же стержень в движении. Двигай $\\beta$.</div>'
+ '<div class="fx-holder" id="fx-len"></div>'
+ '<div class="fx-sliders" id="fx-len-sl"></div></div>';
html += '<div class="wg"><div class="wg-header"><span class="wg-badge">Инт. 4</span><span class="wg-title">Расчёты — γ, Δt, L, сложение скоростей</span></div>'
+ '<div class="wg-help">Решено: <b id="i2-calc-score">0</b> / 6.</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">Инт. 5</span><span class="wg-title">Теория СТО</span></div>'
+ '<div class="wg-help">Решено: <b id="i2-th-score">0</b> / 5.</div>'
+ '<div id="i2-th-q" style="margin:8px 0"></div><div class="opts-row" id="i2-th-opts"></div><div class="feedback" id="i2-th-fb"></div></div>';
html += '<div id="boss-2-slot"></div>';
html += readButton('p2');
html += secNavFor('p2');
box.innerHTML = html;
ensureFx(()=>{
const gEl = document.getElementById('fx-gamma');
const gp = new PHYS.GammaPlot(gEl, {width:580, height:280, beta:0.5});
const slB = PHYS.util.slider({label:'β = v/c', min:0, max:0.99, step:0.01, value:0.5, fmt:v=>v.toFixed(2), onChange:v=>gp.setBeta(v)});
document.getElementById('fx-gamma-sl').innerHTML = slB.html;
slB.wire(document.getElementById('fx-gamma-sl'));
const tEl = document.getElementById('fx-time');
const td = new PHYS.TimeDilation(tEl, {width:600, height:240, beta:0.6});
const slBt = PHYS.util.slider({label:'β = v/c', min:0, max:0.95, step:0.05, value:0.6, fmt:v=>v.toFixed(2), onChange:v=>td.setBeta(v)});
document.getElementById('fx-time-sl').innerHTML = slBt.html;
slBt.wire(document.getElementById('fx-time-sl'));
const lEl = document.getElementById('fx-len');
const lc = new PHYS.LengthContraction(lEl, {width:600, height:220, beta:0.6, L0:320});
const slBl = PHYS.util.slider({label:'β = v/c', min:0, max:0.95, step:0.05, value:0.6, fmt:v=>v.toFixed(2), onChange:v=>lc.setBeta(v)});
document.getElementById('fx-len-sl').innerHTML = slBl.html;
slBl.wire(document.getElementById('fx-len-sl'));
});
runQuizInput('i2-calc', I2_CALC_ITEMS, 20);
runQuizMC('i2-th', I2_TH_ITEMS, 14);
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);
}
/* ===== §26 Релятивистская динамика ===== */
function buildP3(){
const box = document.getElementById('p3-body'); if(!box) return;
let html = '';
html += makeCard('theory', 'Релятивистский импульс и масса', '§ 26.1',
'<p>Из требования сохранения импульса во всех ИСО (и закона сложения скоростей Эйнштейна) следует:</p>'
+ '<p style="text-align:center;margin:8px 0">$$p = \\dfrac{mv}{\\sqrt{1 - v^2/c^2}} = \\gamma mv$$</p>'
+ '<p>где $m$ — <b>масса покоя</b> (инвариантная характеристика частицы).</p>'
+ '<p>При $v \\ll c$ формула переходит в классическую $p = mv$. При $v \\to c$: $p \\to \\infty$.</p>'
+ '<p><b>Следствие:</b> чтобы разогнать тело массой $m > 0$ до $v = c$, нужна бесконечная энергия. Поэтому <b>массивные тела не могут двигаться со скоростью света</b>.</p>'
+ '<p><b>Безмассовые частицы</b> (фотон, гипотетический гравитон): всегда движутся со скоростью $c$, имеют импульс $p = E/c$.</p>');
html += makeCard('rule', 'Энергия покоя. Знаменитая формула', '§ 26.2',
'<p>Самое известное уравнение физики — <b>уравнение Эйнштейна</b>:</p>'
+ '<p style="text-align:center;margin:8px 0;font-size:1.2rem">$$E_0 = mc^2$$</p>'
+ '<p>где $E_0$ — <b>энергия покоя</b> (минимальная энергия, которой обладает тело только в силу того, что у него есть масса).</p>'
+ '<p><b>Полная релятивистская энергия</b> движущегося тела:</p>'
+ '<p style="text-align:center;margin:8px 0">$$E = \\gamma mc^2 = \\dfrac{mc^2}{\\sqrt{1 - v^2/c^2}}$$</p>'
+ '<p><b>Кинетическая энергия</b>:</p>'
+ '<p style="text-align:center;margin:8px 0">$$E_к = E - E_0 = (\\gamma - 1) mc^2$$</p>'
+ '<p>При $v \\ll c$: $E_к \\approx mv^2/2$ — классическая формула.</p>');
html += makeCard('rule', 'Связь энергии, импульса и массы', '§ 26.3',
'<p>Из формул для $E$ и $p$ выводится фундаментальное соотношение:</p>'
+ '<p style="text-align:center;margin:8px 0">$$E^2 = (pc)^2 + (mc^2)^2$$</p>'
+ '<p>Это <b>«релятивистский Пифагор»</b>: полная энергия, импульс и масса покоя образуют «треугольник».</p>'
+ '<p><b>Частные случаи:</b></p>'
+ '<ul>'
+ '<li><b>$v = 0$:</b> $p = 0$, $E = mc^2$ — только энергия покоя.</li>'
+ '<li><b>$m = 0$</b> (фотон): $E = pc$.</li>'
+ '<li><b>$v \\ll c$:</b> $E \\approx mc^2 + p^2/(2m)$.</li>'
+ '</ul>'
+ '<p>Эта формула — основа всей ядерной и квантовой физики, на ней работают <b>атомные станции, термоядерный синтез на Солнце, ускорители</b> элементарных частиц.</p>');
html += makeCard('example', 'Применения: ядерная энергия и аннигиляция', '§ 26.4',
'<p><b>Дефект массы.</b> Если у системы из частиц энергия связи $\\Delta E$, то её масса меньше суммы масс частиц на $\\Delta m = \\Delta E / c^2$.</p>'
+ '<p>В ядерных реакциях деления (АЭС) и синтеза (Солнце) <b>часть массы превращается в энергию</b>. Энергия 1 г вещества: $E = 10^{-3} \\cdot (3\\cdot 10^8)^2 = 9 \\cdot 10^{13}$ Дж — энергия большой ГЭС за сутки.</p>'
+ '<p><b>Аннигиляция:</b> электрон + позитрон $\\to$ 2 фотона с суммарной энергией $2 m_e c^2 = 1{,}022$ МэВ.</p>'
+ '<p><b>Ускорители.</b> В LHC (Большой адронный коллайдер) протоны разгоняются до $\\gamma \\approx 7000$, то есть $v$ почти $c$. Их кинетическая энергия — 7 ТэВ.</p>'
+ '<p><b>Граница СТО.</b> При $v \\ll c$ — обычная ньютоновская механика. При $v \\sim c$ — обязательна СТО.</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">$E_0 = mc^2$, $E = \\gamma mc^2$, $p = \\gamma mv$. Решено: <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">Инт. 2</span><span class="wg-title">Релятивистская динамика — теория</span></div>'
+ '<div class="wg-help">Решено: <b id="i3-th-score">0</b> / 5.</div>'
+ '<div id="i3-th-q" style="margin:8px 0"></div><div class="opts-row" id="i3-th-opts"></div><div class="feedback" id="i3-th-fb"></div></div>';
html += '<div id="boss-3-slot"></div>';
html += readButton('p3');
html += secNavFor('p3');
box.innerHTML = html;
runQuizInput('i3-calc', I3_CALC_ITEMS, 18);
runQuizMC('i3-th', I3_TH_ITEMS, 14);
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);
}
/* ===== Финал главы 4 — 3 интегральных босса ===== */
function buildFinal(){
const box = document.getElementById('final-body'); if(!box) return;
let html = '';
html += '<div class="card" style="background:linear-gradient(135deg,#dbeafe,#bfdbfe);border-color:#2563eb">'
+ '<div class="card-header"><div class="card-icon rule" style="background:#2563eb;color:#fff">'
+ '<svg class="ic" viewBox="0 0 24 24" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>'
+ '</div><div class="card-title">Финал главы 4 — Основы СТО</div><div class="card-num">★</div></div>'
+ '<div class="card-body">'
+ '<p>Перед тобой — <b>3 интегральных босса</b>. Покрывают весь материал §24-§26: принцип относительности, замедление времени, сокращение длин, $E = mc^2$.</p>'
+ '<p>Победив всех, получишь <b>Магистр СТО</b> и +150 XP бонус.</p>'
+ '</div></div>';
for (let i = 1; i <= 3; i++){
html += '<div id="boss-final-'+i+'-slot"></div>';
}
html += '<div class="card" id="final-victory" style="display:none;background:linear-gradient(135deg,#bfdbfe,#3b82f6);border-color:#1e3a8a">'
+ '<div class="card-header"><div class="card-icon example" style="background:#1e3a8a;color:#fff">'
+ '<svg class="ic" viewBox="0 0 24 24" stroke-width="2"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/></svg>'
+ '</div><div class="card-title">Магистр СТО!</div></div>'
+ '<div class="card-body"><p><b>Все 3 финальных босса побеждены!</b> Ты разобрался в постулатах Эйнштейна, эффектах СТО и понимаешь $E = mc^2$.</p>'
+ '<p>Бонус: <b>+150 XP</b>. Впереди — квантовая физика (Глава 5).</p></div></div>';
html += secNavFor('final');
box.innerHTML = html;
for (let i = 1; i <= 3; i++){
const key = 'boss-final-' + i;
const bs = loadBossState(key) || { stage:0, solved:false };
makeAndBindBoss(key+'-slot', 'final-'+i, FINAL_BOSS_DEFS['fb'+i], bs,
()=>{ saveBossState(key, bs); checkFinalDone(); },
()=>{ checkFinalDone(); });
}
renderMath(box);
}
function checkFinalDone(){
let all = true;
for (let i = 1; i <= 3; i++){
const s = loadBossState('boss-final-'+i);
if (!s || !s.solved){ all = false; break; }
}
if (all){
const v = document.getElementById('final-victory'); if (v) v.style.display = '';
bumpProgress('final', 100);
if (!STATE.achievements.has('ch4_master')){
achievement('ch4_master');
addXp(150, 'ch4-master-bonus');
}
if (!STATE.achievements.has('ch4_done')) achievement('ch4_done');
}
}
function loadBossState(key){ try{ return JSON.parse(localStorage.getItem('physics11_ch4_'+key)||'null'); }catch(e){ return null; } }
function saveBossState(key, state){ try{ localStorage.setItem('physics11_ch4_'+key, JSON.stringify(state)); }catch(e){} }
function runQuizMC(id, items, xpReward){
const state = JSON.parse(localStorage.getItem('physics11_ch4_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_ch4_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_ch4_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_ch4_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:'Поезд $V=20$ м/с. В поезде пассажир идёт со скоростью $v\'=2$ м/с (по ходу). Скорость относительно земли (м/с)?', answer:'22', explain:'$v = v\' + V = 22$.' },
{ q:'Лодка плывёт в реке. Скорость лодки $v\'=3$ м/с (вверх по течению), течение $V=1$ м/с. Скорость относительно берега (м/с)?', answer:'2', explain:'$v = 3 - 1 = 2$ м/с.' },
{ q:'Земля движется в эфире $\\sim 30$ км/с. По Галилею ожидали $c$ в направлении движения (км/с)?', answer:'299970', explain:'$c - V \\approx 3 \\cdot 10^5 - 30$.' },
{ q:'Опыт Майкельсона: год?', answer:'1887', explain:'1887.' },
{ q:'Скорость света (км/с)?', answer:'300000', explain:'$3 \\cdot 10^5$ км/с.' }
];
const I1_TH_ITEMS = [
{ q:'Принцип Галилея распространяется на:', opts:['Только механику','ЭМ явления','Любые ИСО','Любые СО'], correct:0, explain:'Только механические законы.' },
{ q:'Эфир — это:', opts:['Газ','Гипотетическая среда для света','Жидкость','Не существует'], correct:1, explain:'Гипотеза, опровергнута.' },
{ q:'Опыт Майкельсона показал, что:', opts:['Эфир есть','$c$ зависит от направления','$c$ одинакова во всех направлениях','Эфир движется'], correct:2, explain:'$c$ = const.' },
{ q:'Кто сформулировал уравнения ЭМ-волн?', opts:['Ньютон','Максвелл','Эйнштейн','Лоренц'], correct:1, explain:'Максвелл.' },
{ q:'Что значит ИСО:', opts:['Инерциальная СО','Идеальная СО','Истинная СО','Изолированная СО'], correct:0, explain:'Инерциальная.' }
];
const I2_CALC_ITEMS = [
{ q:'$\\beta = 0{,}6$. $\\gamma = ?$', answer:['1.25','5/4'], explain:'$1/\\sqrt{1 - 0{,}36} = 1/0{,}8 = 1{,}25$.' },
{ q:'$\\beta = 0{,}8$. $\\gamma$?', answer:['1.667','5/3','1.67'], explain:'$1/\\sqrt{1 - 0{,}64} = 1/0{,}6 = 5/3 \\approx 1{,}67$.' },
{ q:'$\\tau_0 = 2$ с, $\\gamma = 5$. $\\Delta\\tau$ (с)?', answer:'10', explain:'$\\Delta\\tau = \\gamma\\tau_0 = 10$.' },
{ q:'$L_0 = 100$ м, $\\beta = 0{,}8$. $L$ (м)?', answer:'60', explain:'$L = L_0 \\cdot 0{,}6 = 60$.' },
{ q:'$v\' = 0{,}6c$ в ИСО ракеты, $V = 0{,}6c$ ракеты. Скорость в покоящейся ИСО (в долях $c$)?', answer:['0.882','0.88','15/17'], explain:'$v = (0{,}6+0{,}6)/(1+0{,}36) = 1{,}2/1{,}36 \\approx 0{,}88c$.' },
{ q:'Мюон $\\tau_0 = 2{,}2$ мкс, $\\beta = 0{,}99$, $\\gamma \\approx 7{,}1$. Живёт в нашей ИСО (мкс)?', answer:['15.6','15'], explain:'$\\gamma \\tau_0 \\approx 15{,}6$ мкс.' }
];
const I2_TH_ITEMS = [
{ q:'I постулат СТО:', opts:['$c = const$','Все законы физики одинаковы в ИСО','Только механика','Эфир есть'], correct:1, explain:'Принцип отн. Эйнштейна.' },
{ q:'II постулат:', opts:['$c$ зависит от ИСО','$c$ одинакова во всех ИСО','$c$ зависит от источника','$c = \\infty$'], correct:1, explain:'Инвариантность $c$.' },
{ q:'Движущиеся часы идут:', opts:['Быстрее','Медленнее','Так же','Останавливаются'], correct:1, explain:'$\\Delta\\tau = \\gamma\\Delta\\tau_0$.' },
{ q:'Длина движущегося стержня:', opts:['Увеличивается','Уменьшается','Не меняется','Исчезает'], correct:1, explain:'$L = L_0/\\gamma$.' },
{ q:'При $v \\to c$ $\\gamma$:', opts:['$\\to 0$','$\\to 1$','$\\to \\infty$','$\\to -1$'], correct:2, explain:'$\\gamma \\to \\infty$.' }
];
const I3_CALC_ITEMS = [
{ q:'$m = 1$ кг. $E_0$ (Дж)?', answer:['9e16','9·10¹⁶','90000000000000000'], explain:'$E_0 = mc^2 = 9 \\cdot 10^{16}$.' },
{ q:'$m_e = 9{,}1 \\cdot 10^{-31}$ кг. $E_0$ (Дж) ≈?', answer:['8.2e-14','8.19e-14','8.2·10⁻¹⁴'], explain:'$\\approx 8{,}2 \\cdot 10^{-14}$ Дж ≈ 0,511 МэВ.' },
{ q:'$\\beta = 0{,}6$, $m = 1$ г. $E$ (Дж)?', answer:['1.125e14','1.125·10¹⁴','112500000000000'], explain:'$\\gamma = 1{,}25$, $E = 1{,}25 \\cdot 10^{-3} \\cdot 9 \\cdot 10^{16} = 1{,}125 \\cdot 10^{14}$.' },
{ q:'$\\beta = 0{,}8$, $m=1$ кг. $p$ (кг·м/с)?', answer:['4e8','4·10⁸','400000000'], explain:'$\\gamma = 5/3$, $p = (5/3) \\cdot 1 \\cdot 0{,}8 \\cdot 3 \\cdot 10^8 = 4 \\cdot 10^8$.' },
{ q:'Фотон $E = 3$ эВ. $p$ (эВ/c)?', answer:'3', explain:'$E = pc \\Rightarrow p = 3$ эВ/c.' }
];
const I3_TH_ITEMS = [
{ q:'$E = mc^2$ означает:', opts:['Энергия = массе','Энергия покоя','Кинетическая энергия','Импульс'], correct:1, explain:'Энергия покоя.' },
{ q:'Полная релятивистская энергия:', opts:['$E = mc^2$','$E = \\gamma mc^2$','$E = mv^2/2$','$E = pc$'], correct:1, explain:'$E = \\gamma mc^2$.' },
{ q:'Может ли тело с массой $m > 0$ двигаться со скоростью $c$?', opts:['Да','Нет','Только в вакууме','Только фотон'], correct:1, explain:'Нужна бесконечная энергия.' },
{ q:'Безмассовая частица:', opts:['Существует только в покое','Движется со скоростью $c$','Имеет $E = 0$','Не существует'], correct:1, explain:'Фотон: $v = c$.' },
{ q:'Связь $E$, $p$, $m$:', opts:['$E = pc$','$E^2 = (pc)^2 + (mc^2)^2$','$E = p^2/2m$','$E = pmc$'], correct:1, explain:'Релятивистский «Пифагор».' }
];
/* ===== Boss defs ===== */
const BOSS_DEFS = {
b1: { title:'Босс §24 — Принцип отн. Галилея', tag:'§24', xp:70, stages:[
{ q:'Поезд $V = 30$ м/с. Мяч брошен в поезде вперёд $v\' = 5$ м/с. Скорость относ. земли?', type:'input', a:'35', explain:'$v\' + V$.' },
{ q:'Эфир по Майкельсону:', type:'mc', opts:['Подтверждён','Опровергнут','Не проверен','Найден'], correct:1, explain:'Эфирного ветра нет.' },
{ q:'Уравнения Максвелла предсказывают:', type:'mc', opts:['Существование эфира','$c$ из $\\varepsilon_0, \\mu_0$','Принцип Галилея','Только электростатику'], correct:1, explain:'$c = 1/\\sqrt{\\varepsilon_0\\mu_0}$.' },
{ q:'Скорость Земли по орбите (км/с)?', type:'input', a:'30', explain:'$\\approx 30$ км/с.' },
{ q:'Принцип отн. Галилея верен:', type:'mc', opts:['Для механики','Для ЭМ','Для всей физики','Не верен'], correct:0, explain:'Только для механики.' }
]},
b2: { title:'Босс §25 — Постулаты СТО', tag:'§25', xp:80, stages:[
{ q:'$\\beta = 0{,}6$. $\\gamma$?', type:'input', a:['1.25','5/4'], explain:'$1/0{,}8 = 1{,}25$.' },
{ q:'II постулат:', type:'mc', opts:['$c$ зависит от ИСО','$c$ одинакова в ИСО','$c$ зависит от источника','$c = \\infty$'], correct:1, explain:'Инвариантность.' },
{ q:'$L_0 = 200$ м, $\\beta = 0{,}8$. $L$ (м)?', type:'input', a:'120', explain:'$L = 200 \\cdot 0{,}6 = 120$.' },
{ q:'$\\tau_0 = 1$ с, $\\gamma = 10$. $\\Delta\\tau$ (с)?', type:'input', a:'10', explain:'$\\gamma\\tau_0$.' },
{ q:'$v\' = 0{,}5c$, $V = 0{,}5c$. $v$ (в долях $c$)?', type:'input', a:['0.8','4/5'], explain:'$(0{,}5+0{,}5)/(1+0{,}25) = 1/1{,}25 = 0{,}8c$.' }
]},
b3: { title:'Босс §26 — Релятив. динамика', tag:'§26', xp:80, stages:[
{ q:'Знаменитая формула Эйнштейна:', type:'mc', opts:['$F = ma$','$E = mc^2$','$pV = nRT$','$E = h\\nu$'], correct:1, explain:'$E_0 = mc^2$.' },
{ q:'$m = 2$ кг. $E_0$ (Дж)?', type:'input', a:['1.8e17','1.8·10¹⁷','180000000000000000'], explain:'$2 \\cdot 9 \\cdot 10^{16}$.' },
{ q:'Может тело $m>0$ двигаться со скоростью $c$?', type:'mc', opts:['Да','Нет','Иногда','Только в вакууме'], correct:1, explain:'Бесконечная $E$.' },
{ q:'Связь $E$, $p$, $m$:', type:'mc', opts:['$E = pc + mc^2$','$E^2 = (pc)^2 + (mc^2)^2$','$E = pmc$','$E = mc^2 \\cdot p$'], correct:1, explain:'Релятив. Пифагор.' },
{ q:'Фотон $E = 5$ эВ. $p$ (эВ/c)?', type:'input', a:'5', explain:'$E = pc$.' }
]}
};
const FINAL_BOSS_DEFS = {
fb1: { title:'Финал §24-§25 — Принцип отн. и постулаты', tag:'Финал I', xp:50, stages:[
{ q:'Эфир по Майкельсону:', type:'mc', opts:['Есть','Нет','Не проверен','Слабый'], correct:1, explain:'Опровергнут.' },
{ q:'I постулат СТО:', type:'mc', opts:['$c$ const','Все законы одинаковы в ИСО','Только механика','Эфир'], correct:1, explain:'Принцип относительности.' },
{ q:'$\\beta = 0{,}8$, $\\gamma$?', type:'input', a:['1.667','5/3','1.67'], explain:'$1/0{,}6$.' },
{ q:'$\\tau_0 = 2$ с, $\\beta = 0{,}6$. $\\Delta\\tau$ (с)?', type:'input', a:'2.5', explain:'$\\gamma = 1{,}25$, $\\Delta\\tau = 2{,}5$.' },
{ q:'Стержень $L_0 = 50$ м, $\\beta = 0{,}6$. $L$ (м)?', type:'input', a:'40', explain:'$L = 50 \\cdot 0{,}8 = 40$.' }
]},
fb2: { title:'Финал §25-§26 — Кинематика и динамика СТО', tag:'Финал II', xp:50, stages:[
{ q:'$v\' = 0{,}8c$, $V = 0{,}5c$. $v$ (в долях $c$, до 2 знаков)?', type:'input', a:['0.93','13/14'], explain:'$(0{,}8+0{,}5)/(1+0{,}4) = 1{,}3/1{,}4 \\approx 0{,}93c$.' },
{ q:'$m_e = 9{,}1 \\cdot 10^{-31}$ кг. $E_0$ (МэВ)?', type:'input', a:'0.511', explain:'$0{,}511$ МэВ.' },
{ q:'Полная энергия $E$:', type:'mc', opts:['$mc^2$','$\\gamma mc^2$','$mv^2/2$','$pc$'], correct:1, explain:'$E = \\gamma mc^2$.' },
{ q:'Безмассовая частица:', type:'mc', opts:['Покоится','Движется со скоростью $c$','Имеет $E = 0$','Не существует'], correct:1, explain:'Фотон.' },
{ q:'$E^2 = ?$', type:'mc', opts:['$pc + mc^2$','$(pc)^2 + (mc^2)^2$','$mc^4$','$p^2 + m^2$'], correct:1, explain:'Релятивистский Пифагор.' }
]},
fb3: { title:'Финал §26 — Энергия и масса', tag:'Финал III', xp:50, stages:[
{ q:'$m = 0{,}5$ кг. $E_0$ (Дж)?', type:'input', a:['4.5e16','4.5·10¹⁶','45000000000000000'], explain:'$0{,}5 \\cdot 9 \\cdot 10^{16}$.' },
{ q:'Аннигиляция $e^- + e^+$ → 2 фотона. Энергия (МэВ)?', type:'input', a:'1.022', explain:'$2 m_e c^2 \\approx 1{,}022$ МэВ.' },
{ q:'$\\beta = 0{,}6$, $m = 1$ кг. $E_к$ (Дж)?', type:'input', a:['2.25e16','2.25·10¹⁶','22500000000000000'], explain:'$(\\gamma-1)mc^2 = 0{,}25 \\cdot 9 \\cdot 10^{16}$.' },
{ q:'Источник энергии Солнца:', type:'mc', opts:['Горение','Термояд. синтез','Электричество','Гравитация'], correct:1, explain:'Превращение массы в энергию.' },
{ q:'1 г вещества в энергии (Дж)?', type:'input', a:['9e13','9·10¹³','90000000000000'], explain:'$10^{-3} \\cdot 9 \\cdot 10^{16}$.' }
]}
};
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>