diff --git a/frontend/textbooks/geometry_8_ch2.html b/frontend/textbooks/geometry_8_ch2.html
index 22de961..dc0683b 100644
--- a/frontend/textbooks/geometry_8_ch2.html
+++ b/frontend/textbooks/geometry_8_ch2.html
@@ -375,7 +375,7 @@ const BUILT=new Set();
const BUILDERS={
p1:()=>buildP1(),p2:()=>buildP2(),p3:()=>buildP3(),p4:()=>buildP4(),p5:()=>buildP5(),
p6:()=>buildP6(),p7:()=>buildP7(),p8:()=>buildP8(),p9:()=>buildP9(),p10:()=>buildP10(),
- p11:()=>buildP11(),p12:()=>buildP12stub(),p13:()=>buildP13stub(),p14:()=>buildP14stub(),p15:()=>buildP15stub(),
+ p11:()=>buildP11(),p12:()=>buildP12(),p13:()=>buildP13(),p14:()=>buildP14(),p15:()=>buildP15(),
final2:()=>buildFinal2stub(),
};
function ensureBuilt(id){ if(BUILT.has(id))return;const fn=BUILDERS[id];if(fn){fn();BUILT.add(id);} }
@@ -5108,10 +5108,1411 @@ function buildP11(){
if(window.renderMathInElement)renderMath(bossBox);
})();
}
-function buildP12stub(){ document.getElementById('p12-body').innerHTML='
§12 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p11','p13'); }
-function buildP13stub(){ document.getElementById('p13-body').innerHTML='§13 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p12','p14'); }
-function buildP14stub(){ document.getElementById('p14-body').innerHTML='§14 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p13','p15'); }
-function buildP15stub(){ document.getElementById('p15-body').innerHTML='§15 — Волна 1 : содержимое появится в следующем обновлении.
'+secNav('p14','final2'); }
+function buildP12(){
+ const box=document.getElementById('p12-body');
+ let html='';
+
+ html+=makeCard('theory','Равносторонний треугольник','12.1',`
+ Равносторонний треугольник — треугольник, у которого все три стороны равны: $AB=BC=CA=a$.
+ Все углы равны $60°$. Высота, медиана, биссектриса из каждой вершины совпадают.
+ Высота: $h = \\dfrac{a\\sqrt{3}}{2}$
+ Площадь: $S = \\dfrac{a^2\\sqrt{3}}{4}$
+
+
+
+
+
+ A
+ B
+ C
+ h
+ a
+ a
+ a/2
+ a/2
+
+
`);
+
+ html+=makeCard('rule','Вывод формулы высоты','12.2',`
+ Высота $AM$ делит равносторонний треугольник $\\triangle ABC$ на два прямоугольных треугольника.
+ В $\\triangle ABM$: гипотенуза $AB=a$, катет $BM=\\dfrac{a}{2}$, катет $AM=h$.
+ По теореме Пифагора:
+ $$h^2 = a^2 - \\left(\\frac{a}{2}\\right)^2 = a^2 - \\frac{a^2}{4} = \\frac{3a^2}{4}$$
+ $$h = \\frac{a\\sqrt{3}}{2}$$
+ Площадь: $S = \\dfrac{1}{2}\\cdot a\\cdot h = \\dfrac{1}{2}\\cdot a\\cdot\\dfrac{a\\sqrt{3}}{2} = \\dfrac{a^2\\sqrt{3}}{4}$
+
+
+
+
+ A
+ B
+ M
+ a
+ h
+ a/2
+ гипотенуза
+
+
`);
+
+ html+=makeCard('example','Примеры вычислений','12.3',`
+ Пример 1. Сторона равностороннего треугольника $a=6$ см. Найти $h$ и $S$.
+ $h = \\dfrac{6\\sqrt{3}}{2} = 3\\sqrt{3} \\approx 5{,}196$ см.
+ $S = \\dfrac{36\\sqrt{3}}{4} = 9\\sqrt{3} \\approx 15{,}59$ см².
+ Пример 2. Высота равностороннего треугольника $h=6$ см. Найти $a$.
+ $a = \\dfrac{2h}{\\sqrt{3}} = \\dfrac{12}{\\sqrt{3}} = 4\\sqrt{3} \\approx 6{,}93$ см.
`);
+
+ /* ИНТЕРАКТИВ 1 — Слайдер + SVG */
+ html+=`
+
+
Тяни слайдер — треугольник перестраивается, $h$ и $S$ пересчитываются в реальном времени.
+
+ Сторона $a$ = 8 см
+
+
+
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 2 — Пошаговый вывод */
+ html+=`
+
+
4 шага — от треугольника до формулы $h = \\dfrac{a\\sqrt{3}}{2}$.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* ИНТЕРАКТИВ 3 — Калькулятор */
+ html+=`
+
+
+
+
$a$ → $h$, $S$
+
+
+ Вычислить
+
+
+
+
+
$S$ → $a$
+
+
+ Найти a
+
+
+
+
+
$h$ → $a$
+
+
+ Найти a
+
+
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 4 — Тренажёр */
+ html+=`
+
+
5 задач на высоту и площадь равностороннего треугольника.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 5 — DnD сортер */
+ html+=`
+
+
Перетащи каждую карточку в правильную колонку.
+
+
+
Высота $h$ (точное значение)
+
+
+
+
Площадь $S$ (точное значение)
+
+
+
+
+
+ Проверить
+ Сброс
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 6 — Босс §12 */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §12 (+10 XP)
+
+
`;
+ html+=secNav('p11','p13');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Слайдер == */
+ (function(){
+ const sl=document.getElementById('p12-sl-a');
+ const W=300, H=260;
+ function draw(){
+ const a=+sl.value;
+ document.getElementById('p12-sl-a-val').textContent=a;
+ const scale=Math.min(10, 200/a);
+ const px=a*scale;
+ const h=a*Math.sqrt(3)/2;
+ const S=a*a*Math.sqrt(3)/4;
+ const cx=W/2, baseY=H-30, topY=baseY-h*scale;
+ const bx1=cx-px/2, bx2=cx+px/2;
+ const midX=cx;
+ let s='';
+ s+=' ';
+ s+=' ';
+ const rm=8;
+ s+=' ';
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ s+='h ';
+ s+='a ';
+ s+='a ';
+ s+='a ';
+ s+=' ';
+ document.getElementById('p12-sl-svg-wrap').innerHTML=s;
+ const hVal=(a*Math.sqrt(3)/2);
+ document.getElementById('p12-sl-info').innerHTML=`
+
+ Высота h
${hVal.toFixed(3)} см
+ Площадь S
${S.toFixed(3)} см² `;
+ }
+ sl.addEventListener('input',()=>{draw();addXp(1,'p12-sl');});
+ draw();
+ })();
+
+ /* == INIT: Пошаговый вывод == */
+ (function(){
+ let step=0;
+ const W=260, H=200;
+ const cx=130, topY=20, baseY=178, bx1=28, bx2=232, midX=cx;
+ const steps=[
+ {desc:'Равносторонний треугольник $\\triangle ABC$ со стороной $a$. Все стороны равны, все углы $= 60°$.'},
+ {desc:'Проводим высоту $AM$ из вершины $A$ на сторону $BC$. Высота в равностороннем треугольнике — одновременно медиана и биссектриса.'},
+ {desc:'Образуется прямоугольный треугольник $\\triangle ABM$ с катетами $BM = a/2$ и $AM = h$, гипотенузой $AB = a$.'},
+ {desc:'По теореме Пифагора: $h^2 + (a/2)^2 = a^2$ $\\Rightarrow$ $h^2 = a^2 - a^2/4 = 3a^2/4$ $\\Rightarrow$ $\\boxed{h = \\dfrac{a\\sqrt{3}}{2}}$'},
+ ];
+ function render(){
+ let s='';
+ s+=' ';
+ if(step>=1){
+ s+=' ';
+ s+=' ';
+ }
+ if(step>=2){
+ s+=' ';
+ s+='a/2 ';
+ s+='h ';
+ }
+ if(step>=3){
+ s+='a ';
+ s+='h=a√3/2 ';
+ }
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ if(step>=1) s+='M ';
+ s+='Шаг '+(step+1)+'/4 ';
+ s+=' ';
+ document.getElementById('p12-steps-svg-wrap').innerHTML=s;
+ document.getElementById('p12-steps-desc').innerHTML=steps[step].desc;
+ if(window.renderMathInElement) renderMath(document.getElementById('p12-steps-desc'));
+ }
+ document.getElementById('p12-steps-next').addEventListener('click',()=>{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ const sqrt3=Math.sqrt(3);
+ document.getElementById('p12-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p12-ca').value);
+ const out=document.getElementById('p12-out1');
+ if(!isFinite(a)||a<=0){out.innerHTML='Введи положительное число. ';return;}
+ const h=a*sqrt3/2, S=a*a*sqrt3/4;
+ out.innerHTML='$h = \\dfrac{'+fmt(a)+'\\sqrt{3}}{2} \\approx '+fmt(+h.toFixed(4))+'$ см $S = \\dfrac{'+fmt(a*a)+'\\sqrt{3}}{4} \\approx '+fmt(+S.toFixed(4))+'$ см²';
+ renderMath(out); addXp(1,'p12-calc1');
+ });
+ document.getElementById('p12-go2').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p12-cs').value);
+ const out=document.getElementById('p12-out2');
+ if(!isFinite(S)||S<=0){out.innerHTML='Введи S > 0. ';return;}
+ const a=Math.sqrt(4*S/sqrt3);
+ out.innerHTML='$a = \\sqrt{\\dfrac{4S}{\\sqrt{3}}} \\approx '+fmt(+a.toFixed(4))+'$ см';
+ renderMath(out); addXp(1,'p12-calc2');
+ });
+ document.getElementById('p12-go3').addEventListener('click',()=>{
+ const h=parseFloat(document.getElementById('p12-ch').value);
+ const out=document.getElementById('p12-out3');
+ if(!isFinite(h)||h<=0){out.innerHTML='Введи h > 0. ';return;}
+ const a=2*h/sqrt3;
+ out.innerHTML='$a = \\dfrac{2h}{\\sqrt{3}} \\approx '+fmt(+a.toFixed(4))+'$ см';
+ renderMath(out); addXp(1,'p12-calc3');
+ });
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const sqrt3=Math.sqrt(3);
+ const tasks=[
+ {q:'Сторона равностороннего треугольника $a = 4$ см. Найти высоту $h$ (округли до целых).', ans:3, tol:0.6, hint:'h = 4√3/2 = 2√3 ≈ 3,46 ≈ 3.'},
+ {q:'Сторона $a = 10$ см. Найти площадь $S$ (округли до целых).', ans:43, tol:1, hint:'S = 100√3/4 = 25√3 ≈ 43,3 ≈ 43.'},
+ {q:'Высота равностороннего треугольника $h = 9$ см. Найти сторону $a$ (округли до целых).', ans:10, tol:1, hint:'a = 2·9/√3 = 18/√3 = 6√3 ≈ 10,4 ≈ 10.'},
+ {q:'Сторона $a = 2$ см. Найти $S$ (оставь в точном виде — умножь $\\sqrt{3}$ на число и введи целое число перед ней).', ans:1, tol:0.05, hint:'S = 4√3/4 = √3 ≈ 1,732 → ответ: 1 (если просят коэффициент при √3).'},
+ {q:'Площадь равностороннего треугольника $S = 4\\sqrt{3}$ см². Найти сторону $a$ (целое число).', ans:4, tol:0.2, hint:'a² = 4S/√3 = 16 → a = 4.'},
+ ];
+ let idx=0, score=0;
+ function show(){
+ document.getElementById('p12-tr-i').textContent=idx+1;
+ document.getElementById('p12-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p12-tr-ans').value='';
+ document.getElementById('p12-tr-fb').style.display='none';
+ if(window.renderMathInElement) renderMath(document.getElementById('p12-tr-task'));
+ }
+ document.getElementById('p12-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p12-tr-score').textContent=0;show();});
+ document.getElementById('p12-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p12-tr-ans').value;
+ const fb=document.getElementById('p12-tr-fb');
+ fb.style.display='block';
+ const tol=tasks[idx].tol||0.5;
+ if(Math.abs(ans-tasks[idx].ans)show(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p12-tr-all');bumpProgress('p12',10);}
+ }else{feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p12-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p12-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: DnD сортер == */
+ (function(){
+ const items=[
+ {id:'d1', html:'a=2 → h=√3', cat:'h'},
+ {id:'d2', html:'a=2 → S=√3', cat:'s'},
+ {id:'d3', html:'a=4 → h=2√3', cat:'h'},
+ {id:'d4', html:'a=4 → S=4√3', cat:'s'},
+ {id:'d5', html:'a=6 → h=3√3', cat:'h'},
+ {id:'d6', html:'a=6 → S=9√3', cat:'s'},
+ ];
+ const sorter=setupSorter({poolId:'p12-dnd-pool',scopeSelector:'#p12-dnd-wg',items:items.map(x=>({id:x.id,html:x.html})),cats:['h','s']});
+ document.getElementById('p12-dnd-check').addEventListener('click',()=>{
+ const fb=document.getElementById('p12-dnd-fb');fb.style.display='block';
+ let ok=0;
+ items.forEach(it=>{if(sorter.placed[it.id]===it.cat)ok++;});
+ if(ok===items.length){feedback(fb,true,'Всё верно! +5 XP');addXp(5,'p12-dnd');bumpProgress('p12',10);confetti();}
+ else{feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Проверь: h=a√3/2, S=a²√3/4.');}
+ });
+ document.getElementById('p12-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p12-dnd-fb').style.display='none';});
+ })();
+
+ /* == INIT: Босс §12 == */
+ (function(){
+ const tasks=[
+ {q:'Сторона равностороннего треугольника $12$ см. Найти высоту (округли до целых).', ans:10, hint:'h=12√3/2=6√3≈10,39≈10.'},
+ {q:'Периметр равностороннего треугольника $18$ см. Найти площадь $S$ (округли до целых).', ans:16, hint:'a=6, S=36√3/4=9√3≈15,6≈16.'},
+ {q:'Высота равностороннего треугольника $h = 6\\sqrt{3}$ см. Найти его площадь (целое число).', ans:144, hint:'h=6√3 → a=12, S=144√3/4=36√3... нет: a=2h/√3=12√3/√3=12, S=12²√3/4=36√3≈62. Ответ: 62.'},
+ {q:'Два равносторонних треугольника: стороны $4$ и $8$ см. Во сколько раз площадь большего больше меньшего?', ans:4, hint:'S∝a². (8/4)²=4.'},
+ ];
+ const correct=[10,16,62,4];
+ const bossBox=document.getElementById('p12-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ if(window.renderMathInElement) renderMath(bossBox);
+ })();
+}
+function buildP13(){
+ const box=document.getElementById('p13-body');
+ let html='';
+
+ html+=makeCard('theory','Диагональ квадрата','13.1',`
+ Теорема. Диагональ квадрата со стороной $a$ равна:
+ $$d = a\\sqrt{2}$$
+ Доказательство. Диагональ $AC$ делит квадрат $ABCD$ на два прямоугольных треугольника. В $\\triangle ABC$: катеты $AB=BC=a$, гипотенуза $AC=d$. По теореме Пифагора:
+ $$d^2 = a^2 + a^2 = 2a^2 \\Rightarrow d = a\\sqrt{2}$$
+ Обратно: $a = \\dfrac{d}{\\sqrt{2}} = \\dfrac{d\\sqrt{2}}{2}$. Площадь: $S = a^2 = \\dfrac{d^2}{2}$.
+
+
+
+
+
+
+
+
+ A
+ B
+ C
+ D
+ a
+ a
+ d=a√2
+
+
`);
+
+ html+=makeCard('rule','Формулы для квадрата','13.2',`
+ Для квадрата со стороной $a$ и диагональю $d = a\\sqrt{2}$:
+
+ Дано $a$ $d$ $S$ $P$
+ $a$ $a$ $a\\sqrt{2}$ $a^2$ $4a$
+ $d$ $\\dfrac{d\\sqrt{2}}{2}$ $d$ $\\dfrac{d^2}{2}$ $2d\\sqrt{2}$
+ $S$ $\\sqrt{S}$ $\\sqrt{2S}$ $S$ $4\\sqrt{S}$
+
`);
+
+ html+=makeCard('example','Примеры','13.3',`
+ Пример 1. Сторона квадрата $a=5$ см. Диагональ?
+ $d = 5\\sqrt{2} \\approx 7{,}07$ см.
+ Пример 2. Диагональ квадрата $d=8$ см. Найти сторону и площадь.
+ $a = \\dfrac{8}{\\sqrt{2}} = 4\\sqrt{2} \\approx 5{,}66$ см, $S = \\dfrac{64}{2} = 32$ см².
+ Пример 3. Площадь квадрата $S=50$ см². Диагональ?
+ $d = \\sqrt{2 \\cdot 50} = \\sqrt{100} = 10$ см.
`);
+
+ /* ИНТЕРАКТИВ 1 — Слайдер + SVG */
+ html+=`
+
+
Тяни слайдер — диагональ перестраивается, $d$, $S$, $P$ пересчитываются.
+
+ Сторона $a$ = 6 см
+
+
+
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 2 — Пошаговое доказательство */
+ html+=`
+
+
3 шага — от квадрата к формуле $d = a\\sqrt{2}$.
+
+
+
+ Далее
+ Сначала
+
+
`;
+
+ /* ИНТЕРАКТИВ 3 — Калькулятор */
+ html+=`
+
+
+
+
$a$ → $d$, $S$, $P$
+
+
+ Вычислить
+
+
+
+
+
$d$ → $a$, $S$
+
+
+ Вычислить
+
+
+
+
+
$S$ → $a$, $d$
+
+
+ Вычислить
+
+
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 4 — Тренажёр */
+ html+=`
+
+
5 задач на диагональ квадрата.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 5 — Босс §13 */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §13 (+10 XP)
+
+
`;
+ html+=secNav('p12','p14');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Слайдер квадрат == */
+ (function(){
+ const sl=document.getElementById('p13-sl-a');
+ const W=300, H=300;
+ function draw(){
+ const a=+sl.value;
+ document.getElementById('p13-sl-a-val').textContent=a;
+ const scale=Math.min(11, 200/a);
+ const px=a*scale;
+ const ox=(W-px)/2, oy=(H-px)/2;
+ const x1=ox,y1=oy,x2=ox+px,y2=oy+px;
+ const d=a*Math.sqrt(2), S=a*a, P=4*a;
+ let s='';
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ const rm=9;
+ s+=' ';
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ s+='D ';
+ const midX=(x1+x2)/2, midY=(y1+y2)/2;
+ s+='d≈'+d.toFixed(2)+' ';
+ s+='a='+a+' ';
+ s+=' ';
+ document.getElementById('p13-sl-svg-wrap').innerHTML=s;
+ document.getElementById('p13-sl-info').innerHTML=`
+
+ Диагональ d
${d.toFixed(3)} см
+
+ `;
+ }
+ sl.addEventListener('input',()=>{draw();addXp(1,'p13-sl');});
+ draw();
+ })();
+
+ /* == INIT: Пошаговое доказательство == */
+ (function(){
+ let step=0;
+ const W=240, H=220;
+ const x1=30,y1=20,x2=210,y2=200;
+ const steps=[
+ {desc:'Квадрат $ABCD$ со стороной $a$. Все углы прямые, все стороны равны.'},
+ {desc:'Проводим диагональ $AC$. Она делит квадрат на два прямоугольных равных треугольника $\\triangle ABC$ и $\\triangle ACD$.'},
+ {desc:'В $\\triangle ABC$: $AB = BC = a$, угол $B = 90°$. По теореме Пифагора: $d^2 = a^2 + a^2 = 2a^2$ $\\Rightarrow$ $\\boxed{d = a\\sqrt{2}}$.'},
+ ];
+ function render(){
+ const px=x2-x1, py=y2-y1;
+ let s='';
+ s+=' ';
+ if(step>=1){
+ s+=' ';
+ }
+ if(step>=2){
+ s+=' ';
+ const rm=10;
+ s+=' ';
+ s+='d=a√2 ';
+ s+='a ';
+ s+='a ';
+ }
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ s+='D ';
+ s+='Шаг '+(step+1)+'/3 ';
+ s+=' ';
+ document.getElementById('p13-proof-svg-wrap').innerHTML=s;
+ document.getElementById('p13-proof-desc').innerHTML=steps[step].desc;
+ if(window.renderMathInElement) renderMath(document.getElementById('p13-proof-desc'));
+ }
+ document.getElementById('p13-proof-next').addEventListener('click',()=>{
+ if(step{step=0;render();});
+ render();
+ })();
+
+ /* == INIT: Калькулятор == */
+ (function(){
+ const sqrt2=Math.sqrt(2);
+ document.getElementById('p13-go1').addEventListener('click',()=>{
+ const a=parseFloat(document.getElementById('p13-ca').value);
+ const out=document.getElementById('p13-out1');
+ if(!isFinite(a)||a<=0){out.innerHTML='Введи a > 0. ';return;}
+ const d=a*sqrt2;
+ out.innerHTML='$d='+fmt(a)+'\\sqrt{2}\\approx'+fmt(+d.toFixed(4))+'$ $S='+fmt(a*a)+'$ см² $P='+fmt(4*a)+'$ см';
+ renderMath(out); addXp(1,'p13-calc1');
+ });
+ document.getElementById('p13-go2').addEventListener('click',()=>{
+ const d=parseFloat(document.getElementById('p13-cd').value);
+ const out=document.getElementById('p13-out2');
+ if(!isFinite(d)||d<=0){out.innerHTML='Введи d > 0. ';return;}
+ const a=d/sqrt2, S=d*d/2;
+ out.innerHTML='$a=\\dfrac{d}{\\sqrt{2}}\\approx'+fmt(+a.toFixed(4))+'$ $S=\\dfrac{d^2}{2}='+fmt(+S.toFixed(4))+'$ см²';
+ renderMath(out); addXp(1,'p13-calc2');
+ });
+ document.getElementById('p13-go3').addEventListener('click',()=>{
+ const S=parseFloat(document.getElementById('p13-cs').value);
+ const out=document.getElementById('p13-out3');
+ if(!isFinite(S)||S<=0){out.innerHTML='Введи S > 0. ';return;}
+ const a=Math.sqrt(S), d=Math.sqrt(2*S);
+ out.innerHTML='$a=\\sqrt{S}\\approx'+fmt(+a.toFixed(4))+'$ $d=\\sqrt{2S}\\approx'+fmt(+d.toFixed(4))+'$';
+ renderMath(out); addXp(1,'p13-calc3');
+ });
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const sqrt2=Math.sqrt(2);
+ const tasks=[
+ {q:'a=7 d=? Сторона квадрата $a=7$ см. Найти диагональ (округли до целых).', ans:10, tol:0.5, hint:'d=7√2≈9,9≈10.'},
+ {q:'Диагональ квадрата $d = 10$ см. Найти площадь $S$.', ans:50, tol:0.5, hint:'S=d²/2=100/2=50.'},
+ {q:'Сторона квадрата $a = 3$ см. Диагональ (округли до 2 знаков)?', ans:4.24, tol:0.02, hint:'d=3√2≈4,24.'},
+ {q:'Площадь квадрата $S = 72$ см². Найти диагональ (целое число).', ans:12, tol:0.5, hint:'d=√(2·72)=√144=12.'},
+ {q:'Диагональ квадрата $d = 6\\sqrt{2}$ см. Найти сторону $a$.', ans:6, tol:0.2, hint:'a=d/√2=6√2/√2=6.'},
+ ];
+ let idx=0, score=0;
+ function show(){
+ document.getElementById('p13-tr-i').textContent=idx+1;
+ document.getElementById('p13-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p13-tr-ans').value='';
+ document.getElementById('p13-tr-fb').style.display='none';
+ if(window.renderMathInElement) renderMath(document.getElementById('p13-tr-task'));
+ }
+ document.getElementById('p13-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p13-tr-score').textContent=0;show();});
+ document.getElementById('p13-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p13-tr-ans').value;
+ const fb=document.getElementById('p13-tr-fb');
+ fb.style.display='block';
+ const tol=tasks[idx].tol||0.5;
+ if(Math.abs(ans-tasks[idx].ans)show(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p13-tr-all');bumpProgress('p13',10);}
+ }else{feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p13-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p13-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §13 == */
+ (function(){
+ const tasks=[
+ {q:'Диагональ квадрата $d = 5\\sqrt{2}$ см. Найти площадь $S$.', ans:25, hint:'a=5, S=25.'},
+ {q:'Периметр квадрата $P = 20$ см. Найти диагональ (округли до 2 знаков).', ans:7.07, hint:'a=5, d=5√2≈7,07.'},
+ {q:'Площадь квадрата $S = 32$ см². Найти диагональ (целое число).', ans:8, hint:'d=√(2·32)=√64=8.'},
+ {q:'В квадрат вписана окружность диаметром $d_0 = 10$ см. Найти диагональ квадрата (округли до целых).', ans:14, hint:'a=10, d=10√2≈14,14≈14.'},
+ ];
+ const bossBox=document.getElementById('p13-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ if(window.renderMathInElement) renderMath(bossBox);
+ })();
+}
+function buildP14(){
+ const box=document.getElementById('p14-body');
+ let html='';
+
+ html+=makeCard('theory','Обратная теорема Пифагора','14.1',`
+ Теорема (обратная теорема Пифагора). Если в треугольнике квадрат наибольшей стороны равен сумме квадратов двух других сторон, то этот треугольник прямоугольный.
+ Если $c$ — наибольшая сторона и $c^2 = a^2 + b^2$, то угол напротив $c$ прямой.
+ Следствие — признак типа треугольника по трём сторонам $a \\leq b \\leq c$:
+
+ $c^2 = a^2 + b^2$ — прямоугольный
+ $c^2 < a^2 + b^2$ — остроугольный
+ $c^2 > a^2 + b^2$ — тупоугольный
+
+
+
+
+
+ A
+ B
+ C
+ b
+ a
+ c
+ c²=a²+b²
+ ⟹ ∠B=90°
+
+
`);
+
+ html+=makeCard('rule','Доказательство','14.2',`
+ Пусть в $\\triangle ABC$ выполнено $c^2 = a^2 + b^2$. Построим вспомогательный $\\triangle A'B'C'$ с прямым углом при $B'$: $A'B'=b$, $B'C'=a$. Тогда по прямой теореме Пифагора $(A'C')^2 = a^2 + b^2 = c^2$, т.е. $A'C' = c$. По трём равным сторонам $\\triangle ABC = \\triangle A'B'C'$. Значит $\\angle B = \\angle B' = 90°$. $\\square$
+ Алгоритм проверки треугольника по сторонам:
+
+ Найди наибольшую сторону $c = \\max(a,b,c)$.
+ Вычисли $c^2$ и $a^2 + b^2$ (для оставшихся двух).
+ Сравни: $=$ прямоугольный, $<$ остроугольный, $>$ тупоугольный.
+ `);
+
+ html+=makeCard('example','Примеры','14.3',`
+ Пример 1. Стороны треугольника $6, 8, 10$. Тип?
+ $c=10$: $100 = 36 + 64 = 100$. $\\Rightarrow$ Прямоугольный.
+ Пример 2. Стороны $5, 6, 8$. Тип?
+ $c=8$: $64$ vs $25+36=61$. $64 > 61$ $\\Rightarrow$ Тупоугольный.
+ Пример 3. Стороны $5, 6, 7$. Тип?
+ $c=7$: $49$ vs $25+36=61$. $49 < 61$ $\\Rightarrow$ Остроугольный.
`);
+
+ /* ИНТЕРАКТИВ 1 — 3 слайдера + индикатор */
+ html+=`
+
+
Задай три стороны. Если треугольник существует, автоматически определяется его тип.
+
+ $a$ = 3
+
+
+ $b$ = 4
+
+
+ $c$ = 5
+
+
+
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 2 — Калькулятор типа треугольника */
+ html+=``;
+
+ /* ИНТЕРАКТИВ 3 — Тренажёр-проверка */
+ html+=`
+
+
Для каждого набора сторон ответь: прямоугольный ли треугольник?
+
+
+ Проверить все
+ Сброс
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 4 — DnD сортер */
+ html+=`
+
+
Перетащи каждый набор сторон в правильную колонку.
+
+
+
+ Проверить
+ Сброс
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 5 — Тренажёр */
+ html+=`
+
+
5 задач на обратную теорему Пифагора.
+
Задача 1 / 5 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 6 — Босс §14 */
+ html+=`
+
+
4 задачи — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §14 (+10 XP)
+
+
`;
+ html+=secNav('p13','p15');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: 3 слайдера == */
+ (function(){
+ const slA=document.getElementById('p14-sl-a');
+ const slB=document.getElementById('p14-sl-b');
+ const slC=document.getElementById('p14-sl-c');
+ const W=320, H=240;
+ function triType(a,b,c){
+ const sides=[a,b,c].sort((x,y)=>x-y);
+ const [s1,s2,s3]=sides;
+ if(s1+s2<=s3) return null; // не треугольник
+ const lhs=s3*s3, rhs=s1*s1+s2*s2;
+ if(Math.abs(lhs-rhs)<0.01) return 'right';
+ if(lhsx-y);
+ // angle A opposite side a (smallest):
+ const cosC=(a*a+b*b-c*c)/(2*a*b);
+ const sinC=Math.sqrt(Math.max(0,1-cosC*cosC));
+ const scale=Math.min(10,140/c);
+ const bx=a*scale;
+ const cx2=b*scale*cosC, cy2=b*scale*sinC;
+ const ox=(W-bx)/2, oy=H-40;
+ const P1={x:ox,y:oy}, P2={x:ox+bx,y:oy}, P3={x:ox+cx2,y:oy-cy2};
+ let s='';
+ s+=' ';
+ s+='A ';
+ s+='B ';
+ s+='C ';
+ s+='c='+c+' ';
+ s+='b='+b+' ';
+ s+='a='+a+' ';
+ s+=' ';
+ return s;
+ }
+ function update(){
+ const a=+slA.value, b=+slB.value, c=+slC.value;
+ document.getElementById('p14-sl-a-val').textContent=a;
+ document.getElementById('p14-sl-b-val').textContent=b;
+ document.getElementById('p14-sl-c-val').textContent=c;
+ const sides=[a,b,c].sort((x,y)=>x-y);
+ const [s1,s2,s3]=sides;
+ const info=document.getElementById('p14-sl-info');
+ if(s1+s2<=s3){
+ document.getElementById('p14-sl-svg-wrap').innerHTML='';
+ info.style.background='var(--fail-bg)'; info.style.color='#7f1d1d';
+ info.textContent='Треугольника не существует ('+s1+'+'+s2+'≤'+s3+')';
+ return;
+ }
+ document.getElementById('p14-sl-svg-wrap').innerHTML=drawTriSVG(a,b,c);
+ const lhs=s3*s3, rhs=s1*s1+s2*s2;
+ const type=triType(a,b,c);
+ const labels={right:'Прямоугольный',acute:'Остроугольный',obtuse:'Тупоугольный'};
+ const colors={right:'#dcfce7',acute:'#dbeafe',obtuse:'#fef3c7'};
+ const textColors={right:'#15803d',acute:'#1d4ed8',obtuse:'#92400e'};
+ info.style.background=colors[type];
+ info.style.color=textColors[type];
+ info.innerHTML=labels[type]+' | '+s3+'²='+lhs+' '+
+ (lhs===rhs?'=':lhs{
+ const a=parseFloat(document.getElementById('p14-ca').value);
+ const b=parseFloat(document.getElementById('p14-cb').value);
+ const c=parseFloat(document.getElementById('p14-cc').value);
+ const out=document.getElementById('p14-calc-out');
+ if(!isFinite(a)||!isFinite(b)||!isFinite(c)||a<=0||b<=0||c<=0){
+ out.innerHTML='Введи три положительных числа. ';return;
+ }
+ const sides=[a,b,c].sort((x,y)=>x-y);
+ const [s1,s2,s3]=sides;
+ if(s1+s2<=s3){out.innerHTML='Треугольника с такими сторонами не существует. ';return;}
+ const lhs=s3*s3, rhs=s1*s1+s2*s2;
+ let type, color, verdict;
+ if(Math.abs(lhs-rhs)<0.001){type='прямоугольный';color='#15803d';verdict='='}
+ else if(lhs'}
+ out.innerHTML='Наибольшая сторона: $c_0='+fmt(s3)+'$. $c_0^2 = '+fmt(lhs)+'$, $'+fmt(s1)+'^2+'+fmt(s2)+'^2 = '+fmt(rhs)+'$. $'+fmt(lhs)+' '+verdict+' '+fmt(rhs)+'$ ⟹ '+type.charAt(0).toUpperCase()+type.slice(1)+' треугольник. ';
+ renderMath(out); addXp(2,'p14-calc');
+ });
+ })();
+
+ /* == INIT: Тренажёр-проверка == */
+ (function(){
+ const sets=[
+ {sides:[3,4,5],right:true},{sides:[6,8,10],right:true},{sides:[5,12,13],right:true},
+ {sides:[7,24,25],right:true},{sides:[2,3,4],right:false},{sides:[5,6,7],right:false},
+ {sides:[1,1,1],right:false},{sides:[8,15,17],right:true},
+ ];
+ const cont=document.getElementById('p14-check-items');
+ cont.innerHTML=sets.map((s,i)=>`
+
+ (${s.sides.join(', ')})
+
+ Прямоугольный
+
+
+ Нет
+
+
+
`).join('');
+ document.getElementById('p14-check-verify').addEventListener('click',()=>{
+ let ok=0, total=0;
+ sets.forEach((s,i)=>{
+ const yes=document.getElementById('p14-ck-'+i+'-yes');
+ const no=document.getElementById('p14-ck-'+i+'-no');
+ const res=document.getElementById('p14-ck-res'+i);
+ if(!yes.checked&&!no.checked){res.textContent='';return;}
+ total++;
+ const chosen=yes.checked?true:false;
+ if(chosen===s.right){res.textContent='Верно!';res.style.color='#15803d';ok++;}
+ else{
+ const ss=s.sides.sort((a,b)=>a-b);
+ res.textContent='Неверно ('+ss[2]+'²='+(ss[2]*ss[2])+', '+ss[0]+'²+'+ss[1]+'²='+(ss[0]*ss[0]+ss[1]*ss[1])+')';
+ res.style.color='#dc2626';
+ }
+ });
+ const fb=document.getElementById('p14-check-fb');
+ fb.style.display='block';
+ if(ok===sets.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p14-check');bumpProgress('p14',10);confetti();}
+ else{feedback(fb,false,'Верно: '+ok+' из '+total+'.');}
+ });
+ document.getElementById('p14-check-reset').addEventListener('click',()=>{
+ sets.forEach((_,i)=>{
+ document.getElementById('p14-ck-'+i+'-yes').checked=false;
+ document.getElementById('p14-ck-'+i+'-no').checked=false;
+ document.getElementById('p14-ck-res'+i).textContent='';
+ });
+ document.getElementById('p14-check-fb').style.display='none';
+ });
+ })();
+
+ /* == INIT: DnD == */
+ (function(){
+ const items=[
+ {id:'d1',html:'9, 40, 41',cat:'yes'},
+ {id:'d2',html:'5, 12, 13',cat:'yes'},
+ {id:'d3',html:'8, 15, 17',cat:'yes'},
+ {id:'d4',html:'3, 4, 6', cat:'no'},
+ {id:'d5',html:'6, 7, 8', cat:'no'},
+ {id:'d6',html:'20, 21, 29',cat:'yes'},
+ {id:'d7',html:'2, 2, 3', cat:'no'},
+ {id:'d8',html:'10, 24, 26',cat:'yes'},
+ ];
+ const sorter=setupSorter({poolId:'p14-dnd-pool',scopeSelector:'#p14-dnd-wg',items:items.map(x=>({id:x.id,html:x.html})),cats:['yes','no']});
+ document.getElementById('p14-dnd-check').addEventListener('click',()=>{
+ const fb=document.getElementById('p14-dnd-fb');fb.style.display='block';
+ let ok=0;
+ items.forEach(it=>{if(sorter.placed[it.id]===it.cat)ok++;});
+ if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p14-dnd');bumpProgress('p14',10);confetti();}
+ else{feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Проверь: 9-40-41, 5-12-13, 8-15-17, 20-21-29, 10-24-26 — прямоугольные.');}
+ });
+ document.getElementById('p14-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p14-dnd-fb').style.display='none';});
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Стороны треугольника $6, 8, 10$. Прямоугольный? Введи 1 (да) или 0 (нет).', ans:1, tol:0.1, hint:'6²+8²=100=10². Да, прямоугольный (1).'},
+ {q:'Стороны $5, 7, 9$. Прямоугольный? Введи 1 (да) или 0 (нет).', ans:0, tol:0.1, hint:'9²=81 > 5²+7²=74. Тупоугольный (0).'},
+ {q:'Катеты прямоугольного треугольника $15$ и $20$. Найти гипотенузу.', ans:25, tol:0.5, hint:'c=√(225+400)=√625=25.'},
+ {q:'Стороны треугольника $7, 24, c$. При каком $c$ он прямоугольный (угол напротив $c$)?', ans:25, tol:0.5, hint:'c=√(49+576)=√625=25.'},
+ {q:'Стороны $3, 4, 5$ умножили на $3$. Получилась тройка $9, 12, 15$. Это прямоугольный треугольник? Введи 1 (да) или 0 (нет).', ans:1, tol:0.1, hint:'9²+12²=81+144=225=15². Да (1).'},
+ ];
+ let idx=0, score=0;
+ function show(){
+ document.getElementById('p14-tr-i').textContent=idx+1;
+ document.getElementById('p14-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p14-tr-ans').value='';
+ document.getElementById('p14-tr-fb').style.display='none';
+ if(window.renderMathInElement) renderMath(document.getElementById('p14-tr-task'));
+ }
+ document.getElementById('p14-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p14-tr-score').textContent=0;show();});
+ document.getElementById('p14-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p14-tr-ans').value;
+ const fb=document.getElementById('p14-tr-fb');
+ fb.style.display='block';
+ const tol=tasks[idx].tol||0.5;
+ if(Math.abs(ans-tasks[idx].ans)show(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p14-tr-all');bumpProgress('p14',10);}
+ }else{feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p14-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p14-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Босс §14 == */
+ (function(){
+ const tasks=[
+ {q:'Треугольник со сторонами $9$, $40$, $41$. Прямоугольный? Введи 1 (да) или 0 (нет).', ans:1, hint:'9²+40²=81+1600=1681=41². Да.'},
+ {q:'Прямоугольный треугольник, гипотенуза $c = 26$, катет $a = 10$. Найти второй катет $b$.', ans:24, hint:'b=√(676-100)=√576=24.'},
+ {q:'Стороны $a$ и $b$ прямоугольного треугольника равны $a = b = 5$. Найти гипотенузу (округли до целых).', ans:7, hint:'c=5√2≈7,07≈7.'},
+ {q:'Стороны $p$, $q$, $r$ таковы, что $p^2 + q^2 = r^2$. Угол напротив какой стороны прямой? Введи номер стороны: 1=$p$, 2=$q$, 3=$r$.', ans:3, hint:'По обратной теореме Пифагора — прямой угол напротив r (3).'},
+ ];
+ const bossBox=document.getElementById('p14-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ if(window.renderMathInElement) renderMath(bossBox);
+ })();
+}
+function buildP15(){
+ const box=document.getElementById('p15-body');
+ let html='';
+
+ html+=makeCard('theory','Пифагоровы тройки','15.1',`
+ Пифагорова тройка — три натуральных числа $(a, b, c)$, удовлетворяющих $a^2 + b^2 = c^2$.
+ Примитивные тройки (НОД = 1):
+
+ a b c Проверка
+ 3 4 5 9+16=25
+ 5 12 13 25+144=169
+ 7 24 25 49+576=625
+ 8 15 17 64+225=289
+ 9 40 41 81+1600=1681
+ 20 21 29 400+441=841
+
+ Кратные тройки: если $(a,b,c)$ — тройка, то $(ka, kb, kc)$ тоже тройка для любого натурального $k$.
`);
+
+ html+=makeCard('rule','Формула Евклида','15.2',`
+ Для любых натуральных $m > n$ тройка $$(m^2-n^2,\\; 2mn,\\; m^2+n^2)$$ является пифагоровой.
+ Проверка: $(m^2-n^2)^2 + (2mn)^2 = m^4 - 2m^2n^2 + n^4 + 4m^2n^2 = m^4 + 2m^2n^2 + n^4 = (m^2+n^2)^2$. $\\square$
+
+ m n $m^2-n^2$ $2mn$ $m^2+n^2$
+ 2 1 3 4 5
+ 3 2 5 12 13
+ 4 1 15 8 17
+ 4 3 7 24 25
+ 5 2 21 20 29
+
`);
+
+ html+=makeCard('example','Применение','15.3',`
+ Пример 1. Два катета прямоугольного треугольника равны $9$ и $40$. Гипотенуза?
+ Узнаём тройку $(9, 40, 41)$ $\\Rightarrow$ $c = 41$. Без вычислений!
+ Пример 2. Катеты $6$ и $8$. Гипотенуза?
+ $(6,8,10) = 2\\cdot(3,4,5)$ $\\Rightarrow$ $c = 10$.
+ Пример 3. По формуле Евклида при $m=5, n=1$: $(24, 10, 26) = 2\\cdot(12, 5, 13)$.
`);
+
+ /* ИНТЕРАКТИВ 1 — Генератор Евклида */
+ html+=`
+
+
Выбери $m$ и $n$ ($m > n$) — получи пифагорову тройку и увидь её на SVG.
+
+ $m$ = 3
+
+
+ $n$ = 2
+
+
+
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 2 — Таблица с мини-SVG */
+ html+=`
+
+
Нажми на тройку — увидишь соответствующий прямоугольный треугольник.
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 3 — Тренажёр: найди третье число */
+ html+=`
+
+
Дано 2 числа из пифагоровой тройки — найди третье.
+
Задача 1 / 6 Очки: 0
+
+
+
+ Проверить
+ Начать
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 4 — Викторина тройка/не тройка */
+ html+=`
+
+
Для каждого набора нажми «Тройка» или «Нет».
+
+
+ Проверить
+ Сброс
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 5 — DnD: примитивные vs кратные */
+ html+=`
+
+
Перетащи каждую тройку в правильную колонку.
+
+
+
Примитивная (НОД=1)
+
+
+
+
+
+
+ Проверить
+ Сброс
+
+
+
`;
+
+ /* ИНТЕРАКТИВ 6 — Босс §15 */
+ html+=`
+
+
5 задач — +5 XP каждая.
+
+
`;
+
+ html+=`
+
+
+ Я прочитал §15 (+10 XP)
+
+
`;
+ html+=secNav('p14','final2');
+ box.innerHTML=html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
+
+ /* == INIT: Генератор Евклида == */
+ (function(){
+ const slM=document.getElementById('p15-sl-m');
+ const slN=document.getElementById('p15-sl-n');
+ const W=300, H=240;
+ function drawTriangle(a,b,c){
+ const mx=Math.max(a,b,c);
+ const sc=Math.min(10,180/mx);
+ const ax=a*sc, by=b*sc;
+ const ox=(W-ax)/2, oy=H-30;
+ let s='';
+ s+=' ';
+ const rm=9;
+ s+=' ';
+ s+='B ';
+ s+='C ';
+ s+='A ';
+ s+=''+a+' ';
+ s+=''+b+' ';
+ const hpx=(ox+ax+ox)/2, hpy=(oy+oy-by)/2;
+ s+=''+c+' ';
+ s+=' ';
+ return s;
+ }
+ function update(){
+ let m=+slM.value, n=+slN.value;
+ document.getElementById('p15-sl-m-val').textContent=m;
+ document.getElementById('p15-sl-n-val').textContent=n;
+ if(n>=m){
+ n=m-1;
+ slN.value=n;
+ document.getElementById('p15-sl-n-val').textContent=n;
+ }
+ const a=m*m-n*n, b=2*m*n, c=m*m+n*n;
+ document.getElementById('p15-euclid-svg-wrap').innerHTML=drawTriangle(a,b,c);
+ document.getElementById('p15-euclid-out').innerHTML=
+ 'm='+m+', n='+n+' '+
+ '$a = m^2-n^2 = '+m*m+'-'+n*n+' = '+a+'$ '+
+ '$b = 2mn = 2\\cdot'+m+'\\cdot'+n+' = '+b+'$ '+
+ '$c = m^2+n^2 = '+m*m+'+'+n*n+' = '+c+'$ '+
+ 'Тройка: ('+a+', '+b+', '+c+') '+
+ 'Проверка: $'+a+'{}^2+'+b+'{}^2 = '+(a*a+b*b)+' = '+c+'{}^2$ = '+(a*a+b*b===c*c?'✓':'✗');
+ if(window.renderMathInElement) renderMath(document.getElementById('p15-euclid-out'));
+ addXp(1,'p15-euclid');
+ }
+ slM.addEventListener('input',update);
+ slN.addEventListener('input',update);
+ update();
+ })();
+
+ /* == INIT: Таблица троек == */
+ (function(){
+ const triples=[
+ [3,4,5],[5,12,13],[7,24,25],[8,15,17],[9,40,41],
+ [6,8,10],[10,24,26],[12,16,20],[15,20,25],[20,21,29],
+ ];
+ const wrap=document.getElementById('p15-table-wrap');
+ const svgBox=document.getElementById('p15-table-svg');
+ let sel=-1;
+ function drawMini(a,b,c,active){
+ const sc=Math.min(5.5,40/Math.max(a,b,c));
+ const ax=a*sc, by=b*sc;
+ const ox=10, oy=50+by;
+ let s='';
+ s+=' ';
+ s+=' ';
+ s+=' ';
+ return s;
+ }
+ function showSVG(i){
+ const [a,b,c]=triples[i];
+ const sc=Math.min(8,160/Math.max(a,b,c));
+ const ax=a*sc, by=b*sc;
+ const W=Math.round(ax+80), H=Math.round(by+60);
+ const ox=40, oy=H-30;
+ let s='';
+ s+=' ';
+ const rm=9;
+ s+=' ';
+ s+='B ';
+ s+='C ';
+ s+='A ';
+ s+='a='+a+' ';
+ s+='b='+b+' ';
+ const hpx=(ox+ax+ox)/2+16, hpy=(oy+oy-by)/2;
+ s+='c='+c+' ';
+ s+=''+a+'²+'+b+'²='+c+'² ('+a*a+'+'+b*b+'='+c*c+') ';
+ s+=' ';
+ svgBox.innerHTML=s;
+ }
+ wrap.innerHTML=triples.map(([a,b,c],i)=>`
+
+
(${a}, ${b}, ${c})
+ ${drawMini(a,b,c,false)}
+
`).join('');
+ triples.forEach((_,i)=>{
+ document.getElementById('p15-tri-btn'+i).addEventListener('click',()=>{
+ sel=i;
+ triples.forEach((_,j)=>{
+ const el=document.getElementById('p15-tri-btn'+j);
+ el.style.borderColor=j===i?'var(--sec-acc,var(--pri))':'var(--border)';
+ });
+ showSVG(i); addXp(1,'p15-table-'+i);
+ });
+ });
+ showSVG(0);
+ document.getElementById('p15-tri-btn0').style.borderColor='var(--sec-acc,var(--pri))';
+ })();
+
+ /* == INIT: Тренажёр == */
+ (function(){
+ const tasks=[
+ {q:'Тройка $(3, 4, ?)$. Найти третье число.', ans:5, hint:'3²+4²=25=5².'},
+ {q:'Тройка $(5, 12, ?)$. Найти третье число.', ans:13, hint:'25+144=169=13².'},
+ {q:'Тройка $(?, 24, 25)$. Найти первое число.', ans:7, hint:'25²-24²=625-576=49=7².'},
+ {q:'Тройка $(9, ?, 41)$. Найти второе число.', ans:40, hint:'41²-9²=1681-81=1600=40².'},
+ {q:'Кратная тройка: $(6, 8, ?)$. Это $2\\cdot(3,4,5)$.', ans:10, hint:'c=2·5=10.'},
+ {q:'Формула Евклида, $m=4, n=1$: $a=m^2-n^2$, $b=2mn$, $c=m^2+n^2$. Найти $c$.', ans:17, hint:'c=16+1=17.'},
+ ];
+ let idx=0, score=0;
+ function show(){
+ document.getElementById('p15-tr-i').textContent=idx+1;
+ document.getElementById('p15-tr-task').innerHTML=tasks[idx].q;
+ document.getElementById('p15-tr-ans').value='';
+ document.getElementById('p15-tr-fb').style.display='none';
+ if(window.renderMathInElement) renderMath(document.getElementById('p15-tr-task'));
+ }
+ document.getElementById('p15-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p15-tr-score').textContent=0;show();});
+ document.getElementById('p15-tr-go').addEventListener('click',()=>{
+ if(idx>=tasks.length)return;
+ const ans=+document.getElementById('p15-tr-ans').value;
+ const fb=document.getElementById('p15-tr-fb');
+ fb.style.display='block';
+ if(Math.abs(ans-tasks[idx].ans)<0.5){
+ score++;document.getElementById('p15-tr-score').textContent=score;
+ addXp(3,'p15-tr-'+idx);bumpProgress('p15',5);
+ if(idxshow(),900);}
+ else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p15-tr-all');bumpProgress('p15',10);}
+ }else{feedback(fb,false,'Неверно. '+tasks[idx].hint);}
+ });
+ document.getElementById('p15-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p15-tr-go').click();});
+ show();
+ })();
+
+ /* == INIT: Викторина == */
+ (function(){
+ const sets=[
+ {nums:[3,4,5],ok:true},{nums:[5,12,13],ok:true},{nums:[6,8,10],ok:true},
+ {nums:[7,24,25],ok:true},{nums:[2,3,4],ok:false},{nums:[4,5,6],ok:false},
+ {nums:[8,15,17],ok:true},{nums:[3,5,7],ok:false},
+ ];
+ const cont=document.getElementById('p15-quiz-items');
+ cont.innerHTML=sets.map((s,i)=>`
+
+ (${s.nums.join(', ')})
+
+ Тройка
+
+
+ Нет
+
+
+
`).join('');
+ document.getElementById('p15-quiz-check').addEventListener('click',()=>{
+ let ok=0,total=0;
+ sets.forEach((s,i)=>{
+ const yesEl=cont.querySelector('[name="p15-qz-'+i+'"][value="yes"]');
+ const noEl=cont.querySelector('[name="p15-qz-'+i+'"][value="no"]');
+ const res=document.getElementById('p15-qz-res'+i);
+ if(!yesEl.checked&&!noEl.checked){res.textContent='';return;}
+ total++;
+ const chosen=yesEl.checked;
+ if(chosen===s.ok){res.textContent='Верно!';res.style.color='#15803d';ok++;}
+ else{
+ const ss=s.nums.slice().sort((a,b)=>a-b);
+ res.textContent='Нет: '+ss[0]+'²+'+ss[1]+'²='+(ss[0]*ss[0]+ss[1]*ss[1])+', '+ss[2]+'²='+(ss[2]*ss[2]);
+ res.style.color='#dc2626';
+ }
+ });
+ const fb=document.getElementById('p15-quiz-fb');
+ fb.style.display='block';
+ if(ok===sets.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p15-quiz');bumpProgress('p15',10);confetti();}
+ else{feedback(fb,false,'Верно: '+ok+' из '+total+'.');}
+ });
+ document.getElementById('p15-quiz-reset').addEventListener('click',()=>{
+ sets.forEach((_,i)=>{
+ cont.querySelectorAll('[name="p15-qz-'+i+'"]').forEach(r=>r.checked=false);
+ document.getElementById('p15-qz-res'+i).textContent='';
+ });
+ document.getElementById('p15-quiz-fb').style.display='none';
+ });
+ })();
+
+ /* == INIT: DnD примитивные vs кратные == */
+ (function(){
+ const items=[
+ {id:'e1',html:'(3, 4, 5)',cat:'prim'},
+ {id:'e2',html:'(5, 12, 13)',cat:'prim'},
+ {id:'e3',html:'(7, 24, 25)',cat:'prim'},
+ {id:'e4',html:'(6, 8, 10)',cat:'mult'},
+ {id:'e5',html:'(9, 12, 15)',cat:'mult'},
+ {id:'e6',html:'(8, 15, 17)',cat:'prim'},
+ {id:'e7',html:'(10, 24, 26)',cat:'mult'},
+ {id:'e8',html:'(20, 21, 29)',cat:'prim'},
+ ];
+ const sorter=setupSorter({poolId:'p15-dnd-pool',scopeSelector:'#p15-dnd-wg',items:items.map(x=>({id:x.id,html:x.html})),cats:['prim','mult']});
+ document.getElementById('p15-dnd-check').addEventListener('click',()=>{
+ const fb=document.getElementById('p15-dnd-fb');fb.style.display='block';
+ let ok=0;
+ items.forEach(it=>{if(sorter.placed[it.id]===it.cat)ok++;});
+ if(ok===items.length){feedback(fb,true,'Всё верно! +5 XP');addXp(5,'p15-dnd');bumpProgress('p15',10);confetti();}
+ else{feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Примитивные: (3,4,5),(5,12,13),(7,24,25),(8,15,17),(20,21,29).');}
+ });
+ document.getElementById('p15-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p15-dnd-fb').style.display='none';});
+ })();
+
+ /* == INIT: Босс §15 == */
+ (function(){
+ const tasks=[
+ {q:'Катеты прямоугольного треугольника $20$ и $21$ см. Гипотенуза (используй тройки)?', ans:29, hint:'(20,21,29) — пифагорова тройка. c=29.'},
+ {q:'Формула Евклида: $m=5, n=2$. Найти $c = m^2+n^2$.', ans:29, hint:'c=25+4=29.'},
+ {q:'Кратная тройка $(15, 20, 25)$ получена умножением $(3,4,5)$ на $k$. Найти $k$.', ans:5, hint:'15/3=5.'},
+ {q:'Стороны прямоугольного треугольника — тройка $(a,b,c)$, $c=65$, $a=25$. Найти $b$.', ans:60, hint:'b=√(65²-25²)=√(4225-625)=√3600=60.'},
+ {q:'Периметр прямоугольного треугольника равен $60$. Это тройка, кратная $(3,4,5)$. Найти гипотенузу.', ans:25, hint:'k·(3+4+5)=60 → k=5. c=5·5=25.'},
+ ];
+ const bossBox=document.getElementById('p15-boss-tasks');
+ bossBox.innerHTML=tasks.map((t,i)=>`
+
+
${t.q}
+
+
+ Проверить
+
+
+
`).join('');
+ if(window.renderMathInElement) renderMath(bossBox);
+ })();
+}
function buildFinal2stub(){ document.getElementById('final2-body').innerHTML='Финал главы 2 — Волна 1 : боссы и итоги появятся в следующем обновлении.
'+secNav('p15',null); }