#!/usr/bin/env node
'use strict';
/* Генератор stub-файлов для Физики 11 (W0).
 * Запуск: node backend/scripts/gen_phys11_stubs.js
 */
const fs = require('fs');
const path = require('path');

const OUT = path.join(__dirname, '..', '..', 'frontend', 'textbooks');

const CHAPTERS = [
  { n:1, slug:'physics-11-ch1', name:'Механические колебания и волны',
    paraRange:'§1–§6', wm:'∿', themeName:'cyan',
    gradient:['#155e75','#0891b2','#a5f3fc'],
    pri:'#0891b2', pri2:'#0e7490', priSoft:'#cffafe',
    desc:'Колебательное движение, гармонические колебания, маятники, превращения энергии, резонанс, продольные и поперечные волны, звук.',
    paras:[
      {n:1, title:'Колебательное движение. Гармонические колебания', sub:'$T = \\Delta t / N$, $\\nu = 1/T$, $\\omega = 2\\pi/T$, $x = A\\cos(\\omega t + \\varphi_0)$'},
      {n:2, title:'Пружинный и математический маятники', sub:'$T_{пр} = 2\\pi\\sqrt{m/k}$, $T_{мат} = 2\\pi\\sqrt{l/g}$'},
      {n:3, title:'Превращения энергии при гарм. колебаниях', sub:'$W_{мех} = kA^2/2 = m\\omega^2 A^2/2$'},
      {n:4, title:'Свободные и вынужденные колебания. Резонанс', sub:'Затухание, диссипация, $\\omega_{рез} \\approx \\omega_0$'},
      {n:5, title:'Распространение колебаний в упругой среде. Продольные и поперечные волны', sub:'$\\lambda = vT$'},
      {n:6, title:'Звуковые волны', sub:'16 Гц – 20 кГц, $v_{зв}^{возд} \\approx 340$ м/с'}
    ]
  },
  { n:2, slug:'physics-11-ch2', name:'Электромагнитные колебания и волны',
    paraRange:'§7–§13', wm:'⚡', themeName:'violet',
    gradient:['#5b21b6','#7c3aed','#c4b5fd'],
    pri:'#7c3aed', pri2:'#5b21b6', priSoft:'#ede9fe',
    desc:'Колебательный контур, формула Томсона, переменный ток, трансформатор, передача электроэнергии, ЭМ волны.',
    paras:[
      {n:7, title:'Колебательный контур. Свободные ЭМ колебания. Формула Томсона', sub:'$T = 2\\pi\\sqrt{LC}$'},
      {n:8, title:'Вынужденные ЭМ колебания. Переменный ток', sub:'$i = I_0\\sin(\\omega t)$, $I = I_0/\\sqrt{2}$'},
      {n:9, title:'Преобразование переменного тока. Трансформатор', sub:'$k = N_1/N_2 = U_1/U_2$'},
      {n:10, title:'Производство, передача и потребление электроэнергии', sub:'ГЭС, ТЭС, АЭС; потери $P = I^2 R$'},
      {n:11, title:'Экологические проблемы производства и передачи электроэнергии', sub:'ВЭС, СЭС, гео- и приливные'},
      {n:12, title:'ЭМ волны. Шкала ЭМ волн', sub:'$c = 3 \\cdot 10^8$ м/с'},
      {n:13, title:'Действие ЭМ излучения на живые организмы', sub:'Ионизирующее vs неионизирующее'}
    ]
  },
  { n:3, slug:'physics-11-ch3', name:'Оптика',
    paraRange:'§14–§23', wm:'◇', themeName:'amber',
    gradient:['#b45309','#d97706','#fcd34d'],
    pri:'#d97706', pri2:'#b45309', priSoft:'#fef3c7',
    desc:'Электромагнитная природа света, интерференция, дифракция, отражение, зеркала, преломление, тонкая линза, оптические приборы.',
    paras:[
      {n:14, title:'ЭМ природа света. Скорость света', sub:'Опыты Рёмера, Майкельсона'},
      {n:15, title:'Интерференция света', sub:'$\\Delta = k\\lambda$ (max), $\\Delta = (2k+1)\\lambda/2$ (min)'},
      {n:16, title:'Принцип Гюйгенса – Френеля. Дифракция. Дифракционная решётка', sub:'$d\\sin\\varphi = k\\lambda$'},
      {n:17, title:'Прямолинейное распространение и отражение света. Зеркала', sub:'$\\angle_{пад} = \\angle_{отр}$'},
      {n:18, title:'Сферические зеркала. Построение изображений', sub:'$\\frac{1}{F} = \\frac{1}{d} + \\frac{1}{f}$'},
      {n:19, title:'Закон преломления света. Полное отражение', sub:'$n_1\\sin\\alpha = n_2\\sin\\beta$, $\\sin\\alpha_{пр} = 1/n$'},
      {n:20, title:'Прохождение света через оптические элементы', sub:'Призмы, оптоволокно'},
      {n:21, title:'Формула тонкой линзы', sub:'$D = 1/F$, $\\Gamma = f/d$'},
      {n:22, title:'Оптические приборы для действительных изображений', sub:'Фотоаппарат, проектор'},
      {n:23, title:'Оптические приборы для увеличения угла зрения', sub:'Лупа, микроскоп, телескоп'}
    ]
  },
  { n:4, slug:'physics-11-ch4', name:'Основы СТО',
    paraRange:'§24–§26', wm:'c', themeName:'blue',
    gradient:['#1e3a8a','#2563eb','#93c5fd'],
    pri:'#2563eb', pri2:'#1d4ed8', priSoft:'#dbeafe',
    desc:'Принцип относительности Галилея, постулаты Эйнштейна, преобразования Лоренца, релятивистская динамика, E=mc².',
    paras:[
      {n:24, title:'Принцип относ. Галилея и ЭМ явления. Эксп. предпосылки СТО', sub:'Опыт Майкельсона – Морли'},
      {n:25, title:'Постулаты специальной теории относительности', sub:'$\\Delta t = \\gamma\\Delta t_0$, $l = l_0/\\gamma$'},
      {n:26, title:'Элементы релятивистской динамики. Взаимосвязь массы и энергии', sub:'$E_0 = mc^2$, $E^2 = (mc^2)^2 + (pc)^2$'}
    ]
  },
  { n:5, slug:'physics-11-ch5', name:'Фотоны. Действия света',
    paraRange:'§27–§29', wm:'γ', themeName:'pink',
    gradient:['#831843','#db2777','#fbcfe8'],
    pri:'#db2777', pri2:'#9d174d', priSoft:'#fce7f3',
    desc:'Фотоэффект, квантовая гипотеза Планка, фотон, уравнение Эйнштейна, давление света, корпускулярно-волновой дуализм.',
    paras:[
      {n:27, title:'Фотоэффект. Эксперим. законы. Квантовая гипотеза Планка', sub:'$E = h\\nu$, $h = 6{,}63 \\cdot 10^{-34}$ Дж·с'},
      {n:28, title:'Фотон. Уравнение Эйнштейна для фотоэффекта', sub:'$h\\nu = A_{вых} + \\frac{mv_{max}^2}{2}$'},
      {n:29, title:'Давление света. Корпускулярно-волновой дуализм', sub:'$p_{фот} = h\\nu/c$. Опыт Лебедева'}
    ]
  },
  { n:6, slug:'physics-11-ch6', name:'Физика атома',
    paraRange:'§30–§34', wm:'⚛', themeName:'emerald',
    gradient:['#065f46','#10b981','#a7f3d0'],
    pri:'#10b981', pri2:'#047857', priSoft:'#d1fae5',
    desc:'Ядерная модель атома Резерфорда, квантовые постулаты Бора, спектры испускания и поглощения, лазеры.',
    paras:[
      {n:30, title:'Сложное строение атома. Ядерная модель атома', sub:'Опыт Резерфорда, размер ядра $\\sim 10^{-15}$ м'},
      {n:31, title:'Квантовые постулаты Бора', sub:'$E_n = -E_1/n^2 = -13{,}6/n^2$ эВ'},
      {n:32, title:'Излучение и поглощение света атомом. Спектры', sub:'$h\\nu = E_n - E_m$, линейчатые спектры'},
      {n:33, title:'Спонтанное и индуцированное излучение', sub:'Подготовка к лазерам'},
      {n:34, title:'Лазеры', sub:'Инверсная населённость, когерентность'}
    ]
  },
  { n:7, slug:'physics-11-ch7', name:'Ядерная физика и элементарные частицы',
    paraRange:'§35–§44', wm:'☢', themeName:'rose',
    gradient:['#7f1d1d','#dc2626','#fca5a5'],
    pri:'#dc2626', pri2:'#991b1b', priSoft:'#fee2e2',
    desc:'Протонно-нейтронная модель ядра, ядерные реакции, энергия связи, радиоактивность, ядерный реактор, термояд, элементарные частицы.',
    paras:[
      {n:35, title:'Протонно-нейтронная модель строения ядра атома', sub:'$A = Z + N$, изотопы'},
      {n:36, title:'Ядерные реакции. Законы сохранения в ядерных реакциях', sub:'Сохранение заряда, нуклонов, энергии'},
      {n:37, title:'Энергия связи ядра атома', sub:'$E_{св} = \\Delta m \\cdot c^2$, $\\Delta m = Zm_p + Nm_n - m_я$'},
      {n:38, title:'Радиоактивность', sub:'$\\alpha$, $\\beta$, $\\gamma$ распады'},
      {n:39, title:'Закон радиоактивного распада', sub:'$N = N_0 \\cdot 2^{-t/T}$, период полураспада $T$'},
      {n:40, title:'Деление тяжёлых ядер. Цепные ядерные реакции', sub:'$^{235}$U, $k$ — коэф. размножения'},
      {n:41, title:'Ядерный реактор', sub:'Управляющие стержни, замедлитель'},
      {n:42, title:'Реакции ядерного синтеза', sub:'Термояд, $^2$H + $^3$H $\\to ^4$He + n'},
      {n:43, title:'Ионизирующее излучение. Элементы дозиметрии', sub:'Доза $D$, эквивалент $H$, зиверт'},
      {n:44, title:'Элементарные частицы и их взаимодействия', sub:'Стандартная модель, 4 фундаментальных взаимодействия'}
    ]
  },
  { n:8, slug:'physics-11-ch8', name:'Основы единой физической картины мира',
    paraRange:'§45', wm:'∞', themeName:'indigo',
    gradient:['#3730a3','#6366f1','#c7d2fe'],
    pri:'#6366f1', pri2:'#4338ca', priSoft:'#e0e7ff',
    desc:'Современная естественнонаучная картина мира, эволюция физических теорий, четыре фундаментальных взаимодействия.',
    paras:[
      {n:45, title:'Современная естественнонаучная картина мира', sub:'Эволюция представлений: механика → ЭМ → квант'}
    ]
  }
];

