diff --git a/frontend/textbooks/geometry_8_ch2.html b/frontend/textbooks/geometry_8_ch2.html
index dc0683b..36f6874 100644
--- a/frontend/textbooks/geometry_8_ch2.html
+++ b/frontend/textbooks/geometry_8_ch2.html
@@ -376,7 +376,7 @@ 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:()=>buildP12(),p13:()=>buildP13(),p14:()=>buildP14(),p15:()=>buildP15(),
- final2:()=>buildFinal2stub(),
+ final2:()=>buildFinal2(),
};
function ensureBuilt(id){ if(BUILT.has(id))return;const fn=BUILDERS[id];if(fn){fn();BUILT.add(id);} }
function goTo(id){
@@ -6513,7 +6513,621 @@ function buildP15(){
if(window.renderMathInElement) renderMath(bossBox);
})();
}
-function buildFinal2stub(){ document.getElementById('final2-body').innerHTML='
Финал главы 2 — Волна 1: боссы и итоги появятся в следующем обновлении.
'+secNav('p15',null); }
+function buildFinal2(){
+ const box = document.getElementById('final2-body');
+ let html = '';
+
+ /* === ЧАСТЬ 1: Итоговая шпаргалка === */
+ html += `
+
+
+
+
+
+
+
+
§2 Прямоугольник
+
+
+
+
$S = a \cdot b$
+
$P = 2(a+b)$
+
+
+
+
+
+
§3 Параллелограмм
+
+
+
+
$S = a \cdot h$
+
$h$ — высота
+
+
+
+
+
+
§4 Треугольник
+
+
+
+
$S = \\dfrac{1}{2}ah$
+
$a$ — основание
+
+
+
+
+
+
§5 Трапеция
+
+
+
+
$S = \\dfrac{(a+b)}{2} h$
+
$a,b$ — основания
+
+
+
+
+
+
§6 Ромб
+
+
+
+
$S = \\dfrac{d_1 d_2}{2}$
+
$= ah = a^2 \\sin\\alpha$
+
+
+
+
+
+
§7 Прям. треугольник
+
+
+
+
$S = \\dfrac{1}{2}ab$
+
$a,b$ — катеты
+
+
+
+
+
+
§8 Высота к гипотенузе
+
+
+
+
$h_c = \\dfrac{ab}{c}$
+
$c$ — гипотенуза
+
+
+
+
+
+
§9 Общая высота
+
+
$\\dfrac{S_1}{S_2} = \\dfrac{a_1}{a_2}$
+
Треугольники с общей высотой
+
+
+
+
+
§10 Медиана
+
+
$S_{\\text{под}} = \\dfrac{S}{2}$ (медиана)
+
$S_{\\text{части}} = \\dfrac{S}{6}$ (центроид)
+
3 медианы → 6 равных
+
+
+
+
+
§11 Теорема Пифагора
+
+
+
+
$c^2 = a^2 + b^2$
+
$c$ — гипотенуза
+
+
+
+
+
+
§12 Равносторонний
+
+
$h = \\dfrac{a\\sqrt{3}}{2}$
+
$S = \\dfrac{a^2\\sqrt{3}}{4}$
+
+
+
+
+
§13 Диагональ квадрата
+
+
$d = a\\sqrt{2}$
+
из теор. Пифагора
+
+
+
+
+
§14 Обратная теорема
+
+
$a^2+b^2=c^2$
+
$\\Rightarrow$ угол при $c$ прямой
+
+
+
+
+
§15 Пифагоровы тройки
+
+
(3, 4, 5)
+
(5, 12, 13)
+
(8, 15, 17), (7, 24, 25)
+
+
+
+
+
+
`;
+
+ /* === ЧАСТЬ 2: Карта связей фигур === */
+ html += `
+
+
Нажми на фигуру, чтобы увидеть формулу площади и ключевые свойства.
+
+
Нажми на фигуру в схеме выше
+
`;
+
+ /* === ЧАСТЬ 3: 7 боссов === */
+ html += `
+
+
Каждая задача объединяет 2–3 темы главы. +10 XP за каждого побеждённого босса. Победи всех семерых — получишь +50 XP и достижение «Мастер площадей»!
+
+
`;
+
+ /* === ЧАСТЬ 4: Финальная плашка === */
+ html += `
+
+
+ Мастер площадей Главы 2!
+
+
Ты победил всех 7 боссов и освоил всю Главу 2 «Площади». Отличная работа!
+
+
+ Перейти к Главе 3
+
+
`;
+
+ html += `
+
+
`;
+
+ html += secNav('p15', null);
+ box.innerHTML = html;
+
+ /* === JS: Карта связей SVG === */
+ (function(){
+ const W = 620, H = 340;
+ const nodes = [
+ { id:'area', x:310, y:28, rx:58, label:'Площадь',
+ props:'Площадь — мера части плоскости, занятой фигурой. Выражается в кв. единицах.' },
+ { id:'rect', x:100, y:120, rx:54, label:'Прямоуг. фигуры',
+ props:'Квадрат: $S=a^2$. Прямоугольник: $S=ab$. Прямоугольный треугольник: $S=\\frac{1}{2}ab$ (катеты).' },
+ { id:'para', x:310, y:120, rx:56, label:'Параллелограммы',
+ props:'Параллелограмм: $S=ah$. Ромб: $S=\\frac{d_1 d_2}{2}$. Прямоугольник: $S=ab$. Квадрат: $S=a^2$.' },
+ { id:'tri', x:510, y:120, rx:48, label:'Треугольники',
+ props:'Произвольный: $S=\\frac{1}{2}ah$. Прямоугольный: $S=\\frac{1}{2}ab$. Равносторонний: $S=\\frac{a^2\\sqrt{3}}{4}$.' },
+ { id:'sq', x:60, y:230, rx:40, label:'Квадрат',
+ props:'$S=a^2$, $P=4a$, диагональ $d=a\\sqrt{2}$. Частный случай прямоугольника и ромба.' },
+ { id:'recta', x:160, y:230, rx:44, label:'Прямоугольник',
+ props:'$S=ab$, $P=2(a+b)$, $d=\\sqrt{a^2+b^2}$.' },
+ { id:'rhomb', x:280, y:230, rx:42, label:'Ромб',
+ props:'$S=\\frac{d_1 d_2}{2}=ah=a^2\\sin\\alpha$. Диагонали перпендикулярны.' },
+ { id:'trap', x:400, y:230, rx:42, label:'Трапеция',
+ props:'$S=\\frac{(a+b)}{2}h$. Средняя линия $m=\\frac{a+b}{2}$.' },
+ { id:'eqtri', x:520, y:230, rx:44, label:'Равносторонний',
+ props:'$h=\\frac{a\\sqrt{3}}{2}$, $S=\\frac{a^2\\sqrt{3}}{4}$. Все углы $60°$.' },
+ { id:'pytri', x:560, y:310, rx:44, label:'Прям. треугольник',
+ props:'$S=\\frac{1}{2}ab$ (катеты). Теорема Пифагора: $c^2=a^2+b^2$. Высота: $h_c=\\frac{ab}{c}$.' },
+ ];
+ const edges = [
+ ['area','rect'],['area','para'],['area','tri'],
+ ['rect','sq'],['rect','recta'],
+ ['para','sq'],['para','recta'],['para','rhomb'],
+ ['tri','eqtri'],['tri','pytri'],['para','trap'],
+ ];
+ let sel = null;
+ function draw(selId){
+ const colors = { area:'#059669', rect:'#0891b2', para:'#8b5cf6', tri:'#d97706', sq:'#14b8a6', recta:'#2563eb', rhomb:'#ec4899', trap:'#f97316', eqtri:'#e11d48', pytri:'#16a34a' };
+ let s = `';
+ document.getElementById('final2-hier-svg').innerHTML = s;
+ document.getElementById('final2-hier-svg').querySelector('svg').addEventListener('click', function(e){
+ const el = e.target.closest('[data-nid]');
+ if(!el) return;
+ const nid = el.dataset.nid;
+ sel = (sel===nid) ? null : nid;
+ const nd = nodes.find(n=>n.id===nid);
+ if(sel && nd){ document.getElementById('final2-hier-info').innerHTML = '' + nd.label + ': ' + nd.props; renderMath(document.getElementById('final2-hier-info')); }
+ else document.getElementById('final2-hier-info').textContent = 'Нажми на фигуру в схеме выше';
+ draw(sel);
+ });
+ }
+ draw(null);
+ })();
+
+ /* === JS: 7 боссов === */
+ (function(){
+ const bosses = [
+ {
+ n: 1,
+ title: 'Прямоугольный треугольник: Пифагор + высота',
+ color: '#059669',
+ svg: ``,
+ cond: 'В прямоугольном треугольнике $ABC$ с прямым углом $A$ катеты $a = AC = 9$ и $b = AB = 12$.',
+ parts: [
+ { label: 'Найди гипотенузу $c = BC$.', ans: 15, hint: '$c=\\sqrt{9^2+12^2}=\\sqrt{81+144}=\\sqrt{225}=15$' },
+ { label: 'Найди высоту $h_c$ к гипотенузе (десятичный ответ допустим).', ans: 7.2, tol: 0.05, hint: '$h_c=\\dfrac{ab}{c}=\\dfrac{9\\cdot12}{15}=\\dfrac{108}{15}=7.2$' },
+ { label: 'Найди площадь треугольника $ABC$.', ans: 54, hint: '$S=\\dfrac{1}{2}\\cdot9\\cdot12=54$' },
+ ],
+ },
+ {
+ n: 2,
+ title: 'Параллелограмм через угол',
+ color: '#8b5cf6',
+ svg: ``,
+ cond: 'В параллелограмме $ABCD$ сторона $AB = 14$ см, смежная сторона $AD = 8$ см, угол при вершине $A$ равен $30°$.',
+ parts: [
+ { label: 'Найди высоту $h$ параллелограмма ($h = AD \\cdot \\sin 30°$, в сантиметрах).', ans: 4, hint: '$h = AD \\cdot \\sin 30° = 8 \\cdot 0.5 = 4$ см' },
+ { label: 'Найди площадь параллелограмма $S$ (кв. см).', ans: 56, hint: '$S = AB \\cdot h = 14 \\cdot 4 = 56$ кв. см' },
+ ],
+ },
+ {
+ n: 3,
+ title: 'Трапеция и средняя линия',
+ color: '#f97316',
+ svg: ``,
+ cond: 'В трапеции основания $a = 18$ см и $b = 12$ см, высота $h = 5$ см.',
+ parts: [
+ { label: 'Найди среднюю линию $m$ трапеции (см).', ans: 15, hint: '$m = \\dfrac{a+b}{2} = \\dfrac{18+12}{2} = 15$ см' },
+ { label: 'Найди площадь трапеции $S$ (кв. см).', ans: 75, hint: '$S = \\dfrac{(a+b)}{2} \\cdot h = 15 \\cdot 5 = 75$ кв. см' },
+ ],
+ },
+ {
+ n: 4,
+ title: 'Ромб: диагонали, сторона, площадь',
+ color: '#ec4899',
+ svg: ``,
+ cond: 'В ромбе $ABCD$ диагонали $d_1 = 16$ см и $d_2 = 12$ см.',
+ parts: [
+ { label: 'Найди площадь ромба $S$ (кв. см).', ans: 96, hint: '$S = \\dfrac{d_1 d_2}{2} = \\dfrac{16 \\cdot 12}{2} = 96$ кв. см' },
+ { label: 'Найди сторону ромба $a$ (полудиагонали: $8$ и $6$, теорема Пифагора).', ans: 10, hint: '$a = \\sqrt{8^2 + 6^2} = \\sqrt{64+36} = \\sqrt{100} = 10$ см' },
+ { label: 'Найди периметр ромба $P$ (см).', ans: 40, hint: '$P = 4a = 4 \\cdot 10 = 40$ см' },
+ ],
+ },
+ {
+ n: 5,
+ title: 'Медиана и центроид',
+ color: '#2563eb',
+ svg: ``,
+ cond: 'Площадь треугольника $ABC = 36$ кв. см. Точка $G$ — центроид (пересечение медиан).',
+ parts: [
+ { label: 'Найди площадь треугольника, образованного центроидом $G$ и двумя вершинами (например, $\\triangle ABG$).', ans: 12, hint: 'Медиана делит треугольник на 2 равные части: $S/2=18$. Все 6 малых треугольников равны: $S_6 = 36/6 = 12$ кв. см' },
+ { label: 'Одна медиана делит треугольник на 2 треугольника. Какова площадь каждого из них?', ans: 18, hint: 'Медиана делит треугольник на 2 равновеликих: $S/2 = 36/2 = 18$ кв. см' },
+ ],
+ },
+ {
+ n: 6,
+ title: 'Равносторонний треугольник и центроид',
+ color: '#e11d48',
+ svg: ``,
+ cond: 'В равностороннем треугольнике $ABC$ сторона $a = 10$ см. Точка $G$ — центроид.',
+ parts: [
+ { label: 'Найди высоту $h$ равностороннего треугольника (введи значение, умноженное на 10, как целое: $h \\cdot 10 = ?$)', ans: 87, hint: '$h = \\dfrac{10\\sqrt{3}}{2} \\approx \\dfrac{10 \\cdot 1.732}{2} = 8.66$, умноженное на 10 = 86.6 \\approx 87', tol: 1 },
+ { label: 'Найди площадь треугольника (введи значение, умноженное на 10, как целое: $S \\cdot 10 = ?$)', ans: 433, hint: '$S = \\dfrac{a^2\\sqrt{3}}{4} = \\dfrac{100\\sqrt{3}}{4} \\approx 43.3$, умноженное на 10 = 433', tol: 2 },
+ { label: 'Найди расстояние от центроида $G$ до основания $BC$ (центроид делит высоту в отношении $2:1$, введи значение в целых мм: расстояние $\\cdot 10$)', ans: 29, hint: 'Расстояние = $h/3 = 8.66/3 \\approx 2.89$, умноженное на 10 = 28.9 \\approx 29', tol: 1 },
+ ],
+ },
+ {
+ n: 7,
+ title: 'Пифагоровы тройки в задаче',
+ color: '#7c3aed',
+ svg: ``,
+ cond: 'Прямоугольный треугольник имеет периметр $P = 30$ см и гипотенузу $c = 13$ см. Используй пифагорову тройку $(5, 12, 13)$.',
+ parts: [
+ { label: 'Найди сумму катетов $a + b$ (см).', ans: 17, hint: '$a + b = P - c = 30 - 13 = 17$ см' },
+ { label: 'Используя тройку $(5, 12, 13)$: найди меньший катет $a$ (см).', ans: 5, hint: 'Тройка $(5, 12, 13)$: катеты $5$ и $12$, $5+12=17$. Меньший катет $a = 5$ см' },
+ { label: 'Найди площадь треугольника $S$ (кв. см).', ans: 30, hint: '$S = \\dfrac{1}{2} \\cdot 5 \\cdot 12 = 30$ кв. см' },
+ ],
+ },
+ ];
+
+ window.final2BossSolved = new Set();
+ const bossBox = document.getElementById('final2-bosses');
+ bossBox.innerHTML = bosses.map(b => {
+ const partsHtml = b.parts.map((p,pi) => {
+ const labelText = p._label_fix || p.label;
+ return ``;
+ }).join('');
+
+ const svgHtml = b.svg ? `${b.svg}
` : '';
+
+ return `
+
+ БОСС ${b.n}
+ ${b.title}
+ +10 XP
+
+
${b.cond}
+ ${svgHtml}
+ ${partsHtml}
+
`;
+ }).join('');
+
+ function checkPart(bi, pi){
+ const boss = bosses[bi];
+ const part = boss.parts[pi];
+ const inp = bossBox.querySelector(`.final2-boss-inp[data-b="${bi}"][data-p="${pi}"]`);
+ const fb = bossBox.querySelector(`.final2-boss-fb[data-b="${bi}"][data-p="${pi}"]`);
+ const ok = bossBox.querySelector(`.final2-boss-ok[data-b="${bi}"][data-p="${pi}"]`);
+ if(!inp) return;
+ const val = parseFloat(inp.value);
+ const useExact = part._override && part._ans_exact !== undefined;
+ const ansCheck = useExact ? part._ans_exact : part.ans;
+ const tolCheck = useExact ? part._tol_exact : (part.tol !== undefined ? part.tol : 0);
+ const hintText = useExact ? part._hint_fix : part.hint;
+ if(Math.abs(val - ansCheck) <= tolCheck){
+ feedback(fb, true, 'Верно! ' + (hintText ? '
' + hintText + '' : ''));
+ inp.disabled = true;
+ const btn = bossBox.querySelector(`.final2-boss-check[data-b="${bi}"][data-p="${pi}"]`);
+ if(btn) btn.disabled = true;
+ if(ok) ok.style.display = 'inline';
+ const allDone = boss.parts.every((_,pj) => {
+ const el = bossBox.querySelector(`.final2-boss-inp[data-b="${bi}"][data-p="${pj}"]`);
+ return el && el.disabled;
+ });
+ if(allDone && !window.final2BossSolved.has(bi)){
+ window.final2BossSolved.add(bi);
+ addXp(10, 'final2-boss-' + (bi+1));
+ const xpBadge = document.getElementById('final2-boss-xp-' + bi);
+ if(xpBadge) xpBadge.style.display = 'inline';
+ bumpProgress('final2', 8);
+ if(window.final2BossSolved.size === bosses.length){
+ setTimeout(()=>{
+ confetti();
+ if(!STATE.achievements.has('final2-master')){
+ STATE.achievements.set('final2-master', 'Мастер площадей Главы 2');
+ saveProgress();
+ const pop = document.getElementById('ach-popup');
+ if(pop){ document.getElementById('ach-text').textContent = 'Мастер площадей Главы 2!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'), 4000); }
+ }
+ addXp(50, 'final2-all-bosses');
+ bumpProgress('final2', 20);
+ const fin = document.getElementById('final2-finish');
+ if(fin) fin.style.display = 'block';
+ }, 500);
+ }
+ }
+ } else {
+ feedback(fb, false, 'Неверно. Подсказка: ' + (hintText || part.hint));
+ }
+ }
+
+ bossBox.querySelectorAll('.final2-boss-check').forEach(btn=>{
+ btn.addEventListener('click', ()=>{ checkPart(+btn.dataset.b, +btn.dataset.p); });
+ });
+ bossBox.querySelectorAll('.final2-boss-inp').forEach(inp=>{
+ inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ const btn=bossBox.querySelector(`.final2-boss-check[data-b="${inp.dataset.b}"][data-p="${inp.dataset.p}"]`); if(btn)btn.click(); } });
+ });
+ })();
+
+ renderMath(box);
+}