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

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

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

2945 lines
198 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>Физика 10 · Глава 2 · «Термодинамика»</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/g3d.js" defer></script>
<script src="/js/phys.js" defer></script>
<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:#059669; --pri2:#047857; --pri-soft:#d1fae5;
--acc:#34d399; --acc2:#059669; --acc-soft:#d1fae5;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#0a0a0e; --card:#13120a; --card-soft:#18160a; --text:#fef9e7; --ink:#fef9e7; --muted:#a39070; --border:#2a2512}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.hdr{position:relative;background:linear-gradient(110deg,#064e3b 0%,#059669 55%,#34d399 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(255,255,255,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 2';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:'&Delta;U';position:absolute;right:0;top:-30px;font-size:clamp(2rem,12vw,8rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(0,0,0,.18)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(0,0,0,.12);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(0,0,0,.18);font-family:'Unbounded',sans-serif}
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
.psel-name{font-size:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(0,0,0,.10);border-radius:3px;overflow:hidden}
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
.psel-card.final{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft))}
.psel-card.final .psel-num{color:var(--warn)}
.sec[id="sec-p11"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p12"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p13"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p14"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p15"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-final2"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.6rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(0,0,0,.04);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(0,0,0,.08)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.card-body p:last-child{margin-bottom:0}
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(0,0,0,.10);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
.sidecard-row:last-child{margin-bottom:0}
@media(max-width:980px){.col-side{position:static;max-height:none}}
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
.xp-bar{height:9px;background:rgba(0,0,0,.10);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,var(--pri),var(--acc));color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(0,0,0,.32);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
.ach-popup.show{display:flex}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
.col-side-backdrop.show{display:block}
@media(min-width:981px){#sidebar-btn{display:none}.col-side-backdrop.show{display:none}}
@media(max-width:980px){
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
.col-side.open{transform:none}
}
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
.search-modal.show{display:flex}
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
.search-results{flex:1;overflow-y:auto;padding:6px 0}
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border:0;width:100%;color:var(--text)}
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px}
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
/* === PHYS10 POLISH (visual + micro-interactions) === */
@keyframes wgFadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec.active .wg{animation:wgFadeIn .35s cubic-bezier(.16,1,.3,1) backwards}
.sec.active .wg:nth-of-type(1){animation-delay:.02s}
.sec.active .wg:nth-of-type(2){animation-delay:.08s}
.sec.active .wg:nth-of-type(3){animation-delay:.14s}
.sec.active .wg:nth-of-type(4){animation-delay:.20s}
.sec.active .wg:nth-of-type(5){animation-delay:.26s}
.sec.active .wg:nth-of-type(6){animation-delay:.32s}
.wg svg{transition:filter .25s ease}
.wg:hover svg{filter:drop-shadow(0 4px 16px rgba(0,0,0,.10))}
input[type=range]:active{box-shadow:0 0 0 4px var(--pri-soft);border-radius:8px}
.wg input[type=range]{cursor:ew-resize}
.score-display b{transition:transform .22s cubic-bezier(.16,1,.3,1),color .22s;display:inline-block;transform-origin:center}
.score-display b.bump{transform:scale(1.28);color:var(--pri)}
.katex{transition:color .2s}
.wg-help .katex:hover,.card-body .katex:hover{color:var(--pri2,var(--pri));cursor:help}
.hp-fill,.psel-prog-fill,.xp-fill,[id$="-overall-fill"]{transition:width .6s cubic-bezier(.16,1,.3,1)!important}
.boss-card,.btn.primary,.btn-primary{position:relative;overflow:hidden}
.btn.primary::after,.btn-primary::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at center,rgba(255,255,255,.42) 0%,transparent 60%);opacity:0;transition:opacity .25s;pointer-events:none}
.btn.primary:hover::after,.btn-primary:hover::after{opacity:1}
.psel-card{position:relative}
.psel-card .psel-done{position:absolute;top:6px;right:6px;width:18px;height:18px;border-radius:50%;background:#10b981;display:none;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(16,185,129,.45);z-index:2}
.psel-card .psel-done svg{width:11px;height:11px;stroke:#fff;fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.psel-card.done .psel-done{display:flex}
.boss-card{transition:border-color .35s,box-shadow .6s,background .3s,transform .2s}
.boss-card.glow{box-shadow:0 0 24px rgba(16,185,129,.6),0 0 0 2px rgba(16,185,129,.45)!important}
@keyframes bossPulse{0%{box-shadow:0 0 0 0 rgba(16,185,129,.55)}70%{box-shadow:0 0 0 14px rgba(16,185,129,0)}100%{box-shadow:0 0 0 0 rgba(16,185,129,0)}}
.boss-card.pulse{animation:bossPulse .8s ease-out}
.sec{transition:opacity .25s}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Физика 10 · Глава 2</h1>
<div class="hdr-sub">Внутренняя энергия · работа · теплота · 1-й закон · тепловые двигатели</div>
</div>
<div class="hdr-side">
<a href="/textbook/physics-10" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К физике 10</a>
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Термодинамика — превращения энергии</h2>
<p>Термодинамика — наука о превращении энергии. Внутренняя энергия, работа газа, количество теплоты, первый закон и тепловые двигатели.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p11')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 11</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-p11" class="sec" data-watermark="U"><div class="sec-header"><span class="sec-num">§ 11</span><h2 class="sec-h">Внутренняя энергия</h2></div><div id="p11-body"></div></section>
<section id="sec-p12" class="sec" data-watermark="A"><div class="sec-header"><span class="sec-num">§ 12</span><h2 class="sec-h">Работа в термодинамике</h2></div><div id="p12-body"></div></section>
<section id="sec-p13" class="sec" data-watermark="Q"><div class="sec-header"><span class="sec-num">§ 13</span><h2 class="sec-h">Количество теплоты</h2></div><div id="p13-body"></div></section>
<section id="sec-p14" class="sec" data-watermark="1-й"><div class="sec-header"><span class="sec-num">§ 14</span><h2 class="sec-h">Первый закон термодинамики</h2></div><div id="p14-body"></div></section>
<section id="sec-p15" class="sec" data-watermark="Карно"><div class="sec-header"><span class="sec-num">§ 15</span><h2 class="sec-h">Тепловые двигатели. Цикл Карно</h2></div><div id="p15-body"></div></section>
<section id="sec-final2" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#059669,#34d399)"></span><h2 class="sec-h">Финал главы</h2></div><div id="final2-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Физика 10» · Глава 2 · «Термодинамика» · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<div id="search-modal" class="search-modal" role="dialog">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot"><span><kbd>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE = { current:'p11', progress:{}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 6;
const _TB_SLUG = 'physics-10-ch2';
const PARAS = [
{ id:'p11', num:'\u00a7 11', name:"Внутренняя энергия", sub:'$U = \\dfrac{3}{2}\\nu RT$' },
{ id:'p12', num:'\u00a7 12', name:"Работа в термодинамике", sub:'$A = p\\Delta V$' },
{ id:'p13', num:'\u00a7 13', name:"Количество теплоты", sub:'$Q = cm\\Delta T$' },
{ id:'p14', num:'\u00a7 14', name:"Первый закон термодинамики", sub:'$Q = \\Delta U + A$' },
{ id:'p15', num:'\u00a7 15', name:"Тепловые двигатели. Цикл Карно", sub:'$\\eta = (T_1-T_2)/T_1$' },
{ id:'final2', num:'\u2605', name:'Финал главы', sub:'Итоги \u00b7 боссы главы 2', final:true }
];
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
const ACH_LABELS = {
start:"Начало главы 2!",
p11_done:"Внутренняя энергия освоен!",
p12_done:"Работа в термодинамике освоен!",
p13_done:"Количество теплоты освоен!",
p14_done:"Первый закон термодинамики освоен!",
p15_done:"Тепловые двигатели. Цикл Карно освоен!",
ch2_done:"Глава 2 пройдена!"
};
function loadProgress(){
try{
const s=localStorage.getItem('physics10_ch2_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('physics10_ch2_achievements');
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
STATE.xp=+(localStorage.getItem('physics10_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('physics10_ch2_progress', JSON.stringify(STATE.progress));
localStorage.setItem('physics10_ch2_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('physics10_xp', String(STATE.xp));
}catch(e){}
}
function bumpProgress(key, delta){
STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));
saveProgress(); refreshProgressUI();
if(STATE.progress[key]>=50) markParaRead(key);
}
const _markedRead=new Set();
let _pendingProgressBody=null, _progressTimer=null;
function _flushProgress(){
const body=_pendingProgressBody; _pendingProgressBody=null; if(!body) return;
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});
}
function _queueProgress(patch){ _pendingProgressBody=Object.assign(_pendingProgressBody||{},patch); if(_progressTimer) clearTimeout(_progressTimer); _progressTimer=setTimeout(_flushProgress, 600); }
function markLastPara(id){ _queueProgress({last_para:id}); }
function markParaRead(id){ if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({mark_read:id}); }
window.addEventListener('beforeunload', _flushProgress);
function loadServerReadState(){
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
fetch('/api/textbooks/'+_TB_SLUG,{headers:{'Authorization':'Bearer '+tok}}).then(r=>r.ok?r.json():null).then(d=>{
if(!d||!d.progress) return;
(d.progress.read||[]).forEach(k=>{_markedRead.add(k); if((STATE.progress[k]||0)<50) STATE.progress[k]=100;});
saveProgress(); refreshProgressUI();
}).catch(()=>{});
}
function addXp(n,src){
if(!n) return;
const prev=STATE.level; STATE.xp=Math.max(0,(STATE.xp||0)+n); STATE.level=calcLevel(STATE.xp);
saveProgress(); refreshProgressUI();
if(window.LS&&window.LS.xp) window.LS.xp.add(n,'physics10-ch2-'+(src||'misc'));
if(STATE.level>prev){
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
}
}
function refreshProgressUI(){
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
const xpBadge=document.getElementById('hero-xp-badge');
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
}
function achievement(id,text){
if(STATE.achievements.has(id)) return;
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
addXp(20,'ach-'+id);
}
function buildParaSelector(){
const g=document.getElementById('psel-grid'); g.innerHTML='';
PARAS.forEach(p=>{
const card=document.createElement('div');
card.className='psel-card'+(p.final?' final':'');
card.dataset.id=p.id; card.dataset.progCard=p.id;
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
card.addEventListener('click', ()=>goTo(p.id));
g.appendChild(card);
});
}
const BUILT=new Set();
const BUILDERS = { p11:()=>build_p11(), p12:()=>build_p12(), p13:()=>build_p13(), p14:()=>build_p14(), p15:()=>build_p15(), final2:()=>build_final2() };
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 = {
p11:{title:"Шпаргалка §11",rows:[["$U$","$U = \\tfrac{3}{2}\\nu RT$ — для одноат. идеал. газа"],["$\\Delta U$","зависит только от $T$ для идеал. газа"],["Способы","теплопередача, работа"]]},
p12:{title:"Шпаргалка §12",rows:[["$A_{газ}$","$A = p\\Delta V$ при $p = \\text{const}$"],["Геометрия","площадь под графиком $p(V)$"],["Знак","газ расш. — $A > 0$"]]},
p13:{title:"Шпаргалка §13",rows:[["$Q = cm\\Delta T$","нагрев/охлаждение"],["$Q = \\lambda m$","плавление"],["$Q = rm$","парообразование"],["$Q = qm$","сгорание"]]},
p14:{title:"Шпаргалка §14",rows:[["1-й закон","$Q = \\Delta U + A$"],["Изопроц.","частные случаи"],["Адиабат.","$Q = 0 \\Rightarrow A = -\\Delta U$"]]},
p15:{title:"Шпаргалка §15",rows:[["КПД","$\\eta = A_{пол}/Q_1$"],["Карно","$\\eta_{max} = (T_1 - T_2)/T_1$"],["Циклы","Отто, Дизель"]]},
final2:{title:"Финал главы 2",rows:[["§§1115","теория главы 2"],["Награда","+50 XP"]]}
};
const TIPS=[
{sec:'p11',html:"Внутр. энергия идеал. одноат. газа: $U = \\tfrac{3}{2}\\nu RT$. Зависит только от $T$."},
{sec:'p12',html:"Работа газа: $A = p\\Delta V$ при $p = \\text{const}$. Геометрически — площадь под графиком $p(V)$."},
{sec:'p13',html:"$Q = cm\\Delta T$ — нагрев. $Q = \\lambda m$ — плавление. $Q = rm$ — парообразование. $Q = qm$ — сгорание."},
{sec:'p14',html:"1-й закон термодинамики: $Q = \\Delta U + A$ — теплота идёт на изменение внутр. энергии и работу газа."},
{sec:'p15',html:"КПД цикла Карно: $\\eta_{max} = (T_1 - T_2)/T_1$ — максимально возможный при данных $T_1, T_2$."},
{sec:'final2',html:"Финал главы 2 — интегрированные задачи по §§11–15. В разработке (Phase 2+)."}
];
function buildSidebar(id){
const box=document.getElementById('sidebar-content');
const sb=SIDEBARS[id]||SIDEBARS[PARAS[0].id];
let html='';
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
html+='<div class="xp-card" data-gamified><div class="xp-card-title" data-gamified><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' \u2014 '+v:'')+'</div>'; });
html+='</div>';
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
if(tip){
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
}
if(STATE.achievements.size>0){
html+='<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">'+STATE.achievements.size+'</span></h4>';
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">&#10003; '+text+'</div>'; });
html+='</div>';
}
box.innerHTML=html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
function initTheme(){
const t=localStorage.getItem('physics10_ch2_theme')||'light';
if(t==='dark') document.documentElement.classList.add('dark');
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
document.getElementById('theme-btn').addEventListener('click', ()=>{
document.documentElement.classList.toggle('dark');
const dark=document.documentElement.classList.contains('dark');
localStorage.setItem('physics10_ch2_theme', dark?'dark':'light');
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
});
}
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'&#10003; Верно!':'&#10007; Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
function ipow(base, exp){ let r=1; for(let i=0;i<Math.abs(exp);i++) r*=base; return exp<0 ? 1/r : r; }
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
}
function setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
/* === SVG-хелперы (axes2D, plotFunc, pointWithDrop, asymptote, snapToValue, геом.) === */
function axes2D(W, H, pad, xmin, xmax, ymin, ymax){
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (ymax - ymin);
const toX = v => pad + (v - xmin) * ux;
const toY = v => H - pad - (v - ymin) * uy;
let g = '';
g += '<g stroke="#e5e7eb" stroke-width="1">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
g += '<line x1="'+toX(x)+'" y1="'+pad+'" x2="'+toX(x)+'" y2="'+(H-pad)+'"/>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
g += '<line x1="'+pad+'" y1="'+toY(y)+'" x2="'+(W-pad)+'" y2="'+toY(y)+'"/>';
}
g += '</g>';
const y0 = toY(0), x0 = toX(0);
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+x0+'" y1="'+pad+'" x2="'+x0+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(W-pad+2)+'" y="'+(y0-4)+'" font-size="11" fill="#0f172a">x</text>';
g += '<text x="'+(x0+4)+'" y="'+(pad-2)+'" font-size="11" fill="#0f172a">y</text>';
g += '<g font-size="10" fill="#64748b">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
if (x !== 0) g += '<text x="'+(toX(x)-3)+'" y="'+(y0+12)+'">'+x+'</text>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
if (y !== 0) g += '<text x="'+(x0+4)+'" y="'+(toY(y)+3)+'">'+y+'</text>';
}
g += '<text x="'+(x0+4)+'" y="'+(y0+12)+'">0</text>';
g += '</g>';
return { content: g, toX, toY, ux, uy };
}
function plotFunc(f, xmin, xmax, toX, toY, color, N){
N = N || 200;
let d = '';
let prevValid = false;
for (let i = 0; i <= N; i++){
const x = xmin + (xmax - xmin) * i / N;
let y;
try { y = f(x); } catch(e){ y = NaN; }
if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; }
d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2);
prevValid = true;
}
return '<path d="'+d+'" stroke="'+color+'" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';
}
function pointWithDrop(x, fx, toX, toY, color, label){
const px = toX(x), py = toY(fx);
let s = '';
s += '<line x1="'+px+'" y1="'+py+'" x2="'+px+'" y2="'+toY(0)+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
s += '<line x1="'+px+'" y1="'+py+'" x2="'+toX(0)+'" y2="'+py+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
s += '<circle cx="'+px+'" cy="'+py+'" r="4.5" fill="'+color+'" stroke="#fff" stroke-width="2"/>';
if (label){
s += '<text x="'+(px+8)+'" y="'+(py-8)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+color+'">'+label+'</text>';
}
return s;
}
function asymptote(orientation, value, toX, toY, xmin, xmax, ymin, ymax, color){
color = color || '#94a3b8';
if (orientation === 'h'){
const y = toY(value);
return '<line x1="'+toX(xmin)+'" y1="'+y+'" x2="'+toX(xmax)+'" y2="'+y+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
} else {
const x = toX(value);
return '<line x1="'+x+'" y1="'+toY(ymin)+'" x2="'+x+'" y2="'+toY(ymax)+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
}
}
function snapToValue(value, snapPoints, tolerance){
tolerance = tolerance || 0.1;
for (const sp of snapPoints){
if (Math.abs(value - sp) < tolerance) return sp;
}
return value;
}
function rightAngleMark(V, uIn, wIn, s){
s = s || 9;
const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y};
const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y};
const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y};
return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y;
}
function angleArcAuto(V, uA, uB, R){
const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y};
const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y};
const cross = uA.x*uB.y - uA.y*uB.x;
const sweep = cross > 0 ? 1 : 0;
return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y;
}
function unitVec(p1, p2){
const dx = p2.x - p1.x, dy = p2.y - p1.y;
const len = Math.sqrt(dx*dx + dy*dy) || 1;
return {x: dx/len, y: dy/len};
}
function deg2rad(d){ return d * Math.PI / 180; }
const ICONS = {
repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
oral:'<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
};
function secNavFor(curId){
const idx = PARAS.findIndex(p => p.id === curId);
const prev = idx > 0 ? PARAS[idx-1].id : null;
const next = idx < PARAS.length - 1 ? PARAS[idx+1].id : null;
return secNav(prev, next);
}
function secNav(prev, next){
const NAMES = {p11:'\xA711',p12:'\xA712',p13:'\xA713',p14:'\xA714',p15:'\xA715',final2:'Финал'};
let h='<div class="sec-nav">';
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+NAMES[prev]+'</button>':'<span></span>';
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[next]+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
h+='</div>'; return h;
}
function readButton(paraId){
const p = PARAS.find(x => x.id === paraId);
const labelTail = p && p.final ? 'финал' : (p ? p.num : '\xA7?');
return '<div style="margin-top:18px;display:flex;justify-content:center">'
+'<button class="btn primary" id="'+paraId+'-read-btn">'
+'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
+' Я прочитал — '+labelTail+' (+10 XP)'
+'</button></div>';
}
function wireReadBtn(paraId){
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
btn.addEventListener('click', ()=>{
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
const aId = paraId+'_done';
if(ACH_LABELS[aId]) achievement(aId);
});
}
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
function build_p11(){
const box = document.getElementById('p11-body');
let html = '';
/* THEORY 1 — Термодинамическая система и внутренняя энергия */
html += makeCard('theory', "Термодинамическая система и внутренняя энергия", "§11", `
<p><b>Термодинамическая система</b> — совокупность тел, способных взаимодействовать друг с другом и с окружающими телами путём обмена энергией. Система может быть <b>изолированной</b> (нет обмена с окружением) или <b>открытой</b>.</p>
<p style="margin-top:8px"><b>Внутренняя энергия</b> $U$ — энергия движения и взаимодействия частиц системы:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>кинетическая энергия теплового движения молекул и атомов;</li>
<li>потенциальная энергия взаимодействия молекул.</li>
</ul>
<p>Внутренняя энергия <b>не включает</b>:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>кинетическую энергию системы как целого (например, движение сосуда);</li>
<li>потенциальную энергию системы во внешнем поле.</li>
</ul>
<p>$U$ — <b>функция состояния</b>: зависит только от макропараметров системы ($T$, $V$, $\\nu$).</p>
`);
/* THEORY 2 — Внутренняя энергия идеального газа */
html += makeCard('rule', "Внутренняя энергия идеального газа", "§11", `
<p>Для <b>идеального одноатомного газа</b> (He, Ne, Ar) взаимодействие молекул отсутствует, поэтому $U$ — это только кинетическая энергия молекул:</p>
<p style="text-align:center;margin:10px 0">$$U = \\dfrac{3}{2}\\,\\nu R T = \\dfrac{3}{2}\\,N k_B T$$</p>
<p>через массу:</p>
<p style="text-align:center;margin:10px 0">$$U = \\dfrac{3}{2}\\,\\dfrac{m}{M}\\,R T$$</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0"><b>Главное:</b> внутренняя энергия идеального газа зависит <b>только от температуры</b>! Не зависит от объёма или давления.</p>
<p><b>Изменение внутренней энергии</b>:</p>
<p style="text-align:center;margin:10px 0">$$\\Delta U = \\dfrac{3}{2}\\,\\nu R\\,\\Delta T$$</p>
<p>Для <b>двухатомных</b> газов ($\\text{H}_2$, $\\text{N}_2$, $\\text{O}_2$) добавляются 2 вращательные степени свободы (всего $i=5$):</p>
<p style="text-align:center;margin:10px 0">$$U = \\dfrac{5}{2}\\,\\nu R T$$</p>
`);
/* THEORY 3 — Способы изменения внутренней энергии */
html += makeCard('example', "Способы изменения внутренней энергии", "§11", `
<p>Внутреннюю энергию системы можно изменить <b>двумя способами</b>:</p>
<p style="margin-top:8px"><b>1) Совершение работы</b> $A$:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>сжатие газа поршнем (работа внешних сил над газом, $A_{внеш} > 0$) — $U$ растёт;</li>
<li>расширение газа (газ совершает работу, $A_{газ} > 0$) — $U$ уменьшается;</li>
<li><b>пример:</b> накачивание шины насосом — воздух нагревается.</li>
</ul>
<p><b>2) Теплопередача</b> $Q$:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>нагревание (получение тепла, $Q > 0$) — $U$ растёт;</li>
<li>охлаждение (отдача тепла, $Q < 0$) — $U$ уменьшается;</li>
<li><b>пример:</b> чайник на плите получает тепло от спирали.</li>
</ul>
<p style="margin-top:8px">Оба процесса объединяются первым законом термодинамики (§14):</p>
<p style="text-align:center;margin:10px 0">$$\\Delta U = Q + A_{внеш}$$</p>
`);
/* INTERACTIVE 1 — Калькулятор внутренней энергии */
html += `<div class="wg" id="p11-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Калькулятор внутренней энергии $U$</div></div>
<div class="wg-help">Задай $\\nu$, $T$ и тип газа — получи $U = \\tfrac{i}{2}\\nu RT$ и $\\Delta U$ при нагреве на $+100$ К.</div>
<div class="sliders">
<label>Количество $\\nu$: <b id="p11-iv1-nuL">1.0</b> моль <input type="range" id="p11-iv1-nu" min="0.1" max="5" value="1.0" step="0.1"></label>
<label>Температура $T$: <b id="p11-iv1-tL">300</b> К <input type="range" id="p11-iv1-t" min="100" max="1000" value="300" step="10"></label>
</div>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p11-iv1-gas" value="mono" checked> Одноатомный ($i=3$)</label>
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p11-iv1-gas" value="di"> Двухатомный ($i=5$)</label>
</div>
<div class="actions" style="justify-content:center"><button class="btn primary" id="p11-iv1-go">Вычислить $U$</button></div>
<div id="p11-iv1-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p11-iv1-fb"></div>
</div>`;
/* INTERACTIVE 2 — График U(T) */
html += `<div class="wg" id="p11-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Визуализатор $U(T)$</div></div>
<div class="wg-help">Сдвигай $\\nu$ — смотри, как линии $U(T)$ для одно- и двухатомного газа меняют наклон.</div>
<div class="sliders">
<label>Количество $\\nu$: <b id="p11-iv2-nuL">1.0</b> моль <input type="range" id="p11-iv2-nu" min="0.5" max="3" value="1.0" step="0.1"></label>
</div>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-bottom:8px;font-size:.86rem">
<span style="display:inline-flex;align-items:center;gap:6px"><span style="width:18px;height:3px;background:#2563eb;border-radius:2px"></span> Одноатомный ($\\tfrac{3}{2}\\nu RT$)</span>
<span style="display:inline-flex;align-items:center;gap:6px"><span style="width:18px;height:3px;background:#ea580c;border-radius:2px"></span> Двухатомный ($\\tfrac{5}{2}\\nu RT$)</span>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p11-iv2-svg" viewBox="0 0 380 260" width="100%" style="height:auto"></svg>
</div>
<div id="p11-iv2-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.75"></div>
</div>`;
/* INTERACTIVE 3 — DnD: что меняет U */
html += `<div class="wg" id="p11-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Что меняет внутреннюю энергию?</div></div>
<div class="wg-help">Перетащи 6 ситуаций в соответствующий способ изменения $U$.</div>
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 6 ситуаций — 2 способа</div>
<div id="p11-iv3-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="work">Совершение работы</h5><div class="drop-items" data-cat="work"></div></div>
<div class="drop-box"><h5 data-cat="heat">Теплообмен</h5><div class="drop-items" data-cat="heat"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p11-iv3-check">Проверить</button><button class="btn" id="p11-iv3-reset">Сначала</button></div>
<div class="feedback" id="p11-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр U */
html += `<div class="wg" id="p11-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр внутренней энергии</div></div>
<div class="wg-help">6 задач. $R = 8{,}3$ Дж/(моль·К). Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p11-iv4-i">1</b> / 6</span><span>Очки: <b id="p11-iv4-s">0</b> / 6</span></div>
<div id="p11-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p11-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p11-iv4-go">Проверить</button>
<button class="btn" id="p11-iv4-start">Заново</button>
</div>
<div class="feedback" id="p11-iv4-fb"></div>
</div>`;
html += secNav(null, 'p12');
html += readButton('p11');
box.innerHTML = html;
renderMath(box);
/* IV1 — Калькулятор U */
(function(){
const R = 8.314;
const nu = document.getElementById('p11-iv1-nu');
const t = document.getElementById('p11-iv1-t');
const nuL = document.getElementById('p11-iv1-nuL');
const tL = document.getElementById('p11-iv1-tL');
const out = document.getElementById('p11-iv1-out');
const fb = document.getElementById('p11-iv1-fb');
let count = 0; let _done = false;
function getI(){ const r = document.querySelector('input[name="p11-iv1-gas"]:checked'); return r && r.value === 'di' ? 5 : 3; }
function fmtNum(v){ return v.toLocaleString('ru-RU', {maximumFractionDigits: 0}); }
function sync(){
nuL.textContent = (+nu.value).toFixed(1);
const tv = +t.value;
// snap к 273, 300, 373
const snaps = [273, 300, 373];
let ts = tv;
for(const s of snaps){ if(Math.abs(tv - s) <= 4){ ts = s; break; } }
tL.textContent = ts;
t.dataset.snap = ts;
}
nu.addEventListener('input', sync); t.addEventListener('input', sync); sync();
document.getElementById('p11-iv1-go').addEventListener('click', () => {
const nv = +nu.value, tv = +(t.dataset.snap || t.value), i = getI();
const U = i/2 * nv * R * tv;
const U2 = i/2 * nv * R * (tv + 100);
const dU = U2 - U;
const frac = i === 3 ? '\\tfrac{3}{2}' : '\\tfrac{5}{2}';
out.innerHTML = '<div style="margin-bottom:6px"><b>Подстановка:</b></div>'
+ '<div style="margin-bottom:6px">$U = '+frac+'\\,\\nu R T = '+frac+' \\cdot '+nv.toFixed(1)+' \\cdot 8{,}314 \\cdot '+tv+' \\approx '+fmtNum(U)+'$ Дж</div>'
+ '<div style="margin-bottom:6px"><b>При нагреве до $T+100 = '+(tv+100)+'$ К:</b></div>'
+ '<div>$\\Delta U = '+frac+'\\,\\nu R \\cdot 100 \\approx '+fmtNum(dU)+'$ Дж</div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
count++;
if(!_done && count >= 4){ _done = true; addXp(10,'p11-iv1'); bumpProgress('p11', 15); }
});
})();
/* IV2 — График U(T) */
(function(){
const R = 8.314;
const svg = document.getElementById('p11-iv2-svg');
const nu = document.getElementById('p11-iv2-nu');
const nuL = document.getElementById('p11-iv2-nuL');
const info = document.getElementById('p11-iv2-info');
const seen = new Set(); let _done = false;
function render(){
const nv = +nu.value;
nuL.textContent = nv.toFixed(1);
const W=380, H=260, pad=42;
// x: T 200..600 К; y: U 0..15 кДж
const a = axes2D(W, H, pad, 200, 600, 0, 15);
let g = a.content;
g += '<text x="'+(W-pad+2)+'" y="'+(H-pad+14)+'" font-size="10" fill="#0f172a">T, К</text>';
g += '<text x="'+(pad-22)+'" y="'+(pad-6)+'" font-size="10" fill="#0f172a">U, кДж</text>';
// мономерная U(T) в кДж: U_kJ = 1.5 * nv * R * T / 1000
const fMono = T => 1.5 * nv * R * T / 1000;
const fDi = T => 2.5 * nv * R * T / 1000;
g += plotFunc(fMono, 200, 600, a.toX, a.toY, '#2563eb');
g += plotFunc(fDi, 200, 600, a.toX, a.toY, '#ea580c');
// подписи в конце линий
g += '<circle cx="'+a.toX(600)+'" cy="'+a.toY(fMono(600))+'" r="4" fill="#2563eb"/>';
g += '<circle cx="'+a.toX(600)+'" cy="'+a.toY(fDi(600))+'" r="4" fill="#ea580c"/>';
svg.innerHTML = g;
const Umono300 = (1.5 * nv * R * 300).toFixed(0);
const Udi300 = (2.5 * nv * R * 300).toFixed(0);
info.innerHTML = '<b>$\\nu = '+nv.toFixed(1)+'$ моль</b>. При $T = 300$ К: '
+ '<span style="color:#2563eb;font-weight:700">$U_{одно} \\approx '+Umono300+'$ Дж</span>, '
+ '<span style="color:#ea580c;font-weight:700">$U_{двух} \\approx '+Udi300+'$ Дж</span>. '
+ 'Линейная зависимость $U \\propto T$; двухатомный газ имеет больше степеней свободы — наклон круче.';
renderMath(info);
seen.add(Math.round(nv*10));
if(!_done && seen.size >= 4){ _done = true; addXp(10,'p11-iv2'); bumpProgress('p11', 15); }
}
nu.addEventListener('input', render);
render();
})();
/* IV3 — DnD сортер */
(function(){
const items = [
{ id:'a1', cat:'work', html:'Сжатие газа в насосе' },
{ id:'a2', cat:'heat', html:'Нагревание чайника на плите' },
{ id:'a3', cat:'work', html:'Накачивание шины' },
{ id:'a4', cat:'heat', html:'Кубик льда тает в воде' },
{ id:'a5', cat:'work', html:'Газ расширяется в цилиндре, толкая поршень' },
{ id:'a6', cat:'heat', html:'Кофе остывает в чашке' },
];
const sorter = setupSorter({
poolId:'p11-iv3-pool',
scopeSelector:'#p11-iv3',
items: items,
cats:['work','heat'],
columnLayout:false,
});
document.getElementById('p11-iv3-check').addEventListener('click', () => {
const fb = document.getElementById('p11-iv3-fb');
const placedCount = items.filter(it => sorter.placed[it.id]).length;
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
if(placedCount < items.length){ feedback(fb, false, '&#10007; Размести все 6 ситуаций.'); return; }
if(correct === items.length){ feedback(fb, true, '&#10003; Все 6 верно! +10 XP'); addXp(10,'p11-iv3'); bumpProgress('p11', 15); }
else feedback(fb, false, '&#10007; Правильно ' + correct + ' из 6. Попробуй ещё.');
});
document.getElementById('p11-iv3-reset').addEventListener('click', () => { sorter.reset(); document.getElementById('p11-iv3-fb').style.display = 'none'; });
})();
/* IV4 — Тренажёр */
(function(){
const Q = [
{ q:'1 моль одноатомного газа при $T = 300$ К. $U$ в Дж?', ans:3735, tol:50, hint:'$U = \\tfrac{3}{2}\\nu RT = 1{,}5 \\cdot 1 \\cdot 8{,}3 \\cdot 300 = 3735$' },
{ q:'2 моль одноатомного газа при $T = 400$ К. $U$ в Дж?', ans:9960, tol:100, hint:'$U = 1{,}5 \\cdot 2 \\cdot 8{,}3 \\cdot 400 = 9960$' },
{ q:'Одноатомный газ при $T = 300$ К имеет $U = 3735$ Дж. Каким станет $U$ при $T = 600$ К (в Дж)?', ans:7470, tol:100, hint:'$U \\propto T$: при удвоении $T$ — $U$ удваивается.' },
{ q:'1 моль двухатомного газа при $T = 300$ К. $U$ в Дж?', ans:6225, tol:80, hint:'$U = \\tfrac{5}{2}\\nu RT = 2{,}5 \\cdot 1 \\cdot 8{,}3 \\cdot 300 = 6225$' },
{ q:'2 моль одноатомного газа нагрели на $\\Delta T = 100$ К. $\\Delta U$ в Дж?', ans:2490, tol:50, hint:'$\\Delta U = \\tfrac{3}{2}\\nu R \\Delta T = 1{,}5 \\cdot 2 \\cdot 8{,}3 \\cdot 100 = 2490$' },
{ q:'Зависит ли $U$ идеального газа от объёма? Введи 1 (да) или 2 (нет).', ans:2, tol:0.01, hint:'$U$ идеального газа зависит только от $T$.' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p11-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p11-iv4'); bumpProgress('p11', 25); }
else if(score >= 4){ addXp(8, 'p11-iv4'); bumpProgress('p11', 15); }
return;
}
document.getElementById('p11-iv4-i').textContent = (i+1);
document.getElementById('p11-iv4-s').textContent = score;
document.getElementById('p11-iv4-q').innerHTML = Q[i].q;
document.getElementById('p11-iv4-ans').value = '';
renderMath(document.getElementById('p11-iv4-q'));
document.getElementById('p11-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p11-iv4-fb');
const raw = document.getElementById('p11-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
document.getElementById('p11-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p11-iv4-go').addEventListener('click', go);
document.getElementById('p11-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p11-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p11');
}
function build_p12(){
const box = document.getElementById('p12-body');
let html = '';
/* THEORY 1 — Работа газа в изобарном процессе */
html += makeCard('theory', "Работа газа в изобарном процессе", "§12", `
<p><b>Работа газа</b> $A_{газ}$ — работа, совершаемая газом при изменении объёма.</p>
<p style="margin-top:8px"><b>Изобарный процесс</b> ($p = \\text{const}$):</p>
<p style="text-align:center;margin:10px 0">$$A_{газ} = p \\cdot \\Delta V = p\\,(V_2 - V_1)$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Расширение</b> ($V_2 > V_1$): $A_{газ} > 0$ — газ совершает положительную работу.</li>
<li><b>Сжатие</b> ($V_2 < V_1$): $A_{газ} < 0$ — внешние силы совершают работу над газом.</li>
<li><b>Изохорный</b> ($\\Delta V = 0$): $A_{газ} = 0$ — газ не совершает работу.</li>
</ul>
<p>Связь с работой внешних сил:</p>
<p style="text-align:center;margin:10px 0">$$A_{внеш} = -A_{газ}$$</p>
`);
/* THEORY 2 — Графическое представление работы */
html += makeCard('rule', "Графическое представление работы", "§12", `
<p>В координатах $p$–$V$ работа газа равна <b>площади под кривой процесса</b>:</p>
<p style="text-align:center;margin:10px 0">$$A_{газ} = \\int_{V_1}^{V_2} p\\,dV$$</p>
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.92rem">
<thead>
<tr style="background:var(--sec-acc-soft)">
<th style="padding:8px;border:1px solid var(--border);text-align:left">Процесс</th>
<th style="padding:8px;border:1px solid var(--border)">Геометрия площади</th>
<th style="padding:8px;border:1px solid var(--border)">Работа</th>
</tr>
</thead>
<tbody>
<tr><td style="padding:8px;border:1px solid var(--border)"><b style="color:#2563eb">Изобара</b> ($p$=const)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">прямоугольник</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$A = p\\,\\Delta V$</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)"><b style="color:#10b981">Изохора</b> ($V$=const)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">нет ширины</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$A = 0$</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)"><b style="color:#ea580c">Изотерма</b> ($T$=const)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">под гиперболой</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$A = \\nu RT\\ln\\dfrac{V_2}{V_1}$</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)"><b>Цикл</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">внутри замкнутой кривой</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$A_{цикл}$ за оборот</td></tr>
</tbody>
</table>
<p>Геометрический смысл важен для расчёта работы тепловых двигателей (§15).</p>
`);
/* THEORY 3 — Работа в изотермическом процессе */
html += makeCard('example', "Работа в изотермическом процессе", "§12", `
<p><b>Изотермический процесс</b> ($T = \\text{const}$, $pV = \\text{const}$):</p>
<p style="text-align:center;margin:10px 0">$$A_{газ} = \\nu R T \\ln\\dfrac{V_2}{V_1}$$</p>
<p>Это <b>логарифмическая</b> формула — применима при медленном (квазистатическом) изотермическом изменении объёма.</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px;margin:10px 0">Для идеального газа при $T = \\text{const}$ внутренняя энергия не меняется ($\\Delta U = 0$), вся подведённая теплота превращается в работу: $Q = A_{газ}$.</p>
<p><b>Примеры:</b></p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>Газ в цилиндре с поршнем в термостате расширили медленно от $V_1$ до $V_2$ — работа рассчитывается по формуле.</li>
<li>При сжатии нужно <b>отводить</b> тепло, чтобы $T$ оставалась постоянной.</li>
<li>При расширении подводим тепло — иначе газ охлаждался бы.</li>
</ul>
`);
/* INTERACTIVE 1 — Работа на PV-диаграмме (главный) */
html += `<div class="wg" id="p12-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Работа на $p$$V$ диаграмме</div></div>
<div class="wg-help">Переключи процесс и сдвигай параметры — закрашенная площадь под кривой и есть работа $A_{газ}$.</div>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p12-iv1-proc" value="bar" checked> <span style="color:#2563eb;font-weight:700">Изобарный</span></label>
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p12-iv1-proc" value="hor"> <span style="color:#10b981;font-weight:700">Изохорный</span></label>
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p12-iv1-proc" value="iso"> <span style="color:#ea580c;font-weight:700">Изотермический</span></label>
</div>
<div class="sliders" id="p12-iv1-sliders"></div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p12-iv1-svg" viewBox="0 0 420 280" width="100%" style="height:auto"></svg>
</div>
<div id="p12-iv1-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.93rem;line-height:1.85"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор работы */
html += `<div class="wg" id="p12-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор работы $A_{газ}$</div></div>
<div class="wg-help">Выбери тип процесса, введи параметры — получи работу с подстановкой формулы.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-bottom:10px" id="p12-iv2-tabs">
<button class="btn primary" data-mode="bar" style="background:#2563eb;border-color:#2563eb">Изобарный</button>
<button class="btn" data-mode="hor">Изохорный</button>
<button class="btn" data-mode="iso">Изотермический</button>
</div>
<div id="p12-iv2-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center"><button class="btn primary" id="p12-iv2-go">Вычислить</button></div>
<div id="p12-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p12-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — Знак работы (квикфайр) */
html += `<div class="wg" id="p12-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Знак работы газа</div></div>
<div class="wg-help">6 ситуаций. Выбери знак работы газа: $A > 0$, $A < 0$ или $A = 0$.</div>
<div class="score-display"><span>Задание <b id="p12-iv3-i">1</b> / 6</span><span>Очки: <b id="p12-iv3-s">0</b> / 6</span></div>
<div id="p12-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
<button class="btn primary" data-sgn="pos">$A > 0$</button>
<button class="btn primary" data-sgn="neg">$A < 0$</button>
<button class="btn primary" data-sgn="zero">$A = 0$</button>
</div>
<div class="feedback" id="p12-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр работы */
html += `<div class="wg" id="p12-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр работы</div></div>
<div class="wg-help">5 задач. $R = 8{,}3$ Дж/(моль·К), 1 атм = $10^5$ Па, 1 л = $10^{-3}$ м³. Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p12-iv4-i">1</b> / 5</span><span>Очки: <b id="p12-iv4-s">0</b> / 5</span></div>
<div id="p12-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p12-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p12-iv4-go">Проверить</button>
<button class="btn" id="p12-iv4-start">Заново</button>
</div>
<div class="feedback" id="p12-iv4-fb"></div>
</div>`;
html += secNav('p11', 'p13');
html += readButton('p12');
box.innerHTML = html;
renderMath(box);
/* IV1 — PV-диаграмма с закрашиванием площади */
(function(){
const R = 8.314;
const slBox = document.getElementById('p12-iv1-sliders');
const svg = document.getElementById('p12-iv1-svg');
const info = document.getElementById('p12-iv1-info');
const COL = { bar:'#2563eb', hor:'#10b981', iso:'#ea580c' };
const seen = new Set(); let _done = false;
function getMode(){ const r = document.querySelector('input[name="p12-iv1-proc"]:checked'); return r ? r.value : 'bar'; }
function buildSliders(){
const m = getMode();
let h = '';
if(m === 'bar'){
h += '<label>$V_1$: <b id="p12-iv1-v1L">2.0</b> л <input type="range" id="p12-iv1-v1" min="1" max="9" value="2" step="0.5"></label>';
h += '<label>$V_2$: <b id="p12-iv1-v2L">7.0</b> л <input type="range" id="p12-iv1-v2" min="1" max="9" value="7" step="0.5"></label>';
h += '<label>$p$: <b id="p12-iv1-pL">2.0</b> атм <input type="range" id="p12-iv1-p" min="0.5" max="3" value="2.0" step="0.1"></label>';
} else if(m === 'hor'){
h += '<label>$V$: <b id="p12-iv1-v1L">5.0</b> л <input type="range" id="p12-iv1-v1" min="1" max="9" value="5" step="0.5"></label>';
h += '<label>$p_1$: <b id="p12-iv1-pL">1.0</b> атм <input type="range" id="p12-iv1-p" min="0.5" max="3" value="1.0" step="0.1"></label>';
h += '<label>$p_2$: <b id="p12-iv1-p2L">2.5</b> атм <input type="range" id="p12-iv1-p2" min="0.5" max="3" value="2.5" step="0.1"></label>';
} else {
h += '<label>$V_1$: <b id="p12-iv1-v1L">2.0</b> л <input type="range" id="p12-iv1-v1" min="1" max="9" value="2" step="0.5"></label>';
h += '<label>$V_2$: <b id="p12-iv1-v2L">7.0</b> л <input type="range" id="p12-iv1-v2" min="1" max="9" value="7" step="0.5"></label>';
h += '<label>$T$: <b id="p12-iv1-tL">300</b> К <input type="range" id="p12-iv1-t" min="200" max="500" value="300" step="10"></label>';
}
slBox.innerHTML = h;
renderMath(slBox);
slBox.querySelectorAll('input[type=range]').forEach(i => i.addEventListener('input', render));
}
function render(){
const m = getMode();
const W=420, H=280, pad=44;
// оси: V 0..10 л × p 0..5 атм
const a = axes2D(W, H, pad, 0, 10, 0, 5);
let g = a.content;
g += '<text x="'+(W-pad+2)+'" y="'+(H-pad+14)+'" font-size="10" fill="#0f172a">V, л</text>';
g += '<text x="'+(pad-22)+'" y="'+(pad-6)+'" font-size="10" fill="#0f172a">p, атм</text>';
let A_J = 0, formula = '', label = '';
const color = COL[m];
if(m === 'bar'){
let v1 = +document.getElementById('p12-iv1-v1').value;
let v2 = +document.getElementById('p12-iv1-v2').value;
const p = +document.getElementById('p12-iv1-p').value;
document.getElementById('p12-iv1-v1L').textContent = v1.toFixed(1);
document.getElementById('p12-iv1-v2L').textContent = v2.toFixed(1);
document.getElementById('p12-iv1-pL').textContent = p.toFixed(1);
// площадь = прямоугольник между V1 и V2 на высоте p
const vL = Math.min(v1, v2), vR = Math.max(v1, v2);
const sign = v2 >= v1 ? '+' : '';
g += '<rect x="'+a.toX(vL)+'" y="'+a.toY(p)+'" width="'+(a.toX(vR)-a.toX(vL))+'" height="'+(a.toY(0)-a.toY(p))+'" fill="'+color+'" opacity="0.22"/>';
g += '<line x1="'+a.toX(0)+'" y1="'+a.toY(p)+'" x2="'+a.toX(10)+'" y2="'+a.toY(p)+'" stroke="'+color+'" stroke-width="2"/>';
// вертикали состояний
g += '<line x1="'+a.toX(v1)+'" y1="'+a.toY(0)+'" x2="'+a.toX(v1)+'" y2="'+a.toY(p)+'" stroke="'+color+'" stroke-width="1.5" stroke-dasharray="4 3"/>';
g += '<line x1="'+a.toX(v2)+'" y1="'+a.toY(0)+'" x2="'+a.toX(v2)+'" y2="'+a.toY(p)+'" stroke="'+color+'" stroke-width="1.5" stroke-dasharray="4 3"/>';
g += '<circle cx="'+a.toX(v1)+'" cy="'+a.toY(p)+'" r="4.5" fill="'+color+'"/><text x="'+(a.toX(v1)-4)+'" y="'+(a.toY(p)-8)+'" font-size="11" font-weight="700" fill="'+color+'">1</text>';
g += '<circle cx="'+a.toX(v2)+'" cy="'+a.toY(p)+'" r="4.5" fill="'+color+'"/><text x="'+(a.toX(v2)+6)+'" y="'+(a.toY(p)-8)+'" font-size="11" font-weight="700" fill="'+color+'">2</text>';
// стрелка направления
const xMid = (a.toX(v1)+a.toX(v2))/2;
g += '<polygon points="'+xMid+','+(a.toY(p)-4)+' '+(xMid+(v2>v1?8:-8))+','+a.toY(p)+' '+xMid+','+(a.toY(p)+4)+'" fill="'+color+'"/>';
const dV = (v2 - v1) * 1e-3; // м³
A_J = p * 1e5 * dV;
formula = '$A_{газ} = p \\cdot \\Delta V = '+p.toFixed(1)+' \\cdot 10^5 \\cdot ('+v2.toFixed(1)+' - '+v1.toFixed(1)+') \\cdot 10^{-3} = '+A_J.toFixed(0)+'$ Дж';
label = (v2 > v1 ? 'расширение' : (v2 < v1 ? 'сжатие' : 'ΔV=0'));
} else if(m === 'hor'){
const v = +document.getElementById('p12-iv1-v1').value;
const p1 = +document.getElementById('p12-iv1-p').value;
const p2 = +document.getElementById('p12-iv1-p2').value;
document.getElementById('p12-iv1-v1L').textContent = v.toFixed(1);
document.getElementById('p12-iv1-pL').textContent = p1.toFixed(1);
document.getElementById('p12-iv1-p2L').textContent = p2.toFixed(1);
// вертикальная линия V = const
g += '<line x1="'+a.toX(v)+'" y1="'+a.toY(Math.min(p1,p2))+'" x2="'+a.toX(v)+'" y2="'+a.toY(Math.max(p1,p2))+'" stroke="'+color+'" stroke-width="3"/>';
g += '<circle cx="'+a.toX(v)+'" cy="'+a.toY(p1)+'" r="4.5" fill="'+color+'"/><text x="'+(a.toX(v)+6)+'" y="'+(a.toY(p1)+4)+'" font-size="11" font-weight="700" fill="'+color+'">1</text>';
g += '<circle cx="'+a.toX(v)+'" cy="'+a.toY(p2)+'" r="4.5" fill="'+color+'"/><text x="'+(a.toX(v)+6)+'" y="'+(a.toY(p2)+4)+'" font-size="11" font-weight="700" fill="'+color+'">2</text>';
A_J = 0;
formula = '$A_{газ} = 0$, т.к. $\\Delta V = 0$ (изохорный процесс).';
label = 'изохорно';
} else {
let v1 = +document.getElementById('p12-iv1-v1').value;
let v2 = +document.getElementById('p12-iv1-v2').value;
const T = +document.getElementById('p12-iv1-t').value;
document.getElementById('p12-iv1-v1L').textContent = v1.toFixed(1);
document.getElementById('p12-iv1-v2L').textContent = v2.toFixed(1);
document.getElementById('p12-iv1-tL').textContent = T;
// p(V) = nu*R*T / V; на 1 моль; p в атм при V в л
// p_атм = (1 * 8.314 * T) / (V_л * 1e-3) / 1e5 = 8.314*T / (V*100)
const NU = 1;
const fAtm = V => (NU * R * T) / (V * 1e-3) / 1e5;
// нарисовать кривую (всю гиперболу)
g += plotFunc(fAtm, 0.5, 10, a.toX, a.toY, color, 240);
// закрасить площадь между V1, V2
const vL = Math.min(v1, v2), vR = Math.max(v1, v2);
let d = 'M'+a.toX(vL)+','+a.toY(0);
const N = 80;
for(let i = 0; i <= N; i++){
const V = vL + (vR - vL) * i / N;
const pp = Math.min(fAtm(V), 5);
d += ' L'+a.toX(V).toFixed(2)+','+a.toY(pp).toFixed(2);
}
d += ' L'+a.toX(vR)+','+a.toY(0)+' Z';
g += '<path d="'+d+'" fill="'+color+'" opacity="0.22"/>';
g += '<circle cx="'+a.toX(v1)+'" cy="'+a.toY(Math.min(fAtm(v1),5))+'" r="4.5" fill="'+color+'"/><text x="'+(a.toX(v1)+6)+'" y="'+(a.toY(Math.min(fAtm(v1),5))-8)+'" font-size="11" font-weight="700" fill="'+color+'">1</text>';
g += '<circle cx="'+a.toX(v2)+'" cy="'+a.toY(Math.min(fAtm(v2),5))+'" r="4.5" fill="'+color+'"/><text x="'+(a.toX(v2)+6)+'" y="'+(a.toY(Math.min(fAtm(v2),5))-8)+'" font-size="11" font-weight="700" fill="'+color+'">2</text>';
A_J = NU * R * T * Math.log(v2 / v1);
formula = '$A_{газ} = \\nu RT \\ln\\dfrac{V_2}{V_1} = 1 \\cdot 8{,}314 \\cdot '+T+' \\cdot \\ln\\dfrac{'+v2.toFixed(1)+'}{'+v1.toFixed(1)+'} \\approx '+A_J.toFixed(0)+'$ Дж';
label = (v2 > v1 ? 'изотермическое расширение' : (v2 < v1 ? 'изотермическое сжатие' : 'V₁ = V₂'));
}
svg.innerHTML = g;
const sgn = A_J > 0.5 ? 'положительная' : (A_J < -0.5 ? 'отрицательная' : 'нулевая');
info.innerHTML = '<div style="margin-bottom:6px"><b>'+label.charAt(0).toUpperCase()+label.slice(1)+'</b> · работа '+sgn+'.</div>'
+ '<div>'+formula+'</div>';
renderMath(info);
seen.add(m + ':' + Math.round(A_J/10));
if(!_done && seen.size >= 4){ _done = true; addXp(10,'p12-iv1'); bumpProgress('p12', 15); }
}
document.querySelectorAll('input[name="p12-iv1-proc"]').forEach(r => r.addEventListener('change', () => { buildSliders(); render(); }));
buildSliders();
render();
})();
/* IV2 — Калькулятор работы */
(function(){
const R = 8.314;
const tabs = document.getElementById('p12-iv2-tabs');
const inpsBox = document.getElementById('p12-iv2-inputs');
const out = document.getElementById('p12-iv2-out');
const fb = document.getElementById('p12-iv2-fb');
const used = new Set(); let _done = false;
let mode = 'bar';
function fieldHTML(id, label, val){
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
}
function build(){
let h = '';
if(mode === 'bar'){
h += fieldHTML('p12-iv2-p','$p$, атм','2');
h += fieldHTML('p12-iv2-v1','$V_1$, л','5');
h += fieldHTML('p12-iv2-v2','$V_2$, л','10');
} else if(mode === 'hor'){
h += '<div style="grid-column:1/-1;text-align:center;padding:10px;color:var(--muted);font-size:.92rem">В изохорном процессе $\\Delta V = 0$, поэтому $A_{газ} = 0$ при любых параметрах.</div>';
} else {
h += fieldHTML('p12-iv2-nu','$\\nu$, моль','1');
h += fieldHTML('p12-iv2-t','$T$, К','300');
h += fieldHTML('p12-iv2-v1','$V_1$, л','2');
h += fieldHTML('p12-iv2-v2','$V_2$, л','4');
}
inpsBox.innerHTML = h;
renderMath(inpsBox);
out.innerHTML = '';
fb.style.display = 'none';
}
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
function calc(){
let A = 0, res = '';
if(mode === 'bar'){
const p = num('p12-iv2-p'), v1 = num('p12-iv2-v1'), v2 = num('p12-iv2-v2');
if(![p,v1,v2].every(x => isFinite(x)) || p <= 0 || v1 <= 0 || v2 <= 0){ feedback(fb,false,'&#10007; Все значения должны быть положительными.'); return; }
A = p * 1e5 * (v2 - v1) * 1e-3;
res = '$A_{газ} = p \\cdot \\Delta V = '+p+' \\cdot 10^5 \\cdot ('+v2+' - '+v1+') \\cdot 10^{-3} = '+A.toFixed(0)+'$ Дж';
} else if(mode === 'hor'){
A = 0;
res = '$A_{газ} = 0$, поскольку $\\Delta V = 0$.';
} else {
const nv = num('p12-iv2-nu'), T = num('p12-iv2-t'), v1 = num('p12-iv2-v1'), v2 = num('p12-iv2-v2');
if(![nv,T,v1,v2].every(x => isFinite(x) && x > 0)){ feedback(fb,false,'&#10007; Все значения должны быть положительными.'); return; }
A = nv * R * T * Math.log(v2 / v1);
res = '$A_{газ} = \\nu RT\\ln\\dfrac{V_2}{V_1} = '+nv+' \\cdot 8{,}314 \\cdot '+T+' \\cdot \\ln\\dfrac{'+v2+'}{'+v1+'} \\approx '+A.toFixed(0)+'$ Дж';
}
out.innerHTML = '<div style="margin-bottom:8px">'+res+'</div>'
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2)">'+A.toFixed(0)+' Дж</span></div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
used.add(mode);
if(!_done && used.size === 3){ _done = true; addXp(10,'p12-iv2'); bumpProgress('p12', 15); }
}
tabs.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
mode = b.dataset.mode;
tabs.querySelectorAll('button').forEach(x => { x.className = 'btn'; x.style.background=''; x.style.borderColor=''; });
b.className = 'btn primary';
const colMap = { bar:'#2563eb', hor:'#10b981', iso:'#ea580c' };
b.style.background = colMap[mode]; b.style.borderColor = colMap[mode];
build();
});
});
document.getElementById('p12-iv2-go').addEventListener('click', calc);
build();
})();
/* IV3 — Знак работы (квикфайр) */
(function(){
const Q = [
{ q:'Газ изобарно расширяется от $V_1 = 2$ л до $V_2 = 5$ л.', ans:'pos' },
{ q:'Газ изобарно сжимается от $V_1 = 8$ л до $V_2 = 3$ л.', ans:'neg' },
{ q:'Газ в герметичном сосуде нагревают (изохорно).', ans:'zero' },
{ q:'Газ изотермически расширяется ($V_2 > V_1$).', ans:'pos' },
{ q:'Газ толкает поршень наружу.', ans:'pos' },
{ q:'Внешние силы сжимают газ. Знак $A_{газ}$?', ans:'neg' },
];
let i = 0, score = 0; let _done = false;
const box = document.getElementById('p12-iv3');
function show(){
const qEl = document.getElementById('p12-iv3-q');
const fb = document.getElementById('p12-iv3-fb');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
renderMath(qEl);
if(!_done){
_done = true;
if(score === Q.length){ addXp(15,'p12-iv3'); bumpProgress('p12', 25); }
else if(score >= 4){ addXp(8,'p12-iv3'); bumpProgress('p12', 15); }
}
return;
}
document.getElementById('p12-iv3-i').textContent = (i+1);
document.getElementById('p12-iv3-s').textContent = score;
qEl.innerHTML = Q[i].q;
renderMath(qEl);
fb.style.display = 'none';
}
box.querySelectorAll('button[data-sgn]').forEach(b => {
b.addEventListener('click', () => {
if(i >= Q.length) return;
const fb = document.getElementById('p12-iv3-fb');
const ans = b.dataset.sgn;
if(ans === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else {
const labels = {pos:'$A>0$', neg:'$A<0$', zero:'$A=0$'};
feedback(fb, false, '&#10007; Неверно. Правильно: '+labels[Q[i].ans]+'. Дальше ▶');
}
document.getElementById('p12-iv3-s').textContent = score;
i++;
setTimeout(show, 1500);
});
});
show();
})();
/* IV4 — Тренажёр работы */
(function(){
const Q = [
{ q:'Изобарно при $p = 2$ атм газ расширили от 5 л до 10 л. Работа газа в Дж?', ans:1000, tol:30, hint:'$A = p\\Delta V = 2 \\cdot 10^5 \\cdot 5 \\cdot 10^{-3} = 1000$ Дж' },
{ q:'Изохорно газ нагрели от 300 К до 600 К. Работа газа в Дж?', ans:0, tol:1, hint:'$\\Delta V = 0 \\Rightarrow A = 0$' },
{ q:'Изобарно при $p = 1$ атм газ сжали от 10 л до 4 л. Работа газа в Дж (со знаком)?', ans:-600, tol:20, hint:'$A = 10^5 \\cdot (-6 \\cdot 10^{-3}) = -600$ Дж' },
{ q:'Изотермически 1 моль газа при $T = 300$ К расширили от 2 л до 4 л. Работа газа в Дж?', ans:1726, tol:50, hint:'$A = \\nu RT\\ln(V_2/V_1) = 8{,}3 \\cdot 300 \\cdot \\ln 2 \\approx 1726$ Дж' },
{ q:'Газ при $p = 3$ атм совершил работу $A = 1500$ Дж. Найди $\\Delta V$ в литрах.', ans:5, tol:0.2, hint:'$\\Delta V = A/p = 1500/(3 \\cdot 10^5) = 5 \\cdot 10^{-3}$ м³ = 5 л' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p12-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p12-iv4'); bumpProgress('p12', 25); }
else if(score >= 3){ addXp(8, 'p12-iv4'); bumpProgress('p12', 15); }
return;
}
document.getElementById('p12-iv4-i').textContent = (i+1);
document.getElementById('p12-iv4-s').textContent = score;
document.getElementById('p12-iv4-q').innerHTML = Q[i].q;
document.getElementById('p12-iv4-ans').value = '';
renderMath(document.getElementById('p12-iv4-q'));
document.getElementById('p12-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p12-iv4-fb');
const raw = document.getElementById('p12-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
document.getElementById('p12-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p12-iv4-go').addEventListener('click', go);
document.getElementById('p12-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p12-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p12');
}
function build_p13(){
const box = document.getElementById('p13-body');
let html = '';
/* THEORY 1 — Количество теплоты и удельная теплоёмкость */
html += makeCard('theory', "Количество теплоты и удельная теплоёмкость", "§13", `
<p><b>Количество теплоты</b> $Q$ — энергия, передаваемая телу при теплообмене (без совершения работы). Единица — джоуль (Дж).</p>
<p style="margin-top:8px">Для <b>нагревания или охлаждения</b> (без фазового перехода):</p>
<p style="text-align:center;margin:10px 0">$$Q = c\\,m\\,\\Delta T$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$c$ — <b>удельная теплоёмкость</b>, Дж/(кг·К);</li>
<li>$m$ — масса тела, кг;</li>
<li>$\\Delta T = T_{кон} - T_{нач}$ — разность температур (К или °C, числовое значение совпадает).</li>
</ul>
<p><b>Удельная теплоёмкость</b> — это $Q$, нужное, чтобы нагреть 1 кг вещества на 1 К. Характеристика вещества:</p>
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.92rem">
<thead><tr style="background:var(--sec-acc-soft)"><th style="padding:8px;border:1px solid var(--border);text-align:left">Вещество</th><th style="padding:8px;border:1px solid var(--border)">$c$, Дж/(кг·К)</th></tr></thead>
<tbody>
<tr><td style="padding:8px;border:1px solid var(--border)"><b style="color:#2563eb">Вода</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">4200</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Воздух</td><td style="padding:8px;border:1px solid var(--border);text-align:center">1000</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Алюминий</td><td style="padding:8px;border:1px solid var(--border);text-align:center">920</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Железо</td><td style="padding:8px;border:1px solid var(--border);text-align:center">460</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Медь</td><td style="padding:8px;border:1px solid var(--border);text-align:center">390</td></tr>
</tbody>
</table>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Вода имеет аномально большую $c$:</b> поэтому моря медленно нагреваются и медленно остывают — именно так формируется морской климат.</p>
`);
/* THEORY 2 — Фазовые переходы */
html += makeCard('rule', "Фазовые переходы: плавление и парообразование", "§13", `
<p><b>Плавление и кристаллизация</b> (температура не меняется):</p>
<p style="text-align:center;margin:10px 0">$$Q = \\lambda\\,m$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$\\lambda$ — <b>удельная теплота плавления</b>, Дж/кг;</li>
<li>лёд: $\\lambda = 3{,}3 \\cdot 10^5$ Дж/кг ($= 330$ кДж/кг);</li>
<li>при плавлении $Q > 0$ (поглощается), при кристаллизации $Q < 0$ (выделяется).</li>
</ul>
<p><b>Парообразование и конденсация</b>:</p>
<p style="text-align:center;margin:10px 0">$$Q = r\\,m$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$r$ — <b>удельная теплота парообразования</b>, Дж/кг;</li>
<li>вода: $r = 2{,}26 \\cdot 10^6$ Дж/кг ($= 2260$ кДж/кг).</li>
</ul>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Важно:</b> $r > \\lambda$ — испарение требует значительно больше энергии, чем плавление. Поэтому ожог паром гораздо опаснее ожога кипятком.</p>
`);
/* THEORY 3 — Сгорание топлива и тепловой баланс */
html += makeCard('example', "Сгорание топлива. Уравнение теплового баланса", "§13", `
<p><b>Сгорание топлива</b>:</p>
<p style="text-align:center;margin:10px 0">$$Q = q\\,m$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$q$ — <b>удельная теплота сгорания</b>, Дж/кг;</li>
<li>бензин: $q = 4{,}4 \\cdot 10^7$ Дж/кг;</li>
<li>уголь: $q \\approx 2{,}9 \\cdot 10^7$ Дж/кг;</li>
<li>дрова сухие: $q \\approx 1{,}3 \\cdot 10^7$ Дж/кг.</li>
</ul>
<p><b>Уравнение теплового баланса</b> для изолированной системы (нет теплообмена с окружением):</p>
<p style="text-align:center;margin:10px 0">$$\\sum Q_i = 0$$</p>
<p>Теплота, отданная горячими телами, равна теплоте, полученной холодными:</p>
<p style="text-align:center;margin:10px 0">$$Q_{отд} = Q_{пол}$$</p>
<p><b>Пример.</b> В калориметр наливают холодную воду и опускают горячий кусок металла. Установится общая температура $T$, при которой</p>
<p style="text-align:center;margin:10px 0">$$c_1 m_1 (T - T_1) = c_2 m_2 (T_2 - T)$$</p>
<p>отсюда:</p>
<p style="text-align:center;margin:10px 0">$$T = \\dfrac{c_1 m_1 T_1 + c_2 m_2 T_2}{c_1 m_1 + c_2 m_2}$$</p>
`);
/* INTERACTIVE 1 — Универсальный калькулятор Q */
html += `<div class="wg" id="p13-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Калькулятор количества теплоты $Q$</div></div>
<div class="wg-help">Выбери тип процесса — введи параметры — получи формулу и значение в Дж и кДж.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-bottom:10px" id="p13-iv1-tabs">
<button class="btn primary" data-mode="heat">Нагревание $Q = cm\\Delta T$</button>
<button class="btn" data-mode="melt">Плавление $Q = \\lambda m$</button>
<button class="btn" data-mode="evap">Испарение $Q = rm$</button>
<button class="btn" data-mode="burn">Сгорание $Q = qm$</button>
</div>
<div id="p13-iv1-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center"><button class="btn primary" id="p13-iv1-go">Вычислить $Q$</button></div>
<div id="p13-iv1-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p13-iv1-fb"></div>
</div>`;
/* INTERACTIVE 2 — График нагревания и плавления льда */
html += `<div class="wg" id="p13-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">График нагревания льда → воды → пара</div></div>
<div class="wg-help">Меняй массу — четыре сегмента ломаной: нагрев льда, плавление, нагрев воды, испарение.</div>
<div class="sliders">
<label>Масса $m$: <b id="p13-iv2-mL">1.0</b> кг <input type="range" id="p13-iv2-m" min="0.5" max="2" value="1.0" step="0.1"></label>
</div>
<div style="display:flex;gap:12px;flex-wrap:wrap;justify-content:center;margin-bottom:8px;font-size:.84rem">
<span style="display:inline-flex;align-items:center;gap:6px"><span style="width:18px;height:3px;background:#0ea5e9;border-radius:2px"></span> Лёд</span>
<span style="display:inline-flex;align-items:center;gap:6px"><span style="width:18px;height:0;border-top:3px dashed #2563eb"></span> Плавление</span>
<span style="display:inline-flex;align-items:center;gap:6px"><span style="width:18px;height:3px;background:#ea580c;border-radius:2px"></span> Вода</span>
<span style="display:inline-flex;align-items:center;gap:6px"><span style="width:18px;height:0;border-top:3px dashed #dc2626"></span> Испарение</span>
</div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p13-iv2-svg" viewBox="0 0 420 280" width="100%" style="height:auto"></svg>
</div>
<div id="p13-iv2-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.75"></div>
</div>`;
/* INTERACTIVE 3 — DnD сортер */
html += `<div class="wg" id="p13-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какой это тепловой процесс?</div></div>
<div class="wg-help">6 явлений — 4 типа процессов.</div>
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 6 ситуаций — 4 ящика</div>
<div id="p13-iv3-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="heat">Нагревание/охлаждение</h5><div class="drop-items" data-cat="heat"></div></div>
<div class="drop-box"><h5 data-cat="melt">Плавление/кристаллизация</h5><div class="drop-items" data-cat="melt"></div></div>
<div class="drop-box"><h5 data-cat="evap">Парообразование/конденсация</h5><div class="drop-items" data-cat="evap"></div></div>
<div class="drop-box"><h5 data-cat="burn">Сгорание топлива</h5><div class="drop-items" data-cat="burn"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p13-iv3-check">Проверить</button><button class="btn" id="p13-iv3-reset">Сначала</button></div>
<div class="feedback" id="p13-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр теплоты */
html += `<div class="wg" id="p13-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр теплоты</div></div>
<div class="wg-help">6 задач. $c_{воды} = 4200$, $\\lambda_{льда} = 3{,}3 \\cdot 10^5$, $r_{воды} = 2{,}26 \\cdot 10^6$, $q_{бенз} = 4{,}4 \\cdot 10^7$ (СИ). Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p13-iv4-i">1</b> / 6</span><span>Очки: <b id="p13-iv4-s">0</b> / 6</span></div>
<div id="p13-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p13-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p13-iv4-go">Проверить</button>
<button class="btn" id="p13-iv4-start">Заново</button>
</div>
<div class="feedback" id="p13-iv4-fb"></div>
</div>`;
html += secNav('p12', 'p14');
html += readButton('p13');
box.innerHTML = html;
renderMath(box);
/* IV1 — Универсальный калькулятор Q */
(function(){
const tabs = document.getElementById('p13-iv1-tabs');
const inpsBox = document.getElementById('p13-iv1-inputs');
const out = document.getElementById('p13-iv1-out');
const fb = document.getElementById('p13-iv1-fb');
const used = new Set(); let _done = false;
let mode = 'heat';
const C_MAP = { water:4200, air:1000, alum:920, iron:460, copper:390 };
const C_LBL = { water:'Вода', air:'Воздух', alum:'Алюминий', iron:'Железо', copper:'Медь' };
const L_MAP = { ice:330000, lead:25000, iron:270000 };
const L_LBL = { ice:'Лёд (330 кДж/кг)', lead:'Свинец (25 кДж/кг)', iron:'Железо (270 кДж/кг)' };
const R_MAP = { water:2260000, eth:360000, alc:850000 };
const R_LBL = { water:'Вода (2260 кДж/кг)', eth:'Эфир (360 кДж/кг)', alc:'Спирт (850 кДж/кг)' };
const Q_MAP = { gas:4.4e7, coal:2.9e7, wood:1.3e7 };
const Q_LBL = { gas:'Бензин (44 МДж/кг)', coal:'Уголь (29 МДж/кг)', wood:'Дрова (13 МДж/кг)' };
function selectHTML(id, map, lblMap){
let h = '<select id="'+id+'" class="tinp" style="width:100%;margin-top:6px;padding:6px 8px">';
Object.keys(map).forEach(k => { h += '<option value="'+k+'">'+lblMap[k]+'</option>'; });
h += '</select>';
return h;
}
function field(id, label, val){
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
}
function pickField(id, label, mapHtml){
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+mapHtml+'</label>';
}
function build(){
let h = '';
if(mode === 'heat'){
h += pickField('p13-iv1-sub','Вещество',selectHTML('p13-iv1-c', C_MAP, C_LBL));
h += field('p13-iv1-m','$m$, кг','1');
h += field('p13-iv1-dt','$\\Delta T$, К','50');
} else if(mode === 'melt'){
h += pickField('p13-iv1-sub','Вещество',selectHTML('p13-iv1-lam', L_MAP, L_LBL));
h += field('p13-iv1-m','$m$, кг','1');
} else if(mode === 'evap'){
h += pickField('p13-iv1-sub','Вещество',selectHTML('p13-iv1-r', R_MAP, R_LBL));
h += field('p13-iv1-m','$m$, кг','1');
} else {
h += pickField('p13-iv1-sub','Топливо',selectHTML('p13-iv1-q', Q_MAP, Q_LBL));
h += field('p13-iv1-m','$m$, кг','1');
}
inpsBox.innerHTML = h;
renderMath(inpsBox);
out.innerHTML = '';
fb.style.display = 'none';
}
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
function fmtJ(v){ const k = v/1000, M = v/1e6; if(Math.abs(v) >= 1e6) return v.toExponential(2)+' Дж ($\\approx '+M.toFixed(2)+'$ МДж)'; if(Math.abs(v) >= 1000) return v.toFixed(0)+' Дж ($= '+k.toFixed(1)+'$ кДж)'; return v.toFixed(1)+' Дж'; }
function calc(){
let Q = 0, res = '';
const m = num('p13-iv1-m');
if(!isFinite(m) || m <= 0){ feedback(fb,false,'&#10007; Масса должна быть положительной.'); return; }
if(mode === 'heat'){
const sel = document.getElementById('p13-iv1-c').value;
const c = C_MAP[sel];
const dT = num('p13-iv1-dt');
if(!isFinite(dT)){ feedback(fb,false,'&#10007; Введи $\\Delta T$.'); return; }
Q = c * m * dT;
res = '$Q = c\\,m\\,\\Delta T = '+c+' \\cdot '+m+' \\cdot '+dT+' = '+Q.toFixed(0)+'$ Дж';
} else if(mode === 'melt'){
const sel = document.getElementById('p13-iv1-lam').value;
const lam = L_MAP[sel];
Q = lam * m;
res = '$Q = \\lambda\\,m = '+lam.toExponential(2)+' \\cdot '+m+' = '+Q.toFixed(0)+'$ Дж';
} else if(mode === 'evap'){
const sel = document.getElementById('p13-iv1-r').value;
const r = R_MAP[sel];
Q = r * m;
res = '$Q = r\\,m = '+r.toExponential(2)+' \\cdot '+m+' = '+Q.toFixed(0)+'$ Дж';
} else {
const sel = document.getElementById('p13-iv1-q').value;
const q = Q_MAP[sel];
Q = q * m;
res = '$Q = q\\,m = '+q.toExponential(2)+' \\cdot '+m+' = '+Q.toFixed(0)+'$ Дж';
}
out.innerHTML = '<div style="margin-bottom:8px">'+res+'</div>'
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2)">'+fmtJ(Q)+'</span></div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
used.add(mode);
if(!_done && used.size === 4){ _done = true; addXp(10,'p13-iv1'); bumpProgress('p13', 15); }
}
tabs.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
mode = b.dataset.mode;
tabs.querySelectorAll('button').forEach(x => { x.className = 'btn'; });
b.className = 'btn primary';
build();
});
});
document.getElementById('p13-iv1-go').addEventListener('click', calc);
build();
})();
/* IV2 — График нагревания льда */
(function(){
const svg = document.getElementById('p13-iv2-svg');
const mIn = document.getElementById('p13-iv2-m');
const mL = document.getElementById('p13-iv2-mL');
const info = document.getElementById('p13-iv2-info');
const seen = new Set(); let _done = false;
const C_ICE = 2100, C_WATER = 4200, LAMBDA = 330000, R_VAP = 2260000;
function render(){
const m = +mIn.value;
mL.textContent = m.toFixed(1);
// Q-сегменты в Дж
const Q1 = C_ICE * m * 20; // -20 → 0
const Q2 = LAMBDA * m; // плавление
const Q3 = C_WATER * m * 100; // 0 → 100
const Q4 = R_VAP * m; // испарение
// в кДж для оси X
const q1 = Q1/1000, q2 = Q2/1000, q3 = Q3/1000, q4 = Q4/1000;
const Qtotal_kJ = q1 + q2 + q3 + q4;
// оси: X 0..6000 кДж × Y -20..120 °C
const W=420, H=280, pad=44;
// расширяем X-предел чтобы Q4 поместилось
const xMax = Math.ceil((Qtotal_kJ + 200) / 500) * 500;
// axes2D рисует целые тики; используем напрямую координатную сетку
// Зайдём в систему: вычислим toX/toY вручную
const xmin = 0, xmax = xMax, ymin = -20, ymax = 120;
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (ymax - ymin);
const toX = v => pad + (v - xmin) * ux;
const toY = v => H - pad - (v - ymin) * uy;
let g = '';
g += '<rect x="'+pad+'" y="'+pad+'" width="'+(W-2*pad)+'" height="'+(H-2*pad)+'" fill="none" stroke="#e5e7eb"/>';
// сетка X
g += '<g stroke="#e5e7eb" stroke-width="1">';
const xStep = xMax / 10;
for(let xv = 0; xv <= xMax; xv += xStep){
g += '<line x1="'+toX(xv)+'" y1="'+pad+'" x2="'+toX(xv)+'" y2="'+(H-pad)+'"/>';
}
for(let yv = -20; yv <= 120; yv += 20){
g += '<line x1="'+pad+'" y1="'+toY(yv)+'" x2="'+(W-pad)+'" y2="'+toY(yv)+'"/>';
}
g += '</g>';
// оси (Y=0 линия)
g += '<line x1="'+pad+'" y1="'+toY(0)+'" x2="'+(W-pad)+'" y2="'+toY(0)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+pad+'" y1="'+pad+'" x2="'+pad+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
// подписи осей
g += '<text x="'+(W-pad+2)+'" y="'+(H-pad+14)+'" font-size="10" fill="#0f172a">Q, кДж</text>';
g += '<text x="'+(pad-26)+'" y="'+(pad-6)+'" font-size="10" fill="#0f172a">T, °C</text>';
// тики X
g += '<g font-size="9" fill="#64748b">';
for(let xv = 0; xv <= xMax; xv += xStep){
g += '<text x="'+(toX(xv)-8)+'" y="'+(H-pad+11)+'">'+Math.round(xv)+'</text>';
}
for(let yv = -20; yv <= 120; yv += 20){
g += '<text x="'+(pad-26)+'" y="'+(toY(yv)+3)+'">'+yv+'</text>';
}
g += '</g>';
// Сегменты ломаной
let xCur = 0;
// 1) лёд -20 → 0 (голубой)
g += '<line x1="'+toX(xCur)+'" y1="'+toY(-20)+'" x2="'+toX(xCur+q1)+'" y2="'+toY(0)+'" stroke="#0ea5e9" stroke-width="3" stroke-linecap="round"/>';
xCur += q1;
// 2) плавление 0 → 0 (синий пунктир)
g += '<line x1="'+toX(xCur)+'" y1="'+toY(0)+'" x2="'+toX(xCur+q2)+'" y2="'+toY(0)+'" stroke="#2563eb" stroke-width="3" stroke-dasharray="6 4" stroke-linecap="round"/>';
xCur += q2;
// 3) вода 0 → 100 (оранжевый)
g += '<line x1="'+toX(xCur)+'" y1="'+toY(0)+'" x2="'+toX(xCur+q3)+'" y2="'+toY(100)+'" stroke="#ea580c" stroke-width="3" stroke-linecap="round"/>';
xCur += q3;
// 4) испарение 100 → 100 (красный пунктир)
g += '<line x1="'+toX(xCur)+'" y1="'+toY(100)+'" x2="'+toX(xCur+q4)+'" y2="'+toY(100)+'" stroke="#dc2626" stroke-width="3" stroke-dasharray="6 4" stroke-linecap="round"/>';
xCur += q4;
// точки переходов
let xMark = 0;
const marks = [
{x:xMark, y:-20, color:'#0ea5e9'},
{x:(xMark+=q1), y:0, color:'#2563eb'},
{x:(xMark+=q2), y:0, color:'#ea580c'},
{x:(xMark+=q3), y:100, color:'#dc2626'},
{x:(xMark+=q4), y:100, color:'#7c2d12'},
];
marks.forEach(p => { g += '<circle cx="'+toX(p.x)+'" cy="'+toY(p.y)+'" r="3.5" fill="'+p.color+'"/>'; });
svg.innerHTML = g;
const totalMJ = Qtotal_kJ/1000;
info.innerHTML = '<div style="margin-bottom:6px"><b>Масса:</b> $m = '+m.toFixed(1)+'$ кг.</div>'
+ '<div style="margin-bottom:4px">$Q_1 = c_{льда}\\,m\\,\\Delta T = 2100 \\cdot '+m.toFixed(1)+' \\cdot 20 = '+Q1.toFixed(0)+'$ Дж $\\approx '+q1.toFixed(0)+'$ кДж</div>'
+ '<div style="margin-bottom:4px">$Q_2 = \\lambda\\,m = '+LAMBDA+' \\cdot '+m.toFixed(1)+' = '+Q2.toFixed(0)+'$ Дж $\\approx '+q2.toFixed(0)+'$ кДж</div>'
+ '<div style="margin-bottom:4px">$Q_3 = c_{воды}\\,m \\cdot 100 = 4200 \\cdot '+m.toFixed(1)+' \\cdot 100 = '+Q3.toFixed(0)+'$ Дж $\\approx '+q3.toFixed(0)+'$ кДж</div>'
+ '<div style="margin-bottom:6px">$Q_4 = r\\,m = '+R_VAP+' \\cdot '+m.toFixed(1)+' = '+Q4.toFixed(0)+'$ Дж $\\approx '+q4.toFixed(0)+'$ кДж</div>'
+ '<div><b>Итого:</b> $Q_{общ} = Q_1+Q_2+Q_3+Q_4 \\approx '+totalMJ.toFixed(2)+'$ МДж</div>';
renderMath(info);
seen.add(Math.round(m*10));
if(!_done && seen.size >= 4){ _done = true; addXp(10,'p13-iv2'); bumpProgress('p13', 15); }
}
mIn.addEventListener('input', render);
render();
})();
/* IV3 — DnD сортер: тип процесса */
(function(){
const items = [
{ id:'b1', cat:'heat', html:'Нагрев кастрюли с водой на плите' },
{ id:'b2', cat:'melt', html:'Тает лёд в стакане чая' },
{ id:'b3', cat:'evap', html:'Кипит чайник на огне' },
{ id:'b4', cat:'burn', html:'Сжигание бензина в двигателе' },
{ id:'b5', cat:'heat', html:'Чашка кофе постепенно остывает' },
{ id:'b6', cat:'melt', html:'Замерзает вода в формочке' },
];
const sorter = setupSorter({
poolId:'p13-iv3-pool',
scopeSelector:'#p13-iv3',
items: items,
cats:['heat','melt','evap','burn'],
columnLayout:false,
});
document.getElementById('p13-iv3-check').addEventListener('click', () => {
const fb = document.getElementById('p13-iv3-fb');
const placedCount = items.filter(it => sorter.placed[it.id]).length;
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
if(placedCount < items.length){ feedback(fb, false, '&#10007; Размести все 6 явлений.'); return; }
if(correct === items.length){ feedback(fb, true, '&#10003; Все 6 верно! +10 XP'); addXp(10,'p13-iv3'); bumpProgress('p13', 15); }
else feedback(fb, false, '&#10007; Правильно ' + correct + ' из 6. Попробуй ещё.');
});
document.getElementById('p13-iv3-reset').addEventListener('click', () => { sorter.reset(); document.getElementById('p13-iv3-fb').style.display = 'none'; });
})();
/* IV4 — Тренажёр теплоты */
(function(){
const Q = [
{ q:'Нагреть 2 кг воды от 20°C до 80°C. $Q$ в кДж?', ans:504, tol:10, hint:'$Q = c m \\Delta T = 4200 \\cdot 2 \\cdot 60 = 504000$ Дж = 504 кДж' },
{ q:'Растопить 0{,}5 кг льда при 0°C. $Q$ в кДж?', ans:165, tol:5, hint:'$Q = \\lambda m = 330000 \\cdot 0{,}5 = 165$ кДж' },
{ q:'Испарить 1 кг воды при 100°C. $Q$ в кДж?', ans:2260, tol:50, hint:'$Q = r m = 2{,}26 \\cdot 10^6 \\cdot 1 = 2260$ кДж' },
{ q:'Сжечь 0{,}5 кг бензина ($q = 4{,}4 \\cdot 10^7$ Дж/кг). $Q$ в МДж?', ans:22, tol:1, hint:'$Q = q m = 4{,}4 \\cdot 10^7 \\cdot 0{,}5 = 2{,}2 \\cdot 10^7$ Дж = 22 МДж' },
{ q:'В калориметр налили 100 г воды при 20°C и опустили 200 г меди при 100°C ($c_{Cu} = 390$). Конечная $T$ в °C?', ans:33, tol:1.5, hint:'$4200 \\cdot 0{,}1 \\cdot (T-20) = 390 \\cdot 0{,}2 \\cdot (100-T)$ → $T \\approx 32{,}5$°C' },
{ q:'Нагреть 1 кг алюминия ($c = 920$) от 20°C до 100°C. $Q$ в кДж?', ans:73.6, tol:2, hint:'$Q = 920 \\cdot 1 \\cdot 80 = 73600$ Дж = 73{,}6 кДж' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p13-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p13-iv4'); bumpProgress('p13', 25); }
else if(score >= 4){ addXp(8, 'p13-iv4'); bumpProgress('p13', 15); }
return;
}
document.getElementById('p13-iv4-i').textContent = (i+1);
document.getElementById('p13-iv4-s').textContent = score;
document.getElementById('p13-iv4-q').innerHTML = Q[i].q;
document.getElementById('p13-iv4-ans').value = '';
renderMath(document.getElementById('p13-iv4-q'));
document.getElementById('p13-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p13-iv4-fb');
const raw = document.getElementById('p13-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
document.getElementById('p13-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p13-iv4-go').addEventListener('click', go);
document.getElementById('p13-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p13-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p13');
}
function build_p14(){
const box = document.getElementById('p14-body');
let html = '';
/* THEORY 1 — Формулировка первого закона ТД */
html += makeCard('theory', "Формулировка первого закона термодинамики", "§14", `
<p><b>Первый закон термодинамики</b> — закон сохранения энергии для термодинамической системы:</p>
<p style="text-align:center;margin:10px 0">$$\\Delta U = Q + A_{внеш}$$</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>$\\Delta U$ — изменение внутренней энергии системы;</li>
<li>$Q$ — количество теплоты, переданное системе ($Q > 0$, если получено);</li>
<li>$A_{внеш}$ — работа внешних сил над системой.</li>
</ul>
<p>Через работу газа (учитывая $A_{внеш} = -A_{газ}$):</p>
<p style="text-align:center;margin:10px 0">$$Q = \\Delta U + A_{газ}$$</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Словами:</b> теплота, полученная газом, расходуется на изменение его внутренней энергии и на совершение работы газом.</p>
<p>Правила знаков:</p>
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.92rem">
<thead><tr style="background:var(--sec-acc-soft)"><th style="padding:8px;border:1px solid var(--border);text-align:left">Величина</th><th style="padding:8px;border:1px solid var(--border)">$> 0$</th><th style="padding:8px;border:1px solid var(--border)">$< 0$</th></tr></thead>
<tbody>
<tr><td style="padding:8px;border:1px solid var(--border)"><b>$Q$</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">газ получает</td><td style="padding:8px;border:1px solid var(--border);text-align:center">газ отдаёт</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)"><b>$A_{газ}$</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">расширение</td><td style="padding:8px;border:1px solid var(--border);text-align:center">сжатие</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)"><b>$\\Delta U$</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">$T$ растёт</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$T$ падает</td></tr>
</tbody>
</table>
`);
/* THEORY 2 — Применение к изопроцессам */
html += makeCard('rule', "Применение к изопроцессам", "§14", `
<p>Первый закон $Q = \\Delta U + A_{газ}$ принимает простой вид для каждого изопроцесса.</p>
<p style="margin-top:8px"><b>1) Изотермический</b> ($T = \\text{const}$, для идеального газа $\\Delta U = 0$):</p>
<p style="text-align:center;margin:8px 0">$$Q = A_{газ}$$</p>
<p>Вся подведённая теплота превращается в работу газа.</p>
<p style="margin-top:8px"><b>2) Изохорный</b> ($V = \\text{const}$, $A_{газ} = 0$):</p>
<p style="text-align:center;margin:8px 0">$$Q = \\Delta U = \\dfrac{3}{2}\\,\\nu R\\,\\Delta T$$</p>
<p>Вся теплота идёт на увеличение внутренней энергии.</p>
<p style="margin-top:8px"><b>3) Изобарный</b> ($p = \\text{const}$):</p>
<p style="text-align:center;margin:8px 0">$$Q = \\Delta U + p\\,\\Delta V$$</p>
<p>Часть теплоты — на $\\Delta U$, часть — на работу.</p>
<p style="margin-top:8px"><b>4) Адиабатный</b> ($Q = 0$, нет теплообмена):</p>
<p style="text-align:center;margin:8px 0">$$\\Delta U = -A_{газ}$$</p>
<p>Работа совершается за счёт внутренней энергии: расширение → охлаждение, сжатие → нагрев.</p>
`);
/* THEORY 3 — Адиабатный процесс и примеры */
html += makeCard('example', "Адиабатный процесс и примеры", "§14", `
<p><b>Адиабатный процесс</b> — без теплообмена ($Q = 0$). Реализуется:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>в теплоизолированных сосудах;</li>
<li>при <b>быстрых</b> процессах — теплообмен не успевает произойти.</li>
</ul>
<p><b>Примеры:</b></p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Дизельный двигатель.</b> Быстрое сжатие воздуха разогревает его до $\\sim 700°$C — этого достаточно для самовоспламенения солярки (без свечи зажигания).</li>
<li><b>Образование облаков.</b> Тёплый влажный воздух поднимается, адиабатно расширяется, охлаждается — водяной пар конденсируется в капли.</li>
<li><b>Холодильник.</b> Рабочее тело расширяется через дроссель — охлаждается; затем сжимается компрессором — нагревается.</li>
<li><b>Велонасос.</b> При быстром сжатии воздух заметно нагревается — это адиабатный процесс.</li>
</ul>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Уравнение Пуассона</b> для адиабаты: $pV^{\\gamma} = \\text{const}$, где $\\gamma = c_p/c_v$ — показатель адиабаты. Для одноатомного газа $\\gamma = 5/3$, для двухатомного $\\gamma = 7/5$.</p>
`);
/* INTERACTIVE 1 — Первый закон в действии (бар-чарт по 4 процессам) */
html += `<div class="wg" id="p14-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Первый закон в действии</div></div>
<div class="wg-help">Выбери процесс и сдвигай параметр — смотри бар-чарт $Q$, $\\Delta U$, $A_{газ}$.</div>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p14-iv1-proc" value="iso" checked> <span style="color:#ea580c;font-weight:700">Изотермический</span></label>
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p14-iv1-proc" value="hor"> <span style="color:#10b981;font-weight:700">Изохорный</span></label>
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p14-iv1-proc" value="bar"> <span style="color:#2563eb;font-weight:700">Изобарный</span></label>
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p14-iv1-proc" value="adi"> <span style="color:#7c3aed;font-weight:700">Адиабатный</span></label>
</div>
<div class="sliders" id="p14-iv1-sliders"></div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p14-iv1-svg" viewBox="0 0 420 240" width="100%" style="height:auto"></svg>
</div>
<div id="p14-iv1-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.85"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор первого закона */
html += `<div class="wg" id="p14-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор первого закона</div></div>
<div class="wg-help">Введи две величины — получи третью по формуле $Q = \\Delta U + A_{газ}$.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-bottom:10px" id="p14-iv2-tabs">
<button class="btn primary" data-find="dU">Найти $\\Delta U$</button>
<button class="btn" data-find="Q">Найти $Q$</button>
<button class="btn" data-find="A">Найти $A_{газ}$</button>
</div>
<div id="p14-iv2-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center"><button class="btn primary" id="p14-iv2-go">Вычислить</button></div>
<div id="p14-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p14-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — Что неизменно? (квикфайр) */
html += `<div class="wg" id="p14-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Что неизменно в этом процессе?</div></div>
<div class="wg-help">6 ситуаций. Выбери постоянную величину.</div>
<div class="score-display"><span>Задание <b id="p14-iv3-i">1</b> / 6</span><span>Очки: <b id="p14-iv3-s">0</b> / 6</span></div>
<div id="p14-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
<button class="btn primary" data-ans="T">$T = \\text{const}$</button>
<button class="btn primary" data-ans="V">$V = \\text{const}$</button>
<button class="btn primary" data-ans="p">$p = \\text{const}$</button>
<button class="btn primary" data-ans="Q">$Q = 0$</button>
</div>
<div class="feedback" id="p14-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр первого закона */
html += `<div class="wg" id="p14-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр первого закона</div></div>
<div class="wg-help">5 задач. $R = 8{,}3$ Дж/(моль·К). Допуск $\\pm 5\\%$.</div>
<div class="score-display"><span>Задача <b id="p14-iv4-i">1</b> / 5</span><span>Очки: <b id="p14-iv4-s">0</b> / 5</span></div>
<div id="p14-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p14-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p14-iv4-go">Проверить</button>
<button class="btn" id="p14-iv4-start">Заново</button>
</div>
<div class="feedback" id="p14-iv4-fb"></div>
</div>`;
html += secNav('p13', 'p15');
html += readButton('p14');
box.innerHTML = html;
renderMath(box);
/* IV1 — Первый закон в действии: бар-чарт */
(function(){
const R = 8.314;
const slBox = document.getElementById('p14-iv1-sliders');
const svg = document.getElementById('p14-iv1-svg');
const info = document.getElementById('p14-iv1-info');
const COL = { iso:'#ea580c', hor:'#10b981', bar:'#2563eb', adi:'#7c3aed' };
const seen = new Set(); let _done = false;
function getMode(){ const r = document.querySelector('input[name="p14-iv1-proc"]:checked'); return r ? r.value : 'iso'; }
function buildSliders(){
const m = getMode();
let h = '';
if(m === 'iso'){
h += '<label>$T$: <b id="p14-iv1-tL">300</b> К <input type="range" id="p14-iv1-t" min="200" max="500" value="300" step="10"></label>';
h += '<label>Отношение $V_2/V_1$: <b id="p14-iv1-rL">2.0</b> <input type="range" id="p14-iv1-r" min="0.5" max="4" value="2.0" step="0.1"></label>';
} else if(m === 'hor'){
h += '<label>$\\Delta T$: <b id="p14-iv1-dtL">+50</b> К <input type="range" id="p14-iv1-dt" min="-100" max="200" value="50" step="10"></label>';
} else if(m === 'bar'){
h += '<label>$\\Delta T$: <b id="p14-iv1-dtL">+50</b> К <input type="range" id="p14-iv1-dt" min="-100" max="200" value="50" step="10"></label>';
} else {
h += '<label>$A_{газ}$: <b id="p14-iv1-aL">+400</b> Дж <input type="range" id="p14-iv1-a" min="-800" max="800" value="400" step="20"></label>';
}
slBox.innerHTML = h;
renderMath(slBox);
slBox.querySelectorAll('input[type=range]').forEach(i => i.addEventListener('input', render));
}
function drawBars(Q, dU, A, mode){
const W=420, H=240, pad=44;
const innerW = W - 2*pad, innerH = H - 2*pad;
const maxAbs = Math.max(Math.abs(Q), Math.abs(dU), Math.abs(A), 100);
const ySc = innerH / (2 * maxAbs * 1.15);
const y0 = H/2; // ось 0
const barW = innerW / 4;
const labels = ['Q', 'ΔU', 'A_{газ}'];
const vals = [Q, dU, A];
const cols = [COL[mode], '#0ea5e9', '#f59e0b'];
let g = '';
// фон
g += '<rect x="'+pad+'" y="'+pad+'" width="'+innerW+'" height="'+innerH+'" fill="#fafbfc" stroke="#e5e7eb"/>';
// ось 0
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(pad-30)+'" y="'+(y0+4)+'" font-size="10" fill="#0f172a">0</text>';
// подсказки уровней
for(const lv of [-maxAbs, -maxAbs/2, maxAbs/2, maxAbs]){
const y = y0 - lv * ySc;
if(y > pad && y < H - pad){
g += '<line x1="'+pad+'" y1="'+y+'" x2="'+(W-pad)+'" y2="'+y+'" stroke="#e5e7eb" stroke-dasharray="3 3"/>';
g += '<text x="'+(pad-36)+'" y="'+(y+3)+'" font-size="9" fill="#64748b">'+lv.toFixed(0)+'</text>';
}
}
// бары
vals.forEach((v, i) => {
const cx = pad + innerW * (i+0.5) / 3;
const bx = cx - barW/2;
const h = Math.abs(v) * ySc;
const by = v >= 0 ? y0 - h : y0;
g += '<rect x="'+bx+'" y="'+by+'" width="'+barW+'" height="'+h+'" fill="'+cols[i]+'" opacity="0.85" stroke="'+cols[i]+'" stroke-width="2"/>';
g += '<text x="'+cx+'" y="'+(H-pad+18)+'" font-size="12" font-weight="700" text-anchor="middle" fill="#0f172a">'+labels[i]+'</text>';
const labY = v >= 0 ? by - 6 : by + h + 14;
g += '<text x="'+cx+'" y="'+labY+'" font-size="11" font-weight="700" text-anchor="middle" fill="'+cols[i]+'">'+v.toFixed(0)+' Дж</text>';
});
svg.innerHTML = g;
}
function render(){
const m = getMode();
let Q = 0, dU = 0, A = 0, descr = '', formula = '';
const NU = 1;
if(m === 'iso'){
const T = +document.getElementById('p14-iv1-t').value;
const r = +document.getElementById('p14-iv1-r').value;
document.getElementById('p14-iv1-tL').textContent = T;
document.getElementById('p14-iv1-rL').textContent = r.toFixed(1);
dU = 0;
A = NU * R * T * Math.log(r);
Q = A;
descr = 'Изотермический процесс: $T = \\text{const}$, $\\Delta U = 0$.';
formula = '$Q = A_{газ} = \\nu RT\\ln(V_2/V_1) = '+A.toFixed(0)+'$ Дж';
} else if(m === 'hor'){
const dT = +document.getElementById('p14-iv1-dt').value;
document.getElementById('p14-iv1-dtL').textContent = (dT >= 0 ? '+' : '') + dT;
A = 0;
dU = 1.5 * NU * R * dT;
Q = dU;
descr = 'Изохорный процесс: $V = \\text{const}$, $A_{газ} = 0$.';
formula = '$Q = \\Delta U = \\tfrac{3}{2}\\nu R \\Delta T = '+dU.toFixed(0)+'$ Дж';
} else if(m === 'bar'){
const dT = +document.getElementById('p14-iv1-dt').value;
document.getElementById('p14-iv1-dtL').textContent = (dT >= 0 ? '+' : '') + dT;
dU = 1.5 * NU * R * dT;
A = NU * R * dT; // p ΔV = νR ΔT
Q = dU + A;
descr = 'Изобарный процесс: $p = \\text{const}$. Часть теплоты — на $\\Delta U$, часть — на работу.';
formula = '$Q = \\Delta U + p\\Delta V = '+dU.toFixed(0)+' + '+A.toFixed(0)+' = '+Q.toFixed(0)+'$ Дж';
} else {
const av = +document.getElementById('p14-iv1-a').value;
document.getElementById('p14-iv1-aL').textContent = (av >= 0 ? '+' : '') + av;
Q = 0;
A = av;
dU = -A;
descr = 'Адиабатный процесс: $Q = 0$, $\\Delta U = -A_{газ}$.';
formula = '$\\Delta U = -A_{газ} = '+dU.toFixed(0)+'$ Дж'+(A > 0 ? ' (расширение → охлаждение)' : (A < 0 ? ' (сжатие → нагрев)' : ''));
}
drawBars(Q, dU, A, m);
info.innerHTML = '<div style="margin-bottom:6px">'+descr+'</div>'
+ '<div style="margin-bottom:6px">'+formula+'</div>'
+ '<div><b>Проверка $Q = \\Delta U + A_{газ}$:</b> '+Q.toFixed(0)+' = '+dU.toFixed(0)+' + '+A.toFixed(0)+' ✓</div>';
renderMath(info);
seen.add(m);
if(!_done && seen.size === 4){ _done = true; addXp(10,'p14-iv1'); bumpProgress('p14', 15); }
}
document.querySelectorAll('input[name="p14-iv1-proc"]').forEach(r => r.addEventListener('change', () => { buildSliders(); render(); }));
buildSliders();
render();
})();
/* IV2 — Калькулятор первого закона */
(function(){
const tabs = document.getElementById('p14-iv2-tabs');
const inpsBox = document.getElementById('p14-iv2-inputs');
const out = document.getElementById('p14-iv2-out');
const fb = document.getElementById('p14-iv2-fb');
const used = new Set(); let _done = false;
let mode = 'dU';
function field(id, label, val){
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
}
function build(){
let h = '';
if(mode === 'dU'){
h += field('p14-iv2-Q','$Q$, Дж','500');
h += field('p14-iv2-A','$A_{газ}$, Дж','300');
} else if(mode === 'Q'){
h += field('p14-iv2-dU','$\\Delta U$, Дж','400');
h += field('p14-iv2-A','$A_{газ}$, Дж','300');
} else {
h += field('p14-iv2-Q','$Q$, Дж','500');
h += field('p14-iv2-dU','$\\Delta U$, Дж','200');
}
inpsBox.innerHTML = h;
renderMath(inpsBox);
out.innerHTML = '';
fb.style.display = 'none';
}
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
function calc(){
let res = '', val = 0;
if(mode === 'dU'){
const Q = num('p14-iv2-Q'), A = num('p14-iv2-A');
if(![Q,A].every(isFinite)){ feedback(fb,false,'&#10007; Введи оба числа.'); return; }
val = Q - A;
res = '$\\Delta U = Q - A_{газ} = '+Q+' - ('+A+') = '+val.toFixed(0)+'$ Дж';
} else if(mode === 'Q'){
const dU = num('p14-iv2-dU'), A = num('p14-iv2-A');
if(![dU,A].every(isFinite)){ feedback(fb,false,'&#10007; Введи оба числа.'); return; }
val = dU + A;
res = '$Q = \\Delta U + A_{газ} = '+dU+' + ('+A+') = '+val.toFixed(0)+'$ Дж';
} else {
const Q = num('p14-iv2-Q'), dU = num('p14-iv2-dU');
if(![Q,dU].every(isFinite)){ feedback(fb,false,'&#10007; Введи оба числа.'); return; }
val = Q - dU;
res = '$A_{газ} = Q - \\Delta U = '+Q+' - ('+dU+') = '+val.toFixed(0)+'$ Дж';
}
const labels = { dU:'\\Delta U', Q:'Q', A:'A_{газ}' };
out.innerHTML = '<div style="margin-bottom:8px">'+res+'</div>'
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2)">$'+labels[mode]+' = '+val.toFixed(0)+'$ Дж</span></div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
used.add(mode);
if(!_done && used.size === 3){ _done = true; addXp(10,'p14-iv2'); bumpProgress('p14', 15); }
}
tabs.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
mode = b.dataset.find;
tabs.querySelectorAll('button').forEach(x => { x.className = 'btn'; });
b.className = 'btn primary';
build();
});
});
document.getElementById('p14-iv2-go').addEventListener('click', calc);
build();
})();
/* IV3 — Что неизменно? */
(function(){
const Q = [
{ q:'Изотермический процесс — что постоянно?', ans:'T' },
{ q:'Изохорный процесс — что постоянно?', ans:'V' },
{ q:'Изобарный процесс — что постоянно?', ans:'p' },
{ q:'Адиабатный процесс — какая величина равна нулю?', ans:'Q' },
{ q:'Вода кипит при $T = 100°$C — какой параметр постоянен?', ans:'T' },
{ q:'Газ в герметичном баллоне греют. Что неизменно?', ans:'V' },
];
let i = 0, score = 0; let _done = false;
const box = document.getElementById('p14-iv3');
function show(){
const qEl = document.getElementById('p14-iv3-q');
const fb = document.getElementById('p14-iv3-fb');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
renderMath(qEl);
if(!_done){
_done = true;
if(score === Q.length){ addXp(15,'p14-iv3'); bumpProgress('p14', 25); }
else if(score >= 4){ addXp(8,'p14-iv3'); bumpProgress('p14', 15); }
}
return;
}
document.getElementById('p14-iv3-i').textContent = (i+1);
document.getElementById('p14-iv3-s').textContent = score;
qEl.innerHTML = Q[i].q;
renderMath(qEl);
fb.style.display = 'none';
}
box.querySelectorAll('button[data-ans]').forEach(b => {
b.addEventListener('click', () => {
if(i >= Q.length) return;
const fb = document.getElementById('p14-iv3-fb');
const ans = b.dataset.ans;
if(ans === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else {
const labels = {T:'$T = \\text{const}$', V:'$V = \\text{const}$', p:'$p = \\text{const}$', Q:'$Q = 0$'};
feedback(fb, false, '&#10007; Неверно. Правильно: '+labels[Q[i].ans]+'. Дальше ▶');
}
document.getElementById('p14-iv3-s').textContent = score;
i++;
setTimeout(show, 1500);
});
});
show();
})();
/* IV4 — Тренажёр первого закона */
(function(){
const Q = [
{ q:'Газ получил $Q = 500$ Дж тепла и совершил работу $A_{газ} = 300$ Дж. Найди $\\Delta U$ в Дж.', ans:200, tol:5, hint:'$\\Delta U = Q - A = 500 - 300 = 200$' },
{ q:'Изохорно нагрели 1 моль газа на $\\Delta T = 50$ К. $\\Delta U$ в Дж?', ans:622, tol:30, hint:'$\\Delta U = \\tfrac{3}{2}\\nu R\\Delta T = 1{,}5 \\cdot 1 \\cdot 8{,}3 \\cdot 50 \\approx 622$' },
{ q:'Изотермически 1 моль газа при $T = 300$ К расширили вдвое ($V_2 = 2V_1$). $Q$ в Дж?', ans:1726, tol:50, hint:'$Q = A = \\nu RT\\ln 2 = 8{,}3 \\cdot 300 \\cdot 0{,}693 \\approx 1726$' },
{ q:'В адиабатном процессе газ совершил $A_{газ} = 400$ Дж. $\\Delta U$ в Дж (со знаком)?', ans:-400, tol:5, hint:'$Q = 0 \\Rightarrow \\Delta U = -A = -400$' },
{ q:'Газ изобарно расширился, при этом $\\Delta U = 600$ Дж, $A_{газ} = 400$ Дж. $Q$ в Дж?', ans:1000, tol:20, hint:'$Q = \\Delta U + A = 600 + 400 = 1000$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p14-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p14-iv4'); bumpProgress('p14', 25); }
else if(score >= 3){ addXp(8, 'p14-iv4'); bumpProgress('p14', 15); }
return;
}
document.getElementById('p14-iv4-i').textContent = (i+1);
document.getElementById('p14-iv4-s').textContent = score;
document.getElementById('p14-iv4-q').innerHTML = Q[i].q;
document.getElementById('p14-iv4-ans').value = '';
renderMath(document.getElementById('p14-iv4-q'));
document.getElementById('p14-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p14-iv4-fb');
const raw = document.getElementById('p14-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
document.getElementById('p14-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p14-iv4-go').addEventListener('click', go);
document.getElementById('p14-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p14-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p14');
}
function build_p15(){
const box = document.getElementById('p15-body');
let html = '';
/* THEORY 1 — Устройство и принцип теплового двигателя */
html += makeCard('theory', "Устройство и принцип теплового двигателя", "§15", `
<p><b>Тепловой двигатель</b> — устройство, преобразующее часть теплоты в механическую работу.</p>
<p>Три обязательных элемента:</p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Нагреватель</b> — источник теплоты, температура $T_1$.</li>
<li><b>Рабочее тело</b> — газ или пар; циклически расширяется и сжимается.</li>
<li><b>Холодильник</b> — приёмник избыточной теплоты, температура $T_2 < T_1$.</li>
</ul>
<p>За один полный цикл рабочее тело:</p>
<ol style="margin:6px 0 8px 22px;line-height:1.75">
<li>получает $Q_1$ от нагревателя;</li>
<li>совершает полезную работу $A$;</li>
<li>отдаёт $Q_2$ холодильнику.</li>
</ol>
<p>По закону сохранения энергии $Q_1 = A + Q_2$, откуда:</p>
<p style="text-align:center;margin:10px 0">$$A = Q_1 - Q_2$$</p>
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Без холодильника двигатель работать не может</b> — без отвода $Q_2$ цикл не замкнётся.</p>
`);
/* THEORY 2 — КПД и цикл Карно */
html += makeCard('rule', "КПД и цикл Карно", "§15", `
<p><b>Коэффициент полезного действия</b> (КПД) теплового двигателя — отношение совершённой работы к полученной теплоте:</p>
<p style="text-align:center;margin:10px 0">$$\\eta = \\dfrac{A}{Q_1} = \\dfrac{Q_1 - Q_2}{Q_1} = 1 - \\dfrac{Q_2}{Q_1}$$</p>
<p>Всегда $\\eta < 1$ — часть теплоты неизбежно отдаётся холодильнику.</p>
<p style="margin-top:10px"><b>Цикл Карно</b> — идеальный обратимый цикл с максимальным КПД. Состоит из 4 этапов:</p>
<ol style="margin:6px 0 8px 22px;line-height:1.75">
<li><b>Изотермическое расширение</b> при $T_1$ (рабочее тело получает $Q_1$).</li>
<li><b>Адиабатное расширение</b> (охлаждение от $T_1$ до $T_2$).</li>
<li><b>Изотермическое сжатие</b> при $T_2$ (отдаёт $Q_2$).</li>
<li><b>Адиабатное сжатие</b> (нагрев от $T_2$ до $T_1$).</li>
</ol>
<p>КПД цикла Карно зависит <b>только от температур</b>:</p>
<p style="text-align:center;margin:10px 0">$$\\eta_{Карно} = \\dfrac{T_1 - T_2}{T_1} = 1 - \\dfrac{T_2}{T_1}$$</p>
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Полезно:</b> увеличить $T_1$ выгоднее, чем равнозначно уменьшить $T_2$ — растёт знаменатель работы и абсолютная разность.</p>
`);
/* THEORY 3 — Реальные двигатели и экология */
html += makeCard('example', "Реальные двигатели и экология", "§15", `
<p><b>Теорема Карно:</b> $\\eta_{Карно}$ — <b>максимально возможный</b> КПД для теплового двигателя между $T_1$ и $T_2$. Любой реальный двигатель имеет $\\eta \\le \\eta_{Карно}$.</p>
<p><b>Реальные значения КПД:</b></p>
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.92rem">
<thead><tr style="background:var(--sec-acc-soft)"><th style="padding:8px;border:1px solid var(--border);text-align:left">Двигатель</th><th style="padding:8px;border:1px solid var(--border);text-align:center">КПД</th></tr></thead>
<tbody>
<tr><td style="padding:8px;border:1px solid var(--border)">ДВС бензиновый</td><td style="padding:8px;border:1px solid var(--border);text-align:center">2535%</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Дизельный</td><td style="padding:8px;border:1px solid var(--border);text-align:center">3545%</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Паровая турбина (АЭС, ТЭС)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">3040%</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Газовая турбина (комб. цикл)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">до 60%</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border)">Карно при $T_1=800$ К, $T_2=300$ К</td><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>62,5%</b></td></tr>
</tbody>
</table>
<p><b>Второй закон термодинамики</b> (формулировка Кельвина): невозможен процесс, единственным результатом которого было бы превращение теплоты в работу. Невозможен «вечный двигатель 2-го рода» с $\\eta = 100\\%$.</p>
<p><b>Экологические проблемы:</b></p>
<ul style="margin:6px 0 8px 22px;line-height:1.75">
<li>тепловое загрязнение (нагрев водоёмов, атмосферы);</li>
<li>$CO_2$ — парниковый эффект;</li>
<li>$NO_x$, $SO_x$ — кислотные дожди;</li>
<li>сажа, тяжёлые металлы — загрязнение воздуха.</li>
</ul>
<p>Развитие электротранспорта, ВИЭ и АЭС — путь к снижению этих эффектов.</p>
`);
/* INTERACTIVE 1 — Цикл Карно на PV-диаграмме */
html += `<div class="wg" id="p15-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Цикл Карно на $pV$-диаграмме</div></div>
<div class="wg-help">Двигай ползунки $T_1, T_2$. Жёлтым — изотермы, фиолетовым — адиабаты. Заштрихованная область = работа за цикл.</div>
<div class="sliders" id="p15-iv1-sliders"></div>
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
<svg id="p15-iv1-svg" viewBox="0 0 480 360" width="100%" style="height:auto"></svg>
</div>
<div id="p15-iv1-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.85"></div>
</div>`;
/* INTERACTIVE 2 — Калькулятор КПД */
html += `<div class="wg" id="p15-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор КПД</div></div>
<div class="wg-help">Выбери способ — введи данные — получи КПД в процентах.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-bottom:10px" id="p15-iv2-tabs">
<button class="btn primary" data-mode="QQ">Через $Q_1, Q_2$</button>
<button class="btn" data-mode="AQ">Через $A, Q_1$</button>
<button class="btn" data-mode="TT">Карно: $T_1, T_2$</button>
</div>
<div id="p15-iv2-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center"><button class="btn primary" id="p15-iv2-go">Найти КПД</button></div>
<div id="p15-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
<div class="feedback" id="p15-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — Возможен ли двигатель? */
html += `<div class="wg" id="p15-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возможен ли двигатель с таким КПД?</div></div>
<div class="wg-help">6 ситуаций. Сравни заявленный КПД с теоремой Карно.</div>
<div class="score-display"><span>Задание <b id="p15-iv3-i">1</b> / 6</span><span>Очки: <b id="p15-iv3-s">0</b> / 6</span></div>
<div id="p15-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:60px"></div>
<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
<button class="btn primary" data-ans="ok">Возможен</button>
<button class="btn primary" data-ans="no">Нарушает 2-й закон</button>
</div>
<div class="feedback" id="p15-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — Тренажёр КПД */
html += `<div class="wg" id="p15-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр КПД</div></div>
<div class="wg-help">5 задач. Допуск $\\pm 5\\%$. Ответ — число (КПД в %, работа в кДж и т.п.).</div>
<div class="score-display"><span>Задача <b id="p15-iv4-i">1</b> / 5</span><span>Очки: <b id="p15-iv4-s">0</b> / 5</span></div>
<div id="p15-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p15-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
<button class="btn primary" id="p15-iv4-go">Проверить</button>
<button class="btn" id="p15-iv4-start">Заново</button>
</div>
<div class="feedback" id="p15-iv4-fb"></div>
</div>`;
html += secNav('p14', 'final2');
html += readButton('p15');
box.innerHTML = html;
renderMath(box);
/* IV1 — Цикл Карно на PV-диаграмме */
(function(){
const R = 8.314;
const NU = 1;
const GAMMA = 5/3;
const slBox = document.getElementById('p15-iv1-sliders');
const svg = document.getElementById('p15-iv1-svg');
const info = document.getElementById('p15-iv1-info');
const seen = new Set(); let _done = false;
slBox.innerHTML = ''
+ '<label>$T_1$ (нагреватель): <b id="p15-iv1-t1L">600</b> К <input type="range" id="p15-iv1-t1" min="400" max="800" value="600" step="10"></label>'
+ '<label>$T_2$ (холодильник): <b id="p15-iv1-t2L">300</b> К <input type="range" id="p15-iv1-t2" min="100" max="350" value="300" step="10"></label>';
renderMath(slBox);
function render(){
const T1 = +document.getElementById('p15-iv1-t1').value;
let T2 = +document.getElementById('p15-iv1-t2').value;
if(T2 >= T1) T2 = T1 - 20;
document.getElementById('p15-iv1-t1L').textContent = T1;
document.getElementById('p15-iv1-t2L').textContent = T2;
// Точки цикла (PV-плоскость): V в л, p в "виртуальных атм" (используем RT/V напрямую)
// Внутри: давление p = nu R T / V (Дж/м^3 если V в м^3); для шкалы — отнормируем
const V_A = 1, V_B = 3; // л — изотерма T1
const V_C = 5; // л — конец адиабаты от B
// pV^gamma = const на адиабате BC: p_B * V_B^g = p_C * V_C^g
// Также p_C = nu R T2 / V_C (точка C на изотерме T2)
// Адиабата: T V^(g-1) = const → T1 * V_B^(g-1) = T2 * V_C^(g-1)
// Тогда V_C из физики: V_C = V_B * (T1/T2)^(1/(g-1))
const V_C_calc = V_B * Math.pow(T1/T2, 1/(GAMMA-1));
const V_C_use = V_C_calc;
// Аналогично для DA: T1 * V_A^(g-1) = T2 * V_D^(g-1)
const V_D = V_A * Math.pow(T1/T2, 1/(GAMMA-1));
// Давления (произвольные единицы — RT/V, R=8.314, T в К, V в л → давление в Дж/л = кПа)
function pIso(T, V){ return NU * R * T / V; } // кПа
const p_A = pIso(T1, V_A);
const p_B = pIso(T1, V_B);
const p_C = pIso(T2, V_C_use);
const p_D = pIso(T2, V_D);
// Q1 (получено на AB), Q2 (отдано на CD)
const Q1 = NU * R * T1 * Math.log(V_B / V_A);
const Q2 = NU * R * T2 * Math.log(V_C_use / V_D);
const A = Q1 - Q2;
const eta = 1 - T2 / T1;
// SVG: оси V (1..6 л), p (0..max)
const W = 480, H = 360, pad = 50;
const xmin = 0.5, xmax = 6.2;
// верхняя граница p — для красивого масштаба
const pmax = Math.max(p_A, p_B, p_C, p_D) * 1.1;
const pmin = 0;
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (pmax - pmin);
const toX = v => pad + (v - xmin) * ux;
const toY = p => H - pad - (p - pmin) * uy;
let g = '';
// Сетка
g += '<rect x="'+pad+'" y="'+pad+'" width="'+(W-2*pad)+'" height="'+(H-2*pad)+'" fill="#fafbfc" stroke="#e5e7eb"/>';
g += '<g stroke="#e5e7eb" stroke-width="1">';
for(let v=1; v<=6; v++){
g += '<line x1="'+toX(v)+'" y1="'+pad+'" x2="'+toX(v)+'" y2="'+(H-pad)+'"/>';
}
g += '</g>';
// Оси
g += '<line x1="'+pad+'" y1="'+(H-pad)+'" x2="'+(W-pad)+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+pad+'" y1="'+pad+'" x2="'+pad+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(W-pad+4)+'" y="'+(H-pad+4)+'" font-size="12" fill="#0f172a">V, л</text>';
g += '<text x="'+(pad-10)+'" y="'+(pad-6)+'" font-size="12" fill="#0f172a">p, кПа</text>';
// Метки оси V
for(let v=1; v<=6; v++){
g += '<text x="'+(toX(v)-3)+'" y="'+(H-pad+14)+'" font-size="10" fill="#64748b">'+v+'</text>';
}
// Метки оси p (4 деления)
for(let i=1;i<=4;i++){
const pv = pmax*i/4;
g += '<text x="'+(pad-30)+'" y="'+(toY(pv)+3)+'" font-size="10" fill="#64748b">'+pv.toFixed(0)+'</text>';
g += '<line x1="'+pad+'" y1="'+toY(pv)+'" x2="'+(W-pad)+'" y2="'+toY(pv)+'" stroke="#e5e7eb" stroke-dasharray="3 3"/>';
}
// Построим путь по циклу (для заливки) и отдельно линии цвета
function pathIso(T, v1, v2, N){
N = N || 60;
let d = '';
for(let i=0;i<=N;i++){
const v = v1 + (v2-v1)*i/N;
const p = pIso(T, v);
d += (i===0?'M':' L') + toX(v).toFixed(2) + ',' + toY(p).toFixed(2);
}
return d;
}
function pathAdi(pStart, vStart, vEnd, N){
N = N || 60;
const C = pStart * Math.pow(vStart, GAMMA);
let d = '';
for(let i=0;i<=N;i++){
const v = vStart + (vEnd-vStart)*i/N;
const p = C / Math.pow(v, GAMMA);
d += (i===0?'M':' L') + toX(v).toFixed(2) + ',' + toY(p).toFixed(2);
}
return d;
}
// Полный замкнутый путь: A→B (изо T1) → C (адиа) → D (изо T2) → A (адиа)
const dAB = pathIso(T1, V_A, V_B);
const dBC = pathAdi(p_B, V_B, V_C_use);
const dCD = pathIso(T2, V_C_use, V_D);
const dDA = pathAdi(p_D, V_D, V_A);
// Заливка площади цикла
const fullPath = dAB + ' ' + dBC.replace(/^M/, 'L') + ' ' + dCD.replace(/^M/, 'L') + ' ' + dDA.replace(/^M/, 'L') + ' Z';
g += '<path d="'+fullPath+'" fill="#fbbf24" fill-opacity="0.20" stroke="none"/>';
// Линии
g += '<path d="'+dAB+'" stroke="#ea580c" stroke-width="2.5" fill="none"/>'; // изо T1
g += '<path d="'+dBC+'" stroke="#7c3aed" stroke-width="2.5" fill="none"/>'; // адиа
g += '<path d="'+dCD+'" stroke="#2563eb" stroke-width="2.5" fill="none"/>'; // изо T2
g += '<path d="'+dDA+'" stroke="#7c3aed" stroke-width="2.5" fill="none" stroke-dasharray="6 3"/>'; // адиа сжатия
// Точки A,B,C,D
function pt(x,y,lab,col){
return '<circle cx="'+x+'" cy="'+y+'" r="4.5" fill="'+col+'" stroke="#fff" stroke-width="2"/>'
+ '<text x="'+(x+7)+'" y="'+(y-7)+'" font-size="12" font-weight="700" fill="'+col+'">'+lab+'</text>';
}
g += pt(toX(V_A), toY(p_A), 'A', '#ea580c');
g += pt(toX(V_B), toY(p_B), 'B', '#ea580c');
g += pt(toX(V_C_use), toY(p_C), 'C', '#2563eb');
g += pt(toX(V_D), toY(p_D), 'D', '#2563eb');
// Легенда
g += '<g font-size="11" font-family="Inter,sans-serif">';
g += '<rect x="'+(W-pad-150)+'" y="'+(pad+6)+'" width="146" height="58" rx="6" fill="#ffffff" stroke="#e5e7eb"/>';
g += '<line x1="'+(W-pad-140)+'" y1="'+(pad+20)+'" x2="'+(W-pad-118)+'" y2="'+(pad+20)+'" stroke="#ea580c" stroke-width="2.5"/>';
g += '<text x="'+(W-pad-112)+'" y="'+(pad+24)+'" fill="#0f172a">изотерма T₁</text>';
g += '<line x1="'+(W-pad-140)+'" y1="'+(pad+38)+'" x2="'+(W-pad-118)+'" y2="'+(pad+38)+'" stroke="#2563eb" stroke-width="2.5"/>';
g += '<text x="'+(W-pad-112)+'" y="'+(pad+42)+'" fill="#0f172a">изотерма T₂</text>';
g += '<line x1="'+(W-pad-140)+'" y1="'+(pad+56)+'" x2="'+(W-pad-118)+'" y2="'+(pad+56)+'" stroke="#7c3aed" stroke-width="2.5"/>';
g += '<text x="'+(W-pad-112)+'" y="'+(pad+60)+'" fill="#0f172a">адиабаты</text>';
g += '</g>';
svg.innerHTML = g;
// Инфо
info.innerHTML = ''
+ '<div><b>$T_1 = '+T1+'$ К, $T_2 = '+T2+'$ К</b></div>'
+ '<div>$Q_1 = \\nu RT_1\\ln(V_B/V_A) \\approx '+Q1.toFixed(1)+'$ Дж <span style="color:#ea580c">(получено)</span></div>'
+ '<div>$Q_2 = \\nu RT_2\\ln(V_C/V_D) \\approx '+Q2.toFixed(1)+'$ Дж <span style="color:#2563eb">(отдано)</span></div>'
+ '<div>$A = Q_1 - Q_2 \\approx '+A.toFixed(1)+'$ Дж <span style="color:#d97706">(работа за цикл)</span></div>'
+ '<div style="margin-top:6px;font-weight:700">$\\eta_{Карно} = 1 - T_2/T_1 = '+(eta*100).toFixed(1)+'\\%$</div>';
renderMath(info);
seen.add(T1+'_'+T2);
if(!_done && seen.size >= 4){ _done = true; addXp(10,'p15-iv1'); bumpProgress('p15', 15); }
}
document.getElementById('p15-iv1-t1').addEventListener('input', render);
document.getElementById('p15-iv1-t2').addEventListener('input', render);
render();
})();
/* IV2 — Калькулятор КПД */
(function(){
const tabs = document.getElementById('p15-iv2-tabs');
const inpsBox = document.getElementById('p15-iv2-inputs');
const out = document.getElementById('p15-iv2-out');
const fb = document.getElementById('p15-iv2-fb');
const used = new Set(); let _done = false;
let mode = 'QQ';
function field(id, label, val){
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
}
function build(){
let h = '';
if(mode === 'QQ'){
h += field('p15-iv2-Q1','$Q_1$, Дж','1000');
h += field('p15-iv2-Q2','$Q_2$, Дж','700');
} else if(mode === 'AQ'){
h += field('p15-iv2-A','$A$, Дж','300');
h += field('p15-iv2-Q1','$Q_1$, Дж','1000');
} else {
h += field('p15-iv2-T1','$T_1$, К','600');
h += field('p15-iv2-T2','$T_2$, К','300');
}
inpsBox.innerHTML = h;
renderMath(inpsBox);
out.innerHTML = '';
fb.style.display = 'none';
}
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
function calc(){
let res = '', eta = NaN;
if(mode === 'QQ'){
const Q1 = num('p15-iv2-Q1'), Q2 = num('p15-iv2-Q2');
if(![Q1,Q2].every(isFinite) || Q1 <= 0){ feedback(fb,false,'&#10007; Введи корректные $Q_1 > 0$ и $Q_2$.'); return; }
eta = (Q1 - Q2) / Q1;
res = '$\\eta = \\dfrac{Q_1 - Q_2}{Q_1} = \\dfrac{'+Q1+' - '+Q2+'}{'+Q1+'} = '+eta.toFixed(4)+'$';
} else if(mode === 'AQ'){
const A = num('p15-iv2-A'), Q1 = num('p15-iv2-Q1');
if(![A,Q1].every(isFinite) || Q1 <= 0){ feedback(fb,false,'&#10007; Введи $A$ и $Q_1 > 0$.'); return; }
eta = A / Q1;
res = '$\\eta = \\dfrac{A}{Q_1} = \\dfrac{'+A+'}{'+Q1+'} = '+eta.toFixed(4)+'$';
} else {
const T1 = num('p15-iv2-T1'), T2 = num('p15-iv2-T2');
if(![T1,T2].every(isFinite) || T1 <= 0){ feedback(fb,false,'&#10007; Введи $T_1 > 0$ и $T_2$.'); return; }
if(T2 >= T1){ feedback(fb,false,'&#10007; Должно быть $T_2 < T_1$.'); return; }
eta = 1 - T2/T1;
res = '$\\eta_{Карно} = 1 - \\dfrac{T_2}{T_1} = 1 - \\dfrac{'+T2+'}{'+T1+'} = '+eta.toFixed(4)+'$';
}
out.innerHTML = '<div style="margin-bottom:8px">'+res+'</div>'
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2);font-size:1.05rem">$\\eta \\approx '+(eta*100).toFixed(2)+'\\%$</span></div>';
renderMath(out);
feedback(fb, true, '&#10003; Вычислено.');
used.add(mode);
if(!_done && used.size === 3){ _done = true; addXp(10,'p15-iv2'); bumpProgress('p15', 15); }
}
tabs.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
mode = b.dataset.mode;
tabs.querySelectorAll('button').forEach(x => { x.className = 'btn'; });
b.className = 'btn primary';
build();
});
});
document.getElementById('p15-iv2-go').addEventListener('click', calc);
build();
})();
/* IV3 — Возможен ли двигатель? */
(function(){
const Q = [
{ q:'Бензиновый ДВС с заявленным $\\eta = 30\\%$.', ans:'ok', why:'30% — типичный КПД ДВС.' },
{ q:'Двигатель с $\\eta = 99\\%$ при $T_1 = 400$ К, $T_2 = 300$ К. Карно даёт $25\\%$.', ans:'no', why:'$99\\% > \\eta_{Карно} = 25\\%$ — нарушает теорему Карно.' },
{ q:'Двигатель с $\\eta = 60\\%$ при $T_1 = 1000$ К, $T_2 = 400$ К. Карно даёт ровно $60\\%$.', ans:'ok', why:'$\\eta = \\eta_{Карно}$ — идеальный случай (предел).' },
{ q:'Двигатель с $\\eta = 100\\%$ (полностью теплота → работа).', ans:'no', why:'Запрещает 2-й закон термодинамики (вечный двигатель 2-го рода).' },
{ q:'Паровая турбина с $\\eta = 35\\%$ при $T_1 = 600$ К, $T_2 = 320$ К. Карно даёт $\\approx 47\\%$.', ans:'ok', why:'$35\\% < 47\\%$ — реальное значение, всё в порядке.' },
{ q:'Двигатель с $\\eta = 50\\%$ при $T_1 = 500$ К, $T_2 = 300$ К. Карно даёт $40\\%$.', ans:'no', why:'$50\\% > \\eta_{Карно} = 40\\%$ — превышает предел Карно.' },
];
let i = 0, score = 0; let _done = false;
const box = document.getElementById('p15-iv3');
function show(){
const qEl = document.getElementById('p15-iv3-q');
const fb = document.getElementById('p15-iv3-fb');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
renderMath(qEl);
if(!_done){
_done = true;
if(score === Q.length){ addXp(15,'p15-iv3'); bumpProgress('p15', 25); }
else if(score >= 4){ addXp(8,'p15-iv3'); bumpProgress('p15', 15); }
}
return;
}
document.getElementById('p15-iv3-i').textContent = (i+1);
document.getElementById('p15-iv3-s').textContent = score;
qEl.innerHTML = Q[i].q;
renderMath(qEl);
fb.style.display = 'none';
}
box.querySelectorAll('button[data-ans]').forEach(b => {
b.addEventListener('click', () => {
if(i >= Q.length) return;
const fb = document.getElementById('p15-iv3-fb');
if(b.dataset.ans === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].why+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. '+Q[i].why+' Дальше ▶');
document.getElementById('p15-iv3-s').textContent = score;
i++;
setTimeout(show, 1800);
});
});
show();
})();
/* IV4 — Тренажёр КПД */
(function(){
const Q = [
{ q:'Двигатель получил $Q_1 = 1000$ Дж и отдал $Q_2 = 700$ Дж. КПД в %?', ans:30, tol:2, hint:'$\\eta = (1000-700)/1000 = 30\\%$' },
{ q:'Двигатель Карно работает между $T_1 = 600$ К и $T_2 = 300$ К. КПД в %?', ans:50, tol:2, hint:'$\\eta = 1 - 300/600 = 50\\%$' },
{ q:'$T_1 = 500$ К, $T_2 = 300$ К. Что выгоднее: нагреть $T_1$ на 100 К (введи $1$) или охладить $T_2$ на 100 К (введи $2$)?', ans:2, tol:0.4, hint:'$T_1 \\to 600$: $\\eta = 0{,}5$. $T_2 \\to 200$: $\\eta \\approx 0{,}6$. Холодильник выгоднее.' },
{ q:'ДВС бензиновый совершил работу $A = 30$ кДж за цикл при $\\eta = 25\\%$. Сколько $Q_1$ в кДж?', ans:120, tol:6, hint:'$Q_1 = A/\\eta = 30/0{,}25 = 120$ кДж' },
{ q:'Идеальный Карно: $T_1 = 800$ К, $T_2 = 400$ К. КПД в %?', ans:50, tol:2, hint:'$\\eta = 1 - 400/800 = 50\\%$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p15-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15, 'p15-iv4'); bumpProgress('p15', 25); }
else if(score >= 3){ addXp(8, 'p15-iv4'); bumpProgress('p15', 15); }
return;
}
document.getElementById('p15-iv4-i').textContent = (i+1);
document.getElementById('p15-iv4-s').textContent = score;
document.getElementById('p15-iv4-q').innerHTML = Q[i].q;
document.getElementById('p15-iv4-ans').value = '';
renderMath(document.getElementById('p15-iv4-q'));
document.getElementById('p15-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p15-iv4-fb');
const raw = document.getElementById('p15-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
document.getElementById('p15-iv4-s').textContent = score;
i++;
setTimeout(show, 1800);
}
document.getElementById('p15-iv4-go').addEventListener('click', go);
document.getElementById('p15-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p15-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p15');
}
function build_final2(){
const box = document.getElementById('final2-body');
let html = '';
/* Часть А — Шпаргалка главы (5 mini-карточек) */
const SHEET = [
{ t:'§ 11 · Внутренняя энергия', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#0ea5e9" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="3"/></svg>', body:'$U = \\dfrac{i}{2}\\nu RT$. Зависит только от $T$. $i = 3$ (одноат.), $i = 5$ (двухат.).' },
{ t:'§ 12 · Работа', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2" style="width:18px;height:18px"><path d="M3 18l6-6 4 4 8-10"/></svg>', body:'Изобарно $A = p\\Delta V$. Изотермически $A = \\nu RT\\ln(V_2/V_1)$. Изохорно $A = 0$.' },
{ t:'§ 13 · Теплота', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" style="width:18px;height:18px"><path d="M12 3c-3 5-5 8-5 11a5 5 0 0 0 10 0c0-3-2-6-5-11z"/></svg>', body:'$Q = cm\\Delta T$, $Q = \\lambda m$, $Q = rm$, $Q = qm$. Уравнение теплового баланса.' },
{ t:'§ 14 · 1-й закон ТД', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" style="width:18px;height:18px"><path d="M4 12h16"/><path d="M12 4l8 8-8 8"/></svg>', body:'$Q = \\Delta U + A_{газ}$. Изотерм.: $Q = A$. Изохорно: $Q = \\Delta U$. Адиабатно: $\\Delta U = -A$.' },
{ t:'§ 15 · Тепл. двигатели', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><path d="M12 3v9l6 4"/></svg>', body:'$\\eta = (Q_1-Q_2)/Q_1$. Карно: $\\eta = 1 - T_2/T_1$ — максимальный КПД.' },
];
html += `<div class="card">
<div class="card-header">
<span class="card-icon theory">${ICONS.theory}</span>
<span class="card-title">Шпаргалка главы 2 — Термодинамика</span>
<span class="card-num">Итог</span>
</div>
<div class="card-body">
<p>Ключевые формулы и идеи всех 5 параграфов главы — повтори перед битвой с боссами.</p>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-top:10px">
${SHEET.map(s => `<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
${s.icon}
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.88rem">${s.t}</div>
</div>
<div style="font-size:.92rem;line-height:1.55">${s.body}</div>
</div>`).join('')}
</div>
</div>
</div>`;
/* Часть Б — 5 боссов intro */
html += `<div class="card">
<div class="card-header">
<span class="card-icon rule">${ICONS.rule}</span>
<span class="card-title">Боссы главы 2</span>
<span class="card-num">5</span>
</div>
<div class="card-body">
<p>5 интегрированных задач по §§11–15. За каждого побеждённого босса: <b>+10 XP, +18% к прогрессу</b>. Победишь всех — ачивка <b>«Мастер термодинамики»</b> и <b>+50 XP бонус</b>.</p>
<p style="margin-top:6px;font-size:.92rem;color:var(--muted)">Константы: $R = 8{,}3$ Дж/(моль·К), $c_{вода} = 4200$ Дж/(кг·К), $\\lambda_{лёд} = 330$ кДж/кг, $r_{вода} = 2260$ кДж/кг. Допуск $\\pm 5\\%$.</p>
</div>
</div>`;
html += '<div id="ch2-bosses-container"></div>';
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch2-final-summary">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
<div id="ch2-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>
<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">
<div id="ch2-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#d97706,#fbbf24);transition:width .35s"></div>
</div>
<div id="ch2-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid #f59e0b">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:#92400e;font-size:1.05rem;margin-bottom:6px">Мастер термодинамики</div>
<div style="font-size:.92rem;margin-bottom:10px">Глава 2 пройдена! Все 5 боссов повержены. +50 XP бонус.</div>
<a class="btn primary" href="/textbook/physics-10-ch3" style="text-decoration:none">Дальше: Глава 3 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
</div>
</div>`;
html += secNav('p15', null);
box.innerHTML = html;
renderMath(box);
/* Боссы */
const BOSSES = [
{
n:1, color:'#0ea5e9',
title:'Хранитель Внутренней Энергии',
tag:'§ 11',
q:'$\\nu = 1$ моль одноатомного газа при $T = 400$ К. Найди $U$ в кДж (до сотых).',
ans:4.98, tol:0.1,
hint:'$U = \\tfrac{3}{2}\\nu RT = 1{,}5 \\cdot 1 \\cdot 8{,}3 \\cdot 400 = 4980$ Дж $\\approx 4{,}98$ кДж.'
},
{
n:2, color:'#10b981',
title:'Поршневой Гигант',
tag:'§ 12',
q:'Изобарно при $p = 2$ атм газ расширили от $5$ до $15$ л. Работа газа в Дж?',
ans:2000, tol:60,
hint:'$A = p\\Delta V = 2 \\cdot 10^5 \\cdot 10 \\cdot 10^{-3} = 2000$ Дж.'
},
{
n:3, color:'#f59e0b',
title:'Кипящий Левиафан',
tag:'§ 13',
q:'Нагреть $3$ кг воды от $20°$C до $100°$C и испарить. Найди $Q_{общ}$ в МДж (до десятых).',
ans:7.8, tol:0.3,
hint:'$Q_1 = cm\\Delta T = 4200 \\cdot 3 \\cdot 80 \\approx 1{,}01$ МДж. $Q_2 = rm = 2{,}26 \\cdot 3 = 6{,}78$ МДж. Сумма $\\approx 7{,}8$ МДж.'
},
{
n:4, color:'#7c3aed',
title:'Сторож Первого Закона',
tag:'§ 14',
q:'Газ получил $Q = 800$ Дж и совершил работу $A = 500$ Дж. Найди $\\Delta U$ в Дж.',
ans:300, tol:10,
hint:'$\\Delta U = Q - A = 800 - 500 = 300$ Дж.'
},
{
n:5, color:'#dc2626',
title:'Магистр Термодинамики',
tag:'§ 15',
q:'Двигатель Карно: $T_1 = 500$ К, $T_2 = 300$ К, $Q_1 = 1000$ Дж за цикл. Найди работу $A$ в Дж.',
ans:400, tol:15,
hint:'$\\eta = 1 - 300/500 = 0{,}4$. $A = \\eta Q_1 = 0{,}4 \\cdot 1000 = 400$ Дж.'
},
];
const cont = document.getElementById('ch2-bosses-container');
const STATE_KEY = 'physics10_ch2_bosses';
const BOSS_STATE = (function(){
try{ const s = localStorage.getItem(STATE_KEY); if(s){ const p = JSON.parse(s); if(Array.isArray(p) && p.length === BOSSES.length) return p; } }catch(e){}
return BOSSES.map(()=>({defeated:false}));
})();
function saveBosses(){ try{ localStorage.setItem(STATE_KEY, JSON.stringify(BOSS_STATE)); }catch(e){} }
cont.innerHTML = BOSSES.map((b)=>{
return '<div class="boss-card" id="boss2-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
+'<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
+'</div>'
+'<div class="boss-q" id="boss2-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
+'<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
+'<input type="number" id="boss2-'+b.n+'-ans" class="tinp" style="width:130px;text-align:center" step="0.01" placeholder="число">'
+'<button class="btn primary" id="boss2-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
+'<button class="btn" id="boss2-'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="feedback" id="boss2-'+b.n+'-fb"></div>'
+'</div>';
}).join('');
renderMath(cont);
function refreshOverall(){
const won = BOSS_STATE.filter(s => s.defeated).length;
const txt = document.getElementById('ch2-boss-overall');
const fill = document.getElementById('ch2-boss-overall-fill');
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
if(won >= BOSSES.length){
const reward = document.getElementById('ch2-final-reward');
if(reward && reward.style.display === 'none'){
reward.style.display = 'block';
if(!STATE.achievements.has('ch2_done')){
achievement('ch2_done','Мастер термодинамики');
addXp(50, 'ch2-bonus');
bumpProgress('final2', 30);
if(window.confetti){ try{ confetti(); }catch(e){} }
}
}
}
}
BOSSES.forEach((b, idx)=>{
const card = document.getElementById('boss2-'+b.n+'-card');
const goBtn = document.getElementById('boss2-'+b.n+'-go');
const hintBtn = document.getElementById('boss2-'+b.n+'-hint');
const ansInp = document.getElementById('boss2-'+b.n+'-ans');
if(BOSS_STATE[idx].defeated){
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow');
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
ansInp.disabled = true;
}
goBtn.addEventListener('click', ()=>{
if(BOSS_STATE[idx].defeated) return;
const fb = document.getElementById('boss2-'+b.n+'-fb');
const raw = ansInp.value.replace(',', '.');
const val = parseFloat(raw);
if(isNaN(val)){ feedback(fb, false, '&#10007; Введи число.'); return; }
const tol = (typeof b.tol === 'number') ? b.tol : Math.max(0.05 * Math.abs(b.ans), 0.05);
if(Math.abs(val - b.ans) < tol + 0.001){
BOSS_STATE[idx].defeated = true; saveBosses();
feedback(fb, true, '&#10003; Босс '+b.n+' повержен! +10 XP. '+b.hint);
addXp(10, 'boss-ch2-'+b.n);
bumpProgress('final2', 18);
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
ansInp.disabled = true;
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow','pulse');
setTimeout(()=>card.classList.remove('pulse'), 900);
refreshOverall();
} else {
feedback(fb, false, '&#10007; Промах. Попробуй ещё. Подсказка доступна.');
}
});
hintBtn.addEventListener('click', ()=>{
const fb = document.getElementById('boss2-'+b.n+'-fb');
fb.className = 'feedback ok';
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
fb.style.display = 'block';
fb.style.background = 'var(--warn-bg)';
fb.style.color = '#92400e';
fb.style.borderLeftColor = 'var(--warn)';
renderMath(fb);
});
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
});
refreshOverall();
}
/* ===== Search ===== */
const SEARCH_INDEX = (function(){
const arr=[];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
return arr;
})();
function initSearch(){
const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');
if(!modal||!inp||!out) return;
let cur=0,rows=[];
function score(q,it){ const t=(it.title+' '+it.desc).toLowerCase(); if(t.includes(q)) return 100+(it.title.toLowerCase().startsWith(q)?50:0); let s=0; q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;}); return s; }
function rank(q){ q=q.trim().toLowerCase(); if(!q) return SEARCH_INDEX.slice(0,12); return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it); }
function render(){ cur=0; if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;} out.innerHTML=rows.map((r,i)=>'<button class="search-row'+(i===0?' active':'')+'" data-i="'+i+'"><div class="sr-kind">'+r.kind+'</div><div class="sr-title">'+r.title+'</div>'+(r.desc?'<div class="sr-desc">'+(r.desc.length>90?r.desc.slice(0,90)+'…':r.desc)+'</div>':'')+'</button>').join(''); out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();})); }
function pick(){ const r=rows[cur]; if(!r) return; close(); goTo(r.sec); }
function move(d){ const items=out.querySelectorAll('.search-row'); if(!items.length) return; items[cur]&&items[cur].classList.remove('active'); cur=(cur+d+items.length)%items.length; items[cur].classList.add('active'); items[cur].scrollIntoView({block:'nearest'}); }
function open(){ modal.classList.add('show'); inp.value=''; rows=rank(''); render(); setTimeout(()=>inp.focus(),50); }
function close(){ modal.classList.remove('show'); }
btn&&btn.addEventListener('click',open);
modal.addEventListener('click',e=>{if(e.target===modal)close();});
inp.addEventListener('input',()=>{rows=rank(inp.value);render();});
inp.addEventListener('keydown',e=>{ if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();} });
document.addEventListener('keydown',e=>{ if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); if(modal.classList.contains('show')) close(); else open(); } });
}
function initSidebarToggle(){
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
if(!side||!btn) return;
function open(){ side.classList.add('open'); back.classList.add('show'); }
function close(){ side.classList.remove('open'); back.classList.remove('show'); }
btn.addEventListener('click',()=>{ if(side.classList.contains('open')) close(); else open(); });
back.addEventListener('click',close);
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
}
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);
setTimeout(()=>achievement('start'), 600);
if(window.LS&&window.LS.xp){
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
}
}
document.addEventListener('DOMContentLoaded', init);
/* === PHYS10 POLISH JS === */
(function(){
function bumpScore(el){
if(!el) return;
el.classList.remove('bump');
void el.offsetWidth;
el.classList.add('bump');
setTimeout(function(){ try{ el.classList.remove('bump'); }catch(e){} }, 270);
}
window.__phys10BumpScore = bumpScore;
function observeScores(root){
root = root || document;
var nodes = root.querySelectorAll('.score-display b');
nodes.forEach(function(b){
if(b.__scoreObs) return;
b.__scoreObs = true;
var last = b.textContent;
try{
var mo = new MutationObserver(function(){
var nv = b.textContent;
if(nv !== last){ last = nv; bumpScore(b); }
});
mo.observe(b, {childList:true, characterData:true, subtree:true});
}catch(e){}
});
}
function rescanScores(){ try{ observeScores(document); }catch(e){} }
if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', rescanScores);
else rescanScores();
try{
var rootObs = new MutationObserver(function(muts){
var need = false;
for(var i=0;i<muts.length && !need;i++){
var m = muts[i];
for(var j=0;j<m.addedNodes.length;j++){
var n = m.addedNodes[j];
if(n.nodeType===1){
if(n.classList && n.classList.contains('score-display')) { need = true; break; }
if(n.querySelector && n.querySelector('.score-display b')) { need = true; break; }
}
}
}
if(need) rescanScores();
});
rootObs.observe(document.body, {childList:true, subtree:true});
}catch(e){}
function refreshDoneMarks(){
try{
if(typeof STATE === 'undefined' || !STATE.progress) return;
document.querySelectorAll('.psel-card').forEach(function(c){
var id = c.dataset.id || c.dataset.progCard;
if(!id) return;
var pct = +STATE.progress[id] || 0;
if(!c.querySelector('.psel-done')){
var s = document.createElement('span');
s.className = 'psel-done';
s.setAttribute('title','Прочитано');
s.innerHTML = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
c.appendChild(s);
}
c.classList.toggle('done', pct >= 50);
});
}catch(e){}
}
try{
if(typeof window.refreshProgressUI === 'function'){
var _origRP = window.refreshProgressUI;
window.refreshProgressUI = function(){ var r = _origRP.apply(this, arguments); setTimeout(refreshDoneMarks, 0); return r; };
}
}catch(e){}
setTimeout(refreshDoneMarks, 600);
setTimeout(refreshDoneMarks, 1800);
window.addEventListener('focus', function(){ setTimeout(refreshDoneMarks, 200); });
document.addEventListener('click', function(e){
var card = e.target.closest && e.target.closest('.psel-card');
if(!card) return;
var id = card.dataset.id;
if(!id) return;
setTimeout(function(){
var sec = document.getElementById('sec-' + id);
if(sec) try{ sec.scrollIntoView({behavior:'smooth', block:'start'}); }catch(e){}
}, 60);
});
})();
</script>
</body>
</html>