diff --git a/frontend/textbooks/geometry_9_ch3.html b/frontend/textbooks/geometry_9_ch3.html
index 593241d..6bb0f09 100644
--- a/frontend/textbooks/geometry_9_ch3.html
+++ b/frontend/textbooks/geometry_9_ch3.html
@@ -103,6 +103,27 @@ a{color:inherit;text-decoration:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
+.wg{background:linear-gradient(135deg,var(--card),var(--pri-soft));border:1.5px solid var(--pri);border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
+.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
+.wg-badge{padding:4px 9px;background:var(--pri);color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
+.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--pri2);flex:1}
+.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg),var(--pri-soft));border-left:4px solid var(--warn);padding:9px 14px;border-radius:9px}
+.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
+.tinp:focus{outline:0;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-soft)}
+.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
+.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
+.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
+.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--pri2);margin-left:4px}
+.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--pri)}
+.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--pri-soft);border-radius:10px;margin-bottom:12px}
+.score-display b{color:var(--pri2);font-size:1.15rem}
+.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
+.spoiler summary{padding:8px 14px;background:var(--pri-soft);font-weight:700;cursor:pointer;font-size:.88rem;color:var(--pri2);list-style:none;display:flex;align-items:center;gap:8px}
+.spoiler summary::-webkit-details-marker{display:none}
+.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--pri);width:18px}
+.spoiler[open] summary::before{content:'\2212'}
+.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
+
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
@@ -420,6 +441,28 @@ function wireReadBtn(paraId){
});
}
+/* ===== SVG helpers ===== */
+function rightAngleMark(V, uIn, wIn, s){
+ s = s || 9;
+ const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y};
+ const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y};
+ const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y};
+ return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y;
+}
+function angleArcAuto(V, uA, uB, R){
+ const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y};
+ const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y};
+ const cross = uA.x*uB.y - uA.y*uB.x;
+ const sweep = cross > 0 ? 1 : 0;
+ return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y;
+}
+function unitVec(p1, p2){
+ const dx = p2.x - p1.x, dy = p2.y - p1.y;
+ const len = Math.sqrt(dx*dx + dy*dy) || 1;
+ return {x: dx/len, y: dy/len};
+}
+function deg2rad(d){ return d * Math.PI / 180; }
+
/* ===== STUB BUILDERS — наполнение в Phase 7+ ===== */
function _stubBuilder(paraId, num, name, prev, next){
@@ -435,8 +478,580 @@ function _stubBuilder(paraId, num, name, prev, next){
if(window.renderMathInElement) renderMath(body);
}
-function buildP10(){ _stubBuilder('p10', '§10', 'Теорема синусов', null, 'p11'); }
-function buildP11(){ _stubBuilder('p11', '§11', 'Теорема косинусов', 'p10', 'p12'); }
+/* ===== §10 — Теорема синусов ===== */
+function buildP10(){
+ const box = document.getElementById('p10-body');
+ let html = '';
+
+ html += makeCard('theory', 'Формулировка теоремы', '10.1', `
+
В любом треугольнике стороны пропорциональны синусам противолежащих углов:
+ $$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B} = \\dfrac{c}{\\sin C} = 2R$$
+ где $a, b, c$ — стороны треугольника, $A, B, C$ — противолежащие им углы, а $R$ — радиус описанной окружности.
+ Почему именно $2R$?
+ Идея доказательства: в треугольнике, вписанном в окружность радиуса $R$, сторона $a$ — это хорда, стягиваемая центральным углом $2A$ (по теореме о вписанном угле). Длина такой хорды равна $2R \\sin A$, откуда $\\dfrac{a}{\\sin A} = 2R$. То же для $b$ и $c$.
+
`);
+
+ html += makeCard('rule', 'Радиус описанной окружности', '10.2', `
+ Из теоремы синусов сразу получаем формулы для радиуса описанной окружности:
+ $$R = \\dfrac{a}{2 \\sin A} = \\dfrac{b}{2 \\sin B} = \\dfrac{c}{2 \\sin C} = \\dfrac{abc}{4S}$$
+ А также — выражение стороны через радиус и противолежащий угол:
+ $$a = 2R \\sin A, \\qquad b = 2R \\sin B, \\qquad c = 2R \\sin C$$
+ Это очень удобно: зная всего один угол и противолежащую ему сторону, можно сразу найти $R$ — а через него и любую другую сторону.
`);
+
+ html += makeCard('example', 'Применение', '10.3', `
+ Что можно вычислить?
+
+ Найти $b$, зная $a, A, B$: $b = a \\cdot \\dfrac{\\sin B}{\\sin A}$.
+ Найти $R$, зная $a$ и $A$: $R = \\dfrac{a}{2 \\sin A}$.
+ Найти угол $B$, зная $a, b, A$: $\\sin B = \\dfrac{b \\sin A}{a}$.
+
+ Пример. $a = 10$, $A = 30°$, $B = 45°$.
+ $$b = a \\cdot \\dfrac{\\sin B}{\\sin A} = 10 \\cdot \\dfrac{\\sin 45°}{\\sin 30°} = 10 \\cdot \\dfrac{\\sqrt{2}/2}{1/2} = 10\\sqrt{2} \\approx 14{,}14$$
+ Радиус описанной окружности: $R = \\dfrac{a}{2 \\sin A} = \\dfrac{10}{2 \\cdot 0{,}5} = 10$.
`);
+
+ /* IV1 — Треугольник с описанной окружностью */
+ html += `
+
+
Меняй углы $A$ и $B$ — программа найдёт $C = 180° - A - B$ и стороны $a = 2R\\sin A$, $b = 2R\\sin B$, $c = 2R\\sin C$. Радиус $R = 130$ фиксирован.
+
+ $A$ (°)60
+ $B$ (°)50
+
+
+
+
+
+
`;
+
+ /* IV2 — Калькулятор */
+ html += `
+
+
Введи сторону $a$ и два угла $A$, $B$ — программа найдёт $C$, $R$, $b$ и $c$ по теореме синусов.
+
+ сторона $a$
+ угол $A$ (°)
+ угол $B$ (°)
+
+
Вычислить
+
+
+
`;
+
+ /* IV3 — Что вычислить? */
+ html += `
+
+
Дано условие — выбери формулу теоремы синусов, которая поможет найти искомое.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ $\\dfrac{a}{\\sin A} = 2R$
+ $a = 2R\\sin A$
+ $\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B}$
+
+
+
`;
+
+ /* IV4 — Тренажёр */
+ html += `
+
+
Реши задачу и введи число (округляй до 2 знаков после запятой).
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ ответ =
+
+ Проверить
+ Заново
+
+
+
`;
+
+ html += readButton('p10');
+ html += secNav(null, 'p11');
+
+ box.innerHTML = html;
+ renderMath(box);
+
+ /* IV1 — Треугольник в описанной окружности */
+ (function(){
+ const sA=document.getElementById('p10-iv1-a');
+ const sB=document.getElementById('p10-iv1-b');
+ const lA=document.getElementById('p10-iv1-aval');
+ const lB=document.getElementById('p10-iv1-bval');
+ const svg=document.getElementById('p10-iv1-svg');
+ const out=document.getElementById('p10-iv1-out');
+ const seen=new Set();
+ const cx=210, cy=200, R=130;
+ function draw(){
+ let A=+sA.value, B=+sB.value;
+ // ограничение чтобы сумма не превышала 170
+ if(A+B>170){ if(A>B) A=170-B; else B=170-A; }
+ sA.value=A; sB.value=B;
+ const C=180-A-B;
+ lA.textContent=A; lB.textContent=B;
+ // строим треугольник, вписанный в окружность с углами A, B, C
+ // выбираем для удобства: вершина CA на угле 210° (центральном) от центра
+ // Используем: положение вершин на окружности — углы относительно центра 2A, 2B, 2C
+ // Простой способ: разместим точку A_v внизу слева, B_v внизу справа, C_v вверху
+ // Углы при центре: arc BC = 2A, arc CA = 2B, arc AB = 2C
+ // Начнём с A_v на угле theta0=210°, пойдём по окружности
+ const tA=deg2rad(210);
+ const tB=tA + deg2rad(2*C); // от A через дугу AB (=2C) до B
+ const tC=tB + deg2rad(2*A); // от B через дугу BC (=2A) до C
+ const Av={x:cx+R*Math.cos(tA), y:cy+R*Math.sin(tA)};
+ const Bv={x:cx+R*Math.cos(tB), y:cy+R*Math.sin(tB)};
+ const Cv={x:cx+R*Math.cos(tC), y:cy+R*Math.sin(tC)};
+ const a=Math.hypot(Bv.x-Cv.x,Bv.y-Cv.y);
+ const b=Math.hypot(Av.x-Cv.x,Av.y-Cv.y);
+ const c=Math.hypot(Av.x-Bv.x,Av.y-Bv.y);
+ let s='';
+ s += ' ';
+ s += ' ';
+ s += ' ';
+ s += 'O ';
+ // радиус к одной вершине (показать R)
+ s += ' ';
+ const mx=(cx+Av.x)/2, my=(cy+Av.y)/2;
+ s += 'R ';
+ // треугольник
+ s += ' ';
+ // вершины и подписи
+ function lab(P, name, color){
+ const ux=(P.x-cx)/R, uy=(P.y-cy)/R;
+ const lx=P.x+ux*18, ly=P.y+uy*18;
+ return ' '
+ +''+name+' ';
+ }
+ s += lab(Av,'A','#0f172a');
+ s += lab(Bv,'B','#0f172a');
+ s += lab(Cv,'C','#0f172a');
+ svg.innerHTML=s;
+
+ const sinA=Math.sin(deg2rad(A))||1e-9;
+ const ratio = 2*R; // в наших экранных единицах
+ out.innerHTML = '$A = '+A+'°$, $B = '+B+'°$, $C = '+C+'°$ '
+ + '$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B} = \\dfrac{c}{\\sin C} = 2R = '+(2*R).toFixed(0)+'$ (в пикселях SVG) '
+ + '$a \\approx '+a.toFixed(1)+'$, $b \\approx '+b.toFixed(1)+'$, $c \\approx '+c.toFixed(1)+'$ · $a/\\sin A \\approx '+(a/sinA).toFixed(1)+'$';
+ renderMath(out);
+ seen.add(A+'|'+B);
+ if(seen.size>=4 && !seen.has('done')){ addXp(10,'p10-iv1'); bumpProgress('p10',15); seen.add('done'); }
+ }
+ [sA,sB].forEach(s=>s.addEventListener('input', draw));
+ draw();
+ })();
+
+ /* IV2 — Калькулятор */
+ (function(){
+ const aI=document.getElementById('p10-iv2-a');
+ const AI=document.getElementById('p10-iv2-A');
+ const BI=document.getElementById('p10-iv2-B');
+ const go=document.getElementById('p10-iv2-go');
+ const out=document.getElementById('p10-iv2-out');
+ const fb=document.getElementById('p10-iv2-fb');
+ let solved=0;
+ function calc(){
+ const a=parseFloat(aI.value), A=parseFloat(AI.value), B=parseFloat(BI.value);
+ if(!isFinite(a)||!isFinite(A)||!isFinite(B)){ feedback(fb,false,'✗ Введи все значения.'); return; }
+ if(a<=0){ feedback(fb,false,'✗ Сторона должна быть положительной.'); return; }
+ if(A<=0||B<=0||A+B>=180){ feedback(fb,false,'✗ Углы должны быть положительными, сумма $A + B < 180°$.'); return; }
+ const C=180-A-B;
+ const sinA=Math.sin(deg2rad(A)), sinB=Math.sin(deg2rad(B)), sinC=Math.sin(deg2rad(C));
+ const R=a/(2*sinA);
+ const b=2*R*sinB;
+ const c=2*R*sinC;
+ out.innerHTML = '$C = 180° - A - B = '+C.toFixed(1)+'°$ '
+ + '$R = \\dfrac{a}{2 \\sin A} = \\dfrac{'+a+'}{2 \\sin '+A+'°} \\approx '+R.toFixed(3)+'$ '
+ + '$b = 2R \\sin B = 2 \\cdot '+R.toFixed(3)+' \\cdot \\sin '+B+'° \\approx '+b.toFixed(3)+'$ '
+ + '$c = 2R \\sin C = 2 \\cdot '+R.toFixed(3)+' \\cdot \\sin '+C.toFixed(1)+'° \\approx '+c.toFixed(3)+'$';
+ renderMath(out);
+ feedback(fb,true,'✓ Готово!');
+ solved++;
+ if(solved===1){ addXp(10,'p10-iv2'); bumpProgress('p10',10); }
+ }
+ go.addEventListener('click', calc);
+ })();
+
+ /* IV3 — «Какую формулу применить?» */
+ (function(){
+ const Q=[
+ {t:'Известны $a$ и $A$. Найти радиус $R$ описанной окружности.', a:'R'},
+ {t:'Известны $R$ и угол $A$. Найти сторону $a$.', a:'side'},
+ {t:'Известны $a$, $A$ и угол $B$. Найти сторону $b$.', a:'prop'},
+ {t:'Известны $R$ и угол $B$. Найти сторону $b$.', a:'side'},
+ {t:'Известны $b$, $B$ и угол $C$. Найти сторону $c$.', a:'prop'},
+ {t:'Известны сторона $c$ и угол $C$. Найти радиус $R$.', a:'R'}
+ ];
+ const explain={
+ R:'Прямо $R = \\dfrac{a}{2\\sin A}$ — сторону пропускаем через формулу $2R$.',
+ side:'Сторона через радиус: $a = 2R \\sin A$.',
+ prop:'Связываем две стороны через их углы: $\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B}$.'
+ };
+ const labels={R:'$\\dfrac{a}{\\sin A} = 2R$', side:'$a = 2R\\sin A$', prop:'$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B}$'};
+ const qBox=document.getElementById('p10-iv3-q');
+ const iEl=document.getElementById('p10-iv3-i');
+ const sEl=document.getElementById('p10-iv3-s');
+ const fb=document.getElementById('p10-iv3-fb');
+ const btns=document.querySelectorAll('#p10-iv3 button[data-ans]');
+ let i=0, score=0, done=false;
+ function show(){
+ if(i>=Q.length){
+ qBox.innerHTML='Завершено! Очки: '+score+' / '+Q.length;
+ if(!done){ done=true; addXp(15,'p10-iv3'); bumpProgress('p10',25); if(score===Q.length) achievement('p10_done'); }
+ btns.forEach(b=>b.disabled=true);
+ return;
+ }
+ qBox.innerHTML=Q[i].t; renderMath(qBox);
+ iEl.textContent=(i+1); sEl.textContent=score;
+ fb.style.display='none';
+ }
+ btns.forEach(b=>b.addEventListener('click', ()=>{
+ const ok = b.dataset.ans===Q[i].a;
+ if(ok) score++;
+ feedback(fb, ok, ok?('✓ Верно! '+explain[Q[i].a]):('✗ Правильная формула: '+labels[Q[i].a]+'. '+explain[Q[i].a]));
+ i++;
+ setTimeout(show, 1100);
+ }));
+ show();
+ })();
+
+ /* IV4 — Тренажёр */
+ (function(){
+ const Q=[
+ {t:'$a = 10$, $A = 30°$. Найди радиус $R$ описанной окружности.', a:10, tol:0.02},
+ {t:'$R = 5$, $A = 90°$. Найди сторону $a$.', a:10, tol:0.02},
+ {t:'$a = 8$, $A = 45°$, $B = 60°$. Найди сторону $b$.', a:9.80, tol:0.05},
+ {t:'В прямоугольном треугольнике гипотенуза $c = 10$, угол $C = 90°$. Найди $R$.', a:5, tol:0.02},
+ {t:'$a = 6$, $A = 30°$, $B = 60°$. Найди сторону $b$.', a:10.39, tol:0.05},
+ {t:'$b = 12$, $B = 60°$. Найди радиус $R$.', a:6.93, tol:0.05}
+ ];
+ const qBox=document.getElementById('p10-iv4-q');
+ const ans=document.getElementById('p10-iv4-ans');
+ const go=document.getElementById('p10-iv4-go');
+ const reset=document.getElementById('p10-iv4-start');
+ const iEl=document.getElementById('p10-iv4-i');
+ const sEl=document.getElementById('p10-iv4-s');
+ const fb=document.getElementById('p10-iv4-fb');
+ let i=0, score=0, done=false;
+ function show(){
+ if(i>=Q.length){
+ qBox.innerHTML='Финиш! Очки: '+score+' / '+Q.length;
+ if(!done){ done=true; addXp(15,'p10-iv4'); bumpProgress('p10',25); }
+ go.disabled=true; ans.disabled=true;
+ return;
+ }
+ qBox.innerHTML=Q[i].t; renderMath(qBox);
+ iEl.textContent=(i+1); sEl.textContent=score;
+ ans.value=''; fb.style.display='none'; go.disabled=false; ans.disabled=false;
+ }
+ go.addEventListener('click', ()=>{
+ const v=parseFloat((ans.value||'').replace(',', '.'));
+ if(!isFinite(v)){ feedback(fb,false,'✗ Введи число.'); return; }
+ const tol=Q[i].tol||0.05;
+ const ok=Math.abs(v-Q[i].a)<=tol;
+ if(ok) score++;
+ feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$');
+ i++;
+ setTimeout(show, 1000);
+ });
+ reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); });
+ show();
+ })();
+
+ wireReadBtn('p10');
+}
+
+/* ===== §11 — Теорема косинусов ===== */
+function buildP11(){
+ const box = document.getElementById('p11-body');
+ let html = '';
+
+ html += makeCard('theory', 'Формулировка теоремы', '11.1', `
+ В любом треугольнике квадрат стороны равен сумме квадратов двух других сторон минус удвоенное произведение этих сторон на косинус угла между ними:
+ $$a^2 = b^2 + c^2 - 2bc \\cos A$$
+ Аналогично — для двух других сторон:
+ $$b^2 = a^2 + c^2 - 2ac \\cos B, \\qquad c^2 = a^2 + b^2 - 2ab \\cos C$$
+ Это обобщение теоремы Пифагора : при $A = 90°$ имеем $\\cos A = 0$, и формула превращается в $a^2 = b^2 + c^2$.
+ Когда нужна теорема косинусов?
+ Если известны две стороны и угол между ними — третья сторона находится теоремой косинусов. Если известны все три стороны — любой угол находится из неё же. В этих двух случаях теорема синусов не помогает (нет пары «сторона + противолежащий угол»).
+
`);
+
+ html += makeCard('rule', 'Применение для нахождения сторон и углов', '11.2', `
+ Случай 1: известны две стороны $b, c$ и угол $A$ между ними. Тогда
+ $$a = \\sqrt{b^2 + c^2 - 2bc \\cos A}$$
+ Случай 2: известны все три стороны $a, b, c$. Тогда любой угол находится по формуле:
+ $$\\cos A = \\dfrac{b^2 + c^2 - a^2}{2bc}, \\qquad \\cos B = \\dfrac{a^2 + c^2 - b^2}{2ac}, \\qquad \\cos C = \\dfrac{a^2 + b^2 - c^2}{2ab}$$
+ Затем $A = \\arccos(\\cos A)$.
+ Удобно знать: если $\\cos A > 0$ — угол $A$ острый, если $\\cos A < 0$ — тупой, если $\\cos A = 0$ — прямой.
`);
+
+ html += makeCard('example', 'Примеры', '11.3', `
+ Пример 1. Две стороны $b = 5$, $c = 7$, угол между ними $A = 60°$. Найти третью сторону.
+ $$a^2 = 25 + 49 - 2 \\cdot 5 \\cdot 7 \\cdot \\cos 60° = 74 - 70 \\cdot 0{,}5 = 39$$
+ $$a = \\sqrt{39} \\approx 6{,}24$$
+ Пример 2. Стороны $3, 4, 5$. Найти угол между сторонами $3$ и $4$ (т.е. противолежащий стороне $5$).
+ $$\\cos C = \\dfrac{9 + 16 - 25}{2 \\cdot 3 \\cdot 4} = 0 \\Rightarrow C = 90°$$
+ Это знакомый прямоугольный треугольник 3–4–5.
+ Пример 3. Стороны $7, 8, 13$. Найти наибольший угол (он лежит против $13$).
+ $$\\cos A = \\dfrac{49 + 64 - 169}{2 \\cdot 7 \\cdot 8} = \\dfrac{-56}{112} = -0{,}5 \\Rightarrow A = 120°$$`);
+
+ /* IV1 — Slider угла → одна сторона */
+ html += `
+
+
Стороны $b = 100$ и $c = 150$ зафиксированы. Меняй угол $A$ между ними — третья сторона $a$ вычисляется по теореме косинусов.
+
+ $A$ (°)60
+
+
+
+
+
+
`;
+
+ /* IV2 — Калькулятор */
+ html += `
+
+
Две формы: одна находит сторону по двум сторонам и углу, другая — угол по трём сторонам.
+
Найти сторону $a$
+
+ сторона $b$
+ сторона $c$
+ угол $A$ (°)
+
+
Найти $a$
+
+
+
Найти угол $A$
+
+ сторона $a$
+ сторона $b$
+ сторона $c$
+
+
Найти $A$
+
+
+
`;
+
+ /* IV3 — Что найти? */
+ html += `
+
+
Дано условие — выбери, в каком виде применить теорему косинусов: для нахождения стороны или для нахождения угла.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ Для стороны$a^2 = b^2+c^2-2bc\\cos A$
+ Для угла$\\cos A = \\dfrac{b^2+c^2-a^2}{2bc}$
+
+
+
`;
+
+ /* IV4 — Тренажёр */
+ html += `
+
+
Реши задачу и введи число (округляй до 2 знаков после запятой).
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ ответ =
+
+ Проверить
+ Заново
+
+
+
`;
+
+ html += readButton('p11');
+ html += secNav('p10', 'p12');
+
+ box.innerHTML = html;
+ renderMath(box);
+
+ /* IV1 — Slider угла A */
+ (function(){
+ const sA=document.getElementById('p11-iv1-a');
+ const lA=document.getElementById('p11-iv1-aval');
+ const svg=document.getElementById('p11-iv1-svg');
+ const out=document.getElementById('p11-iv1-out');
+ const seen=new Set();
+ const b=100, c=150;
+ function draw(){
+ const A=+sA.value; lA.textContent=A;
+ // A_v в позиции (90, 240), B_v = A_v + (c, 0) = (240, 240). C_v под углом A от A_v на расстояние b.
+ const Av={x:90, y:240};
+ const Bv={x:Av.x+c, y:Av.y};
+ const Cv={x:Av.x+b*Math.cos(deg2rad(A)), y:Av.y-b*Math.sin(deg2rad(A))};
+ const a=Math.hypot(Bv.x-Cv.x, Bv.y-Cv.y);
+ let s='';
+ s += ' ';
+ // треугольник
+ s += ' ';
+ // дуга угла A
+ const uAB=unitVec(Av,Bv), uAC=unitVec(Av,Cv);
+ s += ' ';
+ // прямой угол, если A=90
+ if(Math.abs(A-90)<0.5){
+ s += ' ';
+ }
+ // подписи сторон
+ const midAB={x:(Av.x+Bv.x)/2, y:(Av.y+Bv.y)/2};
+ const midAC={x:(Av.x+Cv.x)/2, y:(Av.y+Cv.y)/2};
+ const midBC={x:(Bv.x+Cv.x)/2, y:(Bv.y+Cv.y)/2};
+ s += 'c = '+c+' ';
+ s += 'b = '+b+' ';
+ s += 'a = '+a.toFixed(1)+' ';
+ // подпись угла A
+ s += 'A = '+A+'° ';
+ // вершины
+ [['A',Av],['B',Bv],['C',Cv]].forEach(([n,P])=>{
+ s += ' ';
+ const dx=(n==='A'?-12:(n==='B'?12:0)), dy=(n==='C'?-10:18);
+ s += ''+n+' ';
+ });
+ svg.innerHTML=s;
+
+ const cosA=Math.cos(deg2rad(A));
+ const a2 = b*b + c*c - 2*b*c*cosA;
+ let info = '$a^2 = b^2 + c^2 - 2bc \\cos A = '+(b*b)+' + '+(c*c)+' - '+(2*b*c)+' \\cdot \\cos '+A+'° \\approx '+a2.toFixed(1)+'$ '
+ + '$a \\approx '+Math.sqrt(a2).toFixed(2)+'$';
+ if(Math.abs(A-90)<0.5) info += '✓ Прямой угол — это теорема Пифагора! ';
+ out.innerHTML=info;
+ renderMath(out);
+ seen.add(A);
+ if(seen.size>=4 && !seen.has('done')){ addXp(10,'p11-iv1'); bumpProgress('p11',15); seen.add('done'); }
+ }
+ sA.addEventListener('input', draw);
+ draw();
+ })();
+
+ /* IV2 — Калькулятор */
+ (function(){
+ const bI=document.getElementById('p11-iv2-b');
+ const cI=document.getElementById('p11-iv2-c');
+ const AI=document.getElementById('p11-iv2-A');
+ const goS=document.getElementById('p11-iv2-goS');
+ const outS=document.getElementById('p11-iv2-outS');
+ const aI=document.getElementById('p11-iv2-a2');
+ const b2I=document.getElementById('p11-iv2-b2');
+ const c2I=document.getElementById('p11-iv2-c2');
+ const goA=document.getElementById('p11-iv2-goA');
+ const outA=document.getElementById('p11-iv2-outA');
+ const fb=document.getElementById('p11-iv2-fb');
+ let solvedS=0, solvedA=0;
+ goS.addEventListener('click', ()=>{
+ const b=parseFloat(bI.value), c=parseFloat(cI.value), A=parseFloat(AI.value);
+ if(!isFinite(b)||!isFinite(c)||!isFinite(A)){ feedback(fb,false,'✗ Введи все значения.'); return; }
+ if(b<=0||c<=0){ feedback(fb,false,'✗ Стороны должны быть положительными.'); return; }
+ if(A<=0||A>=180){ feedback(fb,false,'✗ Угол $A$ от $0°$ до $180°$.'); return; }
+ const a2 = b*b + c*c - 2*b*c*Math.cos(deg2rad(A));
+ const a = Math.sqrt(a2);
+ outS.innerHTML = '$a^2 = b^2 + c^2 - 2bc \\cos A = '+(b*b).toFixed(2)+' + '+(c*c).toFixed(2)+' - '+(2*b*c).toFixed(2)+' \\cdot \\cos '+A+'° \\approx '+a2.toFixed(3)+'$ '
+ + '$a \\approx '+a.toFixed(3)+'$';
+ renderMath(outS);
+ feedback(fb,true,'✓ Сторона найдена.');
+ solvedS++; if(solvedS+solvedA===1){ addXp(10,'p11-iv2'); bumpProgress('p11',10); }
+ });
+ goA.addEventListener('click', ()=>{
+ const a=parseFloat(aI.value), b=parseFloat(b2I.value), c=parseFloat(c2I.value);
+ if(!isFinite(a)||!isFinite(b)||!isFinite(c)){ feedback(fb,false,'✗ Введи все значения.'); return; }
+ if(a<=0||b<=0||c<=0){ feedback(fb,false,'✗ Стороны должны быть положительными.'); return; }
+ // проверка неравенства треугольника
+ if(a+b<=c || a+c<=b || b+c<=a){ feedback(fb,false,'✗ Неравенство треугольника не выполняется.'); return; }
+ let cosA = (b*b + c*c - a*a)/(2*b*c);
+ if(cosA>1) cosA=1; if(cosA<-1) cosA=-1;
+ const A = Math.acos(cosA) * 180 / Math.PI;
+ outA.innerHTML = '$\\cos A = \\dfrac{b^2 + c^2 - a^2}{2bc} = \\dfrac{'+(b*b).toFixed(2)+' + '+(c*c).toFixed(2)+' - '+(a*a).toFixed(2)+'}{'+(2*b*c).toFixed(2)+'} \\approx '+cosA.toFixed(4)+'$ '
+ + '$A = \\arccos('+cosA.toFixed(4)+') \\approx '+A.toFixed(2)+'°$';
+ renderMath(outA);
+ feedback(fb,true,'✓ Угол найден.');
+ solvedA++; if(solvedS+solvedA===1){ addXp(10,'p11-iv2'); bumpProgress('p11',10); }
+ });
+ })();
+
+ /* IV3 — «Что искать?» */
+ (function(){
+ const Q=[
+ {t:'Известны 2 стороны и угол между ними. Найти третью сторону.', a:'side'},
+ {t:'Известны 3 стороны. Найти один из углов.', a:'angle'},
+ {t:'Стороны $b = 5$, $c = 7$, угол $A = 60°$. Найти сторону $a$.', a:'side'},
+ {t:'Стороны $a = 13$, $b = 14$, $c = 15$. Найти угол $B$.', a:'angle'},
+ {t:'Стороны $b = 8$, $c = 10$, угол $A = 120°$. Найти сторону $a$.', a:'side'},
+ {t:'Стороны треугольника $5, 12, 13$. Найти наибольший угол.', a:'angle'}
+ ];
+ const explain={
+ side:'$a^2 = b^2 + c^2 - 2bc \\cos A$ — даёт квадрат искомой стороны.',
+ angle:'$\\cos A = \\dfrac{b^2 + c^2 - a^2}{2bc}$ — даёт косинус искомого угла.'
+ };
+ const labels={side:'для стороны', angle:'для угла'};
+ const qBox=document.getElementById('p11-iv3-q');
+ const iEl=document.getElementById('p11-iv3-i');
+ const sEl=document.getElementById('p11-iv3-s');
+ const fb=document.getElementById('p11-iv3-fb');
+ const btns=document.querySelectorAll('#p11-iv3 button[data-ans]');
+ let i=0, score=0, done=false;
+ function show(){
+ if(i>=Q.length){
+ qBox.innerHTML='Завершено! Очки: '+score+' / '+Q.length;
+ if(!done){ done=true; addXp(15,'p11-iv3'); bumpProgress('p11',25); if(score===Q.length) achievement('p11_done'); }
+ btns.forEach(b=>b.disabled=true);
+ return;
+ }
+ qBox.innerHTML=Q[i].t; renderMath(qBox);
+ iEl.textContent=(i+1); sEl.textContent=score;
+ fb.style.display='none';
+ }
+ btns.forEach(b=>b.addEventListener('click', ()=>{
+ const ok = b.dataset.ans===Q[i].a;
+ if(ok) score++;
+ feedback(fb, ok, ok?('✓ Верно! '+explain[Q[i].a]):('✗ Правильно: '+labels[Q[i].a]+'. '+explain[Q[i].a]));
+ i++;
+ setTimeout(show, 1100);
+ }));
+ show();
+ })();
+
+ /* IV4 — Тренажёр */
+ (function(){
+ const Q=[
+ {t:'Стороны $b = 5$, $c = 8$, угол $A = 60°$. Найди $a$.', a:7, tol:0.02},
+ {t:'Стороны $b = 3$, $c = 4$, угол $A = 90°$. Найди $a$.', a:5, tol:0.02},
+ {t:'Стороны треугольника $7, 8, 13$. Найди наибольший угол (в градусах).', a:120, tol:0.5},
+ {t:'Стороны $b = 6$, $c = 10$, угол $A = 120°$. Найди $a$.', a:14, tol:0.02},
+ {t:'Стороны $3, 5, 7$. Найди угол против стороны $7$ (в градусах).', a:120, tol:0.5},
+ {t:'Стороны $b = 4$, $c = 6$, угол $A = 45°$. Найди $a^2$.', a:18.06, tol:0.1}
+ ];
+ const qBox=document.getElementById('p11-iv4-q');
+ const ans=document.getElementById('p11-iv4-ans');
+ const go=document.getElementById('p11-iv4-go');
+ const reset=document.getElementById('p11-iv4-start');
+ const iEl=document.getElementById('p11-iv4-i');
+ const sEl=document.getElementById('p11-iv4-s');
+ const fb=document.getElementById('p11-iv4-fb');
+ let i=0, score=0, done=false;
+ function show(){
+ if(i>=Q.length){
+ qBox.innerHTML='Финиш! Очки: '+score+' / '+Q.length;
+ if(!done){ done=true; addXp(15,'p11-iv4'); bumpProgress('p11',25); }
+ go.disabled=true; ans.disabled=true;
+ return;
+ }
+ qBox.innerHTML=Q[i].t; renderMath(qBox);
+ iEl.textContent=(i+1); sEl.textContent=score;
+ ans.value=''; fb.style.display='none'; go.disabled=false; ans.disabled=false;
+ }
+ go.addEventListener('click', ()=>{
+ const v=parseFloat((ans.value||'').replace(',', '.'));
+ if(!isFinite(v)){ feedback(fb,false,'✗ Введи число.'); return; }
+ const tol=Q[i].tol||0.05;
+ const ok=Math.abs(v-Q[i].a)<=tol;
+ if(ok) score++;
+ feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$');
+ i++;
+ setTimeout(show, 1000);
+ });
+ reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); });
+ show();
+ })();
+
+ wireReadBtn('p11');
+}
function buildP12(){ _stubBuilder('p12', '§12', 'Формула Герона. Решение треугольников', 'p11', 'final3'); }
function buildFinal3(){