/**
 * Seed: все теоретические курсы с уроками
 *
 * Курсы:
 *   [math] Тригонометрия              — 4 урока
 *   [math] Производные и интегралы    — 4 урока
 *   [math] Теория вероятностей        — 3 урока
 *   [phys] Механика                   — 4 урока
 *   [phys] Электричество и магнетизм  — 4 урока
 *   [phys] Оптика и волны             — 3 урока
 *   [chem] Строение вещества          — 4 урока
 *   [chem] Органическая химия         — 4 урока
 *   [bio]  Клетка и генетика          — 4 урока
 *   [inf]  Алгоритмы                  — 3 урока
 *
 * Запуск: node src/db/seed-theory.js  (из папки backend/)
 */
'use strict';
require('./migrate'); // применить все миграции (включая lesson_blocks v4)
const db = require('./db');

/* ── Guard: пропустить, если уже засеяно ─────────────────────────────── */
const TITLES = [
  'Тригонометрия', 'Производные и интегралы', 'Теория вероятностей и статистика',
  'Механика', 'Электричество и магнетизм', 'Оптика и волны',
  'Строение вещества', 'Органическая химия', 'Клетка и генетика', 'Алгоритмы и структуры данных',
];
const already = db.prepare(
  `SELECT 1 FROM courses WHERE title IN (${TITLES.map(() => '?').join(',')}) LIMIT 1`
).get(...TITLES);
if (already) { console.log('seed-theory: already seeded — skipping.'); process.exit(0); }

/* ── Найти/создать преподавателя ─────────────────────────────────────── */
let teacher = db.prepare("SELECT id FROM users WHERE role IN ('teacher','admin') LIMIT 1").get();
if (!teacher) {
  const bcrypt = require('bcryptjs');
  const hash = bcrypt.hashSync('teacher123', 10);
  const r = db.prepare(
    "INSERT INTO users (email, password_hash, name, role) VALUES (?, ?, ?, 'teacher')"
  ).run('teacher@learnspace.local', hash, 'Преподаватель');
  teacher = { id: Number(r.lastInsertRowid) };
  console.log('Created system teacher, id =', teacher.id);
}
const TEACHER = Number(teacher.id);

/* ── Хелперы ─────────────────────────────────────────────────────────── */
function J(obj) { return JSON.stringify(obj); }

function course(slug, title, desc, emoji, order) {
  return Number(db.prepare(
    `INSERT INTO courses (subject_slug, title, description, cover_emoji, order_index, is_published, created_by)
     VALUES (?, ?, ?, ?, ?, 1, ?)`
  ).run(slug, title, desc, emoji, order, TEACHER).lastInsertRowid);
}
function section(courseId, title, order) {
  return Number(db.prepare(
    `INSERT INTO course_sections (course_id, title, order_index) VALUES (?, ?, ?)`
  ).run(courseId, title, order).lastInsertRowid);
}
function lesson(courseId, sectionId, title, order) {
  return Number(db.prepare(
    `INSERT INTO lessons (course_id, section_id, title, order_index, is_published) VALUES (?, ?, ?, ?, 1)`
  ).run(courseId, sectionId, title, order).lastInsertRowid);
}
function block(lid, type, order, data) {
  db.prepare(
    `INSERT INTO lesson_blocks (lesson_id, type, order_index, data) VALUES (?, ?, ?, ?)`
  ).run(lid, type, order, J(data));
}

/* ═══════════════════════════════════════════════════════════════════════
   Транзакция
   ═══════════════════════════════════════════════════════════════════════ */
