diff --git a/frontend/textbooks/geometry_8_ch2.html b/frontend/textbooks/geometry_8_ch2.html
index 923f2d2..202e36d 100644
--- a/frontend/textbooks/geometry_8_ch2.html
+++ b/frontend/textbooks/geometry_8_ch2.html
@@ -373,7 +373,7 @@ function buildParaSelector(){ const g=document.getElementById('psel-grid');g.inn
const BUILT=new Set();
const BUILDERS={
- p1:()=>buildP1stub(),p2:()=>buildP2stub(),p3:()=>buildP3stub(),p4:()=>buildP4stub(),p5:()=>buildP5stub(),
+ p1:()=>buildP1(),p2:()=>buildP2(),p3:()=>buildP3(),p4:()=>buildP4(),p5:()=>buildP5stub(),
p6:()=>buildP6stub(),p7:()=>buildP7stub(),p8:()=>buildP8stub(),p9:()=>buildP9stub(),p10:()=>buildP10stub(),
p11:()=>buildP11stub(),p12:()=>buildP12stub(),p13:()=>buildP13stub(),p14:()=>buildP14stub(),p15:()=>buildP15stub(),
final2:()=>buildFinal2stub(),
@@ -482,10 +482,1182 @@ function initSidebarToggle(){ const side=document.getElementById('col-side'),bac
function init(){ loadProgress();initTheme();initSidebarToggle();initGlossaryTip();initSearch();buildParaSelector();refreshProgressUI();loadServerReadState();goTo('p1');setTimeout(()=>achievement('start','Начало главы 2!'),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);
-function buildP1stub(){ document.getElementById('p1-body').innerHTML='
§1 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav(null,'p2'); }
-function buildP2stub(){ document.getElementById('p2-body').innerHTML='§2 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p1','p3'); }
-function buildP3stub(){ document.getElementById('p3-body').innerHTML='§3 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p2','p4'); }
-function buildP4stub(){ document.getElementById('p4-body').innerHTML='§4 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p3','p5'); }
+/* ============================================================
+ HELPER: setupSorter (drag-and-drop chip sorter)
+ ============================================================ */
+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=''+it.html+' \xd7 '; 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; 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(); }};
+}
+
+/* ============================================================
+ §1 — ПЛОЩАДЬ КВАДРАТА
+ ============================================================ */
+function buildP1(){
+ const box = document.getElementById('p1-body');
+ let html = '';
+
+ html += makeCard('theory','Понятие площади','1.1',`
+ Площадь — числовая характеристика плоской фигуры, выражающая её размер. Единица площади — единичный квадрат (квадрат со стороной 1).
+ Аксиомы площади:
+
+ Положительность: площадь любой фигуры $> 0$.
+ Равенство: у равных (конгруэнтных) фигур площади равны.
+ Аддитивность: площадь фигуры равна сумме площадей частей, на которые она разбита.
+ `);
+
+ html += makeCard('rule','Площадь квадрата','1.2',`
+ Если квадрат имеет сторону $a$, то его площадь:
+ $$S = a^2$$
+ Доказательство (идея): квадрат разбивается на $a \\times a = a^2$ единичных квадратов. По аксиоме аддитивности $S = a^2$.
+ Именно поэтому в математике «возведение в квадрат» называется так — это площадь квадрата.
`);
+
+ html += makeCard('rule','Единицы измерения площади','1.3',`
+
+ Единица Равна
+
+ $1\\,\\text{км}^2$ $10^6\\,\\text{м}^2$
+ $1\\,\\text{м}^2$ $10^4\\,\\text{см}^2 = 10^6\\,\\text{мм}^2$
+ $1\\,\\text{дм}^2$ $100\\,\\text{см}^2$
+ $1\\,\\text{см}^2$ $100\\,\\text{мм}^2$
+
+
+ При переводе из крупной единицы в мелкую — умножать, из мелкой в крупную — делить.
`);
+
+ html += makeCard('example','Пример','1.4',`
+ Сторона квадрата 7 см. Площадь? $S = 7^2 = 49\\,\\text{см}^2$.
+ Площадь квадрата 36 дм². Найти сторону. $a = \\sqrt{36} = 6\\,\\text{дм}$.
+ Перевести 2,5 м² в см². $2{,}5 \\cdot 10^4 = 25000\\,\\text{см}^2$.
`);
+
+ /* --- INTERACTIVE 1: SVG-квадрат с сеткой --- */
+ html += `
+
+
Двигай слайдер — квадрат заполняется единичными клетками. Количество клеток = $a^2$ — это и есть площадь!
+
+ Сторона $a$ = 4
+
+
+
+
+
`;
+
+ /* --- INTERACTIVE 2: Калькулятор площади --- */
+ html += `
+
+
Введи сторону — получи площадь; или введи площадь — получи сторону.
+
+
+
Сторона → Площадь
+
+
+ = S
+
+
+
+
+
Площадь → Сторона
+
+
+ = a
+
+
+
+
+
`;
+
+ /* --- INTERACTIVE 3: Конвертер единиц --- */
+ html += `
+
+
Введи число и выбери единицу — получишь перевод во все остальные единицы.
+
+
+
+ мм²
+ см²
+ дм²
+ м²
+ км²
+
+ Перевести
+
+
+
`;
+
+ /* --- INTERACTIVE 4: Тренажёр --- */
+ html += `
+
+
Реши задачи на площадь квадрата и конвертацию единиц. Введи ответ и нажми «Проверить».
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* --- INTERACTIVE 5: Босс §1 --- */
+ html += `
+
+
Реши все 4 задачи — каждая верная даёт +5 XP.
+
+
`;
+
+ html += `
+
+
+ Я прочитал §1 (+10 XP)
+
+
`;
+ html += secNav(null,'p2');
+ box.innerHTML = html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: SVG-сетка == */
+ (function(){
+ const sl=document.getElementById('p1-grid-a-sl');
+ const valEl=document.getElementById('p1-grid-a-val');
+ const wrap=document.getElementById('p1-grid-svg-wrap');
+ const info=document.getElementById('p1-grid-info');
+ function draw(){
+ const a=+sl.value;
+ valEl.textContent=a;
+ const cell=34, pad=14;
+ const W=a*cell+pad*2, H=a*cell+pad*2;
+ let s='';
+ // fill cells
+ for(let r=0;r ';
+ }
+ // side label bottom
+ const midX=pad+a*cell/2, botY=pad+a*cell+11;
+ s+='a='+a+' ';
+ // side label left
+ const midY=pad+a*cell/2;
+ s+='a='+a+' ';
+ s+=' ';
+ wrap.innerHTML=s;
+ info.innerHTML='a = '+a+'
a² = '+a+'² = '+(a*a)+'
S = '+(a*a)+' кл.
';
+ }
+ sl.addEventListener('input',draw);
+ draw();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p1-calc-go-a').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p1-calc-a').value);
+ const out=document.getElementById('p1-calc-out-a');
+ if(!isFinite(a)||a<=0){out.innerHTML='Введи положительное число. ';return;}
+ const S=a*a;
+ out.innerHTML='$S = '+a+'^2 = '+fmt(S)+'$';
+ renderMath(out); addXp(1,'p1-calc');
+ });
+ document.getElementById('p1-calc-go-s').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p1-calc-s').value);
+ const out=document.getElementById('p1-calc-out-s');
+ if(!isFinite(S)||S<=0){out.innerHTML='Введи положительное число. ';return;}
+ const a=Math.sqrt(S);
+ out.innerHTML='$a = \\sqrt{'+fmt(S)+'} = '+fmt(a)+'$';
+ renderMath(out); addXp(1,'p1-calc-inv');
+ });
+ document.getElementById('p1-calc-a').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p1-calc-go-a').click();});
+ document.getElementById('p1-calc-s').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p1-calc-go-s').click();});
+ })();
+
+ /* == INIT: Конвертер == */
+ (function(){
+ const factors={mm2:1,cm2:100,dm2:1e4,m2:1e6,km2:1e12};
+ const labels={mm2:'мм²',cm2:'см²',dm2:'дм²',m2:'м²',km2:'км²'};
+ document.getElementById('p1-conv-go').addEventListener('click',()=>{
+ const v=parseFloat(document.getElementById('p1-conv-val').value);
+ const unit=document.getElementById('p1-conv-unit').value;
+ const out=document.getElementById('p1-conv-out');
+ if(!isFinite(v)||v<0){out.style.display='none';return;}
+ const inMm2=v*factors[unit];
+ let rows='';
+ for(const [u,f] of Object.entries(factors)){
+ const res=inMm2/f;
+ rows+=''+labels[u]+': '+fmt(res)+(u===unit?' (исходное) ':'')+'
';
+ }
+ out.innerHTML=rows; out.style.display='block'; addXp(2,'p1-conv');
+ });
+ document.getElementById('p1-conv-val').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p1-conv-go').click();});
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Сторона квадрата 9 см . Найди площадь (в см²).', ans:81, hint:'S = 9² = 81 см².'},
+ {q:'Площадь квадрата 64 м² . Найди сторону (в м).', ans:8, hint:'a = √64 = 8 м.'},
+ {q:'Переведи 3 м² в см². Ответ в см².', ans:30000, hint:'1 м² = 10 000 см², 3·10 000 = 30 000 см².'},
+ {q:'Сторона квадрата 12 дм . Найди площадь в дм².', ans:144, hint:'S = 12² = 144 дм².'},
+ {q:'Площадь квадрата 225 мм² . Найди сторону в мм.', ans:15, hint:'a = √225 = 15 мм.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p1-tr-i').textContent=idx+1;
+ document.getElementById('p1-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p1-tr-ans').value='';
+ document.getElementById('p1-tr-fb').style.display='none';
+ }
+ document.getElementById('p1-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p1-tr-score').textContent=0;show();});
+ document.getElementById('p1-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p1-tr-ans').value;
+ const fb=document.getElementById('p1-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.01){
+ score++;document.getElementById('p1-tr-score').textContent=score;
+ addXp(3,'p1-tr-'+idx);bumpProgress('p1',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p1-tr-all');bumpProgress('p1',10);}
+ } else {
+ feedback(fb,false,'Неверно. '+tasks[idx].hint);
+ }
+ });
+ document.getElementById('p1-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p1-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §1 == */
+ (function(){
+ const tasks=[
+ {q:'Сторона квадрата 13 см . Площадь (в см²)?', ans:169, hint:'13² = 169.'},
+ {q:'Площадь квадрата 196 м² . Сторона (в м)?', ans:14, hint:'√196 = 14.'},
+ {q:'Переведи 50 000 см² в м². Ответ в м².', ans:5, hint:'50 000 / 10 000 = 5 м².'},
+ {q:'Периметр квадрата 28 дм . Площадь в дм²?', ans:49, hint:'a = 28/4 = 7; S = 7² = 49.'},
+ ];
+ const bossBox=document.getElementById('p1-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p1BossSolved=new Set();
+ })();
+}
+
+/* ============================================================
+ §2 — ПЛОЩАДЬ ПРЯМОУГОЛЬНИКА
+ ============================================================ */
+function buildP2(){
+ const box = document.getElementById('p2-body');
+ let html = '';
+
+ html += makeCard('theory','Площадь прямоугольника','2.1',`
+ Теорема. Площадь прямоугольника со сторонами $a$ и $b$:
+ $$S = a \\cdot b$$
+ Доказательство: прямоугольник $a \\times b$ разбивается на $a \\cdot b$ единичных квадратов. По аксиоме аддитивности $S = a \\cdot b$.
+ Квадрат — частный случай прямоугольника при $a = b$: $S = a \\cdot a = a^2$.
`);
+
+ html += makeCard('rule','Периметр и площадь','2.2',`
+ Прямоугольник со сторонами $a$ и $b$:
+ $$S = a \\cdot b, \\quad P = 2(a + b)$$
+ Связь: зная $P$ и одну сторону, можно найти вторую: $b = \\dfrac{P}{2} - a$.
`);
+
+ html += makeCard('example','Примеры','2.3',`
+ Прямоугольник 6 × 4. Площадь? $S = 6 \\cdot 4 = 24\\,\\text{см}^2$.
+ Площадь прямоугольника 36 м², одна сторона 9 м. Найти вторую. $b = 36 / 9 = 4\\,\\text{м}$.
+ Периметр 26 см, одна сторона 3 см. Площадь? $b = 26/2 - 3 = 10\\,\\text{см}$. $S = 3 \\cdot 10 = 30\\,\\text{см}^2$.
`);
+
+ /* --- INTERACTIVE 1: Draggable прямоугольник --- */
+ html += `
+
+
Тащи правый нижний угол мышью или пальцем — меняются стороны и площадь. Сетка показывает разбиение на единичные клетки.
+
+
+
`;
+
+ /* --- INTERACTIVE 2: Калькулятор --- */
+ html += `
+
+
Введи известные величины и нажми кнопку для нахождения неизвестной.
+
+
+
a, b → S и P
+
+
+
+ Найти
+
+
+
+
+
S, a → b
+
+
+
+ Найти b
+
+
+
+
+
`;
+
+ /* --- INTERACTIVE 3: DnD — разложить пары (a,b) для S=24 --- */
+ html += `
+
+
Разложи пары $(a, b)$ на «Площадь = 24» и «Площадь ≠ 24». Нажми карточку, затем нужный ящик.
+
+
+
Проверить Сначала
+
+
`;
+
+ /* --- INTERACTIVE 4: Тренажёр --- */
+ html += `
+
+
5 задач на площадь и периметр прямоугольника.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* --- INTERACTIVE 5: Босс §2 --- */
+ html += `
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html += `
+
+
+ Я прочитал §2 (+10 XP)
+
+
`;
+ html += secNav('p1','p3');
+ box.innerHTML = html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable прямоугольник == */
+ (function(){
+ const W=340, H=300;
+ const MIN_CELLS=1, MAX_CELLS=9;
+ const PAD=30, CELL=24;
+ let cols=6, rows=4;
+ function draw(){
+ const rw=cols*CELL, rh=rows*CELL;
+ const x0=PAD, y0=PAD;
+ let s='';
+ // grid cells
+ for(let r=0;r ';
+ }
+ // border
+ s+=' ';
+ // labels
+ const midX=x0+rw/2, midY=y0+rh/2;
+ s+='a = '+cols+' ';
+ s+='b = '+rows+' ';
+ // drag handle (bottom-right corner)
+ const hx=x0+rw, hy=y0+rh;
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ const svg=document.getElementById('p2-rect-svg');
+ const wrap=document.getElementById('p2-rect-svg-wrap');
+ wrap.innerHTML=s;
+ updateInfo();
+ const handle=wrap.querySelector('#p2-drag-handle');
+ if(handle){
+ handle.addEventListener('pointerdown',ev=>{
+ function onMove(e){
+ const svgEl=wrap.querySelector('#p2-rect-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const sx=W/rect.width, sy=H/rect.height;
+ const nx=Math.round(Math.max(MIN_CELLS,Math.min(MAX_CELLS,((e.clientX-rect.left)*sx-PAD)/CELL)));
+ const ny=Math.round(Math.max(MIN_CELLS,Math.min(MAX_CELLS,((e.clientY-rect.top)*sy-PAD)/CELL)));
+ if(nx!==cols||ny!==rows){cols=nx;rows=ny;draw();}
+ }
+ function onUp(){window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);}
+ window.addEventListener('pointermove',onMove);
+ window.addEventListener('pointerup',onUp);
+ window.addEventListener('pointercancel',onUp);
+ });
+ }
+ }
+ function updateInfo(){
+ const S=cols*rows, P=2*(cols+rows);
+ document.getElementById('p2-rect-info').innerHTML=`
+
+
+
+ `;
+ }
+ draw();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p2-calc-go').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p2-calc-a').value);
+ const b=parseFloat(document.getElementById('p2-calc-b').value);
+ const out=document.getElementById('p2-calc-out');
+ if(!isFinite(a)||!isFinite(b)||a<=0||b<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$S = '+fmt(a)+'\\cdot'+fmt(b)+' = '+fmt(a*b)+'$ $P = 2('+fmt(a)+'+'+fmt(b)+') = '+fmt(2*(a+b))+'$';
+ renderMath(out); addXp(1,'p2-calc');
+ });
+ document.getElementById('p2-calc-go2').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p2-calc-s').value);
+ const a=parseFloat(document.getElementById('p2-calc-a2').value);
+ const out=document.getElementById('p2-calc-out2');
+ if(!isFinite(S)||!isFinite(a)||S<=0||a<=0){out.innerHTML='Введи положительные числа. ';return;}
+ const b=S/a;
+ out.innerHTML='$b = S/a = '+fmt(S)+'/'+fmt(a)+' = '+fmt(b)+'$';
+ renderMath(out); addXp(1,'p2-calc-inv');
+ });
+ })();
+
+ /* == INIT: DnD == */
+ (function(){
+ const items=[
+ {id:'p1x24',html:'1 × 24',ans:'yes'},{id:'p2x12',html:'2 × 12',ans:'yes'},
+ {id:'p3x8', html:'3 × 8', ans:'yes'},{id:'p4x6', html:'4 × 6', ans:'yes'},
+ {id:'p5x5', html:'5 × 5', ans:'no'},{id:'p3x9', html:'3 × 9', ans:'no'},
+ {id:'p6x5', html:'6 × 5', ans:'no'},
+ ];
+ const sorter2=setupSorter({poolId:'p2-dnd-pool',scopeSelector:'#p2-dnd-wg',items,cats:['yes','no']});
+ document.getElementById('p2-dnd-reset').addEventListener('click',()=>{sorter2.reset();document.getElementById('p2-dnd-fb').style.display='none';});
+ document.getElementById('p2-dnd-check').addEventListener('click',()=>{
+ let ok=0;
+ items.forEach(it=>{if(sorter2.placed[it.id]===it.ans)ok++;});
+ const fb=document.getElementById('p2-dnd-fb');
+ if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p2-dnd');bumpProgress('p2',15);}
+ else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Перемножь числа и проверь: = 24?');
+ });
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Прямоугольник 7 × 5 . Площадь?', ans:35, hint:'S = 7·5 = 35.'},
+ {q:'Площадь прямоугольника 48 м² , одна сторона 6 м . Вторая сторона?', ans:8, hint:'b = 48/6 = 8.'},
+ {q:'Периметр прямоугольника 24 см , одна сторона 4 см . Площадь?', ans:32, hint:'b = 24/2 - 4 = 8; S = 4·8 = 32.'},
+ {q:'Прямоугольник 11 × 3 . Периметр?', ans:28, hint:'P = 2(11+3) = 28.'},
+ {q:'Площадь комнаты 5 × 4 м . Количество плиток 50 × 50 см ?', ans:80, hint:'S = 20 м² = 200 000 см². Плитка: 50×50=2500 см². 200 000/2500 = 80.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p2-tr-i').textContent=idx+1;
+ document.getElementById('p2-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p2-tr-ans').value='';
+ document.getElementById('p2-tr-fb').style.display='none';
+ }
+ document.getElementById('p2-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p2-tr-score').textContent=0;show();});
+ document.getElementById('p2-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p2-tr-ans').value;
+ const fb=document.getElementById('p2-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p2-tr-score').textContent=score;
+ addXp(3,'p2-tr-'+idx);bumpProgress('p2',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p2-tr-all');bumpProgress('p2',10);}
+ } else {
+ feedback(fb,false,'Неверно. '+tasks[idx].hint);
+ }
+ });
+ document.getElementById('p2-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p2-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §2 == */
+ (function(){
+ const tasks=[
+ {q:'Прямоугольник 13 × 8 . Площадь?', ans:104, hint:'13·8 = 104.'},
+ {q:'Площадь участка 360 м² , ширина 15 м . Длина?', ans:24, hint:'360/15 = 24.'},
+ {q:'Периметр прямоугольника 34 дм , одна сторона 9 дм . Площадь?', ans:72, hint:'b = 34/2 - 9 = 8; S = 9·8 = 72.'},
+ {q:'Квадрат и прямоугольник 9 × 4 имеют равные площади. Сторона квадрата?', ans:6, hint:'S = 36, a = √36 = 6.'},
+ ];
+ const bossBox=document.getElementById('p2-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p2BossSolved=new Set();
+ })();
+}
+
+/* ============================================================
+ §3 — ПЛОЩАДЬ ПАРАЛЛЕЛОГРАММА
+ ============================================================ */
+function buildP3(){
+ const box = document.getElementById('p3-body');
+ let html = '';
+
+ html += makeCard('theory','Площадь параллелограмма','3.1',`
+ Высота параллелограмма — перпендикуляр, опущенный из любой точки одной стороны на другую (параллельную) сторону.
+ Теорема. Площадь параллелограмма равна произведению основания на высоту:
+ $$S = a \\cdot h$$
+ где $a$ — основание (любая сторона), $h$ — высота, опущенная на это основание.
`);
+
+ html += makeCard('rule','Доказательство через прямоугольник','3.2',`
+ Идея: от параллелограмма отрезаем боковой прямоугольный треугольник и переносим его на другой конец — получается прямоугольник с теми же основанием $a$ и высотой $h$.
+ По аксиоме аддитивности площади параллелограмм и прямоугольник равновелики:
+ $$S_{\\text{пар}} = S_{\\text{прямоуг}} = a \\cdot h$$
+ Ключевое следствие: два параллелограмма с одним основанием и одинаковой высотой имеют равные площади , даже если выглядят по-разному!
`);
+
+ html += makeCard('example','Примеры','3.3',`
+ Параллелограмм, основание 8 см, высота 5 см. Площадь? $S = 8 \\cdot 5 = 40\\,\\text{см}^2$.
+ Площадь 60 м², основание 12 м. Высота? $h = 60 / 12 = 5\\,\\text{м}$.
`);
+
+ /* --- INTERACTIVE 1: Draggable параллелограмм (сдвигаем верх) --- */
+ html += `
+
+
Тащи верхнее основание влево–вправо. Основание $a$ и высота $h$ не меняются — площадь $S = a \\cdot h$ постоянна!
+
+
+
`;
+
+ /* --- INTERACTIVE 2: Анимация доказательства --- */
+ html += `
+
+
Нажимай «Далее» чтобы пройти шаги доказательства формулы $S = a \\cdot h$.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* --- INTERACTIVE 3: Калькулятор --- */
+ html += `
+
+
Задай два из трёх параметров — найди третий.
+
+
+
a, h → S
+
+
+
+ = S
+
+
+
+
+
S, h → a
+
+
+
+ = a
+
+
+
+
+
`;
+
+ /* --- INTERACTIVE 4: Тренажёр --- */
+ html += `
+
+
5 задач на площадь параллелограмма.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* --- INTERACTIVE 5: Босс §3 --- */
+ html += `
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html += `
+
+
+ Я прочитал §3 (+10 XP)
+
+
`;
+ html += secNav('p2','p4');
+ box.innerHTML = html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable параллелограмм == */
+ (function(){
+ const W=380, H=240;
+ const BASE_X=50, BASE_Y=190, BASE_W=200, HEIGHT_PX=100;
+ let shiftX=40;
+ function draw(){
+ const bx=BASE_X, by=BASE_Y, bw=BASE_W, h=HEIGHT_PX;
+ const x1=bx, x2=bx+bw, x3=bx+bw+shiftX, x4=bx+shiftX;
+ const y1=by, y2=by, y3=by-h, y4=by-h;
+ const cx=(x1+x2+x3+x4)/4, cy=(y1+y2+y3+y4)/4;
+ let s='';
+ // height line
+ const hBaseX=x4+(x1-x4)*0;
+ s+=' ';
+ s+='h ';
+ // right angle mark
+ s+=' ';
+ // polygon
+ s+=' ';
+ // base label
+ s+='a ';
+ // S label
+ s+='S=a·h ';
+ // drag handle (upper base midpoint)
+ const hmx=(x4+x3)/2, hmy=(y4+y3)/2;
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ document.getElementById('p3-pgram-svg-wrap').innerHTML=s;
+ document.getElementById('p3-pgram-info').innerHTML=`
+
+
+
+ S = a·h
${BASE_W*HEIGHT_PX} `;
+ const handle=document.getElementById('p3-drag-upper');
+ if(handle){
+ handle.addEventListener('pointerdown',ev=>{
+ const startX=ev.clientX, startShift=shiftX;
+ function onMove(e){
+ const svgEl=document.getElementById('p3-pgram-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=W/rect.width;
+ const delta=Math.round((e.clientX-startX)*scale);
+ shiftX=Math.max(-80,Math.min(180,startShift+delta));
+ draw();
+ }
+ function onUp(){window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);}
+ window.addEventListener('pointermove',onMove);
+ window.addEventListener('pointerup',onUp);
+ window.addEventListener('pointercancel',onUp);
+ });
+ }
+ }
+ draw();
+ })();
+
+ /* == INIT: Анимация доказательства == */
+ (function(){
+ let step=0;
+ const W=360, H=200;
+ const bx=40, by=160, bw=200, H2=100, sh=60;
+ const steps=[
+ {
+ desc:'Исходный параллелограмм ABCD. Основание AB = a, высота h — перпендикуляр от D к AB.',
+ draw(){
+ const x1=bx,x2=bx+bw,x3=bx+bw+sh,x4=bx+sh;
+ const y1=by,y2=by,y3=by-H2,y4=by-H2;
+ return ' '
+ +' '
+ +'a '
+ +'h '
+ +'A '
+ +'B '
+ +'C '
+ +'D ';
+ }
+ },
+ {
+ desc:'Проводим высоту DE — перпендикуляр из D на AB. Отрезаем правый треугольник BCE.',
+ draw(){
+ const x1=bx,x2=bx+bw,x3=bx+bw+sh,x4=bx+sh;
+ const y1=by,y2=by,y3=by-H2,y4=by-H2;
+ return ' '
+ +' '
+ +' '
+ +'Оставшийся трапеция '
+ +'Треугольник ';
+ }
+ },
+ {
+ desc:'Переносим выделенный треугольник на левую сторону. Он встаёт точно на место треугольника ADE.',
+ draw(){
+ const x1=bx,x2=bx+bw,x4=bx+sh;
+ const y1=by,y2=by,y4=by-H2;
+ return ' '
+ +' '
+ +' '
+ +'Получается прямоугольник! ';
+ }
+ },
+ {
+ desc:'Получился прямоугольник со сторонами a и h. Его площадь S = a·h. Значит, и у исходного параллелограмма S = a·h!',
+ draw(){
+ const x1=bx;
+ return ' '
+ +'a = '+bw+' px '
+ +'h = '+H2+' px '
+ +'S = a·h ';
+ }
+ },
+ ];
+ function render(){
+ const s=steps[step];
+ const svgHtml=''+s.draw()+' ';
+ document.getElementById('p3-proof-svg-wrap').innerHTML=svgHtml;
+ document.getElementById('p3-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc;
+ document.getElementById('p3-proof-next').textContent=step{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p3-calc-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p3-calc-a').value);
+ const h=parseFloat(document.getElementById('p3-calc-h').value);
+ const out=document.getElementById('p3-calc-out1');
+ if(!isFinite(a)||!isFinite(h)||a<=0||h<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$S = '+fmt(a)+'\\cdot'+fmt(h)+' = '+fmt(a*h)+'$';
+ renderMath(out); addXp(1,'p3-calc');
+ });
+ document.getElementById('p3-calc-go2').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p3-calc-s').value);
+ const h=parseFloat(document.getElementById('p3-calc-h2').value);
+ const out=document.getElementById('p3-calc-out2');
+ if(!isFinite(S)||!isFinite(h)||S<=0||h<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$a = S/h = '+fmt(S)+'/'+fmt(h)+' = '+fmt(S/h)+'$';
+ renderMath(out); addXp(1,'p3-calc-inv');
+ });
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Параллелограмм, основание 10 см , высота 7 см . Площадь?', ans:70, hint:'S = 10·7 = 70.'},
+ {q:'Площадь параллелограмма 84 м² , высота 6 м . Основание?', ans:14, hint:'a = 84/6 = 14.'},
+ {q:'Основание параллелограмма 15 дм , площадь 120 дм² . Высота?', ans:8, hint:'h = 120/15 = 8.'},
+ {q:'Параллелограмм, сторона 9 см , высота к этой стороне 4 см . Площадь?', ans:36, hint:'S = 9·4 = 36.'},
+ {q:'Два параллелограмма: у обоих основание 8 , высоты 5 и 5 . Какую площадь имеет каждый?', ans:40, hint:'S = 8·5 = 40 — равные площади!'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p3-tr-i').textContent=idx+1;
+ document.getElementById('p3-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p3-tr-ans').value='';
+ document.getElementById('p3-tr-fb').style.display='none';
+ }
+ document.getElementById('p3-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p3-tr-score').textContent=0;show();});
+ document.getElementById('p3-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p3-tr-ans').value;
+ const fb=document.getElementById('p3-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p3-tr-score').textContent=score;
+ addXp(3,'p3-tr-'+idx);bumpProgress('p3',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p3-tr-all');bumpProgress('p3',10);}
+ } else {
+ feedback(fb,false,'Неверно. '+tasks[idx].hint);
+ }
+ });
+ document.getElementById('p3-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p3-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §3 == */
+ (function(){
+ const tasks=[
+ {q:'Параллелограмм, основание 16 дм , высота 9 дм . Площадь?', ans:144, hint:'16·9 = 144.'},
+ {q:'Площадь параллелограмма 195 м² , основание 15 м . Высота?', ans:13, hint:'195/15 = 13.'},
+ {q:'Два параллелограмма с основанием 12 имеют площади 96 и 60 . Найди сумму их высот.', ans:13, hint:'h1=96/12=8, h2=60/12=5, сумма=13.'},
+ {q:'Параллелограмм, боковая сторона 10 см , высота к ней 6 см . Площадь?', ans:60, hint:'S = 10·6 = 60.'},
+ ];
+ const bossBox=document.getElementById('p3-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p3BossSolved=new Set();
+ })();
+}
+
+/* ============================================================
+ §4 — ПЛОЩАДЬ ТРЕУГОЛЬНИКА
+ ============================================================ */
+function buildP4(){
+ const box = document.getElementById('p4-body');
+ let html = '';
+
+ html += makeCard('theory','Площадь треугольника','4.1',`
+ Высота треугольника — перпендикуляр, опущенный из вершины на противоположную сторону (основание) или её продолжение.
+ Теорема. Площадь треугольника равна половине произведения основания на высоту:
+ $$S = \\dfrac{1}{2} \\cdot a \\cdot h$$
+ где $a$ — основание, $h$ — высота к нему.
`);
+
+ html += makeCard('rule','Доказательство','4.2',`
+ Идея: из треугольника $ABC$ строим параллелограмм — копируем треугольник и прикладываем к стороне $AB$. Получается параллелограмм $ABDC$ с тем же основанием $a$ и той же высотой $h$.
+ Площадь параллелограмма: $S_{\\text{пар}} = a \\cdot h$.
+ Треугольник — ровно половина параллелограмма (диагональ делит его на два равных треугольника):
+ $$S_{\\triangle} = \\dfrac{1}{2} \\cdot a \\cdot h$$
+ Важное следствие: треугольники с одним основанием и одинаковой высотой (вершина лежит на прямой, параллельной основанию) имеют равные площади .
`);
+
+ html += makeCard('example','Примеры','4.3',`
+ Треугольник, основание 12 см, высота 8 см. Площадь? $S = \\dfrac{1}{2} \\cdot 12 \\cdot 8 = 48\\,\\text{см}^2$.
+ Площадь 30 м², основание 10 м. Высота? $h = \\dfrac{2S}{a} = \\dfrac{60}{10} = 6\\,\\text{м}$.
+ Площадь 18 дм², высота 6 дм. Основание? $a = \\dfrac{2S}{h} = \\dfrac{36}{6} = 6\\,\\text{дм}$.
`);
+
+ /* --- INTERACTIVE 1: Draggable вершина C --- */
+ html += `
+
+
Тащи вершину C вдоль прямой. Основание AB фиксировано, высота не меняется — площадь $S = \\frac{1}{2} \\cdot AB \\cdot h$ постоянна!
+
+
+
`;
+
+ /* --- INTERACTIVE 2: Анимация "достраиваем до параллелограмма" --- */
+ html += `
+
+
Нажимай «Далее» и смотри, как треугольник превращается в половину параллелограмма.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* --- INTERACTIVE 3: Калькулятор --- */
+ html += `
+
+
Введи два из трёх параметров — найди третий.
+
+
+
a, h → S
+
+
+
+ = S
+
+
+
+
+
S, a → h
+
+
+
+ = h
+
+
+
+
+
S, h → a
+
+
+
+ = a
+
+
+
+
+
`;
+
+ /* --- INTERACTIVE 4: Тренажёр --- */
+ html += `
+
+
5 задач на площадь треугольника.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* --- INTERACTIVE 5: Босс §4 --- */
+ html += `
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html += `
+
+
+ Я прочитал §4 (+10 XP)
+
+
`;
+ html += secNav('p3','p5');
+ box.innerHTML = html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable вершина C == */
+ (function(){
+ const W=380, H=240;
+ const Ax=60, Ay=190, Bx=280, By=190;
+ const BASE=Bx-Ax, H_FIX=100;
+ let Cx=170;
+ const Cy=Ay-H_FIX;
+ function draw(){
+ const S=0.5*BASE*H_FIX;
+ let s='';
+ // parallel line (locus of C)
+ s+=' ';
+ s+='Прямая C ';
+ // height line
+ s+=' ';
+ // right angle mark
+ s+=' ';
+ s+='h ';
+ // triangle
+ s+=' ';
+ // AB label
+ s+='a = AB = '+BASE+' ';
+ // S label — cx and cy both from triangle centroid
+ const lx=(Ax+Bx+Cx)/3, ly=(Ay+By+Cy)/3;
+ s+='S = '+S+' ';
+ // vertex labels
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ // C drag handle
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ document.getElementById('p4-tri-svg-wrap').innerHTML=s;
+ document.getElementById('p4-tri-info').innerHTML=`
+
+
+ `;
+ const handle=document.getElementById('p4-drag-c');
+ if(handle){
+ handle.addEventListener('pointerdown',ev=>{
+ function onMove(e){
+ const svgEl=document.getElementById('p4-tri-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=W/rect.width;
+ const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*scale));
+ Cx=Math.round(nx);
+ draw();
+ }
+ function onUp(){window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);}
+ window.addEventListener('pointermove',onMove);
+ window.addEventListener('pointerup',onUp);
+ window.addEventListener('pointercancel',onUp);
+ });
+ }
+ }
+ draw();
+ })();
+
+ /* == INIT: Анимация доказательства == */
+ (function(){
+ let step=0;
+ const W=380, H=230;
+ const Ax=60,Ay=190,Bx=260,By=190,Cx=160,Cy=90;
+ // mirror of C relative to midpoint of AB gives D
+ const Dx=Ax+Bx-Cx, Dy=Ay+By-Cy;
+ const steps=[
+ {
+ desc:'Исходный треугольник ABC. Основание AB = a, высота h из вершины C.',
+ draw(){
+ return ' '
+ +' '
+ +'a '
+ +'h '
+ +'A '
+ +'B '
+ +'C ';
+ }
+ },
+ {
+ desc:'Берём копию треугольника ABC и поворачиваем её на 180° вокруг середины AB. Вершина C переходит в точку D.',
+ draw(){
+ return ' '
+ +' '
+ +' '
+ +'D '
+ +'C ';
+ }
+ },
+ {
+ desc:'Треугольники ABC и ABD вместе образуют параллелограмм ACBD. Обе его диагонали — это AB и CD.',
+ draw(){
+ return ' '
+ +' '
+ +' '
+ +'Параллелограмм ';
+ }
+ },
+ {
+ desc:'Параллелограмм = два равных треугольника. S(пар) = a·h. Значит S(△) = ½·a·h!',
+ draw(){
+ return ' '
+ +' '
+ +'½·a·h '
+ +'½·a·h ';
+ }
+ },
+ ];
+ function render(){
+ const s=steps[step];
+ document.getElementById('p4-proof-svg-wrap').innerHTML=''+s.draw()+' ';
+ document.getElementById('p4-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc;
+ document.getElementById('p4-proof-next').textContent=step{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p4-calc-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p4-calc-a').value);
+ const h=parseFloat(document.getElementById('p4-calc-h').value);
+ const out=document.getElementById('p4-calc-out1');
+ if(!isFinite(a)||!isFinite(h)||a<=0||h<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$S = \\frac{1}{2}\\cdot'+fmt(a)+'\\cdot'+fmt(h)+' = '+fmt(0.5*a*h)+'$';
+ renderMath(out); addXp(1,'p4-calc');
+ });
+ document.getElementById('p4-calc-go2').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p4-calc-s2').value);
+ const a=parseFloat(document.getElementById('p4-calc-a2').value);
+ const out=document.getElementById('p4-calc-out2');
+ if(!isFinite(S)||!isFinite(a)||S<=0||a<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$h = \\frac{2S}{a} = \\frac{2\\cdot'+fmt(S)+'}{'+fmt(a)+'} = '+fmt(2*S/a)+'$';
+ renderMath(out); addXp(1,'p4-calc-h');
+ });
+ document.getElementById('p4-calc-go3').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p4-calc-s3').value);
+ const h=parseFloat(document.getElementById('p4-calc-h3').value);
+ const out=document.getElementById('p4-calc-out3');
+ if(!isFinite(S)||!isFinite(h)||S<=0||h<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$a = \\frac{2S}{h} = \\frac{2\\cdot'+fmt(S)+'}{'+fmt(h)+'} = '+fmt(2*S/h)+'$';
+ renderMath(out); addXp(1,'p4-calc-a');
+ });
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Треугольник, основание 14 см , высота 6 см . Площадь?', ans:42, hint:'S = ½·14·6 = 42.'},
+ {q:'Площадь треугольника 45 м² , высота 9 м . Основание?', ans:10, hint:'a = 2·45/9 = 10.'},
+ {q:'Площадь треугольника 36 дм² , основание 12 дм . Высота?', ans:6, hint:'h = 2·36/12 = 6.'},
+ {q:'Прямоугольный треугольник, катеты 8 см и 6 см . Площадь?', ans:24, hint:'S = ½·8·6 = 24.'},
+ {q:'Площадь параллелограмма 80 м² . Площадь треугольника на том же основании и высоте?', ans:40, hint:'S(△) = S(пар)/2 = 40.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p4-tr-i').textContent=idx+1;
+ document.getElementById('p4-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p4-tr-ans').value='';
+ document.getElementById('p4-tr-fb').style.display='none';
+ }
+ document.getElementById('p4-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p4-tr-score').textContent=0;show();});
+ document.getElementById('p4-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p4-tr-ans').value;
+ const fb=document.getElementById('p4-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p4-tr-score').textContent=score;
+ addXp(3,'p4-tr-'+idx);bumpProgress('p4',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p4-tr-all');bumpProgress('p4',10);}
+ } else {
+ feedback(fb,false,'Неверно. '+tasks[idx].hint);
+ }
+ });
+ document.getElementById('p4-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p4-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §4 == */
+ (function(){
+ const tasks=[
+ {q:'Треугольник, основание 18 дм , высота 10 дм . Площадь?', ans:90, hint:'½·18·10 = 90.'},
+ {q:'Площадь треугольника 56 м² , основание 14 м . Высота?', ans:8, hint:'h = 2·56/14 = 8.'},
+ {q:'Квадрат со стороной 6 см . Диагональ делит его на 2 треугольника. Площадь каждого?', ans:18, hint:'S(кв) = 36; S(△) = 36/2 = 18.'},
+ {q:'Параллелограмм, основание 20 м , высота 7 м . Площадь вписанного треугольника (того же основания и высоты)?', ans:70, hint:'S(пар) = 140; S(△) = 70.'},
+ ];
+ const bossBox=document.getElementById('p4-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p4BossSolved=new Set();
+ })();
+}
function buildP5stub(){ document.getElementById('p5-body').innerHTML='§5 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p4','p6'); }
function buildP6stub(){ document.getElementById('p6-body').innerHTML='§6 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p5','p7'); }
function buildP7stub(){ document.getElementById('p7-body').innerHTML='§7 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p6','p8'); }