diff --git a/frontend/textbooks/algebra_8_ch2.html b/frontend/textbooks/algebra_8_ch2.html
index 7825be0..d8a17f8 100644
--- a/frontend/textbooks/algebra_8_ch2.html
+++ b/frontend/textbooks/algebra_8_ch2.html
@@ -391,6 +391,16 @@ const ACH_LABELS = {
p10_train: 'Тренажёр разложения',
p10_fraction: 'Сокращение дробей',
p10_sort: 'Разложимо или нет',
+ p11_steps: 'Решил по 4 шагам',
+ p11_train: 'Тренажёр текстовых задач',
+ p11_move: 'Движение по реке',
+ p11_digit: 'Двузначное число',
+ p11_class: 'Классификатор задач',
+ p12_bi: 'Биквадратное решено',
+ p12_train: 'Тренажёр биквадратных',
+ p12_frac: 'Дробное → квадратное',
+ p12_subst: 'Замена переменной',
+ p12_odz: 'Посторонний корень',
};
function loadProgress(){
@@ -528,8 +538,19 @@ const SIDEBARS = {
['Корни','через дискриминант или Виета'],
['Сокращение','через разложение числителя и знаменателя'],
]},
- p11:{ title:'§ 11 — скоро', rows:[['Текстовые задачи','будет в Wave 3']]},
- p12:{ title:'§ 12 — скоро', rows:[['Сводящиеся к квадратным','будет в Wave 3']]},
+ p11:{ title:'Шпаргалка § 11', rows:[
+ ['4 шага','анализ → модель → решение → проверка'],
+ ['Движение','$s = v t$; общее $\\dfrac{s_1}{v_1} + \\dfrac{s_2}{v_2}$'],
+ ['Работа','$A = p \\cdot t$; вместе: $p_1 + p_2 = \\dfrac{1}{t}$'],
+ ['Числа','$\\overline{ab} = 10a + b$'],
+ ]},
+ p12:{ title:'Шпаргалка § 12', rows:[
+ ['Биквадр','$ax^4+bx^2+c=0$ → $t=x^2$'],
+ ['Условие','$t \\geq 0$'],
+ ['$x = \\pm\\sqrt{t}$','для каждого $t \\geq 0$'],
+ ['Дробное','умножить на ОЗ, проверить ОДЗ'],
+ ['ОДЗ','знаменатель $\\neq 0$'],
+ ]},
final2:{ title:'Финал', rows:[['Итоги главы','будет в Wave 4']]},
};
function buildSidebar(id){
@@ -1216,8 +1237,527 @@ function buildP10(){
render();
})();
}
-function buildP11stub(){ document.getElementById('p11-body').innerHTML = `
§ 11 — Текстовые задачи Скоро в Wave 3.
${secNav('p10','p12')}`; }
-function buildP12stub(){ document.getElementById('p12-body').innerHTML = `§ 12 — Сводящиеся к квадратным Скоро в Wave 3.
${secNav('p11','final2')}`; }
+function buildP11stub(){ buildP11(); }
+function buildP11(){
+ const box = document.getElementById('p11-body');
+ let html = '';
+
+ html += makeCard('repeat','Повторение',null,`
+
+ Дискриминант, формулы корней, теорема Виета — из § 8, § 9.
+ Движение: $s = v \\cdot t$; средняя скорость на двух участках через общее $s$ и общее $t$.
+ Работа: $A = p \\cdot t$, где $p$ — производительность.
+ Двузначное число с цифрами $a$ (десятки) и $b$ (единицы): $\\overline{ab} = 10a + b$.
+ `);
+
+ html += makeCard('theory','4 шага решения','11.1',`
+
+ Анализ. Прочесть условие, выделить величины, связи, искомое.
+ Модель. Обозначить неизвестное буквой; выразить остальные величины через неё; составить уравнение.
+ Решение. Решить уравнение — обычно квадратное.
+ Анализ ответа. Проверить смысл (положительные ли значения, целые ли — если требуется), записать ответ.
+ `);
+
+ html += makeCard('example','Образец',null,`
+ Задача. Произведение двух последовательных натуральных чисел равно 132. Найдите эти числа.
+ Решение. Пусть меньшее $= x$. Тогда $x(x+1) = 132 \\Rightarrow x^2 + x - 132 = 0$. $D = 1 + 528 = 529$, $\\sqrt{D}=23$. $x = (-1+23)/2 = 11$ или $x = -12$. Натуральное только $11$.
+ Ответ: $11$ и $12$.
`);
+
+ /* INT 1 — Шаблон «4 шага» */
+ html += widget('Шаблон «4 шага»','INTERACT 1','Решим задачу пошагово. На каждом шаге выберите правильный вариант.',`
+ Задача. Площадь прямоугольника равна 60 см², а одна сторона на 7 см больше другой. Найдите стороны.
+
+
+
`);
+
+ /* INT 2 — Тренажёр задач */
+ html += widget('Тренажёр текстовых задач','INTERACT 2','Решите задачу и введите ответ. После 5 задач — итог.',`
+ Задача 1 / 5 Очки: 0
+
+
+
+ Ответ
+ Подсказка
+
+
+ Начать `);
+
+ /* INT 3 — Конструктор задачи на движение */
+ html += widget('Движение по реке','INTERACT 3','Лодка идёт по реке и обратно. Введите $v_л$ и $v_р$ — узнайте, как изменится время.',`
+
+ Скорость лодки $v_л$ (км/ч) =
+ Скорость реки $v_р$ (км/ч) =
+ Расстояние $S$ (км) =
+
+
`);
+
+ /* INT 4 — Задача про двузначное число */
+ html += widget('Задача про двузначное число','INTERACT 4','Сумма квадратов цифр двузначного числа равна 41, а само число равно сумме его цифр, умноженной на 6. Найдите число.',`
+ Пусть $a$ — десятки, $b$ — единицы. Тогда число $= 10a + b$, $a^2 + b^2 = 41$ и $10a + b = 6(a+b)$.
+
+ Ваш ответ:
+ Проверить
+ Показать решение
+
+
+
`);
+
+ /* INT 5 — Drag: типы задач */
+ html += widget('Классифицируем тип задачи','INTERACT 5','Прочитайте задачу — кликом отнесите её к одной из четырёх категорий.',`
+
+
+ Проверить Сначала
+
`);
+
+ html += makeCard('class','Класс — решите',null,`
+
+ Произведение двух последовательных нечётных чисел равно 195. Найдите их.
+ Длина сада на 5 м больше ширины, а площадь — 84 м². Найдите стороны.
+ Двое рабочих вместе закончили работу за 6 ч. Первый один сделал бы её на 5 ч быстрее, чем второй. За сколько часов сделал бы её каждый?
+ `);
+
+ html += makeCard('home','Домашка',null,`
+
+ Сумма квадратов двух последовательных натуральных чисел равна 113. Найдите их.
+ Лодка прошла 30 км по течению и 30 км против за 4 ч. Скорость течения 1 км/ч. Найдите скорость лодки.
+ Найдите два числа, сумма которых 14, а произведение 48.
+ `);
+
+ html += secNav('p10','p12');
+ box.innerHTML = html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box), 0);
+
+ /* INIT 1 — Шаблон 4 шага */
+ (function(){
+ const steps = [
+ { q:'Шаг 1. Что обозначим за $x$?', opts:['меньшую сторону','произведение','периметр','диагональ'], ok:0 },
+ { q:'Шаг 2. Тогда большая сторона равна:', opts:['$x - 7$','$x + 7$','$60/x$','$7x$'], ok:1 },
+ { q:'Шаг 3. Уравнение из условия $S = a \\cdot b = 60$:', opts:['$x(x+7) = 60$','$x^2 - 7 = 60$','$2x + 7 = 60$','$x/7 = 60$'], ok:0 },
+ { q:'Шаг 4. После раскрытия скобок и переноса:', opts:['$x^2 + 7x - 60 = 0$','$x^2 - 7x + 60 = 0$','$x^2 + 7 = 60$','$x^2 = 67$'], ok:0 },
+ { q:'Шаг 5. $D = 49 + 240 = 289 \\Rightarrow \\sqrt{D} = 17$. Корни:', opts:['$x = 5$ и $x = -12$','$x = 6$ и $x = -10$','$x = 4$ и $x = -15$','$x = 8$ и $x = -7$'], ok:0 },
+ { q:'Шаг 6. Ответ:', opts:['5 см и 12 см','−12 см и 5 см','оба корня подходят','7 см и 15 см'], ok:0 },
+ ];
+ let i = 0;
+ function show(){
+ const s = steps[i];
+ document.getElementById('p11s-step').innerHTML = '' + (i+1) + ' / ' + steps.length + '. ' + s.q;
+ renderMath(document.getElementById('p11s-step'));
+ const opts = document.getElementById('p11s-opts'); opts.innerHTML = '';
+ s.opts.forEach((o,k)=>{
+ const b = document.createElement('button');
+ b.className = 'btn'; b.innerHTML = o; b.style.cssText = 'text-align:left';
+ b.addEventListener('click', ()=>{
+ const fb = document.getElementById('p11s-fb'); fb.style.display = 'block';
+ if(k === s.ok){
+ b.classList.add('ok');
+ feedback(fb, true, '✓');
+ if(i >= steps.length - 1){ feedback(fb, true, '✓ Задача решена!'); achievement('p11_steps'); bumpProgress('p11', 18); confetti(); }
+ else setTimeout(()=>{ i++; show(); }, 700);
+ } else { b.classList.add('fail'); feedback(fb, false, 'Не то, подумайте ещё.'); }
+ });
+ opts.appendChild(b);
+ });
+ renderMath(opts);
+ document.getElementById('p11s-fb').style.display = 'none';
+ }
+ show();
+ })();
+
+ /* INIT 2 — Тренажёр */
+ (function(){
+ const tasks = [
+ { q:'Произведение двух последовательных натуральных чисел равно 56. Найдите большее.', hint:'$x(x+1) = 56$', ans:[8] },
+ { q:'Площадь прямоугольника 48 см², одна сторона на 2 см больше другой. Найдите меньшую.', hint:'$x(x+2)=48$', ans:[6] },
+ { q:'Сумма числа и его квадрата равна 90. Найдите положительное число.', hint:'$x + x^2 = 90$', ans:[9] },
+ { q:'Произведение двух чисел равно 35, а их сумма равна 12. Найдите большее.', hint:'Виета: $x_1+x_2=12,\\ x_1 x_2=35$', ans:[7] },
+ { q:'Лодка прошла 12 км по течению (скорость = $v+1$) и обратно ($v-1$) за 5 ч. Найдите скорость в стоячей воде.', hint:'$\\dfrac{12}{v+1} + \\dfrac{12}{v-1} = 5$', ans:[5] },
+ ];
+ let cur = null, i = 1, score = 0;
+ function show(){
+ cur = tasks[i-1];
+ document.getElementById('p11t-i').textContent = i;
+ document.getElementById('p11t-task').innerHTML = 'Задача ' + i + '. ' + cur.q;
+ renderMath(document.getElementById('p11t-task'));
+ document.getElementById('p11t-inp').value = '';
+ document.getElementById('p11t-fb').style.display = 'none';
+ }
+ document.getElementById('p11t-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p11t-score').textContent = 0; show(); });
+ document.getElementById('p11t-go').addEventListener('click', ()=>{
+ const fb = document.getElementById('p11t-fb'); fb.style.display = 'block';
+ const u = +document.getElementById('p11t-inp').value;
+ const ok = cur.ans.some(a => Math.abs(u - a) < 1e-6);
+ if(ok){ score++; feedback(fb, true, '✓'); }
+ else feedback(fb, false, 'Не то. Ответ: ' + cur.ans.join(' или '));
+ document.getElementById('p11t-score').textContent = score;
+ if(i >= tasks.length){ setTimeout(()=>{ feedback(fb, score >= 3, 'Итог: ' + score + '/' + tasks.length); if(score >= 3){ achievement('p11_train'); bumpProgress('p11', 16); confetti(); } }, 600); }
+ else { i++; setTimeout(show, 900); }
+ });
+ document.getElementById('p11t-hint').addEventListener('click', ()=>{
+ const fb = document.getElementById('p11t-fb'); fb.style.display = 'block';
+ feedback(fb, true, 'Подсказка: ' + cur.hint); renderMath(fb);
+ });
+ })();
+
+ /* INIT 3 — Движение */
+ (function(){
+ const vlE = document.getElementById('p11m-vl'), vrE = document.getElementById('p11m-vr'), sE = document.getElementById('p11m-s');
+ const out = document.getElementById('p11m-out');
+ let done = false;
+ function refresh(){
+ const vl = +vlE.value, vr = +vrE.value, s = +sE.value;
+ if(vl <= vr || vr < 0 || s <= 0){ out.innerHTML = 'Условие: $v_л > v_р \\geq 0,\\ S > 0$. '; renderMath(out); return; }
+ const t1 = s/(vl+vr), t2 = s/(vl-vr), tT = t1 + t2;
+ let html = 'По течению: $v = ' + (vl+vr) + '$ км/ч → $t = ' + s + '/' + (vl+vr) + ' = ' + fmt(t1) + '$ ч
';
+ html += 'Против течения: $v = ' + (vl-vr) + '$ км/ч → $t = ' + fmt(t2) + '$ ч
';
+ html += 'Итого время: $' + fmt(tT) + '$ ч
';
+ const vAvg = 2*s/tT;
+ html += 'Средняя скорость: $\\dfrac{2S}{t_1+t_2} = ' + fmt(vAvg) + '$ км/ч
';
+ out.innerHTML = html; renderMath(out);
+ if(!done){ done = true; setTimeout(()=>{ achievement('p11_move'); bumpProgress('p11', 14); }, 300); }
+ }
+ [vlE,vrE,sE].forEach(e => e.addEventListener('input', refresh));
+ refresh();
+ })();
+
+ /* INIT 4 — Задача про число */
+ (function(){
+ document.getElementById('p11d-check').addEventListener('click', ()=>{
+ const v = +document.getElementById('p11d-inp').value;
+ const fb = document.getElementById('p11d-fb'); fb.style.display = 'block';
+ if(v === 54){ feedback(fb, true, '✓ Верно! Число = 54.'); achievement('p11_digit'); bumpProgress('p11', 14); confetti(); }
+ else feedback(fb, false, 'Не то. Подсказка: $a^2 + b^2 = 41,\\ 10a+b = 6(a+b) \\Rightarrow 4a = 5b$.');
+ renderMath(fb);
+ });
+ document.getElementById('p11d-show').addEventListener('click', ()=>{
+ const s = document.getElementById('p11d-sol');
+ s.style.display = 'block';
+ s.innerHTML = 'Шаг 1. Из $10a+b = 6(a+b)$ получаем $4a = 5b$, то есть $b = \\dfrac{4a}{5}$. Чтобы $b$ было цифрой (0..9), $a$ должно делиться на 5.
Шаг 2. $a = 5$ → $b = 4$. Проверяем: $a^2+b^2 = 25+16 = 41$. ✓
Ответ: число $= 54$.
';
+ renderMath(s);
+ });
+ })();
+
+ /* INIT 5 — Drag типы */
+ (function(){
+ const items = [
+ { id:1, txt:'Лодка прошла 24 км по течению и обратно.', cat:'dv' },
+ { id:2, txt:'Двое рабочих вместе закончили работу за 6 ч.', cat:'wk' },
+ { id:3, txt:'Произведение последовательных чисел равно 90.', cat:'nm' },
+ { id:4, txt:'Сторона квадрата увеличена на 3 см, площадь увеличилась на 33 см².', cat:'gm' },
+ { id:5, txt:'Автомобиль и автобус выехали навстречу из A и B.', cat:'dv' },
+ { id:6, txt:'Сумма квадратов цифр двузначного числа = 25.', cat:'nm' },
+ { id:7, txt:'Бассейн наполняется одной трубой на 2 ч быстрее, чем другой.', cat:'wk' },
+ { id:8, txt:'Площадь прямоугольника 48 см², а периметр 28 см.', cat:'gm' },
+ ];
+ const cats = ['dv','wk','nm','gm'];
+ const labels = { dv:'Движ.', wk:'Раб.', nm:'Числа', gm:'Геом.' };
+ let placed = {};
+ function makeChip(it, where){
+ const wrap = document.createElement('div');
+ wrap.style.cssText = 'display:inline-flex;align-items:center;gap:4px;background:var(--sec-acc-soft);border-radius:8px;padding:4px 6px;font-size:.86rem;flex-wrap:wrap;width:100%';
+ const sp = document.createElement('span'); sp.textContent = it.txt; sp.style.cssText = 'padding:2px 4px;flex:1;min-width:140px';
+ wrap.appendChild(sp);
+ if(where === 'pool'){
+ cats.forEach(cat=>{
+ const b = document.createElement('button'); b.className = 'btn small'; b.textContent = labels[cat];
+ b.style.cssText = 'padding:3px 7px;font-size:.7rem';
+ b.addEventListener('click', ()=>{ placed[it.id] = cat; render(); });
+ wrap.appendChild(b);
+ });
+ } else {
+ const b = document.createElement('button'); b.className = 'btn small'; b.textContent = '×';
+ b.style.cssText = 'padding:2px 7px'; b.addEventListener('click', ()=>{ delete placed[it.id]; render(); });
+ wrap.appendChild(b);
+ }
+ return wrap;
+ }
+ function render(){
+ const pool = document.getElementById('p11c-pool');
+ pool.innerHTML = '';
+ items.forEach(it=>{ if(!placed[it.id]) pool.appendChild(makeChip(it, 'pool')); });
+ cats.forEach(cat=>{
+ const box = document.querySelector('#p11-body .drop-items[data-cat="' + cat + '"]');
+ if(!box) return;
+ box.innerHTML = '';
+ items.forEach(it=>{ if(placed[it.id] === cat) box.appendChild(makeChip(it, 'placed')); });
+ });
+ }
+ document.getElementById('p11c-check').addEventListener('click', ()=>{
+ const fb = document.getElementById('p11c-fb'); fb.style.display = 'block';
+ if(Object.keys(placed).length < items.length){ feedback(fb, false, '⚠ Разложите все ' + items.length + ' задач.'); return; }
+ let ok = 0; items.forEach(it=>{ if(placed[it.id] === it.cat) ok++; });
+ if(ok === items.length){ feedback(fb, true, '✓ Все верно!'); achievement('p11_class'); bumpProgress('p11', 14); confetti(); }
+ else feedback(fb, false, 'Верно ' + ok + ' из ' + items.length);
+ });
+ document.getElementById('p11c-reset').addEventListener('click', ()=>{ placed = {}; document.getElementById('p11c-fb').style.display='none'; render(); });
+ render();
+ })();
+}
+function buildP12stub(){ buildP12(); }
+function buildP12(){
+ const box = document.getElementById('p12-body');
+ let html = '';
+
+ html += makeCard('repeat','Повторение',null,`
+
+ Квадратное уравнение, дискриминант, Виета — §§ 7–9.
+ Разложение трёхчлена $ax^2+bx+c$ — § 10.
+ ОДЗ дробного выражения: знаменатель $\\neq 0$.
+ `);
+
+ html += makeCard('theory','Биквадратное уравнение','12.1',`
+ Биквадратным называют уравнение вида $ax^4 + bx^2 + c = 0$, $a \\neq 0$.
+ Метод. Замена $t = x^2$, $t \\geq 0$. Получаем квадратное $at^2 + bt + c = 0$. Решаем его, потом для каждого $t \\geq 0$ находим $x = \\pm\\sqrt{t}$.
+ $$t = x^2 \\geq 0,\\quad x = \\pm\\sqrt{t}$$
`);
+
+ html += makeCard('rule','Дробные → квадратные','12.2',`
+
+ Найти ОДЗ (знаменатели $\\neq 0$).
+ Привести к общему знаменателю и умножить уравнение на него.
+ Решить полученное многочленное уравнение.
+ Отбросить посторонние корни (вне ОДЗ).
+ `);
+
+ html += makeCard('example','Примеры',null,`
+ 1) $x^4 - 5x^2 + 4 = 0$. Замена $t = x^2$: $t^2 - 5t + 4 = 0 \\Rightarrow t = 1$ или $t = 4$. Тогда $x = \\pm 1$ или $x = \\pm 2$. Ответ: $\\{-2;-1;1;2\\}$.
+ 2) $x^4 + 3x^2 - 4 = 0$. $t^2 + 3t - 4 = 0 \\Rightarrow t = 1$ или $t = -4$. Только $t = 1$ годится: $x = \\pm 1$.
+ 3) $\\dfrac{x}{x-2} - \\dfrac{2}{x+2} = 1$. ОДЗ: $x \\neq \\pm 2$. Умножим на $(x-2)(x+2)$: $x(x+2) - 2(x-2) = (x-2)(x+2) \\Rightarrow x^2 + 2x - 2x + 4 = x^2 - 4 \\Rightarrow 4 = -4$ — противоречие, корней нет.
`);
+
+ /* INT 1 — Биквадратное пошагово */
+ html += widget('Решатель биквадратного','INTERACT 1','Введите $a$, $b$, $c$ — система применит замену $t=x^2$ и доведёт до конца.',`
+
+ $a$ =
+ $b$ =
+ $c$ =
+ Решить
+
+
`);
+
+ /* INT 2 — Тренажёр биквадратных */
+ html += widget('Тренажёр биквадратных','INTERACT 2','Решите устно. Вводите все корни через точку с запятой (или «нет»).',`
+ Задача 1 / 6 Очки: 0
+
+
+
+ Ответ
+
+
+ Начать `);
+
+ /* INT 3 — Дробное уравнение */
+ html += widget('Дробное → квадратное','INTERACT 3','По шагам решаем уравнение с дробями. Внимание к ОДЗ!',`
+ Решим: $\\dfrac{x+3}{x-1} - \\dfrac{2}{x+1} = 2$
+
+
+
`);
+
+ /* INT 4 — Какая замена */
+ html += widget('Выберите подходящую замену','INTERACT 4','Для каждого уравнения подберите замену переменной.',`
+
+
`);
+
+ /* INT 5 — ОДЗ проверка */
+ html += widget('Найди посторонний корень','INTERACT 5','Дано дробное уравнение с готовыми кандидатами на корни. Найдите корень, который НЕ удовлетворяет ОДЗ.',`
+
+
+
+ Начать `);
+
+ html += makeCard('class','Класс — решите',null,`
+
+ $x^4 - 13x^2 + 36 = 0$
+ $x^4 - 5x^2 - 36 = 0$
+ $\\dfrac{2}{x-1} + \\dfrac{3}{x+1} = 1$
+ `);
+
+ html += makeCard('home','Домашка',null,`
+
+ $x^4 - 10x^2 + 9 = 0$
+ $x^4 + 7x^2 - 8 = 0$
+ $\\dfrac{x}{x+1} - \\dfrac{1}{x-1} = \\dfrac{2}{x^2-1}$
+ $(x^2 - 2)^2 - 3(x^2 - 2) - 4 = 0$ (замена!)
+ `);
+
+ html += secNav('p11','final2');
+ box.innerHTML = html;
+ if(window.renderMathInElement) setTimeout(()=>renderMath(box), 0);
+
+ /* INIT 1 — Биквадратное */
+ (function(){
+ document.getElementById('p12b-go').addEventListener('click', ()=>{
+ const a = +document.getElementById('p12b-a').value;
+ const b = +document.getElementById('p12b-b').value;
+ const c = +document.getElementById('p12b-c').value;
+ const out = document.getElementById('p12b-out');
+ if(!a){ out.innerHTML = '$a \\neq 0$'; renderMath(out); return; }
+ let html = 'Дано: $' + a + 'x^4 ' + (b >= 0 ? '+ ' + b : '- ' + Math.abs(b)) + 'x^2 ' + (c >= 0 ? '+ ' + c : '- ' + Math.abs(c)) + ' = 0$
';
+ html += 'Замена $t = x^2,\\ t \\geq 0$: $' + a + 't^2 ' + (b >= 0 ? '+ ' + b : '- ' + Math.abs(b)) + 't ' + (c >= 0 ? '+ ' + c : '- ' + Math.abs(c)) + ' = 0$
';
+ const D = b*b - 4*a*c;
+ html += 'D = ' + (b*b) + ' − ' + (4*a*c) + ' = ' + D + '
';
+ if(D < 0){ html += '$D < 0$ → нет $t$, значит нет $x$.
'; }
+ else {
+ const t1 = (-b - Math.sqrt(D))/(2*a), t2 = (-b + Math.sqrt(D))/(2*a);
+ html += '$t_1 = ' + fmt(t1) + ',\\ t_2 = ' + fmt(t2) + '$
';
+ const roots = [];
+ [t1, t2].forEach((t, k)=>{
+ if(t > 0){ const r = Math.sqrt(t); roots.push(r, -r); html += '$t_' + (k+1) + ' = ' + fmt(t) + ' > 0 \\Rightarrow x = \\pm' + fmt(r) + '$
'; }
+ else if(t === 0){ roots.push(0); html += '$t_' + (k+1) + ' = 0 \\Rightarrow x = 0$
'; }
+ else html += '$t_' + (k+1) + ' = ' + fmt(t) + ' < 0$ — не подходит
';
+ });
+ const u = [...new Set(roots)].sort((a,b)=>a-b);
+ html += 'Ответ: ' + (u.length ? '$\\{' + u.map(fmt).join(';\\ ') + '\\}$' : 'корней нет') + '
';
+ }
+ out.innerHTML = html; renderMath(out);
+ achievement('p12_bi'); bumpProgress('p12', 14);
+ });
+ })();
+
+ /* INIT 2 — Тренажёр биквадр */
+ (function(){
+ const tasks = [
+ { eq:'x^4 - 5x^2 + 4 = 0', ans:[-2,-1,1,2] },
+ { eq:'x^4 - 10x^2 + 9 = 0', ans:[-3,-1,1,3] },
+ { eq:'x^4 - 13x^2 + 36 = 0', ans:[-3,-2,2,3] },
+ { eq:'x^4 + 3x^2 - 4 = 0', ans:[-1,1] },
+ { eq:'x^4 + 2x^2 + 5 = 0', ans:null },
+ { eq:'x^4 - 16 = 0', ans:[-2,2] },
+ ];
+ let cur = null, i = 1, score = 0, shuffled = [];
+ function show(){
+ cur = shuffled[i-1];
+ document.getElementById('p12t-i').textContent = i;
+ document.getElementById('p12t-task').innerHTML = '$' + cur.eq + '$';
+ renderMath(document.getElementById('p12t-task'));
+ document.getElementById('p12t-inp').value = '';
+ document.getElementById('p12t-fb').style.display = 'none';
+ }
+ function parse(s){
+ s = s.trim().toLowerCase();
+ if(/нет|none/.test(s)) return null;
+ return s.replace(/,/g,';').split(/[;\s]+/).filter(Boolean).map(Number).sort((a,b)=>a-b);
+ }
+ function check(){
+ const fb = document.getElementById('p12t-fb'); fb.style.display = 'block';
+ const u = parse(document.getElementById('p12t-inp').value);
+ let ok = false;
+ if(cur.ans === null) ok = u === null;
+ else if(u !== null){ const a = [...cur.ans].sort((x,y)=>x-y); ok = a.length === u.length && a.every((v,k)=>v === u[k]); }
+ if(ok){ score++; feedback(fb, true, '✓'); }
+ else feedback(fb, false, 'Правильно: ' + (cur.ans === null ? 'нет' : cur.ans.join('; ')));
+ document.getElementById('p12t-score').textContent = score;
+ if(i >= shuffled.length){ setTimeout(()=>{ feedback(fb, score >= 4, 'Итог: ' + score + '/' + shuffled.length); if(score >= 4){ achievement('p12_train'); bumpProgress('p12', 16); confetti(); } }, 600); }
+ else { i++; setTimeout(show, 900); }
+ }
+ document.getElementById('p12t-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p12t-score').textContent = 0; shuffled = [...tasks].sort(()=>Math.random()-0.5); show(); });
+ document.getElementById('p12t-go').addEventListener('click', check);
+ document.getElementById('p12t-inp').addEventListener('keyup', e=>{ if(e.key === 'Enter') check(); });
+ })();
+
+ /* INIT 3 — Дробное */
+ (function(){
+ const steps = [
+ { q:'Шаг 1. ОДЗ:', opts:['$x \\neq 1$ и $x \\neq -1$','$x \\neq 0$','$x > 0$','любое $x$'], ok:0 },
+ { q:'Шаг 2. Общий знаменатель:', opts:['$(x-1)(x+1)$','$x-1$','$x+1$','$x^2$'], ok:0 },
+ { q:'Шаг 3. После умножения на ОЗ:', opts:['$(x+3)(x+1) - 2(x-1) = 2(x-1)(x+1)$','$x+3 - 2 = 2$','$(x+3) - 2 = 2(x-1)$','$x+3-2(x+1) = 2$'], ok:0 },
+ { q:'Шаг 4. Раскрытие и приведение:', opts:['$x^2 + 4x + 5 = 2x^2 - 2 \\Rightarrow x^2 - 4x - 7 = 0$','$x^2 = 7$','$x^2 + 4x = 0$','$x = 5$'], ok:0 },
+ { q:'Шаг 5. Корни:', opts:['$x = 2 \\pm \\sqrt{11}$','$x = 1, 7$','$x = -1, 7$','корней нет'], ok:0 },
+ { q:'Шаг 6. Проверка ОДЗ ($x \\neq \\pm 1$):', opts:['оба корня подходят','$x = 2 - \\sqrt{11}$ лишний','$x = 2 + \\sqrt{11}$ лишний','оба лишние'], ok:0 },
+ ];
+ let i = 0;
+ function show(){
+ const s = steps[i];
+ document.getElementById('p12f-step').innerHTML = '' + (i+1) + ' / ' + steps.length + '. ' + s.q;
+ renderMath(document.getElementById('p12f-step'));
+ const opts = document.getElementById('p12f-opts'); opts.innerHTML = '';
+ s.opts.forEach((o,k)=>{
+ const b = document.createElement('button');
+ b.className = 'btn'; b.innerHTML = o; b.style.cssText = 'text-align:left';
+ b.addEventListener('click', ()=>{
+ const fb = document.getElementById('p12f-fb'); fb.style.display = 'block';
+ if(k === s.ok){
+ b.classList.add('ok');
+ feedback(fb, true, '✓');
+ if(i >= steps.length - 1){ feedback(fb, true, '✓ Уравнение решено!'); achievement('p12_frac'); bumpProgress('p12', 16); confetti(); }
+ else setTimeout(()=>{ i++; show(); }, 700);
+ } else { b.classList.add('fail'); feedback(fb, false, 'Не то, подумайте.'); }
+ });
+ opts.appendChild(b);
+ });
+ renderMath(opts);
+ document.getElementById('p12f-fb').style.display = 'none';
+ }
+ show();
+ })();
+
+ /* INIT 4 — Замены */
+ (function(){
+ const items = [
+ { eq:'x^4 + 3x^2 - 4 = 0', ans:'t=x^2' },
+ { eq:'(x^2-3)^2 - 5(x^2-3) + 6 = 0', ans:'t=x^2-3' },
+ { eq:'x^2 + \\dfrac{1}{x^2} - 4 = 0', ans:'t=x^2' },
+ { eq:'(x+\\dfrac{1}{x})^2 - 3(x+\\dfrac{1}{x}) + 2 = 0', ans:'t=x+1/x' },
+ ];
+ const opts = ['t=x^2', 't=x^2-3', 't=x+1/x', 't=x-1'];
+ const pool = document.getElementById('p12s-pool');
+ let answered = 0, score = 0;
+ items.forEach((it, idx)=>{
+ const row = document.createElement('div');
+ row.style.cssText = 'display:flex;gap:8px;align-items:center;padding:8px;border-bottom:1px solid var(--border);flex-wrap:wrap';
+ row.innerHTML = '$' + it.eq + '$
';
+ const sel = document.createElement('select');
+ sel.style.cssText = 'padding:6px;border:1.5px solid var(--border);border-radius:6px';
+ sel.innerHTML = '— выбрать — ' + opts.map(o => '$' + o + '$ ').join('').replace(/\$/g,'');
+ const mark = document.createElement('span'); mark.style.cssText = 'font-weight:700;font-size:1.2rem;width:24px;text-align:center';
+ const check = document.createElement('button'); check.className = 'btn small'; check.textContent = 'Проверить';
+ check.addEventListener('click', ()=>{
+ if(!sel.value){ mark.textContent = '?'; mark.style.color = 'var(--muted)'; return; }
+ if(sel.value === it.ans){ mark.textContent = '✓'; mark.style.color = 'var(--ok)'; if(!row.dataset.scored){ row.dataset.scored='1'; score++; answered++; } }
+ else { mark.textContent = '✗'; mark.style.color = 'var(--bad)'; if(!row.dataset.answered){ row.dataset.answered='1'; answered++; } }
+ if(answered >= items.length){
+ const fb = document.getElementById('p12s-fb'); fb.style.display = 'block';
+ feedback(fb, score >= 3, 'Итог: ' + score + '/' + items.length);
+ if(score >= 3){ achievement('p12_subst'); bumpProgress('p12', 14); confetti(); }
+ }
+ });
+ row.appendChild(sel); row.appendChild(check); row.appendChild(mark);
+ pool.appendChild(row);
+ });
+ if(window.renderMathInElement) renderMath(pool);
+ })();
+
+ /* INIT 5 — Посторонний корень */
+ (function(){
+ const tasks = [
+ { eq:'\\dfrac{1}{x-2} = \\dfrac{x-2}{4}', roots:[0, 4, -2, 2], wrong:[2], odz:'x \\neq 2' },
+ { eq:'\\dfrac{x}{x-3} = \\dfrac{9}{x-3}', roots:[3, 9, -9, 0], wrong:[3], odz:'x \\neq 3' },
+ { eq:'\\dfrac{1}{x+1} + \\dfrac{1}{x-1} = 0', roots:[1, -1, 0, 2], wrong:[1,-1], odz:'x \\neq \\pm 1' },
+ ];
+ let cur = null, i = 1;
+ function show(){
+ cur = tasks[i-1];
+ document.getElementById('p12o-task').innerHTML = 'Уравнение ' + i + ' / ' + tasks.length + ': $' + cur.eq + '$ОДЗ: $' + cur.odz + '$ Кандидаты на корни:';
+ renderMath(document.getElementById('p12o-task'));
+ const opts = document.getElementById('p12o-opts'); opts.innerHTML = '';
+ cur.roots.forEach(r=>{
+ const b = document.createElement('button'); b.className = 'btn'; b.textContent = 'x = ' + r;
+ b.addEventListener('click', ()=>{
+ const fb = document.getElementById('p12o-fb'); fb.style.display = 'block';
+ if(cur.wrong.includes(r)){ b.classList.add('ok'); feedback(fb, true, '✓ Это посторонний корень (не в ОДЗ).'); }
+ else { b.classList.add('fail'); feedback(fb, false, 'Не то — этот корень допустим в ОДЗ. Ищите тот, что нарушает.'); return; }
+ if(i >= tasks.length){ setTimeout(()=>{ feedback(fb, true, 'Все 3 пройдено!'); achievement('p12_odz'); bumpProgress('p12', 14); confetti(); }, 500); }
+ else { i++; setTimeout(show, 800); }
+ });
+ opts.appendChild(b);
+ });
+ document.getElementById('p12o-fb').style.display = 'none';
+ }
+ document.getElementById('p12o-start').addEventListener('click', ()=>{ i=1; show(); });
+ })();
+}
function buildFinal2stub(){ document.getElementById('final2-body').innerHTML = `Финал главы Итоговая самооценка, практика, увлекательная математика и финальный босс — в Wave 4.
${secNav('p12',null)}`; }