diff --git a/frontend/textbooks/algebra_11_ch3.html b/frontend/textbooks/algebra_11_ch3.html
index 2e018fa..127d2bc 100644
--- a/frontend/textbooks/algebra_11_ch3.html
+++ b/frontend/textbooks/algebra_11_ch3.html
@@ -1518,15 +1518,434 @@ function buildP8(){
function buildP9(){
const box = document.getElementById('p9-body');
let html = '';
- html += makeCard('theory', 'В разработке', '9.0', `
-
Содержание параграфа Логарифмические уравнения будет добавлено в Phase 1+.
- Раздел Phase 0 — skeleton. Здесь появятся теория, примеры и интерактивы.
- Ключевая формула: $\log_a f = \log_a g$
- `);
+
+ /* === ТЕОРИЯ === */
+
+ html += makeCard('theory', 'Основной метод: потенциирование', '9.1', `
+ Логарифмическим уравнением называется уравнение, в котором переменная содержится под знаком логарифма (или в его основании).
+ Простейший вид: $\\log_a f(x) = \\log_a g(x)$, где $a > 0$, $a \\ne 1$.
+ Поскольку логарифмическая функция $y = \\log_a x$ монотонна (см. §8) — строго возрастает при $a > 1$ или строго убывает при $0 < a < 1$, — она инъективна . Из равенства логарифмов следует равенство аргументов:
+
+ $\\log_a f(x) = \\log_a g(x) \\;\\Leftrightarrow\\; \\begin{cases} f(x) = g(x) \\\\\\\\ f(x) > 0 \\\\\\\\ g(x) > 0 \\end{cases}$
+
+ Этот переход называется потенциированием . Условия $f(x) > 0$ и $g(x) > 0$ — это ОДЗ (область допустимых значений) уравнения.
+ Алгоритм решения :
+
+ Привести обе части к одному основанию.
+ Применить свойства логарифмов (см. §7) для упрощения.
+ Записать ОДЗ: для каждого $\\log_a h(x)$ требуем $h(x) > 0$.
+ Потенциировать: $f(x) = g(x)$ — и решить.
+ Проверить корни на принадлежность ОДЗ — отбросить посторонние.
+
+ Пример. $\\log_3 (5 - x) = \\log_3 (x + 1)$.
+ ОДЗ: $5 - x > 0$ и $x + 1 > 0$, то есть $-1 < x < 5$.
+ Потенциируем: $5 - x = x + 1 \\Rightarrow 2x = 4 \\Rightarrow x = 2$. Проверка: $2 \\in (-1; 5)$ — корень подходит.
+ Ответ: $x = 2$.
`);
+
+ html += makeCard('rule', 'Определение логарифма и замена переменной', '9.2', `
+ Метод 1. Через определение логарифма. Уравнение вида $\\log_a f(x) = b$ (число справа) — это просто определение логарифма наоборот:
+
+ $\\log_a f(x) = b \\;\\Leftrightarrow\\; f(x) = a^b$ (при $f(x) > 0$).
+
+ Пример. $\\log_3 (2x - 1) = 2 \\Rightarrow 2x - 1 = 3^2 = 9 \\Rightarrow x = 5$. ОДЗ: $2x - 1 > 0 \\Leftrightarrow x > 0{,}5$. $5 > 0{,}5$ — корень подходит.
+
+ Метод 2. Замена переменной. Уравнения вида
+
+ $\\alpha \\log_a^2 x + \\beta \\log_a x + \\gamma = 0$
+
+ — квадратные относительно $t = \\log_a x$. После замены получим $\\alpha t^2 + \\beta t + \\gamma = 0$; найдём $t_1, t_2$, потом обратно $x_i = a^{t_i}$. Все такие $x$ автоматически положительны (это степени положительного числа), поэтому ОДЗ выполняется автоматически.
+ Пример. $\\log_2 x + \\log_x 2 = \\dfrac{5}{2}$.
+ По формуле перехода $\\log_x 2 = \\dfrac{1}{\\log_2 x}$. Пусть $t = \\log_2 x$, тогда
+ $t + \\dfrac{1}{t} = \\dfrac{5}{2} \\Rightarrow 2t^2 - 5t + 2 = 0 \\Rightarrow t = 2$ или $t = \\dfrac{1}{2}$.
+ Обратно: $\\log_2 x = 2 \\Rightarrow x = 4$; $\\;\\log_2 x = \\dfrac{1}{2} \\Rightarrow x = \\sqrt{2}$.
+ Ответ: $x_1 = 4,\\; x_2 = \\sqrt{2}$.
`);
+
+ html += makeCard('example', 'Применение свойств логарифма', '9.3', `
+ Часто уравнения содержат сумму или разность логарифмов — их сводят к одному через свойства из §7:
+
+ $\\log_a u + \\log_a v = \\log_a (u v)$;
+ $\\log_a u - \\log_a v = \\log_a \\dfrac{u}{v}$;
+ $n \\log_a u = \\log_a u^n$.
+
+
+ Внимание! После применения свойств ОДЗ может расшириться . Например, $\\log_a u + \\log_a v$ требует $u > 0$ и $v > 0$, а получившийся $\\log_a (uv)$ требует только $uv > 0$ — то есть допустимо и $u < 0, v < 0$ одновременно.
+ Поэтому при проверке корней используйте ОДЗ исходного уравнения, а не упрощённого!
+
+ Пример. $\\log_2 (x - 1) + \\log_2 (x + 1) = 3$.
+ ОДЗ исходного: $x - 1 > 0$ и $x + 1 > 0$, то есть $x > 1$.
+ Объединяем логарифмы: $\\log_2 \\bigl((x-1)(x+1)\\bigr) = 3 \\Rightarrow (x-1)(x+1) = 2^3$.
+ $x^2 - 1 = 8 \\Rightarrow x^2 = 9 \\Rightarrow x = \\pm 3$.
+ Проверка по ОДЗ: $x = 3 > 1$ — подходит. $x = -3$ не входит в ОДЗ — посторонний корень .
+ Ответ: $x = 3$.
+ Второй пример. $\\log_5 x + \\log_5 (x - 4) = 1$.
+ ОДЗ: $x > 0$ и $x - 4 > 0 \\Rightarrow x > 4$.
+ $\\log_5 \\bigl(x(x-4)\\bigr) = 1 \\Rightarrow x(x-4) = 5 \\Rightarrow x^2 - 4x - 5 = 0 \\Rightarrow x = 5$ или $x = -1$.
+ Проверка: $x = 5 > 4$ — подходит. $x = -1$ — посторонний.
+ Ответ: $x = 5$.
`);
+
+ /* === ИНТЕРАКТИВЫ === */
+
+ /* IV1 — пошаговый решатель с ОДЗ */
+ html += `
+
+
Выбери задачу ползунком и нажимай «Следующий шаг ▶», чтобы открывать решение по одному шагу. Особое внимание — записи ОДЗ и проверке корней. Просмотри все 5 задач — получишь XP.
+
+ Задача № 1 / 5
+
+
+
+
+ Следующий шаг ▶
+ Показать все шаги
+ Скрыть шаги
+
+
`;
+
+ /* IV2 — калькулятор log_a (kx + n) = b */
+ html += ``;
+
+ /* IV3 — посторонний корень или нет? */
+ html += `
+
+
Дано уравнение и найденный корень. Проверь, удовлетворяет ли он ОДЗ исходного уравнения. 6 заданий.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+
+
Начать заново
+
`;
+
+ /* IV4 — тренажёр уравнений */
+ html += `
+
+
Реши уравнение и введи корень (целое или десятичное число, допуск $\\pm 0{,}05$). Где указано — введи сумму корней. 6 задач.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ ответ =
+
+ Проверить
+ Заново
+
+
+
`;
+
html += secNavFor('p9');
html += readButton('p9');
+
box.innerHTML = html;
renderMath(box);
+
+ /* === IV1 — пошаговый решатель === */
+ (function(){
+ const TASKS = [
+ {
+ q: '$\\log_2 (x + 3) = 4$',
+ steps: [
+ 'ОДЗ: $x + 3 > 0 \\Rightarrow x > -3$.',
+ 'По определению логарифма: $x + 3 = 2^4 = 16$.',
+ 'Решаем: $x = 16 - 3 = 13$.',
+ 'Проверка по ОДЗ: $13 > -3$ — корень подходит.',
+ 'Ответ: $x = 13$.'
+ ]
+ },
+ {
+ q: '$\\log_3 (x - 1) = \\log_3 (5 - x)$',
+ steps: [
+ 'ОДЗ: $x - 1 > 0$ и $5 - x > 0 \\Rightarrow 1 < x < 5$.',
+ 'Потенциируем (основания равны): $x - 1 = 5 - x$.',
+ 'Решаем: $2x = 6 \\Rightarrow x = 3$.',
+ 'Проверка: $3 \\in (1; 5)$ — корень подходит.',
+ 'Ответ: $x = 3$.'
+ ]
+ },
+ {
+ q: '$\\log_5 x + \\log_5 (x - 4) = 1$',
+ steps: [
+ 'ОДЗ: $x > 0$ и $x - 4 > 0 \\Rightarrow x > 4$.',
+ 'Объединяем по свойству суммы: $\\log_5 \\bigl(x(x-4)\\bigr) = 1$.',
+ 'По определению: $x(x - 4) = 5 \\Rightarrow x^2 - 4x - 5 = 0$.',
+ 'По теореме Виета: $x_1 = 5$, $x_2 = -1$.',
+ 'Проверка по ОДЗ: $5 > 4$ — подходит; $-1$ не входит в ОДЗ — посторонний корень .',
+ 'Ответ: $x = 5$.'
+ ]
+ },
+ {
+ q: '$\\log_2^2 x - 3 \\log_2 x + 2 = 0$',
+ steps: [
+ 'ОДЗ: $x > 0$.',
+ 'Замена $t = \\log_2 x$: $t^2 - 3t + 2 = 0$.',
+ 'По теореме Виета: $t_1 = 1$, $t_2 = 2$.',
+ 'Обратно: $\\log_2 x = 1 \\Rightarrow x = 2$; $\\;\\log_2 x = 2 \\Rightarrow x = 4$.',
+ 'Оба корня положительны — ОДЗ выполняется.',
+ 'Ответ: $x_1 = 2,\\; x_2 = 4$.'
+ ]
+ },
+ {
+ q: '$\\lg^2 x - \\lg x = 6$',
+ steps: [
+ 'ОДЗ: $x > 0$.',
+ 'Замена $t = \\lg x$: $t^2 - t - 6 = 0$.',
+ 'По теореме Виета: $t_1 = 3$, $t_2 = -2$.',
+ 'Обратно: $\\lg x = 3 \\Rightarrow x = 10^3 = 1000$; $\\;\\lg x = -2 \\Rightarrow x = 10^{-2} = 0{,}01$.',
+ 'Оба корня положительны — ОДЗ выполняется.',
+ 'Ответ: $x_1 = 1000,\\; x_2 = 0{,}01$.'
+ ]
+ }
+ ];
+ const sn = document.getElementById('p9-iv1-sn');
+ const nL = document.getElementById('p9-iv1-n');
+ const qEl = document.getElementById('p9-iv1-q');
+ const stepsEl = document.getElementById('p9-iv1-steps');
+ const nextBtn = document.getElementById('p9-iv1-next');
+ const allBtn = document.getElementById('p9-iv1-all');
+ const resetBtn = document.getElementById('p9-iv1-reset');
+ const seen = new Set();
+ let _done = false;
+ let cur = 0, shown = 0;
+
+ function render(){
+ const t = TASKS[cur];
+ nL.textContent = (cur + 1);
+ qEl.innerHTML = t.q;
+ stepsEl.innerHTML = t.steps.map((s, i) => {
+ const visible = i < shown;
+ const isLast = i === t.steps.length - 1 && visible;
+ const bg = isLast ? '#dcfce7' : 'var(--card)';
+ const brd = isLast ? '2px solid #16a34a' : '1px solid var(--border)';
+ return 'Шаг '+(i+1)+': '+s+'
';
+ }).join('');
+ renderMath(qEl);
+ renderMath(stepsEl);
+ if(shown >= t.steps.length){
+ seen.add(cur);
+ if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p9-iv1'); bumpProgress('p9', 15); }
+ }
+ }
+ function load(n){ cur = Math.max(0, Math.min(TASKS.length - 1, n)); shown = 1; render(); }
+ sn.addEventListener('input', () => load((+sn.value) - 1));
+ nextBtn.addEventListener('click', () => {
+ if(shown < TASKS[cur].steps.length){ shown++; render(); }
+ });
+ allBtn.addEventListener('click', () => { shown = TASKS[cur].steps.length; render(); });
+ resetBtn.addEventListener('click', () => { shown = 1; render(); });
+ load(0);
+ })();
+
+ /* === IV2 — калькулятор log_a (kx + n) = b === */
+ (function(){
+ const aI = document.getElementById('p9-iv2-a');
+ const kI = document.getElementById('p9-iv2-k');
+ const nI = document.getElementById('p9-iv2-n');
+ const bI = document.getElementById('p9-iv2-b');
+ const go = document.getElementById('p9-iv2-go');
+ const out = document.getElementById('p9-iv2-out');
+ const fb = document.getElementById('p9-iv2-fb');
+ const used = new Set();
+ let _done = false;
+
+ document.querySelectorAll('#p9-iv2 [data-set]').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const v = btn.dataset.set.split(',');
+ aI.value = v[0]; kI.value = v[1]; nI.value = v[2]; bI.value = v[3];
+ calc();
+ });
+ });
+
+ function aLabel(a){
+ if(a === 10) return '\\lg';
+ return '\\log_{' + a + '}';
+ }
+ function fxLabel(k, n){
+ let s = '';
+ if(k === 1) s = 'x';
+ else if(k === -1) s = '-x';
+ else s = k + 'x';
+ if(n > 0) s += ' + ' + n;
+ else if(n < 0) s += ' - ' + (-n);
+ return s;
+ }
+
+ function calc(){
+ const a = parseFloat(aI.value);
+ const k = parseFloat(kI.value);
+ const n = parseFloat(nI.value);
+ const b = parseFloat(bI.value);
+ if(![a,k,n,b].every(isFinite)){ feedback(fb, false, '✗ Введи все четыре числа.'); return; }
+ if(a <= 0 || Math.abs(a - 1) < 1e-9){ feedback(fb, false, '✗ Основание $a$ должно быть $> 0$ и $\\ne 1$.'); return; }
+ if(k === 0){ feedback(fb, false, '✗ При $k = 0$ выражение под логарифмом не зависит от $x$.'); return; }
+
+ const fx = fxLabel(k, n);
+ const eq = '$' + aLabel(a) + '(' + fx + ') = ' + b + '$';
+ let html = 'Уравнение: '+eq+'
';
+
+ // ОДЗ: kx + n > 0
+ let odzStr;
+ if(k > 0){
+ const bound = -n / k;
+ odzStr = '$x > ' + (+bound.toFixed(4)) + '$';
+ } else {
+ const bound = -n / k;
+ odzStr = '$x < ' + (+bound.toFixed(4)) + '$';
+ }
+ html += 'ОДЗ: $' + fx + ' > 0 \\Rightarrow$ ' + odzStr + '.
';
+
+ // По определению: kx + n = a^b
+ const ab = Math.pow(a, b);
+ html += 'По определению логарифма: $' + fx + ' = ' + a + '^{' + b + '} = ' + (+ab.toFixed(4)) + '$.
';
+
+ // x = (a^b - n) / k
+ const xVal = (ab - n) / k;
+ html += 'Решаем: $x = \\dfrac{' + (+ab.toFixed(4)) + ' - (' + n + ')}{' + k + '} = ' + (+xVal.toFixed(4)) + '$.
';
+
+ // Проверка ОДЗ
+ const check = k * xVal + n;
+ const ok = check > 1e-9;
+ if(ok){
+ html += 'Проверка ОДЗ: $' + fx.replace('x', '(' + (+xVal.toFixed(4)) + ')') + ' = ' + (+check.toFixed(4)) + ' > 0$ — корень подходит.
';
+ html += '$\\Rightarrow x = ' + (+xVal.toFixed(4)) + '$
';
+ feedback(fb, true, '✓ Решено.');
+ } else {
+ html += 'Проверка ОДЗ: аргумент логарифма получается $\\le 0$ — посторонний корень.
';
+ html += 'Уравнение не имеет решений.
';
+ feedback(fb, true, '✓ Решено: корней нет.');
+ }
+
+ out.innerHTML = html;
+ renderMath(out);
+ used.add(aI.value+','+kI.value+','+nI.value+','+bI.value);
+ if(!_done && used.size >= 4){ _done = true; addXp(10, 'p9-iv2'); bumpProgress('p9', 15); }
+ }
+ go.addEventListener('click', calc);
+ [aI, kI, nI, bI].forEach(i => i.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); }));
+ calc();
+ })();
+
+ /* === IV3 — посторонний корень или нет? === */
+ (function(){
+ const OPTS = ['Подходит', 'Посторонний'];
+ const Q = [
+ { q: '$\\log_2 (x + 1) = 3$, найден $x = 7$', ans: 0, hint: 'ОДЗ: $x + 1 > 0 \\Rightarrow x > -1$. $7 > -1$ — подходит.' },
+ { q: '$\\log_2 (x - 5) = 1$, найден $x = 7$', ans: 0, hint: 'ОДЗ: $x - 5 > 0 \\Rightarrow x > 5$. $7 > 5$ — подходит.' },
+ { q: '$\\log_3 (x - 1) + \\log_3 (x + 1) = 1$, найден $x = -2$', ans: 1, hint: 'ОДЗ исходного: $x - 1 > 0$ и $x + 1 > 0 \\Rightarrow x > 1$. $-2 < 1$ — посторонний.' },
+ { q: '$\\log_2 (4 - x) = 0$, найден $x = 3$', ans: 0, hint: 'ОДЗ: $4 - x > 0 \\Rightarrow x < 4$. $3 < 4$ — подходит (и $4 - 3 = 1 > 0$).' },
+ { q: '$\\lg (x^2 - 5) = \\lg (4x)$, найден $x = -1$', ans: 1, hint: 'ОДЗ: $4x > 0 \\Rightarrow x > 0$. $-1 < 0$ — посторонний.' },
+ { q: '$\\log_5 (x - 3) = 1$, найден $x = 8$', ans: 0, hint: 'ОДЗ: $x - 3 > 0 \\Rightarrow x > 3$. $8 > 3$ — подходит ($x - 3 = 5 > 0$).' },
+ ];
+ let i = 0, score = 0;
+ const qEl = document.getElementById('p9-iv3-q');
+ const oEl = document.getElementById('p9-iv3-opts');
+ const fb = document.getElementById('p9-iv3-fb');
+ const iEl = document.getElementById('p9-iv3-i');
+ const sEl = document.getElementById('p9-iv3-s');
+
+ function show(){
+ if(i >= Q.length){
+ qEl.innerHTML = 'Готово! Результат: '+score+' / '+Q.length;
+ oEl.innerHTML = '';
+ if(score === Q.length){ addXp(15, 'p9-iv3'); bumpProgress('p9', 25); }
+ else if(score >= 4){ addXp(8, 'p9-iv3'); bumpProgress('p9', 15); }
+ return;
+ }
+ iEl.textContent = (i + 1);
+ sEl.textContent = score;
+ const item = Q[i];
+ qEl.innerHTML = item.q;
+ oEl.innerHTML = OPTS.map((o, k) => ''+o+' ').join('');
+ fb.style.display = 'none';
+ renderMath(qEl);
+ oEl.querySelectorAll('button').forEach(b => {
+ b.addEventListener('click', () => {
+ const k = +b.dataset.k;
+ if(k === item.ans){ score++; feedback(fb, true, '✓ Верно! '+item.hint+' Дальше ▶'); }
+ else feedback(fb, false, '✗ Неверно. Правильно: '+OPTS[item.ans]+' . '+item.hint+' Дальше ▶');
+ sEl.textContent = score;
+ oEl.querySelectorAll('button').forEach(x => x.disabled = true);
+ i++;
+ setTimeout(show, 1700);
+ });
+ });
+ }
+ document.getElementById('p9-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
+ show();
+ })();
+
+ /* === IV4 — тренажёр уравнений === */
+ (function(){
+ const Q = [
+ { q: '$\\log_3 x = 2$', ans: 9, hint: '$x = 3^2 = 9$.' },
+ { q: '$\\log_2 (x - 1) = 3$', ans: 9, hint: '$x - 1 = 2^3 = 8 \\Rightarrow x = 9$.' },
+ { q: '$\\lg (x + 1) = 2$', ans: 99, hint: '$x + 1 = 10^2 = 100 \\Rightarrow x = 99$.' },
+ { q: '$\\log_5 (2x + 1) = 1$', ans: 2, hint: '$2x + 1 = 5 \\Rightarrow x = 2$.' },
+ { q: '$\\log_2^2 x - 5 \\log_2 x + 6 = 0$ — введи сумму корней', ans: 12, hint: 'Замена $t = \\log_2 x$: $t = 2$ или $t = 3$. Корни $x = 4, x = 8$, сумма $= 12$.' },
+ { q: '$\\log_3 x + \\log_3 (x - 2) = 1$ — введи корень', ans: 3, hint: 'ОДЗ $x > 2$. $x(x-2) = 3 \\Rightarrow x^2 - 2x - 3 = 0 \\Rightarrow x = 3$ или $-1$. $-1$ — посторонний.' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ const qEl = document.getElementById('p9-iv4-q');
+ const iEl = document.getElementById('p9-iv4-i');
+ const sEl = document.getElementById('p9-iv4-s');
+ const fb = document.getElementById('p9-iv4-fb');
+ const ansI = document.getElementById('p9-iv4-ans');
+ if(i >= Q.length){
+ qEl.innerHTML = 'Готово! Результат: '+score+' / '+Q.length;
+ if(score === Q.length){ addXp(15, 'p9-iv4'); bumpProgress('p9', 25); }
+ else if(score >= 4){ addXp(8, 'p9-iv4'); bumpProgress('p9', 15); }
+ return;
+ }
+ iEl.textContent = (i + 1);
+ sEl.textContent = score;
+ qEl.innerHTML = Q[i].q;
+ ansI.value = '';
+ renderMath(qEl);
+ fb.style.display = 'none';
+ setTimeout(() => ansI.focus(), 30);
+ }
+ function go(){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p9-iv4-fb');
+ const raw = document.getElementById('p9-iv4-ans').value.replace(',', '.');
+ const ans = parseFloat(raw);
+ if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
+ if(Math.abs(ans - Q[i].ans) < 0.05){
+ score++;
+ feedback(fb, true, '✓ Верно! '+Q[i].hint+' Дальше ▶');
+ } else {
+ feedback(fb, false, '✗ Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
+ }
+ document.getElementById('p9-iv4-s').textContent = score;
+ i++;
+ setTimeout(show, 1700);
+ }
+ document.getElementById('p9-iv4-go').addEventListener('click', go);
+ document.getElementById('p9-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
+ document.getElementById('p9-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
+ show();
+ })();
+
wireReadBtn('p9');
}