diff --git a/frontend/textbooks/geometry_9_ch1.html b/frontend/textbooks/geometry_9_ch1.html
index 1797a27..a4e8d80 100644
--- a/frontend/textbooks/geometry_9_ch1.html
+++ b/frontend/textbooks/geometry_9_ch1.html
@@ -1809,8 +1809,590 @@ function buildP4(){
wireReadBtn('p4');
}
-function buildP5(){ _stubBuilder('p5', '§5', 'Формулы площади', 'p4', 'p6'); }
-function buildP6(){ _stubBuilder('p6', '§6', 'Среднее геометрическое', 'p5', 'final1'); }
+/* ===== §5 Формулы площади ===== */
+function buildP5(){
+ const box = document.getElementById('p5-body');
+ let html = '';
+
+ html += makeCard('theory', 'Площадь треугольника через две стороны и угол', '5.1', `
+
Площадь треугольника можно вычислить, зная две стороны и угол между ними:
+
$\\boxed{\\,S_\\triangle = \\dfrac{1}{2} a b \\sin C\\,}$
+
Здесь $a$ и $b$ — две стороны треугольника, а $C$ — угол между ними (то есть угол при общей вершине этих сторон).
+ Откуда берётся $\\sin C$?
+
Опустим высоту $h$ из вершины $A$ на сторону $a = BC$. В прямоугольном треугольнике с гипотенузой $b$ и острым углом $C$ имеем:
+
$h = b \\sin C$.
+
Тогда классическая формула $S = \\dfrac{1}{2} \\cdot a \\cdot h$ превращается в:
+
$S = \\dfrac{1}{2} a \\cdot b \\sin C = \\dfrac{1}{2} a b \\sin C$.
+
Формула работает и для тупого угла $C$: тогда высота попадает на продолжение стороны, но $\\sin C$ всё равно положителен (из §4 знаем: $\\sin(180^\\circ - \\alpha) = \\sin \\alpha$).
$\\boxed{\\,S_{\\text{пар}} = a b \\sin \\alpha\\,}$
+
Здесь $a, b$ — смежные стороны параллелограмма, $\\alpha$ — угол между ними.
+
Почему такая формула? Диагональ параллелограмма делит его на два равных треугольника. Площадь каждого по формуле §5.1 равна $\\dfrac{1}{2}ab\\sin\\alpha$, значит площадь всего параллелограмма — вдвое больше:
+
$S_{\\text{пар}} = 2 \\cdot \\dfrac{1}{2} ab \\sin \\alpha = ab \\sin \\alpha$.
где $d_1, d_2$ — диагонали, $\\varphi$ — угол между ними.
+
Примеры применения:
+
+
Ромб с диагоналями $6$ и $8$: диагонали ромба перпендикулярны, поэтому $\\varphi = 90^\\circ$ и $\\sin\\varphi = 1$:
+ $S = \\dfrac{1}{2} \\cdot 6 \\cdot 8 = 24$.
+
Параллелограмм со сторонами $5$ и $8$, углом $30^\\circ$ между ними:
+ $S = 5 \\cdot 8 \\cdot \\sin 30^\\circ = 40 \\cdot \\tfrac{1}{2} = 20$.
+
Треугольник со сторонами $4$ и $6$ и углом $90^\\circ$ между ними:
+ $S = \\dfrac{1}{2} \\cdot 4 \\cdot 6 \\cdot \\sin 90^\\circ = 12$ — то же, что катет × катет / 2.
+
+ Подсказка по выбору формулы
+
+
Даны две стороны + угол между ними треугольника → $\\frac{1}{2}ab\\sin C$.
+
Даны две стороны + угол параллелограмма → $ab\\sin\\alpha$.
+
Даны обе диагонали + угол между ними → $\\frac{1}{2}d_1 d_2 \\sin\\varphi$ (для ромба $\\varphi = 90°$).
+
+
`);
+
+ /* IV1 — SVG визуализатор «Площадь треугольника по углу» */
+ html += `
+
ИНТЕРАКТИВ 1
Площадь треугольника по углу
+
Двигай ползунки сторон $a, b$ и угла $A$ — треугольник перестраивается, а площадь пересчитывается по формуле $S = \\tfrac{1}{2} a b \\sin A$.
Введи смежные стороны $a, b$ и угол $\\alpha$ между ними — найдём площадь по формуле $S = a b \\sin \\alpha$. При $\\alpha = 90^\\circ$ это просто прямоугольник.
Распредели 6 фигур по трём «формульным ящикам». Тапни карточку, потом — нужный ящик (или перетащи).
+
6 фигур — 3 формулы
+
+
+
$S = \\tfrac{1}{2}ab\\sin C$
+
$S = ab\\sin\\alpha$
+
$S = \\tfrac{1}{2}d_1 d_2 \\sin\\varphi$
+
+
+
+
`;
+
+ /* IV4 — Тренажёр площади */
+ html += `
+
ИНТЕРАКТИВ 4
Тренажёр площади
+
Реши задачу и введи число (целое или округлённое до целых).
+
Задача 1 / 6Очки: 0 / 6
+
+
+ $S$ =
+
+
+
+
+
+
`;
+
+ html += secNav('p4', 'p6');
+ html += readButton('p5');
+
+ box.innerHTML = html;
+ renderMath(box);
+
+ /* IV1 — SVG визуализатор */
+ (function(){
+ const slA = document.getElementById('p5-iv1-a');
+ const slB = document.getElementById('p5-iv1-b');
+ const slAng = document.getElementById('p5-iv1-A');
+ const labA = document.getElementById('p5-iv1-aval');
+ const labB = document.getElementById('p5-iv1-bval');
+ const labAng = document.getElementById('p5-iv1-Aval');
+ const svg = document.getElementById('p5-iv1-svg');
+ const out = document.getElementById('p5-iv1-out');
+ const seen = new Set();
+ function draw(){
+ const a = +slA.value, b = +slB.value, Adeg = +slAng.value;
+ labA.textContent = a; labB.textContent = b; labAng.textContent = Adeg;
+ const aRad = deg2rad(Adeg);
+ // В пикселях: множитель 20 на единицу
+ const k = 20;
+ const aPx = a * k, bPx = b * k;
+ // A в (80, 240), B справа по горизонтали, C — поворот на угол A вокруг A.
+ const A = {x: 80, y: 240};
+ const B = {x: 80 + aPx, y: 240};
+ const C = {x: 80 + bPx * Math.cos(aRad), y: 240 - bPx * Math.sin(aRad)};
+ // векторы в A: вдоль AB и вдоль AC
+ const uAB = unitVec(A, B);
+ const uAC = unitVec(A, C);
+ let s = '';
+ s += '';
+ s += '';
+ // дуга угла A
+ s += '';
+ // подпись угла A
+ const halfRad = aRad / 2;
+ const tx = A.x + 52*Math.cos(halfRad);
+ const ty = A.y - 52*Math.sin(halfRad);
+ s += 'A='+Adeg+'°';
+ // вершины
+ s += '';
+ s += '';
+ s += '';
+ s += 'A';
+ s += 'B';
+ s += 'C';
+ // подписи сторон
+ s += 'a='+a+'';
+ const midAC = {x:(A.x+C.x)/2, y:(A.y+C.y)/2};
+ // нормаль к AC, направленная "наружу" треугольника (от B)
+ const nAC = {x:-(C.y-A.y), y:(C.x-A.x)};
+ const nL = Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1;
+ // выбираем сторону, противоположную B
+ const sign = ((B.x-A.x)*nAC.x + (B.y-A.y)*nAC.y) > 0 ? -1 : 1;
+ const labP = {x: midAC.x + sign*14*nAC.x/nL, y: midAC.y + sign*14*nAC.y/nL};
+ s += 'b='+b+'';
+ svg.innerHTML = s;
+ // расчёт площади
+ const S = 0.5 * a * b * Math.sin(aRad);
+ out.innerHTML = '$S = \\dfrac{1}{2} \\cdot a \\cdot b \\cdot \\sin A = \\dfrac{1}{2} \\cdot '+a+' \\cdot '+b+' \\cdot \\sin '+Adeg+'^\\circ \\approx '+S.toFixed(2)+'$';
+ renderMath(out);
+ seen.add(a+'_'+b+'_'+Adeg);
+ if(seen.size >= 5 && !seen.has('done')){ addXp(10,'p5-iv1'); bumpProgress('p5', 15); seen.add('done'); }
+ }
+ [slA, slB, slAng].forEach(sl => sl.addEventListener('input', draw));
+ draw();
+ })();
+
+ /* IV2 — Калькулятор параллелограмма */
+ (function(){
+ const aI = document.getElementById('p5-iv2-a');
+ const bI = document.getElementById('p5-iv2-b');
+ const AI = document.getElementById('p5-iv2-A');
+ const go = document.getElementById('p5-iv2-go');
+ const out = document.getElementById('p5-iv2-out');
+ const fb = document.getElementById('p5-iv2-fb');
+ let solved = 0;
+ function calc(){
+ const a = parseFloat(aI.value), b = parseFloat(bI.value), Adeg = parseFloat(AI.value);
+ if(isNaN(a) || isNaN(b) || isNaN(Adeg)){ feedback(fb, false, '✗ Введи все три числа.'); return; }
+ if(a<=0 || b<=0){ feedback(fb, false, '✗ Стороны должны быть положительны.'); return; }
+ if(Adeg<=0 || Adeg>=180){ feedback(fb, false, '✗ Угол должен быть в диапазоне (0°; 180°).'); return; }
+ const r = deg2rad(Adeg);
+ const S = a * b * Math.sin(r);
+ let html = '$S = a \\cdot b \\cdot \\sin \\alpha = '+a+' \\cdot '+b+' \\cdot \\sin '+Adeg+'^\\circ \\approx '+S.toFixed(2)+'$';
+ if(Math.abs(Adeg - 90) < 1e-9){
+ html += ' Особый случай: $\\alpha = 90^\\circ$ — это прямоугольник, $S = a b = '+(a*b).toFixed(2)+'$.';
+ }
+ out.innerHTML = html;
+ renderMath(out);
+ feedback(fb, true, '✓ Площадь найдена.');
+ solved++;
+ if(solved === 1){ addXp(10,'p5-iv2'); bumpProgress('p5', 15); }
+ }
+ go.addEventListener('click', calc);
+ calc();
+ })();
+
+ /* IV3 — DnD сортер */
+ (function(){
+ const items = [
+ { id:'f1', cat:'t', html:'треугольник со сторонами $a, b$ и углом $C$ между ними' },
+ { id:'f2', cat:'p', html:'параллелограмм со сторонами $a, b$ и углом $\\alpha$' },
+ { id:'f3', cat:'p', html:'прямоугольник со сторонами $a, b$ ($\\alpha=90^\\circ$)' },
+ { id:'f4', cat:'d', html:'ромб с диагоналями $d_1, d_2$' },
+ { id:'f5', cat:'d', html:'трапеция с диагоналями $d_1, d_2$ и углом $\\varphi$' },
+ { id:'f6', cat:'t', html:'равносторонний треугольник со стороной $a$ ($C = 60^\\circ$)' },
+ ];
+ const sorter = setupSorter({
+ poolId:'p5-iv3-pool',
+ scopeSelector:'#p5-iv3',
+ items: items,
+ cats:['t','p','d'],
+ columnLayout:true,
+ });
+ document.getElementById('p5-iv3-check').addEventListener('click', ()=>{
+ const fb = document.getElementById('p5-iv3-fb');
+ const placedCount = items.filter(it => sorter.placed[it.id]).length;
+ const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
+ if(placedCount < items.length){ feedback(fb, false, '✗ Размести все 6 фигур.'); return; }
+ if(correct === items.length){ feedback(fb, true, '✓ Все 6 на месте! +15 XP'); addXp(15,'p5-iv3'); bumpProgress('p5', 25); }
+ else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
+ });
+ document.getElementById('p5-iv3-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p5-iv3-fb').style.display = 'none'; });
+ })();
+
+ /* IV4 — Тренажёр */
+ (function(){
+ const Q = [
+ { q:'Треугольник: $a = 5$, $b = 8$, $C = 30^\\circ$. Найди $S$.', ans:10, tol:0.2, hint:'$S = \\tfrac{1}{2} \\cdot 5 \\cdot 8 \\cdot \\sin 30^\\circ = 20 \\cdot 0{,}5 = 10$' },
+ { q:'Параллелограмм: $a = 6$, $b = 10$, $\\alpha = 90^\\circ$. Найди $S$.', ans:60, tol:0.2, hint:'$\\sin 90^\\circ = 1$, $S = a b = 60$' },
+ { q:'Ромб с диагоналями $12$ и $16$. Найди $S$.', ans:96, tol:0.2, hint:'диагонали ромба перпендикулярны: $S = \\tfrac{1}{2} \\cdot 12 \\cdot 16 = 96$' },
+ { q:'Треугольник: $a = b = 4$, $C = 60^\\circ$. Найди $S$ (округли до целого).', ans:7, tol:0.5, hint:'$S = \\tfrac{1}{2} \\cdot 16 \\cdot \\sin 60^\\circ = 8 \\cdot \\tfrac{\\sqrt{3}}{2} = 4\\sqrt{3} \\approx 6{,}93 \\approx 7$' },
+ { q:'Прямоугольник со сторонами $7$ и $12$. Найди $S$.', ans:84, tol:0.2, hint:'$S = 7 \\cdot 12 = 84$' },
+ { q:'Треугольник: $a = 10$, $b = 10$, $C = 90^\\circ$. Найди $S$.', ans:50, tol:0.2, hint:'$S = \\tfrac{1}{2} \\cdot 100 \\cdot \\sin 90^\\circ = 50$' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ if(i >= Q.length){
+ document.getElementById('p5-iv4-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length;
+ if(score === Q.length){ addXp(15,'p5-iv4'); bumpProgress('p5', 25); achievement('p5_done'); }
+ else if(score >= 4){ addXp(8,'p5-iv4'); bumpProgress('p5', 15); }
+ return;
+ }
+ document.getElementById('p5-iv4-i').textContent = (i+1);
+ document.getElementById('p5-iv4-s').textContent = score;
+ document.getElementById('p5-iv4-q').innerHTML = Q[i].q;
+ document.getElementById('p5-iv4-ans').value = '';
+ renderMath(document.getElementById('p5-iv4-q'));
+ document.getElementById('p5-iv4-fb').style.display = 'none';
+ }
+ function go(){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p5-iv4-fb');
+ const ans = parseFloat(document.getElementById('p5-iv4-ans').value);
+ if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
+ if(Math.abs(ans - Q[i].ans) <= Q[i].tol){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
+ else feedback(fb, false, '✗ Неверно. Ответ: '+Q[i].ans+' ('+Q[i].hint+'). Дальше ▶');
+ document.getElementById('p5-iv4-s').textContent = score;
+ i++;
+ setTimeout(show, 1400);
+ }
+ document.getElementById('p5-iv4-go').addEventListener('click', go);
+ document.getElementById('p5-iv4-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
+ document.getElementById('p5-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
+ show();
+ })();
+
+ wireReadBtn('p5');
+}
+
+/* ===== §6 Среднее геометрическое в прямоугольном треугольнике ===== */
+function buildP6(){
+ const box = document.getElementById('p6-body');
+ let html = '';
+
+ html += makeCard('theory', 'Высота к гипотенузе и проекции катетов', '6.1', `
+
Рассмотрим прямоугольный треугольник $ABC$ с прямым углом $C$. Из вершины $C$ опустим высоту $h = CH$ на гипотенузу $c = AB$. Эта высота делит гипотенузу на два отрезка: $AH = b_1$ и $BH = a_1$, причём $a_1 + b_1 = c$.
+
Тогда в треугольнике справедливы три замечательных соотношения:
+
$\\boxed{\\,h^2 = a_1 b_1,\\qquad a^2 = c \\cdot a_1,\\qquad b^2 = c \\cdot b_1\\,}$
+
Здесь $a, b$ — катеты ($a = BC$ напротив $A$, $b = AC$ напротив $B$), $h$ — высота к гипотенузе, $a_1$ — проекция катета $a$ на гипотенузу (отрезок $BH$), $b_1$ — проекция катета $b$ на гипотенузу (отрезок $AH$).
+ Откуда берутся эти соотношения?
+
Высота $CH$ разбивает $\\triangle ABC$ на два маленьких прямоугольных треугольника — $\\triangle ACH$ и $\\triangle CBH$. Все три треугольника подобны друг другу (имеют по два равных угла).
+
Из подобия $\\triangle ACH \\sim \\triangle CBH$ получаем $\\dfrac{AH}{CH} = \\dfrac{CH}{BH}$, то есть $\\dfrac{b_1}{h} = \\dfrac{h}{a_1}$, откуда $h^2 = a_1 b_1$.
+
Из подобия $\\triangle ABC \\sim \\triangle ACH$ получаем $\\dfrac{AB}{AC} = \\dfrac{AC}{AH}$, то есть $\\dfrac{c}{b} = \\dfrac{b}{b_1}$, откуда $b^2 = c b_1$. Аналогично — третье соотношение.
Эти три формулы — это среднее геометрическое в действии. Среднее геометрическое чисел $x, y$ — это $\\sqrt{xy}$. Получаем:
+
+
$h = \\sqrt{a_1 b_1}$ — высота к гипотенузе есть среднее геометрическое проекций катетов.
+
$a = \\sqrt{c \\cdot a_1}$ — катет $a$ есть среднее геометрическое гипотенузы и проекции этого катета.
+
$b = \\sqrt{c \\cdot b_1}$ — катет $b$ есть среднее геометрическое гипотенузы и проекции этого катета.
+
+
Эти три факта называют метрическими соотношениями в прямоугольном треугольнике, и они моментально решают задачи, в которых заданы какие-то «куски» гипотенузы.
+ Почему «среднее геометрическое»?
+
Запись $h^2 = a_1 b_1$ означает, что $h$ — такое число, квадрат которого равен произведению $a_1$ и $b_1$. Это и есть классическое определение среднего геометрического двух положительных чисел: $h = \\sqrt{a_1 b_1}$.
Сверка площадей: $S = \\tfrac{1}{2} a b = \\tfrac{1}{2} \\cdot 6 \\cdot 8 = 24$ и $S = \\tfrac{1}{2} c h = \\tfrac{1}{2} \\cdot 10 \\cdot 4{,}8 = 24$. Совпало — соотношение работает.
`);
+
+ /* IV1 — SVG визуализатор */
+ html += `
+
ИНТЕРАКТИВ 1
Конструктор соотношений
+
Меняй угол $A$ — точка $H$ перемещается по гипотенузе, проекции $a_1, b_1$ меняются, и три соотношения $h^2 = a_1 b_1$, $a^2 = c a_1$, $b^2 = c b_1$ всё время выполняются.
+
+
+
+
+
+
+
+
`;
+
+ /* IV2 — Калькулятор h */
+ html += `
+
ИНТЕРАКТИВ 2
Калькулятор $h$ и $c$
+
Введи проекции катетов $a_1, b_1$ — найдём высоту $h$, гипотенузу $c$, катеты $a, b$ и площадь $S$.
+
+ $a_1$ =
+
+ $b_1$ =
+
+
+
+
+
+
`;
+
+ /* IV3 — Какое соотношение? */
+ html += `
+
ИНТЕРАКТИВ 3
Какое соотношение применить?
+
Даны какие-то элементы прямоугольного треугольника — выбери, какое из трёх соотношений поможет найти искомую величину.
+
Задача 1 / 6Очки: 0 / 6
+
+
+
+
+
+
+
+
`;
+
+ /* IV4 — Тренажёр */
+ html += `
+
ИНТЕРАКТИВ 4
Тренажёр среднего геометрического
+
Реши задачу и введи число (целое или округлённое до сотых).
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ =
+
+
+
+
+
+
`;
+
+ html += secNav('p5', 'final1');
+ html += readButton('p6');
+
+ box.innerHTML = html;
+ renderMath(box);
+
+ /* IV1 — SVG */
+ (function(){
+ const sl = document.getElementById('p6-iv1-a');
+ const lab = document.getElementById('p6-iv1-aval');
+ const svg = document.getElementById('p6-iv1-svg');
+ const out = document.getElementById('p6-iv1-out');
+ const seen = new Set();
+ function draw(){
+ const Adeg = +sl.value;
+ lab.textContent = Adeg;
+ const Arad = deg2rad(Adeg);
+ // Гипотенуза AB длиной 300px по низу.
+ const cPx = 300;
+ const A = {x: 90, y: 260};
+ const B = {x: 90 + cPx, y: 260};
+ // Точка C — над гипотенузой, угол при A равен Adeg, угол при B равен 90 - Adeg.
+ // AC = c * cos A, BC = c * sin A. Из A под углом Adeg вверх.
+ const ACpx = cPx * Math.cos(Arad);
+ // Координаты C: от A на расстояние AC под углом Adeg к AB (вверх)
+ const C = {x: A.x + ACpx * Math.cos(Arad), y: A.y - ACpx * Math.sin(Arad)};
+ // H — основание высоты из C на AB.
+ const H = {x: C.x, y: A.y};
+ // длины
+ const a1 = Math.abs(H.x - B.x); // BH (проекция BC)
+ const b1 = Math.abs(H.x - A.x); // AH (проекция AC)
+ const hPx = Math.abs(C.y - H.y);
+ const aPx = Math.sqrt((B.x-C.x)*(B.x-C.x) + (B.y-C.y)*(B.y-C.y));
+ const bPx = Math.sqrt((A.x-C.x)*(A.x-C.x) + (A.y-C.y)*(A.y-C.y));
+ // в "единичных" единицах (делим px на 30)
+ const sc = 30;
+ const a = aPx/sc, b = bPx/sc, c = cPx/sc, h = hPx/sc, _a1 = a1/sc, _b1 = b1/sc;
+ let s = '';
+ s += '';
+ // подсветка маленьких треугольников
+ s += '';
+ s += '';
+ // основной треугольник (контур)
+ s += '';
+ // высота CH
+ s += '';
+ // маркер прямого угла при C (катеты CA и CB)
+ const uCA = unitVec(C, A);
+ const uCB = unitVec(C, B);
+ s += '';
+ // маркер прямого угла при H (CH и HB)
+ const uHC = unitVec(H, C);
+ const uHB = unitVec(H, B);
+ s += '';
+ // вершины
+ s += '';
+ s += '';
+ s += '';
+ s += '';
+ s += 'A';
+ s += 'B';
+ s += 'C';
+ s += 'H';
+ // подписи отрезков
+ s += 'b₁='+b1.toFixed(0)+'';
+ s += 'a₁='+a1.toFixed(0)+'';
+ s += 'h='+hPx.toFixed(0)+'';
+ s += 'b='+bPx.toFixed(0)+'';
+ s += 'a='+aPx.toFixed(0)+'';
+ svg.innerHTML = s;
+ // соотношения (в пикселях — но проверка работает в любых единицах)
+ out.innerHTML = 'Три соотношения (в пикселях): '
+ + '$h^2 = '+hPx.toFixed(0)+'^2 \\approx '+(hPx*hPx).toFixed(0)+'$, $a_1 \\cdot b_1 = '+a1.toFixed(0)+' \\cdot '+b1.toFixed(0)+' \\approx '+(a1*b1).toFixed(0)+'$ ✓ '
+ + '$a^2 = '+aPx.toFixed(0)+'^2 \\approx '+(aPx*aPx).toFixed(0)+'$, $c \\cdot a_1 = '+cPx+' \\cdot '+a1.toFixed(0)+' \\approx '+(cPx*a1).toFixed(0)+'$ ✓ '
+ + '$b^2 = '+bPx.toFixed(0)+'^2 \\approx '+(bPx*bPx).toFixed(0)+'$, $c \\cdot b_1 = '+cPx+' \\cdot '+b1.toFixed(0)+' \\approx '+(cPx*b1).toFixed(0)+'$ ✓';
+ renderMath(out);
+ seen.add(Adeg);
+ if(seen.size >= 5 && !seen.has('done')){ addXp(10,'p6-iv1'); bumpProgress('p6', 15); seen.add('done'); }
+ }
+ sl.addEventListener('input', draw);
+ draw();
+ })();
+
+ /* IV2 — Калькулятор */
+ (function(){
+ const aI = document.getElementById('p6-iv2-a1');
+ const bI = document.getElementById('p6-iv2-b1');
+ const go = document.getElementById('p6-iv2-go');
+ const out = document.getElementById('p6-iv2-out');
+ const fb = document.getElementById('p6-iv2-fb');
+ let solved = 0;
+ function calc(){
+ const a1 = parseFloat(aI.value), b1 = parseFloat(bI.value);
+ if(isNaN(a1) || isNaN(b1)){ feedback(fb, false, '✗ Введи оба числа.'); return; }
+ if(a1<=0 || b1<=0){ feedback(fb, false, '✗ Проекции должны быть положительны.'); return; }
+ const c = a1 + b1;
+ const h = Math.sqrt(a1 * b1);
+ const a = Math.sqrt(c * a1);
+ const b = Math.sqrt(c * b1);
+ const S = 0.5 * c * h;
+ out.innerHTML = '$c = a_1 + b_1 = '+a1+' + '+b1+' = '+c+'$ '
+ + '$h = \\sqrt{a_1 \\cdot b_1} = \\sqrt{'+a1+' \\cdot '+b1+'} = \\sqrt{'+(a1*b1)+'} \\approx '+h.toFixed(2)+'$ '
+ + '$a = \\sqrt{c \\cdot a_1} = \\sqrt{'+(c*a1)+'} \\approx '+a.toFixed(2)+'$ '
+ + '$b = \\sqrt{c \\cdot b_1} = \\sqrt{'+(c*b1)+'} \\approx '+b.toFixed(2)+'$ '
+ + '$S = \\dfrac{1}{2} c h \\approx '+S.toFixed(2)+'$ (сверка: $\\tfrac{1}{2}ab \\approx '+(0.5*a*b).toFixed(2)+'$)';
+ renderMath(out);
+ feedback(fb, true, '✓ Все элементы найдены.');
+ solved++;
+ if(solved === 1){ addXp(10,'p6-iv2'); bumpProgress('p6', 15); }
+ }
+ go.addEventListener('click', calc);
+ calc();
+ })();
+
+ /* IV3 — Какое соотношение? */
+ (function(){
+ const Q = [
+ { expr:'Известно $c = 10$, $a_1 = 4$. Найти катет $a$.', ans:'a', why:'$a^2 = c \\cdot a_1 = 40 \\Rightarrow a = \\sqrt{40}$' },
+ { expr:'Известно $a_1 = 9$, $b_1 = 16$. Найти высоту $h$.', ans:'h', why:'$h^2 = a_1 \\cdot b_1 = 144 \\Rightarrow h = 12$' },
+ { expr:'Известно $c = 25$, $b_1 = 9$. Найти катет $b$.', ans:'b', why:'$b^2 = c \\cdot b_1 = 225 \\Rightarrow b = 15$' },
+ { expr:'Известно $a = 6$, $c = 9$. Найти $a_1$.', ans:'a', why:'из $a^2 = c \\cdot a_1$: $a_1 = a^2/c = 36/9 = 4$' },
+ { expr:'Известно $h = 6$, $a_1 = 4$. Найти $b_1$.', ans:'h', why:'из $h^2 = a_1 b_1$: $b_1 = h^2/a_1 = 36/4 = 9$' },
+ { expr:'Известно $b = 8$, $c = 16$. Найти $b_1$.', ans:'b', why:'из $b^2 = c \\cdot b_1$: $b_1 = b^2/c = 64/16 = 4$' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ if(i >= Q.length){
+ document.getElementById('p6-iv3-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length;
+ if(score === Q.length){ addXp(15,'p6-iv3'); bumpProgress('p6', 25); }
+ else if(score >= Q.length - 1){ addXp(8,'p6-iv3'); bumpProgress('p6', 15); }
+ return;
+ }
+ document.getElementById('p6-iv3-i').textContent = (i+1);
+ document.getElementById('p6-iv3-s').textContent = score;
+ document.getElementById('p6-iv3-q').innerHTML = Q[i].expr;
+ renderMath(document.getElementById('p6-iv3-q'));
+ document.getElementById('p6-iv3-fb').style.display = 'none';
+ }
+ function answer(a){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p6-iv3-fb');
+ if(a === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! '+Q[i].why+'. Дальше ▶'); }
+ else feedback(fb, false, '✗ Нет. Нужно: '+Q[i].why+'. Дальше ▶');
+ document.getElementById('p6-iv3-s').textContent = score;
+ i++;
+ setTimeout(show, 1200);
+ }
+ ['h','a','b'].forEach(k=>{
+ const btn = document.getElementById('p6-iv3-'+k); if(btn) btn.addEventListener('click', ()=>answer(k));
+ });
+ show();
+ })();
+
+ /* IV4 — Тренажёр */
+ (function(){
+ const Q = [
+ { q:'$a_1 = 4$, $b_1 = 9$. Найти $h$.', ans:6, tol:0.05, hint:'$h = \\sqrt{4 \\cdot 9} = \\sqrt{36} = 6$' },
+ { q:'$c = 20$, $b_1 = 5$. Найти $b$.', ans:10, tol:0.05, hint:'$b = \\sqrt{c b_1} = \\sqrt{100} = 10$' },
+ { q:'$h = 12$, $a_1 = 9$. Найти $b_1$.', ans:16, tol:0.1, hint:'$b_1 = h^2/a_1 = 144/9 = 16$' },
+ { q:'$a = 6$, $c = 9$. Найти $a_1$.', ans:4, tol:0.05, hint:'$a_1 = a^2/c = 36/9 = 4$' },
+ { q:'Среднее геометрическое чисел $4$ и $25$.', ans:10, tol:0.05, hint:'$\\sqrt{4 \\cdot 25} = \\sqrt{100} = 10$' },
+ { q:'$c = 10$, $a_1 = 2$. Найти $a^2$.', ans:20, tol:0.1, hint:'$a^2 = c \\cdot a_1 = 10 \\cdot 2 = 20$' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ if(i >= Q.length){
+ document.getElementById('p6-iv4-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length;
+ if(score === Q.length){ addXp(15,'p6-iv4'); bumpProgress('p6', 25); }
+ else if(score >= 4){ addXp(8,'p6-iv4'); bumpProgress('p6', 15); }
+ return;
+ }
+ document.getElementById('p6-iv4-i').textContent = (i+1);
+ document.getElementById('p6-iv4-s').textContent = score;
+ document.getElementById('p6-iv4-q').innerHTML = Q[i].q;
+ document.getElementById('p6-iv4-ans').value = '';
+ renderMath(document.getElementById('p6-iv4-q'));
+ document.getElementById('p6-iv4-fb').style.display = 'none';
+ }
+ function go(){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p6-iv4-fb');
+ const ans = parseFloat(document.getElementById('p6-iv4-ans').value);
+ if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
+ if(Math.abs(ans - Q[i].ans) <= Q[i].tol){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
+ else feedback(fb, false, '✗ Неверно. Ответ: '+Q[i].ans+' ('+Q[i].hint+'). Дальше ▶');
+ document.getElementById('p6-iv4-s').textContent = score;
+ i++;
+ setTimeout(show, 1400);
+ }
+ document.getElementById('p6-iv4-go').addEventListener('click', go);
+ document.getElementById('p6-iv4-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
+ document.getElementById('p6-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
+ show();
+ })();
+
+ wireReadBtn('p6');
+}
function buildFinal1(){
const body = document.getElementById('final1-body');