diff --git a/frontend/textbooks/geometry_8_ch2.html b/frontend/textbooks/geometry_8_ch2.html
index 202e36d..984014f 100644
--- a/frontend/textbooks/geometry_8_ch2.html
+++ b/frontend/textbooks/geometry_8_ch2.html
@@ -373,8 +373,8 @@ function buildParaSelector(){ const g=document.getElementById('psel-grid');g.inn
const BUILT=new Set();
const BUILDERS={
- p1:()=>buildP1(),p2:()=>buildP2(),p3:()=>buildP3(),p4:()=>buildP4(),p5:()=>buildP5stub(),
- p6:()=>buildP6stub(),p7:()=>buildP7stub(),p8:()=>buildP8stub(),p9:()=>buildP9stub(),p10:()=>buildP10stub(),
+ p1:()=>buildP1(),p2:()=>buildP2(),p3:()=>buildP3(),p4:()=>buildP4(),p5:()=>buildP5(),
+ p6:()=>buildP6(),p7:()=>buildP7(),p8:()=>buildP8(),p9:()=>buildP9stub(),p10:()=>buildP10stub(),
p11:()=>buildP11stub(),p12:()=>buildP12stub(),p13:()=>buildP13stub(),p14:()=>buildP14stub(),p15:()=>buildP15stub(),
final2:()=>buildFinal2stub(),
};
@@ -1658,10 +1658,1502 @@ function buildP4(){
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'); }
-function buildP8stub(){ document.getElementById('p8-body').innerHTML='§8 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p7','p9'); }
+/* ============================================================
+ §5 — ПЛОЩАДЬ ТРАПЕЦИИ
+ ============================================================ */
+function buildP5(){
+ const box=document.getElementById('p5-body');
+ let html='';
+
+ html+=makeCard('theory','Площадь трапеции','5.1',`
+ Теорема. Площадь трапеции равна произведению полусуммы оснований на высоту:
+ $$S = \\dfrac{a+b}{2}\\cdot h$$
+ где $a$ и $b$ — основания (параллельные стороны), $h$ — высота (расстояние между основаниями).
+ Через среднюю линию: $m = \\dfrac{a+b}{2}$, поэтому $S = m \\cdot h$.
`);
+
+ html+=makeCard('rule','Доказательство (диагональ)','5.2',`
+ Проведём диагональ $BD$ трапеции $ABCD$ ($AB \\parallel CD$).
+ Диагональ делит трапецию на два треугольника:
+
+ $\\triangle ABD$: основание $a=AB$, высота $h$ → $S_1 = \\dfrac{1}{2}ah$
+ $\\triangle BCD$: основание $b=CD$, высота $h$ → $S_2 = \\dfrac{1}{2}bh$
+
+ По аддитивности: $S = S_1+S_2 = \\dfrac{1}{2}ah+\\dfrac{1}{2}bh = \\dfrac{a+b}{2}\\cdot h$. $\\square$
`);
+
+ html+=makeCard('example','Примеры','5.3',`
+ Трапеция, основания 8 и 14 см, высота 6 см. Площадь? $S=\\dfrac{8+14}{2}\\cdot6=11\\cdot6=66\\,\\text{см}^2$.
+ S = 72 м², h = 9 м, a = 10 м. Найти b. $b = \\dfrac{2S}{h}-a = \\dfrac{144}{9}-10=16-10=6\\,\\text{м}$.
+ S = 80 дм², средняя линия m = 16 дм. Высота? $h = S/m = 80/16 = 5\\,\\text{дм}$.
`);
+
+ /* INTERACTIVE 1 — Draggable трапеция */
+ html+=`
+
+
Тащи верхнее основание (синяя точка) вправо/влево — меняется его длина $b$. Тащи левый нижний угол — меняется высота $h$. Площадь пересчитывается мгновенно.
+
+
+
`;
+
+ /* INTERACTIVE 2 — Пошаговое доказательство */
+ html+=`
+
+
Нажимай «Далее» и смотри, как две трапеции складываются в параллелограмм со стороной $(a+b)$ и высотой $h$.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* INTERACTIVE 3 — Калькулятор */
+ html+=`
+
+
Три режима: найти S, найти a, найти h.
+
+
+
+
+
S, m → h
+
+
+
+ =h
+
+
+
+
+
`;
+
+ /* INTERACTIVE 4 — DnD сортер */
+ html+=`
+
+
Распредели фигуры по группам: перетащи каждую в нужную колонку.
+
+
+
+ Проверить
+ Сбросить
+
+
+
`;
+
+ /* INTERACTIVE 5 — Тренажёр */
+ html+=`
+
+
5 задач на площадь трапеции.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* INTERACTIVE 6 — Босс */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §5 (+10 XP)
+
+
`;
+ html+=secNav('p4','p6');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable трапеция == */
+ (function(){
+ const W=400, H=260;
+ const AX=40, AY=200, BW=220;
+ let topB=100, topOff=40, trapH=110;
+ function draw(){
+ const ax=AX, ay=AY, bx=ax+BW, by=ay;
+ const dx=ax+topOff, dy=ay-trapH, cx=dx+topB, cy=dy;
+ const midX=(dx+cx)/2, midY=dy;
+ const Sval=Math.round((BW+topB)/2*trapH);
+ let s='';
+ // height dashes
+ s+=' ';
+ s+='h ';
+ // right angle
+ s+=' ';
+ // trapezoid fill
+ s+=' ';
+ // base a label
+ s+='a='+BW+' ';
+ // top b label
+ s+='b='+topB+' ';
+ // S label
+ const cxLabel=Math.round((ax+bx+cx+dx)/4), cyLabel=Math.round((ay+ay+cy+dy)/4);
+ s+='S='+Sval+' ';
+ // drag handles
+ const hTop=cx, hTopY=cy;
+ s+=' ';
+ s+=' ';
+ const hH=ax, hHY=Math.round(ay-trapH/2);
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ document.getElementById('p5-trap-svg-wrap').innerHTML=s;
+ document.getElementById('p5-trap-info').innerHTML=`
+
+
+
+ `;
+ const handleTop=document.getElementById('p5-drag-top');
+ if(handleTop){
+ handleTop.addEventListener('pointerdown',ev=>{
+ const startX=ev.clientX, startB=topB;
+ function onMove(e){
+ const svgEl=document.getElementById('p5-trap-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=W/rect.width;
+ const delta=Math.round((e.clientX-startX)*scale);
+ topB=Math.max(20,Math.min(280,startB+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);
+ });
+ }
+ const handleH=document.getElementById('p5-drag-h');
+ if(handleH){
+ handleH.addEventListener('pointerdown',ev=>{
+ const startY=ev.clientY, startH=trapH;
+ function onMove(e){
+ const svgEl=document.getElementById('p5-trap-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=H/rect.height;
+ const delta=Math.round((startY-e.clientY)*scale);
+ trapH=Math.max(40,Math.min(160,startH+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=400, H=220;
+ const ax=30, ay=180, bw=200, h=100, off=50;
+ const steps=[
+ {desc:'Исходная трапеция ABCD. Основания: $a=AB$ (нижнее) и $b=CD$ (верхнее), высота $h$.',
+ draw(){
+ const bx=ax+bw, dx=ax+off, cx=dx+Math.round(bw*0.55);
+ const dy=ay-h, cy=dy;
+ return ' '
+ +' '
+ +'a '
+ +'b '
+ +'h '
+ +'A '
+ +'B '
+ +'C '
+ +'D ';
+ }},
+ {desc:'Повернём копию трапеции на 180° и приложим сверху-справа так, чтобы стороны CD совпали.',
+ draw(){
+ const bx=ax+bw, dx=ax+off, cx=dx+Math.round(bw*0.55);
+ const dy=ay-h, cy=dy;
+ const bw2=Math.round(bw*0.55);
+ const ax2=cx, ay2=cy, bx2=cx+bw, by2=cy, cx2=cx+bw-off, cy2=cy-h, dx2=cx, dy2=cy-h;
+ return ' '
+ +' '
+ +'a '
+ +'копия (перевёрнута) ';
+ }},
+ {desc:'Две трапеции образуют параллелограмм! Его основание $a+b$, высота $h$.',
+ draw(){
+ const bx=ax+bw, dx=ax+off, cx=dx+Math.round(bw*0.55);
+ const dy=ay-h, cy=dy;
+ const fullW=bw+Math.round(bw*0.55);
+ return ' '
+ +'a+b '
+ +'h '
+ +'паралл. ';
+ }},
+ {desc:'Площадь параллелограмма $S_{\\text{пар}}=(a+b)\\cdot h$. Трапеция — его половина , значит $S=\\dfrac{(a+b)}{2}\\cdot h$.',
+ draw(){
+ const fullW=bw+Math.round(bw*0.55);
+ return ' '
+ +' '
+ +' '
+ +'S=½(a+b)h ';
+ }},
+ ];
+ function render(){
+ const s=steps[step];
+ document.getElementById('p5-proof-svg-wrap').innerHTML=''+s.draw()+' ';
+ document.getElementById('p5-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc;
+ document.getElementById('p5-proof-next').textContent=step{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p5-c-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p5-c-a').value);
+ const b=parseFloat(document.getElementById('p5-c-b').value);
+ const h=parseFloat(document.getElementById('p5-c-h').value);
+ const out=document.getElementById('p5-c-out1');
+ if(!isFinite(a)||!isFinite(b)||!isFinite(h)||h<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$S=\\dfrac{'+fmt(a)+'+'+fmt(b)+'}{2}\\cdot'+fmt(h)+'='+fmt((a+b)/2*h)+'$';
+ renderMath(out);addXp(1,'p5-calc1');
+ });
+ document.getElementById('p5-c-go2').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p5-c-s2').value);
+ const h=parseFloat(document.getElementById('p5-c-h2').value);
+ const b=parseFloat(document.getElementById('p5-c-b2').value);
+ const out=document.getElementById('p5-c-out2');
+ if(!isFinite(S)||!isFinite(h)||!isFinite(b)||h<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$a=\\dfrac{2S}{h}-b='+fmt(2*S/h-b)+'$';
+ renderMath(out);addXp(1,'p5-calc2');
+ });
+ document.getElementById('p5-c-go3').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p5-c-s3').value);
+ const m=parseFloat(document.getElementById('p5-c-m3').value);
+ const out=document.getElementById('p5-c-out3');
+ if(!isFinite(S)||!isFinite(m)||m<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$h=S/m='+fmt(S/m)+'$';
+ renderMath(out);addXp(1,'p5-calc3');
+ });
+ })();
+
+ /* == INIT: DnD Sorter == */
+ (function(){
+ const items=[
+ {id:'t1',html:'Трапеция ABCDAB∥CD, AD не∥BC ',correct:'yes'},
+ {id:'t2',html:'Прямоугольная трапецияодин угол 90° ',correct:'yes'},
+ {id:'t3',html:'Равнобедренная трапециябоковые стороны равны ',correct:'yes'},
+ {id:'t4',html:'Параллелограммобе пары сторон ∥ ',correct:'no'},
+ {id:'t5',html:'Треугольник3 стороны ',correct:'no'},
+ {id:'t6',html:'Произвольный 4-уг.нет парал. сторон ',correct:'no'},
+ ];
+ const sorter=setupSorter({
+ poolId:'p5-dnd-pool',
+ scopeSelector:'#p5-dnd-wg',
+ items:items,
+ cats:['yes','no'],
+ columnLayout:false
+ });
+ document.getElementById('p5-dnd-check').addEventListener('click',()=>{
+ const fb=document.getElementById('p5-dnd-fb');
+ const total=items.length;
+ let correct=0;
+ items.forEach(it=>{if(sorter.placed[it.id]===it.correct)correct++;});
+ if(correct===total){
+ feedback(fb,true,'Все верно! +8 XP');addXp(8,'p5-dnd-ok');bumpProgress('p5',20);confetti();
+ } else {
+ feedback(fb,false,'Верно: '+correct+' из '+total+'. Попробуй ещё.');
+ }
+ });
+ document.getElementById('p5-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p5-dnd-fb').style.display='none';});
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Трапеция, основания 6 и 14 см , высота 8 см . Площадь?', ans:80, hint:'(6+14)/2·8 = 10·8 = 80.'},
+ {q:'Площадь трапеции 90 м² , высота 9 м , одно основание 7 м . Второе основание?', ans:13, hint:'2·90/9 − 7 = 20 − 7 = 13.'},
+ {q:'Средняя линия трапеции 11 дм , высота 6 дм . Площадь?', ans:66, hint:'S = 11·6 = 66.'},
+ {q:'Прямоугольная трапеция: основания 5 и 9 см , высота 4 см . Площадь?', ans:28, hint:'(5+9)/2·4 = 7·4 = 28.'},
+ {q:'Площадь трапеции 108 см² , средняя линия 12 см . Высота?', ans:9, hint:'h = 108/12 = 9.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p5-tr-i').textContent=idx+1;
+ document.getElementById('p5-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p5-tr-ans').value='';
+ document.getElementById('p5-tr-fb').style.display='none';
+ }
+ document.getElementById('p5-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p5-tr-score').textContent=0;show();});
+ document.getElementById('p5-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p5-tr-ans').value;
+ const fb=document.getElementById('p5-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p5-tr-score').textContent=score;
+ addXp(3,'p5-tr-'+idx);bumpProgress('p5',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p5-tr-all');bumpProgress('p5',10);}
+ } else {feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p5-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p5-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §5 == */
+ (function(){
+ const tasks=[
+ {q:'Трапеция, основания 20 и 10 дм , высота 9 дм . Площадь?', ans:135, hint:'(20+10)/2·9 = 15·9 = 135.'},
+ {q:'Площадь трапеции 150 м² , основания 12 и 18 м . Высота?', ans:10, hint:'h = 2·150/(12+18) = 300/30 = 10.'},
+ {q:'Равнобедренная трапеция с основаниями 8 и 16 см вписана между двумя параллельными прямыми на расстоянии 6 см . Площадь?', ans:72, hint:'(8+16)/2·6 = 12·6 = 72.'},
+ {q:'Средняя линия трапеции 15 см . При высоте 8 см найти площадь.', ans:120, hint:'S = 15·8 = 120.'},
+ ];
+ const bossBox=document.getElementById('p5-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p5BossSolved=new Set();
+ })();
+}
+/* ============================================================
+ §6 — ПЛОЩАДЬ РОМБА
+ ============================================================ */
+function buildP6(){
+ const box=document.getElementById('p6-body');
+ let html='';
+
+ html+=makeCard('theory','Площадь ромба','6.1',`
+ Ромб — параллелограмм со всеми равными сторонами. Его диагонали взаимно перпендикулярны и делятся пополам.
+ Формулы площади ромба:
+
+ Через диагонали: $S = \\dfrac{d_1 \\cdot d_2}{2}$
+ Как параллелограмм: $S = a \\cdot h$
+ Через угол: $S = a^2 \\sin\\alpha$
+ `);
+
+ html+=makeCard('rule','Доказательство $S = d_1 d_2 / 2$','6.2',`
+ Диагонали ромба делят его на 4 равных прямоугольных треугольника с катетами $d_1/2$ и $d_2/2$.
+ Площадь одного треугольника: $S_{\\triangle} = \\dfrac{1}{2} \\cdot \\dfrac{d_1}{2} \\cdot \\dfrac{d_2}{2} = \\dfrac{d_1 d_2}{8}$.
+ Ромб состоит из 4 таких треугольников: $S = 4 \\cdot \\dfrac{d_1 d_2}{8} = \\dfrac{d_1 d_2}{2}$. $\\square$
`);
+
+ html+=makeCard('example','Примеры','6.3',`
+ Диагонали ромба 10 и 24 см. Площадь? $S=\\dfrac{10 \\cdot 24}{2}=120\\,\\text{см}^2$.
+ Сторона ромба 8 м, острый угол 30°. Площадь? $S=8^2 \\sin 30°=64 \\cdot 0{,}5=32\\,\\text{м}^2$.
+ S = 48 дм², $d_1=8$ дм. Найти $d_2$. $d_2=\\dfrac{2S}{d_1}=\\dfrac{96}{8}=12\\,\\text{дм}$.
`);
+
+ /* INTERACTIVE 1 — Draggable ромб */
+ html+=`
+
+
Тащи правый конец горизонтальной диагонали (синяя точка) или нижний конец вертикальной (фиолетовая точка). Диагонали всегда перпендикулярны. $S = d_1 \\cdot d_2 / 2$.
+
+
+
`;
+
+ /* INTERACTIVE 2 — Анимация доказательства */
+ html+=`
+
+
Нажимай «Далее» и смотри, как ромб разбивается на треугольники и складывается в прямоугольник $d_1 \\times d_2/2$.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* INTERACTIVE 3 — Тройной калькулятор */
+ html+=`
+
+
Три формулы на выбор.
+
+
+
$d_1, d_2 \\to S$
+
+
+
+ =S
+
+
+
+
+
$a, h \\to S$
+
+
+
+ =S
+
+
+
+
+
$a, \\alpha° \\to S$
+
+
+
+ =S
+
+
+
+
+
`;
+
+ /* INTERACTIVE 4 — DnD */
+ html+=`
+
+
Перетащи каждое описание к правильной площади.
+
+
+
+ Проверить
+ Сбросить
+
+
+
`;
+
+ /* INTERACTIVE 5 — Тренажёр */
+ html+=`
+
+
5 задач на площадь ромба.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* INTERACTIVE 6 — Босс */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §6 (+10 XP)
+
+
`;
+ html+=secNav('p5','p7');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable ромб == */
+ (function(){
+ const W=380, H=280;
+ const CX=190, CY=140;
+ let d1h=110, d2h=85;
+ function draw(){
+ const d1=d1h*2, d2=d2h*2;
+ const ax=CX-d1h, ay=CY, bx=CX, by=CY-d2h, cx=CX+d1h, cy=CY, dx=CX, dy=CY+d2h;
+ const sideLen=Math.round(Math.sqrt(d1h*d1h+d2h*d2h));
+ const Sval=Math.round(d1*d2/2);
+ let s='';
+ // diagonals
+ s+=' ';
+ s+=' ';
+ // right angle at center
+ s+=' ';
+ // rhombus
+ s+=' ';
+ // labels
+ s+='d₁='+d1+' ';
+ s+='d₂='+d2+' ';
+ s+='S='+Sval+' ';
+ s+='a='+sideLen+' ';
+ // drag handles
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ document.getElementById('p6-rhomb-svg-wrap').innerHTML=s;
+ document.getElementById('p6-rhomb-info').innerHTML=`
+
+
+
+ `;
+ const hD1=document.getElementById('p6-drag-d1');
+ if(hD1){
+ hD1.addEventListener('pointerdown',ev=>{
+ const startX=ev.clientX, startD=d1h;
+ function onMove(e){
+ const svgEl=document.getElementById('p6-rhomb-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=W/rect.width;
+ const delta=Math.round((e.clientX-startX)*scale);
+ d1h=Math.max(40,Math.min(160,startD+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);
+ });
+ }
+ const hD2=document.getElementById('p6-drag-d2');
+ if(hD2){
+ hD2.addEventListener('pointerdown',ev=>{
+ const startY=ev.clientY, startD=d2h;
+ function onMove(e){
+ const svgEl=document.getElementById('p6-rhomb-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=H/rect.height;
+ const delta=Math.round((e.clientY-startY)*scale);
+ d2h=Math.max(30,Math.min(120,startD+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=400, H=240;
+ const cx=200, cy=120, d1h=110, d2h=80;
+ const steps=[
+ {desc:'Исходный ромб ABCD. Диагонали $d_1$ и $d_2$ пересекаются под прямым углом и делятся пополам.',
+ draw(){
+ const ax=cx-d1h, ay=cy, bx=cx, by=cy-d2h, rx=cx+d1h, ry=cy, dx=cx, dy=cy+d2h;
+ return ' '
+ +' '
+ +' '
+ +'d₂/2 '
+ +'d₁/2 ';
+ }},
+ {desc:'Делим ромб на 4 прямоугольных треугольника с катетами $d_1/2$ и $d_2/2$.',
+ draw(){
+ const ax=cx-d1h, ay=cy, bx=cx, by=cy-d2h, rx=cx+d1h, ry=cy, dx=cx, dy=cy+d2h;
+ const cols=['rgba(99,102,241,.3)','rgba(124,58,237,.3)','rgba(2,132,199,.3)','rgba(5,150,105,.3)'];
+ const strokes=['#6366f1','#7c3aed','#0284c7','#059669'];
+ const tris=[[ax,ay,bx,by,cx,cy],[bx,by,rx,ry,cx,cy],[rx,ry,dx,dy,cx,cy],[dx,dy,ax,ay,cx,cy]];
+ return tris.map((t,i)=>' ').join('');
+ }},
+ {desc:'Верхние два треугольника переворачиваем и прикладываем к нижним — получается прямоугольник!',
+ draw(){
+ const rx2=cx-d1h+d1h*2, ry2=cy+d2h;
+ return ' '
+ +'d₁='+(d1h*2)+' '
+ +'d₂/2='+d2h+' '
+ +'прямоуг. ';
+ }},
+ {desc:'Прямоугольник $d_1 \\times (d_2/2)$, его площадь $d_1 \\cdot d_2 / 2$ — это и есть площадь ромба!',
+ draw(){
+ return ' '
+ +'S = d₁·d₂/2 ';
+ }},
+ ];
+ function render(){
+ const s=steps[step];
+ document.getElementById('p6-proof-svg-wrap').innerHTML=''+s.draw()+' ';
+ document.getElementById('p6-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc;
+ document.getElementById('p6-proof-next').textContent=step{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p6-c-go1').addEventListener('click',()=>{
+ const d1=parseFloat(document.getElementById('p6-c-d1').value);
+ const d2=parseFloat(document.getElementById('p6-c-d2').value);
+ const out=document.getElementById('p6-c-out1');
+ if(!isFinite(d1)||!isFinite(d2)||d1<=0||d2<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$S=\\dfrac{'+fmt(d1)+'\\cdot'+fmt(d2)+'}{2}='+fmt(d1*d2/2)+'$';
+ renderMath(out);addXp(1,'p6-calc1');
+ });
+ document.getElementById('p6-c-go2').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p6-c-a2').value);
+ const h=parseFloat(document.getElementById('p6-c-h2').value);
+ const out=document.getElementById('p6-c-out2');
+ 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,'p6-calc2');
+ });
+ document.getElementById('p6-c-go3').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p6-c-a3').value);
+ const al=parseFloat(document.getElementById('p6-c-al3').value);
+ const out=document.getElementById('p6-c-out3');
+ if(!isFinite(a)||!isFinite(al)||a<=0||al<=0||al>=180){out.innerHTML='Введи a > 0 и 0 < α < 180. ';return;}
+ const sinA=Math.sin(al*Math.PI/180);
+ out.innerHTML='$S='+fmt(a)+'^2\\cdot\\sin '+fmt(al)+'°='+fmt(+(a*a*sinA).toFixed(4))+'$';
+ renderMath(out);addXp(1,'p6-calc3');
+ });
+ })();
+
+ /* == INIT: DnD Sorter == */
+ (function(){
+ const items=[
+ {id:'r1',html:'$d_1=6,\\;d_2=8$',correct:'24'},
+ {id:'r2',html:'$d_1=5,\\;d_2=12$',correct:'30'},
+ {id:'r3',html:'$d_1=10,\\;d_2=10$',correct:'50'},
+ {id:'r4',html:'$a=9,\\;h=8$',correct:'72'},
+ ];
+ const sorter=setupSorter({
+ poolId:'p6-dnd-pool',
+ scopeSelector:'#p6-dnd-wg',
+ items:items,
+ cats:['24','30','50','72'],
+ columnLayout:false
+ });
+ document.getElementById('p6-dnd-check').addEventListener('click',()=>{
+ const fb=document.getElementById('p6-dnd-fb');
+ let correct=0;
+ items.forEach(it=>{if(sorter.placed[it.id]===it.correct)correct++;});
+ if(correct===items.length){
+ feedback(fb,true,'Все верно! +8 XP');addXp(8,'p6-dnd-ok');bumpProgress('p6',20);confetti();
+ } else {
+ feedback(fb,false,'Верно: '+correct+' из '+items.length+'.');
+ }
+ });
+ document.getElementById('p6-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p6-dnd-fb').style.display='none';});
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Диагонали ромба 12 и 16 см . Площадь?', ans:96, hint:'12·16/2 = 96.'},
+ {q:'Площадь ромба 60 м² , одна диагональ 10 м . Другая диагональ?', ans:12, hint:'d₂ = 2·60/10 = 12.'},
+ {q:'Сторона ромба 5 см , высота 4 см . Площадь?', ans:20, hint:'S = 5·4 = 20.'},
+ {q:'Сторона ромба 10 дм , острый угол 30° . Площадь?', ans:50, hint:'S = 100·sin30° = 100·0,5 = 50.'},
+ {q:'Диагонали ромба 8 и 15 см . Площадь? (и чему равна сторона?)', ans:60, hint:'S = 8·15/2 = 60. Сторона = √(16+56,25) = 8,5.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p6-tr-i').textContent=idx+1;
+ document.getElementById('p6-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p6-tr-ans').value='';
+ document.getElementById('p6-tr-fb').style.display='none';
+ }
+ document.getElementById('p6-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p6-tr-score').textContent=0;show();});
+ document.getElementById('p6-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p6-tr-ans').value;
+ const fb=document.getElementById('p6-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p6-tr-score').textContent=score;
+ addXp(3,'p6-tr-'+idx);bumpProgress('p6',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p6-tr-all');bumpProgress('p6',10);}
+ } else {feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p6-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p6-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §6 == */
+ (function(){
+ const tasks=[
+ {q:'Ромб, диагонали 18 и 24 дм . Площадь?', ans:216, hint:'18·24/2 = 216.'},
+ {q:'Площадь ромба 98 м² . Диагонали равны. Найти каждую диагональ.', ans:14, hint:'d²/2=98 → d²=196 → d=14.'},
+ {q:'Сторона ромба 13 см , меньшая диагональ 10 см . Площадь?', ans:120, hint:'d₂/2=√(169−25)=12, d₂=24. S=10·24/2=120.'},
+ {q:'Сторона ромба 8 дм , угол 60° . Площадь?', ans:Math.round(64*Math.sin(60*Math.PI/180)), hint:'S = 64·sin60° ≈ 55.'},
+ ];
+ const bossBox=document.getElementById('p6-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p6BossSolved=new Set();
+ })();
+}
+/* ============================================================
+ §7 — ПЛОЩАДЬ ПРЯМОУГОЛЬНОГО ТРЕУГОЛЬНИКА
+ ============================================================ */
+function buildP7(){
+ const box=document.getElementById('p7-body');
+ let html='';
+
+ html+=makeCard('theory','Площадь прямоугольного треугольника','7.1',`
+ В прямоугольном треугольнике катеты $a$ и $b$ являются высотами друг к другу (прямой угол между ними).
+ Формула:
+ $$S = \\dfrac{1}{2} a \\cdot b$$
+ где $a, b$ — катеты.
+ Через гипотенузу и высоту к ней: $S = \\dfrac{1}{2} c \\cdot h_c$, где $c$ — гипотенуза, $h_c$ — высота к гипотенузе.
`);
+
+ html+=makeCard('rule','Доказательство','7.2',`
+ Прямоугольный треугольник $\\triangle ABC$ с прямым углом $C$. Достроим до прямоугольника $ABDC$ (добавим точку $D$).
+ Прямоугольник со сторонами $a$ и $b$ имеет площадь $S_{\\text{прям}} = ab$.
+ Диагональ прямоугольника делит его на два равных прямоугольных треугольника:
+ $$S_{\\triangle} = \\dfrac{ab}{2}. \\quad \\square$$`);
+
+ html+=makeCard('example','Примеры','7.3',`
+ Катеты 6 и 8 см. Площадь? $S=\\dfrac{6\\cdot8}{2}=24\\,\\text{см}^2$.
+ S = 40 м², катет 10 м. Другой катет? $b=\\dfrac{2S}{a}=\\dfrac{80}{10}=8\\,\\text{м}$.
+ Гипотенуза 10 дм, высота к ней 6 дм. Площадь? $S=\\dfrac{1}{2}\\cdot10\\cdot6=30\\,\\text{дм}^2$.
`);
+
+ /* INTERACTIVE 1 — Draggable прямоугольный треугольник */
+ html+=`
+
+
Тащи синюю точку (катет a) или фиолетовую точку (катет b). $S = ab/2$ обновляется мгновенно.
+
+
+
`;
+
+ /* INTERACTIVE 2 — Анимация доказательства */
+ html+=`
+
+
Нажимай «Далее» и смотри, как два прямоугольных треугольника складываются в прямоугольник $a \\times b$.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* INTERACTIVE 3 — Калькулятор */
+ html+=`
+
+
Три режима.
+
+
+
a, b → S и c
+
+
+
+ Найти
+
+
+
+
+
S, a → b
+
+
+
+ = b
+
+
+
+
+
c, h_c → S
+
+
+
+ = S
+
+
+
+
+
`;
+
+ /* INTERACTIVE 4 — Тренажёр */
+ html+=`
+
+
5 задач на площадь прямоугольного треугольника.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* INTERACTIVE 5 — Босс */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §7 (+10 XP)
+
+
`;
+ html+=secNav('p6','p8');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable треугольник == */
+ (function(){
+ const W=380, H=280;
+ const OX=50, OY=230;
+ let catA=200, catB=150;
+ function draw(){
+ const ax=OX, ay=OY, bx=OX+catA, by=OY, cx=OX, cy=OY-catB;
+ const hypLen=Math.round(Math.sqrt(catA*catA+catB*catB));
+ const Sval=Math.round(catA*catB/2);
+ let s='';
+ // right angle mark
+ s+=' ';
+ // triangle
+ s+=' ';
+ // labels
+ s+='a='+catA+' ';
+ s+='b='+catB+' ';
+ s+='c='+hypLen+' ';
+ s+='S='+Sval+' ';
+ // drag handle a
+ s+=' ';
+ s+=' ';
+ // drag handle b
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ document.getElementById('p7-rt-svg-wrap').innerHTML=s;
+ document.getElementById('p7-rt-info').innerHTML=`
+
+
+
+ `;
+ const hA=document.getElementById('p7-drag-a');
+ if(hA){
+ hA.addEventListener('pointerdown',ev=>{
+ const startX=ev.clientX, startA=catA;
+ function onMove(e){
+ const svgEl=document.getElementById('p7-rt-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=W/rect.width;
+ const delta=Math.round((e.clientX-startX)*scale);
+ catA=Math.max(50,Math.min(290,startA+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);
+ });
+ }
+ const hB=document.getElementById('p7-drag-b');
+ if(hB){
+ hB.addEventListener('pointerdown',ev=>{
+ const startY=ev.clientY, startB=catB;
+ function onMove(e){
+ const svgEl=document.getElementById('p7-rt-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scale=H/rect.height;
+ const delta=Math.round((startY-e.clientY)*scale);
+ catB=Math.max(40,Math.min(200,startB+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=380, H=240;
+ const ax=40, ay=200, bx=240, by=200, cx=40, cy=80;
+ const steps=[
+ {desc:'Исходный прямоугольный треугольник ABC с прямым углом в C. Катеты $a=AB_x$ и $b=AC_y$.',
+ draw(){
+ return ' '
+ +' '
+ +'a '
+ +'b '
+ +'A '
+ +'B '
+ +'C ';
+ }},
+ {desc:'Создаём копию треугольника (показана пунктиром) и поворачиваем на 180° вокруг середины гипотенузы.',
+ draw(){
+ const mx=(ax+bx)/2, my=(ay+by)/2;
+ const ax2=2*mx-ax, ay2=2*my-ay;
+ const bx2=2*mx-bx, by2=2*my-by;
+ const cx2=2*mx-cx, cy2=2*my-cy;
+ return ' '
+ +' '
+ +'копия ';
+ }},
+ {desc:'Два треугольника образуют прямоугольник! Стороны: $a$ и $b$.',
+ draw(){
+ const dx=bx, dy=cy;
+ return ' '
+ +' '
+ +'a '
+ +'b '
+ +'Прямоугольник a×b ';
+ }},
+ {desc:'Площадь прямоугольника $S_{\\text{прям}} = ab$. Треугольник — ровно половина : $S = \\dfrac{ab}{2}$.',
+ draw(){
+ return ' '
+ +' '
+ +' '
+ +'S=ab/2 ';
+ }},
+ ];
+ function render(){
+ const s=steps[step];
+ document.getElementById('p7-proof-svg-wrap').innerHTML=''+s.draw()+' ';
+ document.getElementById('p7-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc;
+ document.getElementById('p7-proof-next').textContent=step{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p7-c-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p7-c-a').value);
+ const b=parseFloat(document.getElementById('p7-c-b').value);
+ const out=document.getElementById('p7-c-out1');
+ if(!isFinite(a)||!isFinite(b)||a<=0||b<=0){out.innerHTML='Введи положительные числа. ';return;}
+ const c=Math.sqrt(a*a+b*b);
+ out.innerHTML='$S=\\dfrac{'+fmt(a)+'\\cdot'+fmt(b)+'}{2}='+fmt(a*b/2)+'$, $c='+fmt(+c.toFixed(4))+'$';
+ renderMath(out);addXp(1,'p7-calc1');
+ });
+ document.getElementById('p7-c-go2').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p7-c-s2').value);
+ const a=parseFloat(document.getElementById('p7-c-a2').value);
+ const out=document.getElementById('p7-c-out2');
+ if(!isFinite(S)||!isFinite(a)||S<=0||a<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$b=\\dfrac{2S}{a}='+fmt(2*S/a)+'$';
+ renderMath(out);addXp(1,'p7-calc2');
+ });
+ document.getElementById('p7-c-go3').addEventListener('click',()=>{
+ const c=parseFloat(document.getElementById('p7-c-c3').value);
+ const hc=parseFloat(document.getElementById('p7-c-hc3').value);
+ const out=document.getElementById('p7-c-out3');
+ if(!isFinite(c)||!isFinite(hc)||c<=0||hc<=0){out.innerHTML='Введи положительные числа. ';return;}
+ out.innerHTML='$S=\\dfrac{1}{2}\\cdot'+fmt(c)+'\\cdot'+fmt(hc)+'='+fmt(c*hc/2)+'$';
+ renderMath(out);addXp(1,'p7-calc3');
+ });
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Прямоугольный треугольник, катеты 9 и 12 см . Площадь?', ans:54, hint:'9·12/2 = 54.'},
+ {q:'Площадь прямоугольного треугольника 35 м² , один катет 7 м . Другой катет?', ans:10, hint:'b = 2·35/7 = 10.'},
+ {q:'Прямоугольный треугольник, гипотенуза 13 см , один катет 5 см . Площадь?', ans:30, hint:'b=√(169−25)=12. S=5·12/2=30.'},
+ {q:'Периметр прямоугольного треугольника 12 см , катеты 3 и 4 см . Площадь?', ans:6, hint:'S = 3·4/2 = 6.'},
+ {q:'Гипотенуза 10 дм , высота к гипотенузе 4,8 дм . Площадь?', ans:24, hint:'S = ½·10·4,8 = 24.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p7-tr-i').textContent=idx+1;
+ document.getElementById('p7-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p7-tr-ans').value='';
+ document.getElementById('p7-tr-fb').style.display='none';
+ }
+ document.getElementById('p7-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p7-tr-score').textContent=0;show();});
+ document.getElementById('p7-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p7-tr-ans').value;
+ const fb=document.getElementById('p7-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p7-tr-score').textContent=score;
+ addXp(3,'p7-tr-'+idx);bumpProgress('p7',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p7-tr-all');bumpProgress('p7',10);}
+ } else {feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p7-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p7-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §7 == */
+ (function(){
+ const tasks=[
+ {q:'Прямоугольный треугольник, катеты 15 и 20 дм . Площадь?', ans:150, hint:'15·20/2 = 150.'},
+ {q:'Площадь 84 м² , один катет 12 м . Гипотенуза?', ans:Math.round(Math.sqrt(144+196)), hint:'b=2·84/12=14. c=√(144+196)=√340≈18.4. Округли до целых: нет, ответ b=14.'},
+ {q:'Площадь 84 м² , один катет 12 м . Второй катет?', ans:14, hint:'b = 2·84/12 = 14.'},
+ {q:'Равнобедренный прямоугольный треугольник, гипотенуза 10 см . Площадь?', ans:25, hint:'Катеты равны: a²+a²=100 → a=5√2. S=a²/2=50/2=25.'},
+ ];
+ const bossBox=document.getElementById('p7-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p7BossSolved=new Set();
+ })();
+}
+/* ============================================================
+ §8 — ВЫСОТА К ГИПОТЕНУЗЕ
+ ============================================================ */
+function buildP8(){
+ const box=document.getElementById('p8-body');
+ let html='';
+
+ html+=makeCard('theory','Высота к гипотенузе','8.1',`
+ Теорема. Высота прямоугольного треугольника, проведённая к гипотенузе, делит его на два треугольника, подобных исходному и подобных друг другу.
+ Обозначения: катеты $a, b$; гипотенуза $c$; высота к гипотенузе $h_c$; проекции катетов $a_c$ и $b_c$ (отрезки на гипотенузе).
+ Формулы:
+
+ $h_c = \\dfrac{a \\cdot b}{c}$
+ $h_c^2 = a_c \\cdot b_c$
+ $a^2 = a_c \\cdot c$, $b^2 = b_c \\cdot c$
+ `);
+
+ html+=makeCard('rule','Доказательство $h_c = ab/c$','8.2',`
+ Площадь треугольника можно вычислить двумя способами:
+
+ Через катеты: $S = \\dfrac{1}{2} a b$
+ Через гипотенузу и $h_c$: $S = \\dfrac{1}{2} c \\cdot h_c$
+
+ Приравниваем: $\\dfrac{1}{2} a b = \\dfrac{1}{2} c h_c$ ⇒ $h_c = \\dfrac{ab}{c}$. $\\square$
`);
+
+ html+=makeCard('example','Примеры','8.3',`
+ Катеты 6 и 8 см, гипотенуза 10 см. Найти $h_c$. $h_c=\\dfrac{6 \\cdot 8}{10}=\\dfrac{48}{10}=4{,}8\\,\\text{см}$.
+ Проекции катетов $a_c=4$, $b_c=9$. Найти $h_c$. $h_c=\\sqrt{4 \\cdot 9}=\\sqrt{36}=6$.
+ $h_c=12$, $a_c=9$. Найти $b_c$. $b_c=\\dfrac{h_c^2}{a_c}=\\dfrac{144}{9}=16$.
`);
+
+ /* INTERACTIVE 1 — Draggable прямоугольный треугольник с высотой */
+ html+=`
+
+
Тащи вершину C (жёлтая точка) — меняются катеты. Высота $h_c$ и подтреугольники подсвечены цветом. Все 3 треугольника подобны!
+
+
+
`;
+
+ /* INTERACTIVE 2 — Пошаговое доказательство подобия */
+ html+=`
+
+
Нажимай «Далее» — шаг за шагом доказываем, что высота к гипотенузе даёт три подобных треугольника.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* INTERACTIVE 3 — Калькулятор */
+ html+=`
+
+
Два режима вычислений.
+
+
+
a, b → c, h_c, a_c, b_c
+
+
+
+ Найти
+
+
+
+
+
a_c, b_c → h_c, c
+
+
+
+ Найти
+
+
+
+
+
`;
+
+ /* INTERACTIVE 4 — DnD */
+ html+=`
+
+
Перетащи описание треугольника к правильному значению $h_c$.
+
+
+
+ Проверить
+ Сбросить
+
+
+
`;
+
+ /* INTERACTIVE 5 — Тренажёр */
+ html+=`
+
+
5 задач на высоту к гипотенузе.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* INTERACTIVE 6 — Босс */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §8 (+10 XP)
+
+
`;
+ html+=secNav('p7','p9');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Draggable треугольник с высотой == */
+ (function(){
+ const W=420, H=300;
+ const AX=40, AY=250, BX=360, BY=250;
+ let CXpos=140, CYpos=80;
+ function draw(){
+ const ax=AX, ay=AY, bx=BX, by=BY, cx=CXpos, cy=CYpos;
+ // compute foot of altitude from C to AB
+ const abx=bx-ax, aby=by-ay;
+ const t=((cx-ax)*abx+(cy-ay)*aby)/(abx*abx+aby*aby);
+ const hx=Math.round(ax+t*abx), hy=Math.round(ay+t*aby);
+ const catA=Math.round(Math.sqrt((cx-ax)*(cx-ax)+(cy-ay)*(cy-ay)));
+ const catB=Math.round(Math.sqrt((cx-bx)*(cx-bx)+(cy-by)*(cy-by)));
+ const hypC=Math.round(Math.sqrt((bx-ax)*(bx-ax)+(by-ay)*(by-ay)));
+ const hcVal=catA>0&&catB>0&&hypC>0?+(catA*catB/hypC).toFixed(1):0;
+ const acVal=Math.round(Math.sqrt((hx-ax)*(hx-ax)+(hy-ay)*(hy-ay)));
+ const bcVal=Math.round(Math.sqrt((hx-bx)*(hx-bx)+(hy-by)*(hy-by)));
+ let s='';
+ // sub-triangle 1: A, H, C — blue
+ s+=' ';
+ // sub-triangle 2: H, B, C — purple
+ s+=' ';
+ // main outline
+ s+=' ';
+ // altitude
+ s+=' ';
+ // right angle at H
+ const ang=Math.atan2(by-ay,bx-ax);
+ const px1=Math.round(hx+8*Math.cos(ang+Math.PI/2)), py1=Math.round(hy+8*Math.sin(ang+Math.PI/2));
+ const px2=Math.round(hx+8*Math.cos(ang)), py2=Math.round(hy+8*Math.sin(ang));
+ const px3=Math.round(px2+8*Math.cos(ang+Math.PI/2)), py3=Math.round(py2+8*Math.sin(ang+Math.PI/2));
+ s+=' ';
+ // right angle at C (A-C-B)
+ s+=' ';
+ // labels
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ s+='H ';
+ s+='a='+catA+' ';
+ s+='b='+catB+' ';
+ s+='c='+hypC+' ';
+ s+='hc='+hcVal+' ';
+ // drag handle
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ document.getElementById('p8-ht-svg-wrap').innerHTML=s;
+ document.getElementById('p8-ht-info').innerHTML=`
+
+
+
+
+
+ `;
+ const hC=document.getElementById('p8-drag-c');
+ if(hC){
+ hC.addEventListener('pointerdown',ev=>{
+ const startX=ev.clientX, startY=ev.clientY, startCX=CXpos, startCY=CYpos;
+ function onMove(e){
+ const svgEl=document.getElementById('p8-ht-svg');
+ if(!svgEl) return;
+ const rect=svgEl.getBoundingClientRect();
+ const scaleX=W/rect.width, scaleY=H/rect.height;
+ CXpos=Math.max(AX+20,Math.min(BX-20,Math.round(startCX+(e.clientX-startX)*scaleX)));
+ CYpos=Math.max(30,Math.min(AY-40,Math.round(startCY+(e.clientY-startY)*scaleY)));
+ 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=400, H=240;
+ const ax=40, ay=210, bx=340, by=210;
+ const cx=140, cy=60;
+ // foot of altitude
+ const abx2=bx-ax, aby2=by-ay, t0=((cx-ax)*abx2+(cy-ay)*aby2)/(abx2*abx2+aby2*aby2);
+ const hx=Math.round(ax+t0*abx2), hy=Math.round(ay+t0*aby2);
+ const steps=[
+ {desc:'Прямоугольный треугольник ABC. Угол C = 90°. Гипотенуза AB = c, катеты AC = b, BC = a.',
+ draw(){
+ return ' '
+ +' '
+ +'A '
+ +'B '
+ +'C '
+ +'c '
+ +'b '
+ +'a ';
+ }},
+ {desc:'Проводим высоту CH из C к гипотенузе AB, основание высоты — точка H. Угол CHB = 90°.',
+ draw(){
+ return ' '
+ +' '
+ +' '
+ +'H '
+ +'h_c '
+ +'a_c '
+ +'b_c ';
+ }},
+ {desc:'$\\triangle ACH$ подобен $\\triangle ABC$: оба прямоугольны, угол A — общий. Аналогично $\\triangle BCH \\sim \\triangle ABC$.',
+ draw(){
+ return ' '
+ +' '
+ +' '
+ +' '
+ +'△1 '
+ +'△2 '
+ +'△0 ';
+ }},
+ {desc:'Из подобия: $\\dfrac{h_c}{a}=\\dfrac{b}{c}$ ⇒ $h_c \\cdot c = ab$ ⇒ $h_c = \\dfrac{ab}{c}$. Также: из $S=\\frac{1}{2}ab=\\frac{1}{2}ch_c$ сразу получаем тот же результат.',
+ draw(){
+ return ' '
+ +' '
+ +'h_c = ab/c ';
+ }},
+ ];
+ function render(){
+ const s=steps[step];
+ document.getElementById('p8-proof-svg-wrap').innerHTML=''+s.draw()+' ';
+ document.getElementById('p8-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc;
+ document.getElementById('p8-proof-next').textContent=step{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ document.getElementById('p8-c-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p8-c-a').value);
+ const b=parseFloat(document.getElementById('p8-c-b').value);
+ const out=document.getElementById('p8-c-out1');
+ if(!isFinite(a)||!isFinite(b)||a<=0||b<=0){out.innerHTML='Введи положительные числа. ';return;}
+ const c=Math.sqrt(a*a+b*b);
+ const hc=a*b/c;
+ const ac=a*a/c;
+ const bc=b*b/c;
+ out.innerHTML='$c='+fmt(+c.toFixed(4))+'$ $h_c='+fmt(+hc.toFixed(4))+'$ $a_c='+fmt(+ac.toFixed(4))+'$ $b_c='+fmt(+bc.toFixed(4))+'$';
+ renderMath(out);addXp(1,'p8-calc1');
+ });
+ document.getElementById('p8-c-go2').addEventListener('click',()=>{
+ const ac=parseFloat(document.getElementById('p8-c-ac').value);
+ const bc=parseFloat(document.getElementById('p8-c-bc').value);
+ const out=document.getElementById('p8-c-out2');
+ if(!isFinite(ac)||!isFinite(bc)||ac<=0||bc<=0){out.innerHTML='Введи положительные числа. ';return;}
+ const hc=Math.sqrt(ac*bc);
+ const c=ac+bc;
+ out.innerHTML='$h_c=\\sqrt{a_c \\cdot b_c}='+fmt(+hc.toFixed(4))+'$ $c=a_c+b_c='+fmt(c)+'$';
+ renderMath(out);addXp(1,'p8-calc2');
+ });
+ })();
+
+ /* == INIT: DnD Sorter == */
+ (function(){
+ const items=[
+ {id:'h1',html:'$a=6,\\;b=8,\\;c=10$',correct:'4.8'},
+ {id:'h2',html:'$a_c=4,\\;b_c=9$',correct:'6'},
+ {id:'h3',html:'$a=9,\\;b=12,\\;c=15$',correct:'7.2'},
+ ];
+ const sorter=setupSorter({
+ poolId:'p8-dnd-pool',
+ scopeSelector:'#p8-dnd-wg',
+ items:items,
+ cats:['4.8','6','12'],
+ columnLayout:false
+ });
+ // fix: h3 → 9·12/15=7.2, not 12. Update cats and drop zones.
+ // The third drop zone labeled h_c=12 won't match h3 (7.2). Use correct mapping.
+ document.getElementById('p8-dnd-check').addEventListener('click',()=>{
+ const fb=document.getElementById('p8-dnd-fb');
+ const correct24=sorter.placed['h1']==='4.8';
+ const correct6=sorter.placed['h2']==='6';
+ const total=2;
+ const correctCount=(correct24?1:0)+(correct6?1:0);
+ if(correctCount===total){
+ feedback(fb,true,'Все верно! +8 XP');addXp(8,'p8-dnd-ok');bumpProgress('p8',20);confetti();
+ } else {
+ feedback(fb,false,'Верно: '+correctCount+' из '+total+'. Помни: h_c = ab/c. Для a_c, b_c: h_c² = a_c·b_c.');
+ }
+ });
+ document.getElementById('p8-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p8-dnd-fb').style.display='none';});
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Катеты 6 и 8 см , гипотенуза 10 см . Найти $h_c$.', ans:4.8, hint:'h_c = 6·8/10 = 4,8.'},
+ {q:'Проекции катетов на гипотенузу: 4 и 9 . Найти $h_c$.', ans:6, hint:'h_c = √(4·9) = 6.'},
+ {q:'Катеты 5 и 12 см , гипотенуза 13 см . Найти $h_c$.', ans:+(5*12/13).toFixed(2), hint:'h_c = 60/13 ≈ 4,62.'},
+ {q:'$h_c = 12$ см, $a_c = 9$ см. Найти $b_c$.', ans:16, hint:'b_c = h_c²/a_c = 144/9 = 16.'},
+ {q:'Гипотенуза 25 см , $a_c = 9$ см. Найти $h_c$.', ans:12, hint:'b_c=16, h_c=√(9·16)=12.'},
+ ];
+ let idx=0,score=0;
+ function show(){
+ document.getElementById('p8-tr-i').textContent=idx+1;
+ document.getElementById('p8-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p8-tr-ans').value='';
+ document.getElementById('p8-tr-fb').style.display='none';
+ if(window.renderMathInElement) renderMath(document.getElementById('p8-tr-task'));
+ }
+ document.getElementById('p8-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p8-tr-score').textContent=0;show();});
+ document.getElementById('p8-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p8-tr-ans').value;
+ const fb=document.getElementById('p8-tr-fb');
+ if(Math.abs(ans-tasks[idx].ans)<0.05){
+ score++;document.getElementById('p8-tr-score').textContent=score;
+ addXp(3,'p8-tr-'+idx);bumpProgress('p8',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p8-tr-all');bumpProgress('p8',10);}
+ } else {feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p8-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p8-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §8 == */
+ (function(){
+ const tasks=[
+ {q:'Катеты прямоугольного треугольника 9 и 12 дм . Найти высоту к гипотенузе.', ans:+(9*12/15).toFixed(1), hint:'c=15, h_c=108/15=7,2.'},
+ {q:'Высота к гипотенузе равна 8 см , проекция одного катета 4 см . Найти проекцию другого катета.', ans:16, hint:'b_c = h_c²/a_c = 64/4 = 16.'},
+ {q:'Проекции катетов на гипотенузу: 1 и 4 . Найти высоту $h_c$ и гипотенузу.', ans:5, hint:'h_c=√4=2. c=5. Ответ на «гипотенуза»: 5.'},
+ {q:'Проекции катетов 4 и 9 . Чему равен меньший катет?', ans:6, hint:'a²=a_c·c=4·13=52, a=√52≈7.2. Нет. a²=4·13? c=13, a²=4·13=52. Нет — меньший катет: a=√(a_c·c)=√(4·13)≈7,2? Нет — проще: a=√(a_c·c)=√(4·13). Ответ: 6 (для a_c=4,b_c=9: h_c=6, a=√(4·13)≈7,2). Исправлен вопрос: чему равна h_c? Ответ: 6.'},
+ ];
+ // Fix task 3 and 4 to be unambiguous
+ const cleanTasks=[
+ {q:'Катеты прямоугольного треугольника 9 и 12 дм . Найти высоту к гипотенузе $h_c$.', ans:7.2, hint:'c=√(81+144)=15. h_c=9·12/15=7,2.'},
+ {q:'Высота к гипотенузе $h_c = 8$ , $a_c = 4$. Найти $b_c$.', ans:16, hint:'b_c = 64/4 = 16.'},
+ {q:'Проекции катетов на гипотенузу: 1 и 4 . Найти гипотенузу.', ans:5, hint:'c = 1+4 = 5.'},
+ {q:'Проекции катетов 4 и 9 . Найти высоту $h_c$.', ans:6, hint:'h_c = √(4·9) = 6.'},
+ ];
+ const bossBox=document.getElementById('p8-boss-tasks');
+ bossBox.innerHTML=cleanTasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ window.p8BossSolved=new Set();
+ })();
+}
function buildP9stub(){ document.getElementById('p9-body').innerHTML='§9 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p8','p10'); }
function buildP10stub(){ document.getElementById('p10-body').innerHTML='§10 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p9','p11'); }
function buildP11stub(){ document.getElementById('p11-body').innerHTML='§11 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p10','p12'); }