db.transaction(() => {

/* ══════════════════════════════════════════════════════════════
   КУРС: Тригонометрия  [math]
   ══════════════════════════════════════════════════════════════ */
const C_TRIG = course('math', 'Тригонометрия',
  'Единичная окружность, тригонометрические функции, формулы и уравнения', '📐', 1);
const S_T1 = section(C_TRIG, 'Основы', 1);
const S_T2 = section(C_TRIG, 'Формулы', 2);

{
  const L = lesson(C_TRIG, S_T1, 'Единичная окружность и углы', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Единичная окружность' });
  block(L, 'text', ++b, { text: 'Единичная окружность — окружность с центром в начале координат и радиусом 1. Любая точка на ней имеет координаты (cos α, sin α), где α — угол от положительной оси OX против часовой стрелки.' });
  block(L, 'callout', ++b, { style: 'info', text: 'Радиус равен 1, поэтому sin α = y/1 = y, cos α = x/1 = x. Это упрощает все формулы.' });
  block(L, 'heading', ++b, { level: 3, text: 'Радианы и градусы' });
  block(L, 'text', ++b, { text: 'Один радиан — угол, при котором длина дуги равна радиусу. Полный оборот: 360° = 2π рад.' });
  block(L, 'formula', ++b, { tex: '\\alpha_{\\text{рад}} = \\dfrac{\\alpha_{\\text{град}}}{180°} \\cdot \\pi' });
  block(L, 'table', ++b, {
    header: ['Градусы', '0°', '30°', '45°', '60°', '90°', '180°', '270°', '360°'],
    rows: [['Радианы', '0', 'π/6', 'π/4', 'π/3', 'π/2', 'π', '3π/2', '2π']],
  });
  block(L, 'heading', ++b, { level: 3, text: 'Знаки в четвертях' });
  block(L, 'table', ++b, {
    header: ['Четверть', 'Углы', 'sin', 'cos', 'tg'],
    rows: [
      ['I', '0°–90°', '+', '+', '+'],
      ['II', '90°–180°', '+', '−', '−'],
      ['III', '180°–270°', '−', '−', '+'],
      ['IV', '270°–360°', '−', '+', '−'],
    ],
  });
  block(L, 'callout', ++b, { style: 'success', text: 'Запоминалка: «Все Студенты Такие Кретины» (All Students Take Calculus) — в I все положительны, во II только sin, в III только tg, в IV только cos.' });
  block(L, 'quiz', ++b, {
    question: 'В какой четверти cos α < 0 и sin α > 0?',
    options: ['I', 'II', 'III', 'IV'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_TRIG, S_T1, 'Синус и косинус', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Синус и косинус угла' });
  block(L, 'text', ++b, { text: 'sin α — ордината точки на единичной окружности. cos α — абсцисса. Оба лежат в [−1, 1].' });
  block(L, 'formula', ++b, { tex: '-1 \\le \\sin \\alpha \\le 1, \\quad -1 \\le \\cos \\alpha \\le 1' });
  block(L, 'table', ++b, {
    header: ['α', '0°', '30°', '45°', '60°', '90°'],
    rows: [
      ['sin α', '0', '½', '√2/2', '√3/2', '1'],
      ['cos α', '1', '√3/2', '√2/2', '½', '0'],
    ],
  });
  block(L, 'callout', ++b, { style: 'success', text: 'Для sin: √0/2, √1/2, √2/2, √3/2, √4/2 = 0, ½, √2/2, √3/2, 1. Для cos — в обратном порядке.' });
  block(L, 'heading', ++b, { level: 3, text: 'Основное тригонометрическое тождество' });
  block(L, 'formula', ++b, { tex: '\\sin^2 \\alpha + \\cos^2 \\alpha = 1' });
  block(L, 'text', ++b, { text: 'Следует из теоремы Пифагора: точка (cos α, sin α) лежит на окружности r = 1, поэтому x² + y² = 1.' });
  block(L, 'quiz', ++b, {
    question: 'sin²(60°) + cos²(60°) = ?',
    options: ['0', '½', '1', '√3'],
    correctIndex: 2,
  });
  block(L, 'flashcard', ++b, { front: 'sin 30° = ?', back: '½ = 0.5' });
  block(L, 'flashcard', ++b, { front: 'cos 45° = ?', back: '√2/2 ≈ 0.707' });
}

{
  const L = lesson(C_TRIG, S_T1, 'Тангенс и котангенс', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Тангенс и котангенс' });
  block(L, 'formula', ++b, { label: 'Тангенс', tex: '\\tan \\alpha = \\dfrac{\\sin \\alpha}{\\cos \\alpha}' });
  block(L, 'formula', ++b, { label: 'Котангенс', tex: '\\cot \\alpha = \\dfrac{\\cos \\alpha}{\\sin \\alpha} = \\dfrac{1}{\\tan \\alpha}' });
  block(L, 'callout', ++b, { style: 'warning', text: 'tg α не определён при cos α = 0 (α = 90° + 180°·n).\nctg α не определён при sin α = 0 (α = 180°·n).' });
  block(L, 'table', ++b, {
    header: ['α', '0°', '30°', '45°', '60°', '90°'],
    rows: [
      ['tg α', '0', '√3/3', '1', '√3', '—'],
      ['ctg α', '—', '√3', '1', '√3/3', '0'],
    ],
  });
  block(L, 'quiz', ++b, {
    question: 'tg 45° = ?',
    options: ['0', '½', '1', '√3'],
    correctIndex: 2,
  });
}

{
  const L = lesson(C_TRIG, S_T2, 'Формулы приведения и сложения', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Формулы приведения' });
  block(L, 'callout', ++b, { style: 'info', text: 'Правило: 1) определите четверть → знак результата; 2) если аргумент π/2 или 3π/2 — функция меняется на кофункцию (sin↔cos, tg↔ctg); π или 2π — не меняется.' });
  block(L, 'table', ++b, {
    header: ['', 'sin', 'cos', 'tg'],
    rows: [
      ['π/2 − α', 'cos α', 'sin α', 'ctg α'],
      ['π − α', 'sin α', '−cos α', '−tg α'],
      ['π + α', '−sin α', '−cos α', 'tg α'],
      ['2π − α', '−sin α', 'cos α', '−tg α'],
    ],
  });
  block(L, 'heading', ++b, { level: 2, text: 'Формулы сложения' });
  block(L, 'formula', ++b, { tex: '\\sin(\\alpha \\pm \\beta) = \\sin\\alpha\\cos\\beta \\pm \\cos\\alpha\\sin\\beta' });
  block(L, 'formula', ++b, { tex: '\\cos(\\alpha \\pm \\beta) = \\cos\\alpha\\cos\\beta \\mp \\sin\\alpha\\sin\\beta' });
  block(L, 'formula', ++b, { tex: '\\tan(\\alpha \\pm \\beta) = \\dfrac{\\tan\\alpha \\pm \\tan\\beta}{1 \\mp \\tan\\alpha\\tan\\beta}' });
  block(L, 'heading', ++b, { level: 3, text: 'Формулы двойного угла' });
  block(L, 'formula', ++b, { tex: '\\sin 2\\alpha = 2\\sin\\alpha\\cos\\alpha' });
  block(L, 'formula', ++b, { tex: '\\cos 2\\alpha = \\cos^2\\alpha - \\sin^2\\alpha = 2\\cos^2\\alpha - 1 = 1 - 2\\sin^2\\alpha' });
  block(L, 'quiz', ++b, {
    question: 'sin(π − α) = ?',
    options: ['sin α', '−sin α', 'cos α', '−cos α'],
    correctIndex: 0,
  });
  block(L, 'flashcard', ++b, { front: 'sin(α + β) = ?', back: 'sin α · cos β + cos α · sin β' });
  block(L, 'flashcard', ++b, { front: 'cos 2α = ?', back: 'cos²α − sin²α = 2cos²α − 1 = 1 − 2sin²α' });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Производные и интегралы  [math]
   ══════════════════════════════════════════════════════════════ */
const C_CALC = course('math', 'Производные и интегралы',
  'Дифференциальное и интегральное исчисление: производная, правила, применение, первообразная', '∫', 2);
const S_C1 = section(C_CALC, 'Дифференциальное исчисление', 1);
const S_C2 = section(C_CALC, 'Интегральное исчисление', 2);

{
  const L = lesson(C_CALC, S_C1, 'Понятие производной', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Производная функции' });
  block(L, 'text', ++b, { text: 'Производная — мгновенная скорость изменения функции. Геометрически: тангенс угла наклона касательной к графику.' });
  block(L, 'formula', ++b, { label: 'Определение', tex: "f'(x) = \\lim_{\\Delta x \\to 0} \\frac{f(x + \\Delta x) - f(x)}{\\Delta x}" });
  block(L, 'table', ++b, {
    header: ['f(x)', "f'(x)"],
    rows: [
      ['C', '0'], ['xⁿ', 'n·xⁿ⁻¹'], ['eˣ', 'eˣ'], ['aˣ', 'aˣ·ln a'],
      ['ln x', '1/x'], ['sin x', 'cos x'], ['cos x', '−sin x'],
      ['tg x', '1/cos²x'], ['√x', '1/(2√x)'],
    ],
  });
  block(L, 'formula', ++b, { label: 'Произведение', tex: '(fg)\' = f\'g + fg\'' });
  block(L, 'formula', ++b, { label: 'Частное', tex: '\\left(\\dfrac{f}{g}\\right)\' = \\dfrac{f\'g - fg\'}{g^2}' });
  block(L, 'formula', ++b, { label: 'Сложная функция', tex: '(f(g(x)))\' = f\'(g(x)) \\cdot g\'(x)' });
  block(L, 'accordion', ++b, { title: 'Пример: f(x) = x³ − 5x + 2', content: "f'(x) = 3x² − 5\n(x³)' = 3x², (−5x)' = −5, (2)' = 0" });
  block(L, 'quiz', ++b, {
    question: "(4x³)' = ?",
    options: ['4x²', '12x²', '12x³', '4x⁴'],
    correctIndex: 1,
  });
  block(L, 'flashcard', ++b, { front: "(xⁿ)' = ?", back: 'n · xⁿ⁻¹\nПример: (x⁵)\' = 5x⁴' });
}

{
  const L = lesson(C_CALC, S_C1, 'Применение производной', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Применение производной' });
  block(L, 'callout', ++b, { style: 'info', text: "Алгоритм нахождения экстремумов:\n1. Найти f'(x)\n2. Решить f'(x) = 0\n3. Определить знак f' слева и справа\n4. «+→−» — максимум, «−→+» — минимум" });
  block(L, 'formula', ++b, { label: 'Признак через вторую производную', tex: "f''(x_0) > 0 \\Rightarrow \\text{min}, \\quad f''(x_0) < 0 \\Rightarrow \\text{max}" });
  block(L, 'formula', ++b, { label: 'Уравнение касательной', tex: "y = f(x_0) + f'(x_0)(x - x_0)" });
  block(L, 'diagram', ++b, {
    code: "graph LR\n  A[\"Найти f'(x)\"] --> B[\"Нули f'(x)\"]\n  B --> C[\"Знаки f'(x)\"]\n  C --> D[\"min / max\"]\n  D --> E[\"f''(x) — выпуклость\"]",
    caption: 'Алгоритм исследования функции',
  });
  block(L, 'quiz', ++b, {
    question: 'f(x) = x² − 4x + 3. При каком x минимум?',
    options: ['x = 0', 'x = 2', 'x = 3', 'x = −2'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_CALC, S_C2, 'Первообразная и неопределённый интеграл', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Первообразная' });
  block(L, 'text', ++b, { text: 'F(x) — первообразная f(x), если F\'(x) = f(x). Первообразных бесконечно много: F(x) + C.' });
  block(L, 'formula', ++b, { label: 'Неопределённый интеграл', tex: '\\int f(x)\\, dx = F(x) + C' });
  block(L, 'table', ++b, {
    header: ['Интеграл', 'Результат'],
    rows: [
      ['∫ xⁿ dx', 'xⁿ⁺¹/(n+1) + C'], ['∫ dx/x', 'ln|x| + C'],
      ['∫ eˣ dx', 'eˣ + C'], ['∫ sin x dx', '−cos x + C'],
      ['∫ cos x dx', 'sin x + C'], ['∫ aˣ dx', 'aˣ/ln a + C'],
    ],
  });
  block(L, 'callout', ++b, { style: 'warning', text: 'Проверить интеграл: взять производную результата — должна получиться подынтегральная функция.' });
  block(L, 'quiz', ++b, {
    question: '∫ 3x² dx = ?',
    options: ['6x + C', 'x³ + C', '3x³ + C', 'x² + C'],
    correctIndex: 1,
  });
  block(L, 'flashcard', ++b, { front: '∫ cos x dx = ?', back: "sin x + C\nПроверка: (sin x)' = cos x ✓" });
}

{
  const L = lesson(C_CALC, S_C2, 'Определённый интеграл и площадь', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Определённый интеграл' });
  block(L, 'formula', ++b, { label: 'Формула Ньютона–Лейбница', tex: '\\int_a^b f(x)\\, dx = F(b) - F(a)' });
  block(L, 'text', ++b, { text: 'Геометрический смысл: площадь под графиком f(x) ≥ 0 на [a, b]. Ниже оси — вычитается.' });
  block(L, 'formula', ++b, { label: 'Площадь между двумя кривыми', tex: 'S = \\int_a^b |f(x) - g(x)|\\, dx' });
  block(L, 'table', ++b, {
    header: ['Свойство', 'Запись'],
    rows: [
      ['Линейность', '∫[αf + βg] = α∫f + β∫g'],
      ['Аддитивность', '∫ₐᵇ = ∫ₐᶜ + ∫ᶜᵇ'],
      ['Смена пределов', '∫ₐᵇ = −∫ᵦₐ'],
    ],
  });
  block(L, 'quiz', ++b, {
    question: '∫₀² x dx = ?',
    options: ['1', '2', '4', '½'],
    correctIndex: 1,
  });
  block(L, 'alert', ++b, { style: 'exam', text: 'ЕГЭ профиль: отработайте алгоритм — найти первообразную → подставить пределы → вычесть F(a) из F(b).' });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Теория вероятностей  [math]
   ══════════════════════════════════════════════════════════════ */
const C_PROB = course('math', 'Теория вероятностей и статистика',
  'Классическая вероятность, формулы сложения и умножения, случайные величины', '🎲', 3);
const S_P1 = section(C_PROB, 'Классическая вероятность', 1);
const S_P2 = section(C_PROB, 'Случайные величины', 2);

{
  const L = lesson(C_PROB, S_P1, 'Классическое определение вероятности', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Вероятность событий' });
  block(L, 'formula', ++b, { label: 'Классическая вероятность', tex: 'P(A) = \\dfrac{m}{n}' });
  block(L, 'text', ++b, { text: 'm — число благоприятных исходов, n — общее число равновозможных.\nВсегда: 0 ≤ P(A) ≤ 1.' });
  block(L, 'table', ++b, {
    header: ['Схема', 'Формула', 'Смысл'],
    rows: [
      ['Перестановки', 'Pₙ = n!', 'Расставить n предметов'],
      ['Размещения', 'Aₙᵏ = n!/(n−k)!', 'k из n с учётом порядка'],
      ['Сочетания', 'Cₙᵏ = n!/(k!(n−k)!)', 'k из n без порядка'],
    ],
  });
  block(L, 'accordion', ++b, {
    title: 'Пример: вероятность выпадения 3 при броске кубика',
    content: 'n = 6, m = 1 → P = 1/6 ≈ 0.167',
  });
  block(L, 'quiz', ++b, {
    question: 'В урне 4 белых и 6 чёрных. Вероятность вытащить белый?',
    options: ['0.4', '0.6', '0.25', '0.5'],
    correctIndex: 0,
  });
}

{
  const L = lesson(C_PROB, S_P1, 'Формулы сложения и умножения', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Операции над событиями' });
  block(L, 'formula', ++b, { label: 'Сложение (несовместные)', tex: 'P(A \\cup B) = P(A) + P(B)' });
  block(L, 'formula', ++b, { label: 'Сложение (общий случай)', tex: 'P(A \\cup B) = P(A) + P(B) - P(A \\cap B)' });
  block(L, 'formula', ++b, { label: 'Противоположное событие', tex: 'P(\\bar{A}) = 1 - P(A)' });
  block(L, 'formula', ++b, { label: 'Умножение (независимые)', tex: 'P(A \\cap B) = P(A) \\cdot P(B)' });
  block(L, 'formula', ++b, { label: 'Условная вероятность', tex: 'P(B|A) = \\dfrac{P(A \\cap B)}{P(A)}' });
  block(L, 'quiz', ++b, {
    question: 'Стрелок A попадает с P=0.7, B с P=0.8. Оба стреляют. P(хотя бы одного попадания)?',
    options: ['1.5', '0.56', '0.94', '0.75'],
    correctIndex: 2,
  });
}

{
  const L = lesson(C_PROB, S_P2, 'Случайные величины и математическое ожидание', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Дискретная случайная величина' });
  block(L, 'formula', ++b, { label: 'Математическое ожидание', tex: 'M(X) = \\sum_{i=1}^{n} x_i p_i' });
  block(L, 'formula', ++b, { label: 'Дисперсия', tex: 'D(X) = \\sum_{i}(x_i - M(X))^2 p_i' });
  block(L, 'formula', ++b, { label: 'СКО', tex: '\\sigma = \\sqrt{D(X)}' });
  block(L, 'callout', ++b, { style: 'info', text: 'M(X) — «центр тяжести» распределения. σ имеет те же единицы, что X. Правило трёх сигм: 68% в [μ−σ, μ+σ], 95% в [μ−2σ, μ+2σ], 99.7% в [μ−3σ, μ+3σ].' });
  block(L, 'formula', ++b, { label: 'Нормальное распределение (плотность)', tex: 'f(x) = \\dfrac{1}{\\sigma\\sqrt{2\\pi}} e^{-\\frac{(x-\\mu)^2}{2\\sigma^2}}' });
  block(L, 'quiz', ++b, {
    question: 'X принимает 1, 2, 3 с вероятностями 0.2, 0.5, 0.3. M(X) = ?',
    options: ['1.5', '2.0', '2.1', '1.8'],
    correctIndex: 2,
  });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Механика  [phys]
   ══════════════════════════════════════════════════════════════ */
const C_MECH = course('phys', 'Механика',
  'Кинематика, динамика, законы Ньютона, энергия и импульс', '🚀', 1);
const S_M1 = section(C_MECH, 'Кинематика', 1);
const S_M2 = section(C_MECH, 'Динамика', 2);
const S_M3 = section(C_MECH, 'Законы сохранения', 3);

{
  const L = lesson(C_MECH, S_M1, 'Прямолинейное движение', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Основные понятия кинематики' });
  block(L, 'text', ++b, { text: 'Кинематика описывает движение без анализа причин.\n\nПеремещение s⃗ [м] — вектор от старта к финишу.\nСкорость v⃗ [м/с] — быстрота изменения положения.\nУскорение a⃗ [м/с²] — быстрота изменения скорости.' });
  block(L, 'callout', ++b, { style: 'info', text: 'Путь ≠ перемещение. Путь — длина траектории (скаляр ≥ 0). При движении «туда-обратно» путь > 0, перемещение = 0.' });
  block(L, 'formula', ++b, { label: 'Равномерное движение', tex: 'x = x_0 + v \\cdot t' });
  block(L, 'formula', ++b, { label: 'Скорость при равноускоренном', tex: 'v = v_0 + a \\cdot t' });
  block(L, 'formula', ++b, { label: 'Координата', tex: 'x = x_0 + v_0 t + \\dfrac{a t^2}{2}' });
  block(L, 'formula', ++b, { label: 'Без времени', tex: 'v^2 = v_0^2 + 2a \\cdot s' });
  block(L, 'quiz', ++b, {
    question: 'Автомобиль разгоняется из покоя с a = 2 м/с². Путь за 5 с?',
    options: ['10 м', '25 м', '50 м', '5 м'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_MECH, S_M2, 'Законы Ньютона', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Три закона Ньютона' });
  block(L, 'text', ++b, { text: 'I закон (инерция): тело сохраняет покой или равномерное прямолинейное движение, если равнодействующая сил = 0.' });
  block(L, 'formula', ++b, { label: 'II закон', tex: '\\vec{F} = m \\vec{a}' });
  block(L, 'formula', ++b, { label: 'III закон', tex: '\\vec{F}_{12} = -\\vec{F}_{21}' });
  block(L, 'text', ++b, { text: 'Силы в механике:\n• Тяжесть: F = mg (вниз)\n• Нормальная реакция N ⊥ опоре\n• Трение: F = μN (против движения)\n• Упругость: F = −kx (закон Гука)' });
  block(L, 'quiz', ++b, {
    question: 'К телу m = 4 кг приложена F = 12 Н (без трения). Ускорение?',
    options: ['2 м/с²', '3 м/с²', '4 м/с²', '48 м/с²'],
    correctIndex: 1,
  });
  block(L, 'flashcard', ++b, { front: 'Второй закон Ньютона', back: 'F⃗ = m·a⃗\nУскорение пропорционально силе и обратно пропорционально массе.' });
}

{
  const L = lesson(C_MECH, S_M3, 'Работа и энергия', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Работа и механическая энергия' });
  block(L, 'formula', ++b, { label: 'Работа', tex: 'A = F \\cdot s \\cdot \\cos \\alpha' });
  block(L, 'formula', ++b, { label: 'Кинетическая энергия', tex: 'E_k = \\dfrac{mv^2}{2}' });
  block(L, 'formula', ++b, { label: 'Потенциальная энергия', tex: 'E_p = mgh' });
  block(L, 'formula', ++b, { label: 'Закон сохранения', tex: 'E_k + E_p = \\text{const}' });
  block(L, 'callout', ++b, { style: 'warning', text: 'С трением: E_k1 + E_p1 = E_k2 + E_p2 + Q, где Q — тепловые потери.' });
  block(L, 'quiz', ++b, {
    question: 'Мяч 0.5 кг падает с h = 20 м (g = 10 м/с²). Eₖ у земли?',
    options: ['50 Дж', '100 Дж', '200 Дж', '10 Дж'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_MECH, S_M3, 'Импульс и закон сохранения', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Импульс тела' });
  block(L, 'formula', ++b, { tex: '\\vec{p} = m \\vec{v}' });
  block(L, 'formula', ++b, { label: 'Импульс силы', tex: '\\vec{F} \\cdot \\Delta t = \\Delta \\vec{p}' });
  block(L, 'formula', ++b, { label: 'Закон сохранения импульса', tex: 'm_1 \\vec{v}_1 + m_2 \\vec{v}_2 = m_1 \\vec{v}_1\\prime + m_2 \\vec{v}_2\\prime' });
  block(L, 'columns', ++b, {
    cols: [
      { content: '<strong>Упругий удар</strong><br>Сохраняется и импульс, и кинетическая энергия.' },
      { content: '<strong>Неупругий удар</strong><br>Тела слипаются, сохраняется только импульс.' },
    ],
  });
  block(L, 'quiz', ++b, {
    question: 'Вагон 30 т (v = 2 м/с) сцепляется с неподвижным 20 т. Скорость?',
    options: ['1 м/с', '1.2 м/с', '1.5 м/с', '2 м/с'],
    correctIndex: 1,
  });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Электричество и магнетизм  [phys]
   ══════════════════════════════════════════════════════════════ */
const C_EM = course('phys', 'Электричество и магнетизм',
  'Закон Кулона, электрическое поле, постоянный ток, магнитное поле, индукция', '⚡', 2);
const S_E1 = section(C_EM, 'Электростатика', 1);
const S_E2 = section(C_EM, 'Постоянный ток', 2);
const S_E3 = section(C_EM, 'Магнетизм', 3);

{
  const L = lesson(C_EM, S_E1, 'Закон Кулона и электрическое поле', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Электрический заряд' });
  block(L, 'text', ++b, { text: 'Заряд квантуется: e ≈ 1.6·10⁻¹⁹ Кл. Закон сохранения: суммарный заряд замкнутой системы постоянен.' });
  block(L, 'formula', ++b, { label: 'Закон Кулона', tex: 'F = k \\dfrac{|q_1 q_2|}{r^2}, \\quad k = 9 \\cdot 10^9 \\text{ Н·м}^2/\\text{Кл}^2' });
  block(L, 'formula', ++b, { label: 'Напряжённость поля', tex: 'E = k \\dfrac{Q}{r^2}, \\quad E = \\dfrac{U}{d}' });
  block(L, 'formula', ++b, { label: 'Потенциал', tex: '\\varphi = k \\dfrac{Q}{r}, \\quad U_{AB} = \\varphi_A - \\varphi_B' });
  block(L, 'quiz', ++b, {
    question: 'q₁ = q₂ = 10⁻⁶ Кл, r = 1 м. Сила?',
    options: ['9·10⁻³ Н', '9·10⁻⁶ Н', '9·10⁻² Н', '9 Н'],
    correctIndex: 0,
  });
}

{
  const L = lesson(C_EM, S_E2, 'Постоянный ток. Законы Ома', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Законы Ома' });
  block(L, 'formula', ++b, { label: 'Закон Ома (участок)', tex: 'I = \\dfrac{U}{R}' });
  block(L, 'formula', ++b, { label: 'Закон Ома (полная цепь)', tex: 'I = \\dfrac{\\mathcal{E}}{R + r}' });
  block(L, 'columns', ++b, {
    cols: [
      { content: '<strong>Последовательно:</strong><br>R = R₁ + R₂<br>I одинаковый<br>U = U₁ + U₂' },
      { content: '<strong>Параллельно:</strong><br>1/R = 1/R₁ + 1/R₂<br>U одинаковое<br>I = I₁ + I₂' },
    ],
  });
  block(L, 'formula', ++b, { label: 'Мощность', tex: 'P = UI = I^2R = U^2/R' });
  block(L, 'formula', ++b, { label: 'Закон Джоуля–Ленца', tex: 'Q = I^2Rt' });
  block(L, 'quiz', ++b, {
    question: 'R₁ = 3 Ом и R₂ = 6 Ом параллельно. Общее сопротивление?',
    options: ['9 Ом', '4.5 Ом', '2 Ом', '18 Ом'],
    correctIndex: 2,
  });
}

{
  const L = lesson(C_EM, S_E3, 'Магнитное поле. Сила Лоренца', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Магнитное поле' });
  block(L, 'formula', ++b, { label: 'Сила Лоренца', tex: 'F = qvB\\sin\\alpha' });
  block(L, 'formula', ++b, { label: 'Сила Ампера', tex: 'F = BIl\\sin\\alpha' });
  block(L, 'callout', ++b, { style: 'info', text: 'Направление силы Лоренца — правило левой руки: пальцы по v (+), ладонь на B, большой палец — F.' });
  block(L, 'formula', ++b, { label: 'Радиус кругового движения заряда', tex: 'r = \\dfrac{mv}{qB}' });
  block(L, 'formula', ++b, { label: 'Закон Фарадея (ЭДС индукции)', tex: '\\mathcal{E} = -\\dfrac{\\Delta\\Phi}{\\Delta t}, \\quad \\Phi = BS\\cos\\alpha' });
  block(L, 'callout', ++b, { style: 'warning', text: 'Знак «−» — правило Ленца: индукционный ток противодействует изменению потока.' });
  block(L, 'quiz', ++b, {
    question: 'Протон (q=1.6·10⁻¹⁹, m=1.67·10⁻²⁷ кг) летит v=10⁶ м/с ⊥ полю B=0.1 Тл. Радиус?',
    options: ['≈ 0.1 м', '≈ 0.01 м', '≈ 1 м', '≈ 10 м'],
    correctIndex: 0,
  });
}

{
  const L = lesson(C_EM, S_E3, 'Колебательный контур и переменный ток', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Электромагнитные колебания' });
  block(L, 'formula', ++b, { label: 'Формула Томсона', tex: 'T = 2\\pi\\sqrt{LC}' });
  block(L, 'formula', ++b, { label: 'Действующее значение', tex: 'I_{\\text{rms}} = \\dfrac{I_m}{\\sqrt{2}} \\approx 0.707\\, I_m' });
  block(L, 'callout', ++b, { style: 'info', text: 'Приборы измеряют действующее значение. В розетке 220 В — это RMS, амплитуда ≈ 311 В.' });
  block(L, 'formula', ++b, { label: 'Резонанс (X_L = X_C)', tex: '\\omega_0 = \\dfrac{1}{\\sqrt{LC}}, \\quad Z_{\\min} = R' });
  block(L, 'quiz', ++b, {
    question: 'Контур L=0.1 Гн, C=10 мкФ. Частота?',
    options: ['≈ 159 Гц', '≈ 16 Гц', '≈ 1590 Гц', '≈ 100 Гц'],
    correctIndex: 0,
  });
  block(L, 'flashcard', ++b, { front: 'Формула Томсона', back: 'T = 2π√(LC) — период свободных колебаний контура' });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Оптика и волны  [phys]
   ══════════════════════════════════════════════════════════════ */
const C_OPT = course('phys', 'Оптика и волны',
  'Геометрическая оптика, линзы, волновые явления: интерференция, дифракция', '🔭', 3);
const S_O1 = section(C_OPT, 'Геометрическая оптика', 1);
const S_O2 = section(C_OPT, 'Волновая оптика', 2);

{
  const L = lesson(C_OPT, S_O1, 'Отражение и преломление света', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Законы геометрической оптики' });
  block(L, 'callout', ++b, { style: 'info', text: 'c ≈ 3·10⁸ м/с. В среде с n: v = c/n. Стекло: n ≈ 1.5, вода: n ≈ 1.33.' });
  block(L, 'formula', ++b, { label: 'Закон отражения', tex: '\\alpha_1 = \\alpha_2' });
  block(L, 'formula', ++b, { label: 'Закон Снеллиуса', tex: 'n_1 \\sin\\alpha_1 = n_2 \\sin\\alpha_2' });
  block(L, 'formula', ++b, { label: 'Критический угол полного отражения', tex: '\\sin\\theta_c = \\dfrac{n_2}{n_1}' });
  block(L, 'text', ++b, { text: 'При переходе из более плотной среды (n₁ > n₂) при угле > θc возникает полное внутреннее отражение. На этом работает оптоволокно.' });
  block(L, 'quiz', ++b, {
    question: 'Луч из воды (n=1.33) в воздух (n=1). Критический угол?',
    options: ['sin⁻¹(0.75) ≈ 48.75°', '90°', '45°', '30°'],
    correctIndex: 0,
  });
}

{
  const L = lesson(C_OPT, S_O1, 'Линзы', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Тонкая линза' });
  block(L, 'formula', ++b, { label: 'Формула тонкой линзы', tex: '\\dfrac{1}{f} = \\dfrac{1}{d} + \\dfrac{1}{v}' });
  block(L, 'formula', ++b, { label: 'Увеличение', tex: 'M = \\dfrac{|v|}{d}' });
  block(L, 'table', ++b, {
    header: ['Положение предмета', 'Изображение', 'Применение'],
    rows: [
      ['d > 2f', 'Действит., уменьш., перевёрн.', 'Объективы'],
      ['f < d < 2f', 'Действит., увелич., перевёрн.', 'Проекторы'],
      ['d < f', 'Мнимое, увелич., прямое', 'Лупа, очки'],
    ],
  });
  block(L, 'accordion', ++b, {
    title: 'Пример: f = 10 см, d = 15 см → где изображение?',
    content: '1/v = 1/10 − 1/15 = 1/30 → v = 30 см\nМ = 30/15 = 2 (увеличенное, перевёрнутое)',
  });
  block(L, 'quiz', ++b, {
    question: 'Линза f=10 см, предмет d=15 см. Изображение?',
    options: ['30 см', '−30 см', '20 см', '15 см'],
    correctIndex: 0,
  });
  block(L, 'flashcard', ++b, { front: 'Формула тонкой линзы', back: '1/f = 1/d + 1/v\nf > 0 — собирающая, f < 0 — рассеивающая' });
}

{
  const L = lesson(C_OPT, S_O2, 'Интерференция и дифракция', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Волновые свойства света' });
  block(L, 'text', ++b, { text: 'Свет — поперечная ЭМ волна. Длина волны: фиолетовый ≈ 400 нм, красный ≈ 700 нм.' });
  block(L, 'formula', ++b, { label: 'Максимум интерференции', tex: '\\Delta = m\\lambda, \\quad m = 0, \\pm1, \\pm2, \\ldots' });
  block(L, 'formula', ++b, { label: 'Минимум интерференции', tex: '\\Delta = (2m+1)\\dfrac{\\lambda}{2}' });
  block(L, 'formula', ++b, { label: 'Дифракционная решётка', tex: 'd \\sin\\theta = m\\lambda' });
  block(L, 'formula', ++b, { label: 'Закон Малюса (поляризация)', tex: 'I = I_0 \\cos^2\\theta' });
  block(L, 'quiz', ++b, {
    question: 'Решётка d=2 мкм, λ=600 нм, m=1. Угол θ?',
    options: ['17.5°', '30°', '15°', '45°'],
    correctIndex: 0,
  });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Строение вещества  [chem]
   ══════════════════════════════════════════════════════════════ */
const C_ATOM = course('chem', 'Строение вещества',
  'Атом, электронные оболочки, периодический закон, химическая связь', '⚛️', 1);
const S_A1 = section(C_ATOM, 'Строение атома', 1);
const S_A2 = section(C_ATOM, 'Периодический закон', 2);
const S_A3 = section(C_ATOM, 'Химическая связь', 3);

{
  const L = lesson(C_ATOM, S_A1, 'Модели атома', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Строение атома' });
  block(L, 'text', ++b, { text: 'Атом = ядро (протоны p⁺ + нейтроны n⁰) + электронная оболочка (e⁻).' });
  block(L, 'callout', ++b, { style: 'info', text: 'Протон: заряд +1, масса ≈ 1 а.е.м.\nНейтрон: заряд 0, масса ≈ 1 а.е.м.\nЭлектрон: заряд −1, масса ≈ 1/1836 а.е.м.' });
  block(L, 'formula', ++b, { label: 'Массовое число', tex: 'A = Z + N' });
  block(L, 'text', ++b, { text: 'Z — число протонов (порядковый номер). N — число нейтронов. Изотопы — одинаковый Z, разный N.' });
  block(L, 'quiz', ++b, {
    question: 'Сколько нейтронов в ²⁷₁₃Al?',
    options: ['13', '14', '27', '40'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_ATOM, S_A1, 'Электронные оболочки', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Электронные оболочки атома' });
  block(L, 'text', ++b, { text: 'Электроны занимают энергетические уровни n = 1, 2, 3… Максимум на уровне: 2n².' });
  block(L, 'table', ++b, {
    header: ['Уровень n', 'Обозначение', 'Макс. e⁻', 'Подуровни'],
    rows: [['1', 'K', '2', 's'], ['2', 'L', '8', 's, p'], ['3', 'M', '18', 's, p, d'], ['4', 'N', '32', 's, p, d, f']],
  });
  block(L, 'text', ++b, { text: 'Порядок заполнения: 1s→2s→2p→3s→3p→4s→3d→4p→…\n\nПринцип Паули: ≤ 2 e⁻ на орбитали.\nПравило Гунда: на одном подуровне сначала по одному.\nПравило Клечковского: порядок по (n+l).' });
  block(L, 'callout', ++b, { style: 'success', text: 'Углерод (Z=6): 1s² 2s² 2p² — на 2p два электрона на разных орбиталях (правило Гунда).' });
  block(L, 'quiz', ++b, {
    question: 'Электронная конфигурация Na (Z=11)?',
    options: ['1s² 2s² 2p⁶ 3s¹', '1s² 2s² 2p⁷', '1s² 2s² 2p⁶ 3p¹', '1s² 2s² 3s² 3p⁵'],
    correctIndex: 0,
  });
  block(L, 'flashcard', ++b, { front: 'Макс. e⁻ на 3-м уровне?', back: '2n² = 18\n3s²(2) + 3p⁶(6) + 3d¹⁰(10) = 18' });
}

{
  const L = lesson(C_ATOM, S_A2, 'Периодический закон Менделеева', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Периодический закон' });
  block(L, 'text', ++b, { text: 'Свойства элементов периодически зависят от заряда ядра.\n\nПериод — строка (номер = число уровней). Группа — столбец (сходные свойства).' });
  block(L, 'columns', ++b, {
    cols: [
      { content: '<strong>В периоде (→):</strong><br>Z растёт<br>Радиус ↓<br>ЭО ↑<br>Металл. свойства ↓' },
      { content: '<strong>В группе (↓):</strong><br>Радиус ↑<br>ЭО ↓<br>Металл. свойства ↑' },
    ],
  });
  block(L, 'callout', ++b, { style: 'success', text: 'Самый электроотрицательный элемент — F (правый верхний). Самый активный металл — Fr (левый нижний).' });
  block(L, 'quiz', ++b, {
    question: 'Какой элемент имеет наибольший атомный радиус: Li, Na, K, Rb?',
    options: ['Li', 'Na', 'K', 'Rb'],
    correctIndex: 3,
  });
}

{
  const L = lesson(C_ATOM, S_A3, 'Типы химической связи', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Химическая связь' });
  block(L, 'table', ++b, {
    header: ['Тип', 'Механизм', 'Пример', 'Прочность'],
    rows: [
      ['Ковалентная неполярная', 'Обобществление e⁻ (одинак. атомы)', 'H₂, Cl₂', 'Высокая'],
      ['Ковалентная полярная', 'Обобществление e⁻ (разные неМе)', 'HCl, H₂O', 'Высокая'],
      ['Ионная', 'Передача e⁻ (Ме+неМе)', 'NaCl, CaO', 'Высокая'],
      ['Металлическая', 'Электронный газ', 'Fe, Cu', 'Средняя'],
      ['Водородная', 'H···F/O/N', 'H₂O···H₂O', 'Низкая'],
    ],
  });
  block(L, 'callout', ++b, { style: 'info', text: 'Водородная связь объясняет аномально высокую t° кипения воды (100°C против −80°C у H₂S).' });
  block(L, 'quiz', ++b, {
    question: 'Тип связи в NaCl?',
    options: ['Ковалентная неполярная', 'Ковалентная полярная', 'Ионная', 'Металлическая'],
    correctIndex: 2,
  });
  block(L, 'flashcard', ++b, { front: 'Чем отличается ковалентная полярная от неполярной?', back: 'Полярная: e⁻ пара смещена к более ЭО атому (разные эл-ты).\nНеполярная: e⁻ пара симметрична (одинаковые эл-ты).' });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Органическая химия  [chem]
   ══════════════════════════════════════════════════════════════ */
const C_ORG = course('chem', 'Органическая химия',
  'Углеводороды, кислородсодержащие соединения, биополимеры', '🧪', 2);
const S_G1 = section(C_ORG, 'Углеводороды', 1);
const S_G2 = section(C_ORG, 'Кислородсодержащие', 2);
const S_G3 = section(C_ORG, 'Биополимеры', 3);

{
  const L = lesson(C_ORG, S_G1, 'Алканы — предельные углеводороды', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Алканы CₙH₂ₙ₊₂' });
  block(L, 'formula', ++b, { tex: 'C_n H_{2n+2}' });
  block(L, 'text', ++b, { text: 'Все связи C–C и C–H одинарные (σ-связи). Метан CH₄, этан C₂H₆, пропан C₃H₈, бутан C₄H₁₀…\n\nС C₄ возможна структурная изомерия.' });
  block(L, 'text', ++b, { text: 'Химические свойства:\n1. Горение: CH₄ + 2O₂ → CO₂ + 2H₂O\n2. Замещение: CH₄ + Cl₂ → CH₃Cl + HCl (свет, SR)\n3. Крекинг: C₁₀H₂₂ → C₅H₁₂ + C₅H₁₀' });
  block(L, 'callout', ++b, { style: 'warning', text: 'Реакции присоединения (AE) для алканов НЕВОЗМОЖНЫ — нет π-связей. Это главное отличие от алкенов.' });
  block(L, 'quiz', ++b, {
    question: 'Общая формула алканов?',
    options: ['CₙHₙ', 'CₙH₂ₙ', 'CₙH₂ₙ₊₂', 'CₙH₂ₙ₋₂'],
    correctIndex: 2,
  });
  block(L, 'flashcard', ++b, { front: 'Алканы / алкены / алкины — общие формулы?', back: 'Алканы: CₙH₂ₙ₊₂\nАлкены: CₙH₂ₙ\nАлкины: CₙH₂ₙ₋₂' });
}

{
  const L = lesson(C_ORG, S_G1, 'Алкены и алкины', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Ненасыщенные углеводороды' });
  block(L, 'formula', ++b, { label: 'Алкены', tex: 'C_n H_{2n}' });
  block(L, 'formula', ++b, { label: 'Алкины', tex: 'C_n H_{2n-2}' });
  block(L, 'text', ++b, { text: 'Реакции присоединения алкенов:\n1. Гидрирование: CH₂=CH₂ + H₂ → C₂H₆ (Ni, t°)\n2. Галогенирование: CH₂=CH₂ + Br₂ → CH₂BrCH₂Br (обесцвечивает бромную воду!)\n3. Гидратация: CH₂=CH₂ + H₂O → C₂H₅OH\n4. Полимеризация: nCH₂=CH₂ → (CH₂–CH₂)ₙ' });
  block(L, 'callout', ++b, { style: 'info', text: 'Правило Марковникова: H присоединяется к более гидрогенизированному углероду несимметричного алкена.' });
  block(L, 'quiz', ++b, {
    question: 'Какая реакция невозможна для алканов?',
    options: ['Горение', 'Замещение', 'Присоединение', 'Крекинг'],
    correctIndex: 2,
  });
}

{
  const L = lesson(C_ORG, S_G2, 'Спирты и карбоновые кислоты', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Кислородсодержащие соединения' });
  block(L, 'matching', ++b, {
    question: 'Класс → функциональная группа:',
    pairs: [
      { left: 'Спирт', right: '–OH' },
      { left: 'Альдегид', right: '–CHO' },
      { left: 'Кислота', right: '–COOH' },
      { left: 'Амин', right: '–NH₂' },
    ],
  });
  block(L, 'text', ++b, { text: 'Свойства спиртов:\n1. + Na: 2C₂H₅OH + 2Na → 2C₂H₅ONa + H₂↑\n2. Окисление: C₂H₅OH → CH₃CHO → CH₃COOH\n3. Дегидратация (140°): → эфир\n4. Дегидратация (170°): C₂H₅OH → CH₂=CH₂ + H₂O' });
  block(L, 'text', ++b, { text: 'Этерификация (кислота + спирт):\nCH₃COOH + C₂H₅OH ⇌ CH₃COOC₂H₅ + H₂O (кат. H₂SO₄)\n\nРеакция обратима. Продукт — сложный эфир.' });
  block(L, 'quiz', ++b, {
    question: 'При дегидратации C₂H₅OH при 170°C образуется:',
    options: ['Эфир', 'CH₂=CH₂', 'Ацетальдегид', 'Ацетилен'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_ORG, S_G3, 'Углеводы и белки', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Биополимеры' });
  block(L, 'formula', ++b, { label: 'Углеводы', tex: 'C_n(H_2O)_m' });
  block(L, 'text', ++b, { text: 'Моносахариды: глюкоза и фруктоза C₆H₁₂O₆ (изомеры).\nДисахариды: сахароза C₁₂H₂₂O₁₁.\nПолисахариды: крахмал, целлюлоза (C₆H₁₀O₅)ₙ.\n\nБрожение: C₆H₁₂O₆ → 2C₂H₅OH + 2CO₂' });
  block(L, 'text', ++b, { text: 'Белки — полимеры из аминокислот, связанных пептидными связями (–CO–NH–).\n\nСтруктуры: первичная (последовательность), вторичная (α-спираль), третичная (3D-форма), четвертичная (несколько цепей).' });
  block(L, 'callout', ++b, { style: 'warning', text: 'Денатурация — разрушение вторичной и выше структур (не первичной). Причины: нагрев, кислоты, щёлочи. Необратима.' });
  block(L, 'quiz', ++b, {
    question: 'Какая связь соединяет аминокислоты в белке?',
    options: ['Ионная', 'Водородная', 'Пептидная (–CO–NH–)', 'Дисульфидная'],
    correctIndex: 2,
  });
  block(L, 'timeline', ++b, {
    items: [
      { date: '1869', title: 'ДНК выделена Мишером', text: 'Из лейкоцитов выделено «нуклеиновое вещество».' },
      { date: '1944', title: 'ДНК — носитель наследственности', text: 'Эйвери показал: именно ДНК передаёт наследственную информацию.' },
      { date: '1953', title: 'Двойная спираль', text: 'Уотсон и Крик предложили модель двойной спирали ДНК.' },
    ],
  });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Клетка и генетика  [bio]
   ══════════════════════════════════════════════════════════════ */
const C_BIO = course('bio', 'Клетка и генетика',
  'Строение клетки, фотосинтез, клеточное дыхание, законы Менделя, ДНК и синтез белка', '🧬', 1);
const S_B1 = section(C_BIO, 'Строение клетки', 1);
const S_B2 = section(C_BIO, 'Обмен веществ', 2);
const S_B3 = section(C_BIO, 'Генетика', 3);

{
  const L = lesson(C_BIO, S_B1, 'Строение эукариотической клетки', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Строение клетки' });
  block(L, 'callout', ++b, { style: 'info', text: 'Прокариоты (бактерии) — нет ядра и мембранных органоидов. Эукариоты (животные, растения, грибы) — есть ядро.' });
  block(L, 'table', ++b, {
    header: ['Органоид', 'Функция'],
    rows: [
      ['Клеточная мембрана', 'Барьер, транспорт, рецепция'],
      ['Ядро', 'Хранение ДНК, регуляция'],
      ['Митохондрии', 'Синтез АТФ (дыхание)'],
      ['Рибосомы', 'Синтез белков'],
      ['ЭПС шероховатая', 'Синтез и транспорт белков'],
      ['Аппарат Гольджи', 'Модификация, упаковка белков'],
      ['Лизосомы', 'Переваривание (аутолиз)'],
      ['Хлоропласты', 'Фотосинтез (только у растений)'],
    ],
  });
  block(L, 'diagram', ++b, {
    code: "graph LR\n  A[Профаза] --> B[Метафаза] --> C[Анафаза] --> D[Телофаза]\n  D --> E[2 дочерние клетки 2n]",
    caption: 'Фазы митоза',
  });
  block(L, 'quiz', ++b, {
    question: 'Какой органоид синтезирует АТФ?',
    options: ['Рибосома', 'Хлоропласт', 'Митохондрия', 'Лизосома'],
    correctIndex: 2,
  });
  block(L, 'flashcard', ++b, { front: 'Митоз vs мейоз?', back: 'Митоз: 2n → 2n (соматические, идентичные)\nМейоз: 2n → n (гаметы, 2 деления, кроссинговер)' });
}

{
  const L = lesson(C_BIO, S_B2, 'Фотосинтез и клеточное дыхание', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Энергетический обмен клетки' });
  block(L, 'formula', ++b, { label: 'Фотосинтез', tex: '6CO_2 + 6H_2O \\xrightarrow{h\\nu} C_6H_{12}O_6 + 6O_2' });
  block(L, 'columns', ++b, {
    cols: [
      { content: '<strong>Световые реакции</strong> (тилакоиды):<br>Фотолиз воды → O₂<br>АТФ (фотофосфорилирование)<br>НАДФН' },
      { content: '<strong>Тёмновые реакции</strong> (строма, цикл Калвина):<br>Фиксация CO₂<br>Синтез глюкозы<br>Расход АТФ и НАДФН' },
    ],
  });
  block(L, 'formula', ++b, { label: 'Клеточное дыхание', tex: 'C_6H_{12}O_6 + 6O_2 \\rightarrow 6CO_2 + 6H_2O + 38\\,\\text{АТФ}' });
  block(L, 'table', ++b, {
    header: ['Этап', 'Место', 'АТФ'],
    rows: [
      ['Гликолиз', 'Цитоплазма', '2'],
      ['Цикл Кребса', 'Матрикс митохондрий', '2'],
      ['Окислит. фосфорилирование', 'Внутр. мембрана митохондрий', '34'],
    ],
  });
  block(L, 'callout', ++b, { style: 'info', text: 'Без O₂: анаэробный гликолиз → 2 АТФ. Аэробное дыхание → 38 АТФ, в 19 раз эффективнее!' });
  block(L, 'quiz', ++b, {
    question: 'На каком этапе дыхания выделяется CO₂?',
    options: ['Гликолиз', 'Цикл Кребса', 'Окислительное фосфорилирование', 'Все этапы'],
    correctIndex: 1,
  });
}

{
  const L = lesson(C_BIO, S_B3, 'Законы Менделя', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Генетика наследственности' });
  block(L, 'text', ++b, { text: 'Ген — участок ДНК, кодирующий признак. Аллели — варианты гена. Доминантный (A) подавляет рецессивный (a). Генотип — набор генов, фенотип — его проявление.' });
  block(L, 'table', ++b, {
    header: ['Скрещивание', 'Расщепление F₂ по фенотипу'],
    rows: [
      ['Aa × Aa (моногибридное)', '3:1'],
      ['AaBb × AaBb (дигибридное)', '9:3:3:1'],
      ['Aa × Aa (неполное доминирование)', '1:2:1'],
      ['Aa × aa (анализирующее)', '1:1'],
    ],
  });
  block(L, 'callout', ++b, { style: 'warning', text: 'III закон (независимое наследование) нарушается при сцеплении генов — если они в одной хромосоме.' });
  block(L, 'quiz', ++b, {
    question: 'Скрещивание Aa × Aa. Вероятность фенотипа aa?',
    options: ['25%', '50%', '75%', '100%'],
    correctIndex: 0,
  });
  block(L, 'flashcard', ++b, { front: 'II закон Менделя (расщепление)', back: 'Aa × Aa:\nФенотип: 3 доминантных : 1 рецессивный\nГенотип: 1 AA : 2 Aa : 1 aa' });
}

{
  const L = lesson(C_BIO, S_B3, 'ДНК и синтез белка', 4);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Молекула ДНК и реализация наследственности' });
  block(L, 'text', ++b, { text: 'ДНК — двойная спираль. Принцип комплементарности: A–T (2 связи), G–C (3 связи).\nПравило Чаргаффа: %A = %T, %G = %C.' });
  block(L, 'columns', ++b, {
    cols: [
      { content: '<strong>Транскрипция</strong> (ядро):<br>ДНК → мРНК<br>РНК-полимераза<br>T заменяется на U' },
      { content: '<strong>Трансляция</strong> (рибосомы):<br>мРНК → белок<br>Кодон = 3 нуклеотида<br>Код универсален' },
    ],
  });
  block(L, 'formula', ++b, { label: 'Нуклеотиды ↔ аминокислоты', tex: 'N_{\\text{нукл}} = 3 \\times N_{\\text{а.к.}}' });
  block(L, 'diagram', ++b, {
    code: "graph LR\n  DNA[ДНК] -->|Репликация| DNA2[ДНК-копия]\n  DNA -->|Транскрипция| mRNA[мРНК]\n  mRNA -->|Трансляция| P[Белок]",
    caption: 'Центральная догма молекулярной биологии',
  });
  block(L, 'quiz', ++b, {
    question: 'В ДНК 30% аденина. Сколько % тимина?',
    options: ['30%', '20%', '70%', '60%'],
    correctIndex: 0,
  });
  block(L, 'alert', ++b, { style: 'exam', text: 'В мРНК нет тимина — только урацил. При транскрипции: A→U, T→A, G→C, C→G.' });
}

/* ══════════════════════════════════════════════════════════════
   КУРС: Алгоритмы и структуры данных  [inf]
   ══════════════════════════════════════════════════════════════ */
const C_ALG = course('inf', 'Алгоритмы и структуры данных',
  'Сложность, сортировки, деревья и графы', '💻', 1);
const S_L1 = section(C_ALG, 'Основы', 1);
const S_L2 = section(C_ALG, 'Сортировки', 2);
const S_L3 = section(C_ALG, 'Структуры данных', 3);

{
  const L = lesson(C_ALG, S_L1, 'Сложность алгоритмов (O-нотация)', 1);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Big-O нотация' });
  block(L, 'text', ++b, { text: 'O-нотация описывает верхнюю оценку роста времени выполнения при n→∞. Константы и малые слагаемые игнорируются: O(2n) = O(n).' });
  block(L, 'table', ++b, {
    header: ['Сложность', 'Название', 'Пример'],
    rows: [
      ['O(1)', 'Константная', 'Доступ к элементу массива'],
      ['O(log n)', 'Логарифмическая', 'Бинарный поиск'],
      ['O(n)', 'Линейная', 'Линейный поиск'],
      ['O(n log n)', 'Квазилинейная', 'Быстрая сортировка'],
      ['O(n²)', 'Квадратичная', 'Сортировка пузырьком'],
      ['O(2ⁿ)', 'Экспоненциальная', 'Перебор подмножеств'],
    ],
  });
  block(L, 'code', ++b, {
    lang: 'python',
    code: `# O(log n) — бинарный поиск
def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:   return mid
        elif arr[mid] < target:  left = mid + 1
        else:                    right = mid - 1
    return -1`,
  });
  block(L, 'quiz', ++b, {
    question: 'Два вложенных цикла по n элементам — сложность?',
    options: ['O(n)', 'O(n log n)', 'O(n²)', 'O(2ⁿ)'],
    correctIndex: 2,
  });
  block(L, 'flashcard', ++b, { front: 'O(n log n) vs O(n²) при n=1000?', back: 'O(n log n) ≈ 10 000 оп.\nO(n²) = 1 000 000 оп.\nРазница в 100 раз!' });
}

{
  const L = lesson(C_ALG, S_L2, 'Алгоритмы сортировки', 2);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Сравнение алгоритмов сортировки' });
  block(L, 'table', ++b, {
    header: ['Алгоритм', 'Лучшее', 'Среднее', 'Худшее', 'Память'],
    rows: [
      ['Пузырьковая', 'O(n)', 'O(n²)', 'O(n²)', 'O(1)'],
      ['Вставками', 'O(n)', 'O(n²)', 'O(n²)', 'O(1)'],
      ['Слиянием', 'O(n log n)', 'O(n log n)', 'O(n log n)', 'O(n)'],
      ['Быстрая', 'O(n log n)', 'O(n log n)', 'O(n²)', 'O(log n)'],
      ['Кучей', 'O(n log n)', 'O(n log n)', 'O(n log n)', 'O(1)'],
    ],
  });
  block(L, 'code', ++b, {
    lang: 'python',
    code: `def quicksort(arr):
    if len(arr) <= 1: return arr
    pivot = arr[len(arr) // 2]
    left  = [x for x in arr if x < pivot]
    mid   = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quicksort(left) + mid + quicksort(right)`,
  });
  block(L, 'callout', ++b, { style: 'warning', text: 'QuickSort в худшем случае (уже отсортированный + крайний pivot) деградирует до O(n²). Решение: случайный pivot.' });
  block(L, 'ordering', ++b, {
    question: 'Шаги сортировки слиянием в правильном порядке:',
    items: [
      'Разделить массив на две половины',
      'Рекурсивно отсортировать левую',
      'Рекурсивно отсортировать правую',
      'Слить две отсортированные половины',
    ],
  });
  block(L, 'quiz', ++b, {
    question: 'Какая сортировка гарантирует O(n log n) в худшем случае без доп. памяти?',
    options: ['Быстрая', 'Слиянием', 'Кучей (HeapSort)', 'Пузырьковая'],
    correctIndex: 2,
  });
}

{
  const L = lesson(C_ALG, S_L3, 'Деревья и графы', 3);
  let b = 0;
  block(L, 'heading', ++b, { level: 2, text: 'Бинарное дерево поиска (BST)' });
  block(L, 'text', ++b, { text: 'В BST: всё левое поддерево < узел < всё правое. Поиск/вставка/удаление: O(h). У сбалансированного дерева h = O(log n).' });
  block(L, 'code', ++b, {
    lang: 'python',
    code: `class Node:
    def __init__(self, val):
        self.val = val
        self.left = self.right = None

def insert(root, val):
    if not root: return Node(val)
    if val < root.val: root.left  = insert(root.left,  val)
    else:              root.right = insert(root.right, val)
    return root

def inorder(root):     # обход: лево → корень → право
    if root:
        inorder(root.left)
        print(root.val, end=' ')
        inorder(root.right)`,
  });
  block(L, 'heading', ++b, { level: 3, text: 'Графы' });
  block(L, 'table', ++b, {
    header: ['Алгоритм', 'Задача', 'Сложность'],
    rows: [
      ['BFS', 'Кратчайший путь (без весов)', 'O(V+E)'],
      ['DFS', 'Обход, топосортировка', 'O(V+E)'],
      ['Dijkstra', 'Кратчайший путь (≥0 веса)', 'O((V+E) log V)'],
      ['Bellman–Ford', 'Кратчайший путь (любые веса)', 'O(V·E)'],
    ],
  });
  block(L, 'diagram', ++b, {
    code: "graph TD\n  A((A)) -- 4 --> B((B))\n  A -- 2 --> C((C))\n  B -- 1 --> D((D))\n  C -- 5 --> D\n  B -- 3 --> E((E))\n  D -- 1 --> E",
    caption: 'Взвешенный граф',
  });
  block(L, 'quiz', ++b, {
    question: 'Какой алгоритм находит кратчайшие пути при отрицательных весах рёбер?',
    options: ['Dijkstra', 'BFS', 'Bellman–Ford', 'DFS'],
    correctIndex: 2,
  });
  block(L, 'flashcard', ++b, { front: 'BFS vs DFS?', back: 'BFS — очередь, обход по уровням, кратчайший путь (без весов).\nDFS — стек/рекурсия, идёт вглубь, топосортировка, компоненты связности.' });
}

// ── Итог ──────────────────────────────────────────────────────────────
console.log(`✓ Theory seed complete:
  [math] Тригонометрия              (id=${C_TRIG}) — 4 урока
  [math] Производные и интегралы    (id=${C_CALC}) — 4 урока
  [math] Теория вероятностей        (id=${C_PROB}) — 3 урока
  [phys] Механика                   (id=${C_MECH}) — 4 урока
  [phys] Электричество и магнетизм  (id=${C_EM})   — 4 урока
  [phys] Оптика и волны             (id=${C_OPT})  — 3 урока
  [chem] Строение вещества          (id=${C_ATOM}) — 4 урока
  [chem] Органическая химия         (id=${C_ORG})  — 4 урока
  [bio]  Клетка и генетика          (id=${C_BIO})  — 4 урока
  [inf]  Алгоритмы                  (id=${C_ALG})  — 3 урока
  Итого: 37 уроков`);

})(); // end transaction