function makeChapter(c){
  /* В какой волне будет реализована эта глава (см. PLAN_FIZIKA_11.md) */
  const waveOf = {1:'W1-W2', 2:'W3-W4', 3:'W5-W7', 4:'W8', 5:'W9', 6:'W10-W11', 7:'W12-W13', 8:'W14'};
  const wave = waveOf[c.n] || 'W1+';
  const parasHtml = c.paras.map(p => `
    <article class="para-card">
      <div class="para-num">§ ${p.n}</div>
      <div class="para-body">
        <h2 class="para-title">${p.title}</h2>
        <p class="para-sub">${p.sub}</p>
        <div class="para-status">
          <svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
          Будет добавлено в волне ${wave}
        </div>
      </div>
    </article>`).join('\n');

  return `<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Физика 11 · Глава ${c.n} · ${c.name}</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@400;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
  onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\\\[',right:'\\\\]',display:true},{left:'\\\\(',right:'\\\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/phys-fx.js?v=1" defer></script>
<style>
:root{
  --bg:#f8fafc; --card:#fff;
  --text:#0f172a; --muted:#475569;
  --border:#e2e8f0;
  --pri:${c.pri}; --pri-d:${c.pri2};
  --pri-soft:${c.priSoft};
  --dark:${c.gradient[0]};
  --sh:0 4px 16px rgba(0,0,0,.06);
}
html.dark{
  --bg:#020617; --card:#0a1929;
  --text:#dbeafe; --muted:#94a3b8;
  --border:#1e293b;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{min-height:100vh}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;transition:background .25s,color .25s}

.hdr{position:relative;background:linear-gradient(110deg,${c.gradient[0]} 0%,${c.gradient[1]} 55%,${c.gradient[2]} 100%);color:#fff;padding:32px 24px 28px;overflow:hidden}
.hdr::before{content:'${c.wm}';position:absolute;right:8px;top:-20%;font-family:'Outfit',sans-serif;font-size:clamp(8rem,22vw,18rem);font-weight:900;color:rgba(255,255,255,.10);line-height:1;pointer-events:none;user-select:none}
.hdr-inner{position:relative;z-index:1;max-width:1100px;margin:0 auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.hdr-back{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(255,255,255,.14);border-radius:9px;color:#fff;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .15s}
.hdr-back:hover{background:rgba(255,255,255,.24)}
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.7rem;font-weight:900;letter-spacing:-.01em}
.hdr-sub{font-size:.92rem;opacity:.85;margin-top:4px}
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

main{max-width:980px;margin:0 auto;padding:32px 24px 60px}

.intro-card{background:var(--card);border:1.5px solid var(--border);border-radius:16px;padding:22px 26px;margin-bottom:28px;box-shadow:var(--sh)}
.intro-num{display:inline-block;padding:4px 10px;background:var(--pri-soft);color:var(--pri-d);border-radius:99px;font-size:.72rem;font-weight:800;letter-spacing:.06em;margin-bottom:8px;text-transform:uppercase}
.intro-card h2{font-family:'Outfit',sans-serif;font-size:1.4rem;font-weight:800;margin-bottom:6px}
.intro-card p{color:var(--muted);font-size:.95rem}

.para-grid{display:grid;grid-template-columns:1fr;gap:14px}
.para-card{background:var(--card);border:1.5px solid var(--border);border-radius:14px;padding:18px 20px;display:flex;gap:16px;align-items:flex-start;transition:transform .15s,box-shadow .15s,border-color .15s}
.para-card:hover{transform:translateY(-2px);box-shadow:var(--sh);border-color:var(--pri)}
.para-num{font-family:'Outfit',sans-serif;font-size:1rem;font-weight:900;color:#fff;background:linear-gradient(135deg,var(--pri),var(--pri-d));padding:8px 12px;border-radius:9px;min-width:56px;text-align:center;letter-spacing:-.02em;flex-shrink:0}
.para-body{flex:1}
.para-title{font-family:'Outfit',sans-serif;font-size:1.05rem;font-weight:800;margin-bottom:4px;color:var(--text)}
.para-sub{font-size:.88rem;color:var(--muted);margin-bottom:10px;line-height:1.55}
.para-status{display:inline-flex;align-items:center;gap:6px;font-size:.78rem;color:var(--muted);background:rgba(0,0,0,.04);padding:6px 10px;border-radius:8px;font-weight:600}
html.dark .para-status{background:rgba(255,255,255,.06)}
.para-status .ic{width:14px;height:14px}

.banner-soon{margin-top:30px;text-align:center;padding:20px;background:linear-gradient(135deg,var(--pri-soft),transparent);border:1px dashed var(--pri);border-radius:14px;color:var(--pri-d);font-weight:700;font-size:.92rem}
.banner-soon b{font-family:'Outfit',sans-serif}

.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border)}
</style>
</head>
<body>

<header class="hdr">
  <div class="hdr-inner">
    <div>
      <a href="/textbook/physics-11" class="hdr-back">
        <svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
        К курсу физики 11
      </a>
    </div>
    <div>
      <h1>Глава ${c.n}. ${c.name}</h1>
      <div class="hdr-sub">${c.desc.split('.')[0]} · ${c.paraRange}</div>
    </div>
  </div>
</header>

<main>

  <div class="intro-card">
    <span class="intro-num">Глава ${c.n}</span>
    <h2>${c.name}</h2>
    <p>${c.desc} Глава содержит ${c.paras.length} параграф${c.paras.length === 1 ? '' : (c.paras.length < 5 ? 'а' : 'ов')} и финальный этап с боссами.</p>
  </div>

  <div class="para-grid">
${parasHtml}
  </div>

  <div class="banner-soon">
    <b>Глава в разработке.</b> Полная реализация — в следующих волнах. Базовая библиотека <code>phys-fx.js</code> уже доступна.
  </div>

</main>

<footer class="foot">
  Физика — 11 класс · Глава ${c.n} · LearnSpace
</footer>

</body>
</html>
`;
}

