${ch.heroH2}
+${ch.heroP}
+diff --git a/backend/scripts/gen_alg9_chapters.js b/backend/scripts/gen_alg9_chapters.js
new file mode 100644
index 0000000..ba3a053
--- /dev/null
+++ b/backend/scripts/gen_alg9_chapters.js
@@ -0,0 +1,784 @@
+#!/usr/bin/env node
+'use strict';
+/**
+ * Phase 0 skeleton generator for Алгебра 9 chapter files.
+ * Produces: algebra_9_ch1.html ... ch4.html as functioning skeletons
+ * with stub bodies for each section. Phase 1+ will fill in the content.
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+const OUT_DIR = path.join(__dirname, '..', '..', 'frontend', 'textbooks');
+
+/* ===== Chapter data ===== */
+const CHAPTERS = [
+ {
+ chN: 1,
+ title: 'Рациональные выражения',
+ sub: 'Рациональные дроби · ОДЗ · действия с дробями',
+ heroH2: 'Рациональные выражения — алгебра дробей',
+ heroP: 'Здесь мы изучаем рациональные дроби (выражения вида $\\dfrac{P(x)}{Q(x)}$), их область допустимых значений, основное свойство и сокращение, четыре арифметических действия и преобразование сложных рациональных выражений.',
+ palette: {
+ pri:'#d97706', pri2:'#b45309', priSoft:'#fef3c7',
+ acc:'#f59e0b', acc2:'#d97706', accSoft:'#fef9c3',
+ hdrGrad:'linear-gradient(110deg,#92400e 0%,#d97706 55%,#fbbf24 100%)',
+ hdrShadow:'rgba(251,191,36,.2)',
+ hdrWmStroke:'rgba(255,235,180,.12)',
+ darkBg:'#0a0a0e', darkCard:'#13120a', darkCardSoft:'#18160a', darkText:'#fef9e7', darkMuted:'#a39070', darkBorder:'#2a2512',
+ confetti:['#d97706','#f59e0b','#fbbf24','#10b981','#0891b2'],
+ heroWm:'A/B',
+ },
+ paras: [
+ { id:'p1', num:'§ 1', name:'Рациональная дробь', sub:'ОДЗ выражения', watermark:'P/Q', secAcc:'#d97706', secAccD:'#b45309', secAccSoft:'#fef3c7' },
+ { id:'p2', num:'§ 2', name:'Основное свойство дроби', sub:'Сокращение', watermark:'k', secAcc:'#f59e0b', secAccD:'#d97706', secAccSoft:'#fef9c3' },
+ { id:'p3', num:'§ 3', name:'Сложение и вычитание', sub:'Общий знаменатель', watermark:'+', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' },
+ { id:'p4', num:'§ 4', name:'Умножение и деление', sub:'×, ÷ дробей', watermark:'×', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' },
+ { id:'p5', num:'§ 5', name:'Преобразование выражений', sub:'Сложные дроби', watermark:'…', secAcc:'#db2777', secAccD:'#9d174d', secAccSoft:'#fce7f3' },
+ ],
+ achLabels: {
+ start:'Начало главы 1!',
+ p2_done:'Сокращение дробей освоено!',
+ p4_done:'Действия с дробями освоены!',
+ p5_done:'Преобразование выражений освоено!',
+ ch1_done:'Глава 1 пройдена!',
+ },
+ tips: [
+ { sec:'p1', html:'ОДЗ — это значения, при которых знаменатель $\\ne 0$. Всегда выписывай ОДЗ перед работой с дробью.' },
+ { sec:'p2', html:'Сокращение возможно после разложения на множители числителя и знаменателя.' },
+ { sec:'p3', html:'Для сложения дробей с разными знаменателями ищи наименьший общий знаменатель.' },
+ { sec:'p4', html:'$\\dfrac{a}{b} \\cdot \\dfrac{c}{d} = \\dfrac{ac}{bd}$, $\\dfrac{a}{b} : \\dfrac{c}{d} = \\dfrac{ad}{bc}$.' },
+ { sec:'p5', html:'Сложные выражения упрощай по действиям, не забывай об ОДЗ.' },
+ { sec:'final1', html:'5 боссов главы 1. Удачи!' },
+ ],
+ sidebars: {
+ p1:[ ['Дробь','$\\dfrac{P(x)}{Q(x)}$, где $P, Q$ — многочлены'], ['ОДЗ','$Q(x) \\ne 0$'], ['Целое','частный случай при $Q = 1$'] ],
+ p2:[ ['Свойство','$\\dfrac{P \\cdot R}{Q \\cdot R} = \\dfrac{P}{Q}$ при $R \\ne 0$'], ['Сокращение','делим числитель и знаменатель на общий множитель'], ['Знак','$\\dfrac{-a}{-b} = \\dfrac{a}{b}$, $\\dfrac{-a}{b} = -\\dfrac{a}{b}$'] ],
+ p3:[ ['Одинак.знам.','$\\dfrac{a}{c} \\pm \\dfrac{b}{c} = \\dfrac{a \\pm b}{c}$'], ['Разные знам.','приведи к общему знаменателю'], ['НОЗ','наименьший общий знаменатель'] ],
+ p4:[ ['Умножение','$\\dfrac{a}{b} \\cdot \\dfrac{c}{d} = \\dfrac{ac}{bd}$'], ['Деление','$\\dfrac{a}{b} : \\dfrac{c}{d} = \\dfrac{a}{b} \\cdot \\dfrac{d}{c}$'], ['Степень','$\\left(\\dfrac{a}{b}\\right)^n = \\dfrac{a^n}{b^n}$'] ],
+ p5:[ ['Шаг 1','выпиши ОДЗ'], ['Шаг 2','разложи на множители'], ['Шаг 3','выполни действия по порядку'], ['Шаг 4','сократи результат'] ],
+ final1:[ ['§§1–5','теория главы 1'], ['Боссов','5'], ['Награда','+100 XP'] ],
+ },
+ },
+ {
+ chN: 2,
+ title: 'Функции',
+ sub: 'Числовой аргумент · свойства · чётность · сдвиги',
+ heroH2: 'Функции — изучаем поведение и графики',
+ heroP: 'Здесь мы знакомимся с функцией числового аргумента: область определения $D(f)$, область значений $E(f)$, возрастание/убывание, нули, наибольшее и наименьшее значения, чётность и сдвиги графиков $y = f(x) + b$, $y = f(x \\pm a)$.',
+ palette: {
+ pri:'#059669', pri2:'#047857', priSoft:'#d1fae5',
+ acc:'#10b981', acc2:'#059669', accSoft:'#ecfdf5',
+ hdrGrad:'linear-gradient(110deg,#064e3b 0%,#059669 55%,#34d399 100%)',
+ hdrShadow:'rgba(167,243,208,.2)',
+ hdrWmStroke:'rgba(209,250,229,.12)',
+ darkBg:'#021410', darkCard:'#0a1f1a', darkCardSoft:'#0d2620', darkText:'#e0fcf3', darkMuted:'#7aa896', darkBorder:'#163d2f',
+ confetti:['#059669','#10b981','#34d399','#f59e0b','#0891b2'],
+ heroWm:'f(x)',
+ },
+ paras: [
+ { id:'p6', num:'§ 6', name:'Функция числового аргумента', sub:'$D(f)$, $E(f)$', watermark:'D/E', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' },
+ { id:'p7', num:'§ 7', name:'Свойства функции', sub:'нули, монотонность, экстр.', watermark:'↗', secAcc:'#10b981', secAccD:'#059669', secAccSoft:'#ecfdf5' },
+ { id:'p8', num:'§ 8', name:'Чётные и нечётные функции', sub:'симметрия графика', watermark:'±', secAcc:'#0891b2', secAccD:'#0e7490', secAccSoft:'#cffafe' },
+ { id:'p9', num:'§ 9', name:'Сдвиги графиков', sub:'$y=f(x)+b$, $y=f(x \\pm a)$', watermark:'→', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' },
+ ],
+ achLabels: {
+ start:'Начало главы 2!',
+ p7_done:'Свойства функции освоены!',
+ p8_done:'Чётность освоена!',
+ p9_done:'Сдвиги графиков освоены!',
+ ch2_done:'Глава 2 пройдена!',
+ },
+ tips: [
+ { sec:'p6', html:'Функция — это правило: каждому $x$ из $D(f)$ соответствует ровно одно $y$.' },
+ { sec:'p7', html:'Нули функции — это решения уравнения $f(x) = 0$.' },
+ { sec:'p8', html:'Чётная функция: $f(-x) = f(x)$. Нечётная: $f(-x) = -f(x)$.' },
+ { sec:'p9', html:'$y = f(x) + b$ — сдвиг по $Oy$. $y = f(x - a)$ — сдвиг по $Ox$ вправо на $a$.' },
+ { sec:'final2', html:'4 босса главы 2.' },
+ ],
+ sidebars: {
+ p6:[ ['Функция','правило $x \\to y$'], ['$D(f)$','область определения'], ['$E(f)$','область значений'] ],
+ p7:[ ['Нуль','$f(x_0) = 0$'], ['Возрастает','при бо́льшем $x$ — бо́льшее $f(x)$'], ['Убывает','при бо́льшем $x$ — меньшее $f(x)$'], ['$y_{max}$','наиб. значение на промежутке'] ],
+ p8:[ ['Чётная','$f(-x) = f(x)$ — симм. отн. $Oy$'], ['Нечётная','$f(-x) = -f(x)$ — симм. отн. $O$'], ['Ни та, ни др.','общий случай'] ],
+ p9:[ ['$f(x) + b$','сдвиг вверх на $b$'], ['$f(x) - b$','сдвиг вниз на $b$'], ['$f(x - a)$','сдвиг вправо на $a$'], ['$f(x + a)$','сдвиг влево на $a$'] ],
+ final2:[ ['§§6–9','теория главы 2'], ['Боссов','4'], ['Награда','+100 XP'] ],
+ },
+ },
+ {
+ chN: 3,
+ title: 'Дробно-рациональные уравнения и неравенства',
+ sub: 'Уравнения · системы · окружность · метод интервалов',
+ heroH2: 'Дробно-рациональные уравнения и неравенства',
+ heroP: 'Здесь мы изучаем дробно-рациональные уравнения, системы нелинейных уравнений (включая графический способ), длину отрезка и уравнение окружности $(x-a)^2 + (y-b)^2 = r^2$, а также метод интервалов для дробно-рациональных неравенств.',
+ palette: {
+ pri:'#7c3aed', pri2:'#6d28d9', priSoft:'#ede9fe',
+ acc:'#a78bfa', acc2:'#7c3aed', accSoft:'#f5f3ff',
+ hdrGrad:'linear-gradient(110deg,#3b0764 0%,#7c3aed 55%,#a78bfa 100%)',
+ hdrShadow:'rgba(196,181,253,.2)',
+ hdrWmStroke:'rgba(237,233,254,.12)',
+ darkBg:'#0d0418', darkCard:'#1a0d2a', darkCardSoft:'#1f1130', darkText:'#f3e8ff', darkMuted:'#a08fb5', darkBorder:'#3a1f54',
+ confetti:['#7c3aed','#a78bfa','#c4b5fd','#f59e0b','#0891b2'],
+ heroWm:'≠0',
+ },
+ paras: [
+ { id:'p10', num:'§ 10', name:'Дробно-рациональные уравнения', sub:'$\\dfrac{P}{Q} = 0$', watermark:'=0', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' },
+ { id:'p11', num:'§ 11', name:'Системы нелинейных уравнений', sub:'подстановка · графика', watermark:'{', secAcc:'#0891b2', secAccD:'#0e7490', secAccSoft:'#cffafe' },
+ { id:'p12', num:'§ 12', name:'Уравнение окружности', sub:'$(x-a)^2+(y-b)^2=r^2$', watermark:'○', secAcc:'#db2777', secAccD:'#9d174d', secAccSoft:'#fce7f3' },
+ { id:'p13', num:'§ 13', name:'Метод интервалов', sub:'неравенства', watermark:'>0', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' },
+ ],
+ achLabels: {
+ start:'Начало главы 3!',
+ p11_done:'Системы нелинейных уравнений освоены!',
+ p12_done:'Уравнение окружности освоено!',
+ p13_done:'Метод интервалов освоен!',
+ ch3_done:'Глава 3 пройдена!',
+ },
+ tips: [
+ { sec:'p10', html:'Дробно-рациональное уравнение $\\dfrac{P(x)}{Q(x)} = 0$ равносильно системе: $P(x) = 0$ и $Q(x) \\ne 0$.' },
+ { sec:'p11', html:'В системах нелинейных уравнений часто помогает метод подстановки или сложение.' },
+ { sec:'p12', html:'Длина отрезка: $d = \\sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}$. Окружность: $(x - a)^2 + (y - b)^2 = r^2$.' },
+ { sec:'p13', html:'Метод интервалов: нули → точки на оси → знаки на промежутках.' },
+ { sec:'final3', html:'4 босса главы 3.' },
+ ],
+ sidebars: {
+ p10:[ ['Дробно-рац. уравн.','$\\dfrac{P(x)}{Q(x)} = 0$'], ['Условие','$P(x) = 0$ и $Q(x) \\ne 0$'], ['Алгоритм','найди корни $P$ → проверь ОДЗ'] ],
+ p11:[ ['Система','несколько уравнений с общими $x, y$'], ['Подстановка','выразил → подставил'], ['Графически','точки пересечения графиков'] ],
+ p12:[ ['Длина','$d = \\sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}$'], ['Окружность','$(x-a)^2 + (y-b)^2 = r^2$'], ['Центр','$(a; b)$'], ['Радиус','$r$'] ],
+ p13:[ ['Шаг 1','перенеси всё влево, приведи к виду $\\dfrac{P}{Q}$'], ['Шаг 2','найди нули $P$ и $Q$'], ['Шаг 3','отметь на оси'], ['Шаг 4','определи знаки'] ],
+ final3:[ ['§§10–13','теория главы 3'], ['Боссов','4'], ['Награда','+100 XP'] ],
+ },
+ },
+ {
+ chN: 4,
+ title: 'Прогрессии',
+ sub: 'Последовательности · арифметическая · геометрическая',
+ heroH2: 'Прогрессии — арифметика и геометрия чисел',
+ heroP: 'Здесь мы изучаем числовые последовательности, арифметическую прогрессию $(a_n = a_1 + (n-1)d)$ и геометрическую прогрессию $(b_n = b_1 q^{n-1})$, формулы сумм $n$ первых членов и сумму бесконечно убывающей геометрической прогрессии $S = \\dfrac{b_1}{1 - q}$.',
+ palette: {
+ pri:'#0891b2', pri2:'#0e7490', priSoft:'#cffafe',
+ acc:'#22d3ee', acc2:'#0891b2', accSoft:'#ecfeff',
+ hdrGrad:'linear-gradient(110deg,#164e63 0%,#0891b2 55%,#22d3ee 100%)',
+ hdrShadow:'rgba(165,243,252,.2)',
+ hdrWmStroke:'rgba(209,250,255,.12)',
+ darkBg:'#04141a', darkCard:'#0a1b22', darkCardSoft:'#0d2229', darkText:'#e0fcff', darkMuted:'#7aa8b3', darkBorder:'#163842',
+ confetti:['#0891b2','#22d3ee','#67e8f9','#f59e0b','#10b981'],
+ heroWm:'aₙ',
+ },
+ paras: [
+ { id:'p14', num:'§ 14', name:'Числовая последовательность', sub:'$a_1, a_2, \\dots, a_n$', watermark:'aₙ', secAcc:'#0891b2', secAccD:'#0e7490', secAccSoft:'#cffafe' },
+ { id:'p15', num:'§ 15', name:'Арифметическая прогрессия', sub:'$a_n = a_1 + (n-1)d$', watermark:'+d', secAcc:'#06b6d4', secAccD:'#0891b2', secAccSoft:'#cffafe' },
+ { id:'p16', num:'§ 16', name:'Сумма арифм. прогрессии', sub:'$S_n = \\tfrac{a_1 + a_n}{2} n$', watermark:'Σ', secAcc:'#2563eb', secAccD:'#1d4ed8', secAccSoft:'#dbeafe' },
+ { id:'p17', num:'§ 17', name:'Геометрическая прогрессия', sub:'$b_n = b_1 q^{n-1}$', watermark:'·q', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' },
+ { id:'p18', num:'§ 18', name:'Сумма геом. прогрессии', sub:'$S_n = \\tfrac{b_1(q^n - 1)}{q - 1}$', watermark:'Σ', secAcc:'#db2777', secAccD:'#9d174d', secAccSoft:'#fce7f3' },
+ { id:'p19', num:'§ 19', name:'Бесконечно убывающая', sub:'$S = \\tfrac{b_1}{1 - q}$', watermark:'∞', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' },
+ ],
+ achLabels: {
+ start:'Начало главы 4!',
+ p15_done:'Арифметическая прогрессия освоена!',
+ p17_done:'Геометрическая прогрессия освоена!',
+ p19_done:'Бесконечно убывающая освоена!',
+ ch4_done:'Глава 4 пройдена! Алгебра 9 — финал!',
+ },
+ tips: [
+ { sec:'p14', html:'Числовая последовательность — это функция натурального аргумента: $a: \\mathbb{N} \\to \\mathbb{R}$.' },
+ { sec:'p15', html:'В арифметической прогрессии разность $d = a_{n+1} - a_n$ — постоянна.' },
+ { sec:'p16', html:'$S_n = \\dfrac{(a_1 + a_n) n}{2} = \\dfrac{(2 a_1 + (n - 1) d) n}{2}$.' },
+ { sec:'p17', html:'В геометрической прогрессии знаменатель $q = \\dfrac{b_{n+1}}{b_n}$ — постоянен.' },
+ { sec:'p18', html:'$S_n = \\dfrac{b_1 (q^n - 1)}{q - 1}$ при $q \\ne 1$.' },
+ { sec:'p19', html:'При $|q| < 1$: $S = \\dfrac{b_1}{1 - q}$.' },
+ { sec:'final4', html:'6 боссов главы 4. После — вся Алгебра 9 в твоём арсенале!' },
+ ],
+ sidebars: {
+ p14:[ ['Послед-сть','$(a_n)$, $n \\in \\mathbb{N}$'], ['Способы','формула $n$-го члена, реккурентно, словесно'], ['Член','$a_n$ — $n$-й член'] ],
+ p15:[ ['Опр.','$a_{n+1} - a_n = d$'], ['Форм.','$a_n = a_1 + (n - 1) d$'], ['Свойство','$a_n = \\tfrac{a_{n-1} + a_{n+1}}{2}$'] ],
+ p16:[ ['Формула 1','$S_n = \\tfrac{a_1 + a_n}{2} n$'], ['Формула 2','$S_n = \\tfrac{2 a_1 + (n - 1) d}{2} n$'] ],
+ p17:[ ['Опр.','$\\dfrac{b_{n+1}}{b_n} = q$, $b_1 \\ne 0$, $q \\ne 0$'], ['Форм.','$b_n = b_1 q^{n-1}$'], ['Свойство','$b_n^2 = b_{n-1} b_{n+1}$'] ],
+ p18:[ ['$q \\ne 1$','$S_n = \\tfrac{b_1(q^n - 1)}{q - 1}$'], ['$q = 1$','$S_n = n \\cdot b_1$'] ],
+ p19:[ ['Условие','$|q| < 1$'], ['Сумма','$S = \\tfrac{b_1}{1 - q}$'] ],
+ final4:[ ['§§14–19','теория главы 4'], ['Боссов','6'], ['Награда','+100 XP'], ['Алгебра 9','полностью пройдена!'] ],
+ },
+ },
+];
+
+/* ===== HTML generator ===== */
+function genChapter(ch) {
+ const chN = ch.chN;
+ const P = ch.palette;
+ const paras = ch.paras;
+ const allParas = [...paras, { id:'final'+chN, num:'★', name:'Финал главы', sub:'Итоги · '+paras.length+' боссов', final:true, watermark:'★', secAcc:P.pri, secAccD:P.pri2, secAccSoft:P.priSoft }];
+ const total = allParas.length;
+ const slug = `algebra-9-ch${chN}`;
+ const lsPrefix = `algebra9_ch${chN}`;
+
+ // Build section colors block
+ const secColors = allParas.map(p =>
+ `.sec[id="sec-${p.id}"]{ --sec-acc:${p.secAcc}; --sec-acc-d:${p.secAccD}; --sec-acc-soft:${p.secAccSoft}; }`
+ ).join('\n');
+
+ // Build section html
+ const secsHtml = allParas.map(p => {
+ if (p.final) {
+ return ` Итоги. ${paras.length} боссов главы ${chN}
${p.name}
Боссы и итоговые задания будут добавлены в Phase 1.
' : 'Раздел Phase 1.
'; + return `function ${fnName}(){ + const root = document.getElementById('${p.id}-body'); + root.innerHTML = \` +Содержание ${p.final ? 'финала главы' : 'параграфа'} ${titleText} будет добавлено в следующих обновлениях.
+ ${xpHint} +Боссы и итоговые задания будут добавлены в Phase 1.
' : 'Раздел Phase 1.
'; + const prev = p.idx > 0 ? `'${allParas[p.idx-1].id}'` : 'null'; + const next = p.idx < allParas.length-1 ? `'${allParas[p.idx+1].id}'` : 'null'; + return `function ${fnName}(){ + const root = document.getElementById('${p.id}-body'); + root.innerHTML = \` +Содержание ${p.final ? 'финала главы' : 'параграфа'} ${titleText} будет добавлено в следующих обновлениях.
+ ${xpHint} +${ch.heroP}
+Здесь мы изучаем рациональные дроби (выражения вида $\dfrac{P(x)}{Q(x)}$), их область допустимых значений, основное свойство и сокращение, четыре арифметических действия и преобразование сложных рациональных выражений.
+Здесь мы знакомимся с функцией числового аргумента: область определения $D(f)$, область значений $E(f)$, возрастание/убывание, нули, наибольшее и наименьшее значения, чётность и сдвиги графиков $y = f(x) + b$, $y = f(x \pm a)$.
+Здесь мы изучаем дробно-рациональные уравнения, системы нелинейных уравнений (включая графический способ), длину отрезка и уравнение окружности $(x-a)^2 + (y-b)^2 = r^2$, а также метод интервалов для дробно-рациональных неравенств.
+Здесь мы изучаем числовые последовательности, арифметическую прогрессию $(a_n = a_1 + (n-1)d)$ и геометрическую прогрессию $(b_n = b_1 q^{n-1})$, формулы сумм $n$ первых членов и сумму бесконечно убывающей геометрической прогрессии $S = \dfrac{b_1}{1 - q}$.
+