function makeHub(){
  const cards = CHAPTERS.map((c, i) => `
    <a href="/textbook/${c.slug}" class="ch-card" style="--ch:${c.pri};--ch-d:${c.pri2};--ch-soft:${c.priSoft}">
      <div class="ch-cover" style="background:linear-gradient(135deg,${c.gradient[0]},${c.gradient[1]} 60%,${c.gradient[2]})">
        <div class="ch-cover-wm">${c.wm}</div>
        <div class="ch-num">Глава ${c.n}</div>
        <div class="ch-title">${c.name}</div>
        <div class="ch-range">${c.paraRange} + Финал</div>
      </div>
      <div class="ch-body">
        <div class="ch-desc">${c.desc}</div>
        <div class="ch-prog">
          <div class="ch-prog-label"><span>Прогресс</span><span id="prog-${c.n}">0%</span></div>
          <div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-${c.n}" style="width:0%"></div></div>
        </div>
        <div class="ch-action">
          <span id="btn-${c.n}">Открыть главу</span>
          <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
        </div>
      </div>
    </a>`).join('\n');

  return `<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Физика 11 класс — учебник</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@400;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
  onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\\\[',right:'\\\\]',display:true},{left:'\\\\(',right:'\\\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<style>
:root{
  --bg:#ecfeff; --card:#fff;
  --text:#0f172a; --muted:#475569;
  --border:#a5f3fc;
  --pri:#0891b2; --pri-d:#0e7490;
  --pri-soft:#cffafe;
  --sh:0 4px 16px rgba(8,145,178,.10);
  --sh-h:0 12px 36px rgba(8,145,178,.18);
}
html.dark{
  --bg:#062326; --card:#0a2e35;
  --text:#cffafe; --muted:#67e8f9;
  --border:#0f4750;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{min-height:100vh}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;transition:background .25s,color .25s}

.hdr{position:relative;background:linear-gradient(110deg,#155e75 0%,#0891b2 55%,#67e8f9 100%);color:#fff;padding:32px 24px 28px;overflow:hidden;border-bottom:2px solid rgba(165,243,252,.18)}
.hdr::before{content:'ФИЗИКА';position:absolute;right:-14px;top:-18%;font-family:'Outfit',sans-serif;font-size:clamp(5rem,16vw,13rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(207,250,254,.12);line-height:1;pointer-events:none;user-select:none}
.hdr-inner{position:relative;z-index:1;max-width:1180px;margin:0 auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.hdr-back{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(255,255,255,.14);border-radius:9px;color:#fff;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .15s}
.hdr-back:hover{background:rgba(255,255,255,.24)}
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.85rem;font-weight:900;letter-spacing:-.01em}
.hdr-sub{font-size:.92rem;opacity:.88;margin-top:4px}
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

main{max-width:1180px;margin:0 auto;padding:32px 24px 60px}

.prog-overall{background:linear-gradient(135deg,var(--pri-soft),rgba(103,232,249,.12));border:1px solid var(--border);border-radius:14px;padding:14px 18px;margin-bottom:28px;display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.po-icon{width:46px;height:46px;border-radius:12px;background:linear-gradient(135deg,#0891b2,#67e8f9);color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:'Outfit',sans-serif;font-size:1.4rem;font-weight:900}
.po-text{flex:1;min-width:160px}
.po-label{font-size:.78rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}
.po-bar{height:8px;background:rgba(8,145,178,.14);border-radius:5px;overflow:hidden;margin-top:6px}
.po-fill{height:100%;background:linear-gradient(90deg,var(--pri),#67e8f9);border-radius:5px;transition:width .5s}

.ch-grid{display:grid;grid-template-columns:1fr;gap:18px;margin-bottom:30px}
@media(min-width:680px){.ch-grid{grid-template-columns:1fr 1fr}}
@media(min-width:1100px){.ch-grid{grid-template-columns:repeat(4,1fr)}}

.ch-card{background:var(--card);border:1.5px solid var(--border);border-radius:18px;overflow:hidden;display:flex;flex-direction:column;transition:transform .2s,box-shadow .2s,border-color .2s;cursor:pointer;text-decoration:none;color:inherit}
.ch-card:hover{transform:translateY(-4px);box-shadow:var(--sh-h)}
.ch-cover{padding:22px 22px 18px;color:#fff;position:relative;overflow:hidden}
.ch-cover-wm{position:absolute;right:-8px;top:-22px;font-size:5.2rem;font-weight:900;font-family:'Outfit',sans-serif;line-height:1;color:rgba(255,255,255,.20);pointer-events:none;letter-spacing:-.04em}
.ch-num{display:inline-block;padding:4px 10px;background:rgba(255,255,255,.22);border-radius:99px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;position:relative;z-index:1}
.ch-title{font-family:'Outfit',sans-serif;font-size:1.05rem;font-weight:800;letter-spacing:-.01em;position:relative;z-index:1;line-height:1.3}
.ch-range{font-size:.82rem;opacity:.88;margin-top:4px;position:relative;z-index:1;font-weight:500}

.ch-body{padding:16px 20px 18px;display:flex;flex-direction:column;flex:1}
.ch-desc{font-size:.86rem;color:var(--text);opacity:.84;flex:1;margin-bottom:12px;line-height:1.55}

.ch-prog{margin-bottom:12px}
.ch-prog-label{display:flex;justify-content:space-between;font-size:.74rem;color:var(--muted);font-weight:600;margin-bottom:4px}
.ch-prog-bar{height:6px;background:rgba(0,0,0,.07);border-radius:4px;overflow:hidden}
.ch-prog-fill{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--ch),var(--ch-d));transition:width .5s}

.ch-action{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:11px;font-weight:700;font-size:.9rem;color:#fff;background:linear-gradient(135deg,var(--ch),var(--ch-d));transition:filter .15s}
.ch-action:hover{filter:brightness(1.08)}

.banner-soon{margin-top:18px;text-align:center;padding:20px;background:linear-gradient(135deg,var(--pri-soft),transparent);border:1px dashed var(--pri);border-radius:14px;color:var(--pri-d);font-weight:700;font-size:.92rem}
.banner-soon b{font-family:'Outfit',sans-serif;display:block;margin-bottom:4px;font-size:1.05rem}

.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border)}
</style>
</head>
<body>

<header class="hdr">
  <div class="hdr-inner">
    <div>
      <a href="/textbooks" class="hdr-back">
        <svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
        К каталогу
      </a>
    </div>
    <div>
      <h1>Физика — 11 класс</h1>
      <div class="hdr-sub">Жилко · Маркович · Сокольский (2021) · 8 глав · 45 параграфов</div>
    </div>
  </div>
</header>

<main>

  <section class="prog-overall">
    <div class="po-icon">∿</div>
    <div class="po-text">
      <div class="po-label">Общий прогресс по курсу</div>
      <div id="overall-text" style="font-size:1.05rem;font-weight:700">Загрузка...</div>
      <div class="po-bar"><div id="overall-fill" class="po-fill" style="width:0%"></div></div>
    </div>
  </section>

  <div class="ch-grid">
${cards}
  </div>

  <div class="banner-soon">
    <b>Курс в активной разработке (W0)</b>
    Инфраструктура готова: миграция БД, библиотека phys-fx.js (Oscillogram, SpringMass, Pendulum) и 8 stub-страниц глав. Реализация по плану PLAN_FIZIKA_11.md — 15 волн (~26 сессий).
  </div>

</main>

<footer class="foot">
  Интерактивный учебник «Физика — 11 класс» · LearnSpace
</footer>

<script>
'use strict';
var TOTAL = 45;
var CH_PARA = {${CHAPTERS.map(c => "'" + c.slug + "': " + c.paras.length).join(', ')}};
var CH_IDX = {${CHAPTERS.map(c => "'" + c.slug + "': " + c.n).join(', ')}};

function setChProg(idx, readCount, total){
  var pct = total ? Math.round(readCount * 100 / total) : 0;
  var labelEl = document.getElementById('prog-' + idx);
  var fillEl  = document.getElementById('fill-' + idx);
  var btnEl   = document.getElementById('btn-' + idx);
  if (labelEl) labelEl.textContent = pct + '%';
  if (fillEl)  fillEl.style.width  = pct + '%';
  if (btnEl){
    if (readCount > 0 && readCount < total) btnEl.textContent = 'Продолжить';
    else if (readCount >= total)            btnEl.textContent = 'Открыть снова';
    else                                    btnEl.textContent = 'Открыть главу';
  }
}

function renderProgress(children){
  var totalRead = 0;
  for (var i = 0; i < children.length; i++){
    var ch    = children[i];
    var idx   = CH_IDX[ch.slug]; if (!idx) continue;
    var read  = ch.progress ? ch.progress.read.length : 0;
    var total = ch.para_count || CH_PARA[ch.slug] || 1;
    totalRead += read;
    setChProg(idx, read, total);
  }
  var pct = Math.round(totalRead * 100 / TOTAL);
  var overallEl = document.getElementById('overall-text');
  var fillEl    = document.getElementById('overall-fill');
  if (overallEl) overallEl.textContent = totalRead + ' из ' + TOTAL + ' параграфов · ' + pct + '%';
  if (fillEl)    fillEl.style.width = pct + '%';
}

function loadProgress(){
  if (typeof window.LS === 'undefined' || typeof window.LS.api !== 'function'){
    renderProgress([]); return;
  }
  window.LS.api('/api/textbooks/physics-11/children')
    .then(function(data){
      if (data && data.children) renderProgress(data.children);
      else renderProgress([]);
    })
    .catch(function(){ renderProgress([]); });
}

if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', loadProgress);
else loadProgress();
window.addEventListener('focus', loadProgress);
</script>

</body>
</html>
`;
}

/* Write all 9 files */
fs.writeFileSync(path.join(OUT, 'physics_11_hub.html'), makeHub(), 'utf8');
console.log('Wrote: physics_11_hub.html');
CHAPTERS.forEach(c => {
  const fname = 'physics_11_ch' + c.n + '.html';
  fs.writeFileSync(path.join(OUT, fname), makeChapter(c), 'utf8');
  console.log('Wrote:', fname);
});
console.log('Done. 9 stub files generated.');
