// Физика 7 · Глава 4 «Давление» · виджеты §§28–35 + финал.
// Палитра главы — amber (#d97706).

(function(){
'use strict';

const ACCENT = '#d97706';
const ACCENT_D = '#92400e';
const ACCENT_SOFT = '#fef3c7';

function renderMath(root){
  if(window.renderMathInElement){
    try{
      window.renderMathInElement(root, {
        delimiters: [{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],
        throwOnError: false
      });
    }catch(e){}
  }
}

function makeCard(kind, title, badge, body){
  const colorByKind = { theory:ACCENT, rule:'#0284c7', example:'#10b981' };
  const labelByKind = { theory:'Теория', rule:'Правило', example:'Пример' };
  const c = colorByKind[kind] || ACCENT;
  return '<div style="background:#fff;border:1.5px solid ' + ACCENT_SOFT + ';border-left:5px solid ' + c + ';border-radius:11px;padding:14px 16px;margin-bottom:14px;box-shadow:0 2px 8px rgba(0,0,0,.05)">'
    + '<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap">'
    +   '<span style="background:' + c + ';color:#fff;padding:3px 10px;border-radius:99px;font-size:.7rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em">' + labelByKind[kind] + '</span>'
    +   '<span style="font-size:.72rem;font-weight:700;color:#64748b;letter-spacing:.04em">' + (badge||'') + '</span>'
    +   '<span style="font-family:Unbounded,sans-serif;font-weight:800;font-size:.96rem;color:#0f172a;flex:1;min-width:0">' + title + '</span>'
    + '</div>'
    + '<div style="font-size:.94rem;line-height:1.6;color:#0f172a">' + body + '</div>'
    + '</div>';
}

function wgWrap(id, badge, title, hint, body){
  return '<div id="' + id + '" style="background:#fff;border:1.5px solid ' + ACCENT_SOFT + ';border-radius:12px;padding:14px 16px;margin-bottom:14px;box-shadow:0 2px 8px rgba(0,0,0,.05)">'
    + '<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap">'
    +   '<span style="background:' + ACCENT + ';color:#fff;padding:3px 10px;border-radius:99px;font-size:.7rem;font-weight:800;letter-spacing:.05em">' + badge + '</span>'
    +   '<span style="font-family:Unbounded,sans-serif;font-weight:800;font-size:.92rem;color:#0f172a">' + title + '</span>'
    + '</div>'
    + (hint ? '<div style="font-size:.84rem;color:#64748b;background:' + ACCENT_SOFT + ';border-left:3px solid ' + ACCENT + ';border-radius:6px;padding:8px 12px;margin-bottom:10px;line-height:1.5">' + hint + '</div>' : '')
    + body
    + '</div>';
}

function readButton(pid){
  return '<div style="text-align:center;margin-top:18px"><button class="ph7-read-btn" data-pid="' + pid + '" '
    + 'style="background:linear-gradient(135deg,#10b981,#047857);color:#fff;border:none;padding:11px 22px;border-radius:11px;font-weight:700;font-size:.92rem;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:8px;box-shadow:0 4px 14px rgba(16,185,129,.32)">'
    + '<svg viewBox="0 0 24 24" style="width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round"><polyline points="20 6 9 17 4 12"/></svg>'
    + 'Я прочитал §  &nbsp;<span style="opacity:.85;font-size:.86rem">+10 XP</span>'
    + '</button></div>';
}

function wireReadBtn(pid){
  const btn = document.querySelector('.ph7-read-btn[data-pid="' + pid + '"]');
  if(!btn) return;
  const KEY = 'physics7_ch4_read_' + pid;
  if(localStorage.getItem(KEY) === '1'){
    btn.innerHTML = '<svg viewBox="0 0 24 24" style="width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round"><polyline points="20 6 9 17 4 12"/></svg> Прочитано';
    btn.disabled = true; btn.style.background = '#94a3b8'; btn.style.cursor = 'default';
    return;
  }
  btn.addEventListener('click', function(){
    if(localStorage.getItem(KEY) === '1') return;
    localStorage.setItem(KEY, '1');
    if(typeof window.bumpProgress === 'function') window.bumpProgress(pid, 30);
    if(typeof window.addXp === 'function') window.addXp(10, 'read-' + pid);
    btn.innerHTML = '<svg viewBox="0 0 24 24" style="width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round"><polyline points="20 6 9 17 4 12"/></svg> Прочитано';
    btn.disabled = true; btn.style.background = '#94a3b8'; btn.style.cursor = 'default';
  });
}

function wireDnd(host, items){
  const root = document.getElementById(host);
  if(!root) return;
  const checkBtn = root.querySelector('.dnd-check');
  const fb = root.querySelector('.dnd-fb');
  let placed = {}, armed = null;
  root.querySelectorAll('.dnd-chip').forEach(chip => chip.addEventListener('click', () => {
    if(armed === chip){ armed.classList.remove('armed'); armed.style.boxShadow = ''; armed = null; return; }
    if(armed){ armed.classList.remove('armed'); armed.style.boxShadow = ''; }
    armed = chip;
    chip.classList.add('armed');
    chip.style.boxShadow = '0 0 0 3px rgba(217,119,6,.3)';
  }));
  root.querySelectorAll('.drop-box').forEach(box => box.addEventListener('click', () => {
    if(!armed) return;
    const cat = box.dataset.cat;
    const id = armed.dataset.id;
    placed[id] = cat;
    const clone = armed.cloneNode(true);
    clone.classList.remove('armed'); clone.classList.add('placed');
    clone.style.background = ACCENT_SOFT; clone.style.borderColor = ACCENT; clone.style.boxShadow = '';
    clone.addEventListener('click', e => {
      e.stopPropagation();
      delete placed[id];
      clone.remove();
      armed = null;
      const orig = root.querySelector('.dnd-chip[data-id="' + id + '"]:not(.placed)');
      if(orig) orig.style.display = '';
    });
    box.querySelector('.drop-items').appendChild(clone);
    armed.style.display = 'none';
    armed.classList.remove('armed'); armed.style.boxShadow = '';
    armed = null;
  }));
  if(checkBtn) checkBtn.addEventListener('click', () => {
    const total = items.length;
    let correct = 0;
    items.forEach(it => { if(placed[it.id] === it.cat) correct++; });
    fb.style.display = 'block';
    if(correct === total){
      fb.style.background = '#d1fae5'; fb.style.color = '#065f46'; fb.style.borderLeft = '4px solid #10b981';
      fb.innerHTML = '&#10003; Идеально! ' + total + '/' + total + '.';
      if(typeof window.addXp === 'function') window.addXp(15, 'dnd-' + host);
    } else {
      fb.style.background = '#fee2e2'; fb.style.color = '#7f1d1d'; fb.style.borderLeft = '4px solid #dc2626';
      fb.innerHTML = '&#10007; Правильно: ' + correct + '/' + total + '. Попробуй ещё раз.';
    }
  });
}

function dndPool(host, items, cats){
  const chips = items.map(it => '<button class="dnd-chip" data-id="' + it.id + '" type="button" style="background:#fff;border:1.5px solid ' + ACCENT_SOFT + ';border-radius:10px;padding:7px 13px;cursor:pointer;font-size:.9rem;font-family:inherit;transition:all .15s">' + it.html + '</button>').join('');
  const boxes = cats.map(c => '<div class="drop-box" data-cat="' + c.cat + '" style="background:#fff;border:1.5px dashed ' + ACCENT_SOFT + ';border-radius:10px;padding:10px;min-height:80px"><h5 style="font-family:Unbounded,sans-serif;font-size:.76rem;color:' + ACCENT_D + ';margin-bottom:8px;text-transform:uppercase;letter-spacing:.04em">' + c.label + '</h5><div class="drop-items" style="display:flex;flex-wrap:wrap;gap:6px;min-height:32px"></div></div>').join('');
  return '<div id="' + host + '" class="dnd-host">'
    + '<div style="font-size:.86rem;color:#475569;margin-bottom:10px">Кликни по карточке, потом по корзине. Чтобы вернуть — кликни в корзине.</div>'
    + '<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed ' + ACCENT_SOFT + ';border-radius:10px;background:#fffbeb">' + chips + '</div>'
    + '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-bottom:12px">' + boxes + '</div>'
    + '<div style="display:flex;gap:8px"><button class="dnd-check" type="button" style="background:linear-gradient(135deg,' + ACCENT + ',' + ACCENT_D + ');color:#fff;border:none;padding:9px 18px;border-radius:9px;font-weight:700;font-size:.86rem;cursor:pointer;font-family:inherit">Проверить</button></div>'
    + '<div class="dnd-fb" style="padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none;line-height:1.45"></div>'
    + '</div>';
}

function quizQuestion(host, idx, q, opts, correctIdx, explain){
  const optsHtml = opts.map((o,i) => '<button class="qz-opt" data-i="' + i + '" type="button" style="background:#fff;border:1.5px solid ' + ACCENT_SOFT + ';border-radius:9px;padding:9px 14px;cursor:pointer;font-size:.92rem;font-family:inherit;text-align:left;width:100%;margin-bottom:6px">' + o + '</button>').join('');
  return '<div class="qz-q" data-idx="' + idx + '" style="background:' + ACCENT_SOFT + ';border:1.5px solid ' + ACCENT_SOFT + ';border-radius:10px;padding:12px 14px;margin-bottom:10px">'
    + '<div style="font-weight:700;margin-bottom:8px;font-size:.94rem">' + (idx+1) + '. ' + q + '</div>'
    + '<div class="qz-opts" data-correct="' + correctIdx + '" data-explain="' + (explain||'').replace(/"/g,'&quot;') + '">' + optsHtml + '</div>'
    + '<div class="qz-fb" style="padding:9px 12px;border-radius:8px;font-size:.86rem;margin-top:6px;display:none;line-height:1.45"></div>'
    + '</div>';
}

function wireQuiz(host, onAllCorrect){
  const root = document.getElementById(host);
  if(!root) return;
  const all = root.querySelectorAll('.qz-q');
  const done = new Set();
  all.forEach(qDiv => {
    const opts = qDiv.querySelectorAll('.qz-opt');
    const correct = +qDiv.querySelector('.qz-opts').dataset.correct;
    const explain = qDiv.querySelector('.qz-opts').dataset.explain;
    const fb = qDiv.querySelector('.qz-fb');
    opts.forEach(o => o.addEventListener('click', () => {
      if(done.has(qDiv)) return;
      const i = +o.dataset.i;
      opts.forEach(x => x.disabled = true);
      if(i === correct){
        o.style.background = '#d1fae5'; o.style.borderColor = '#10b981'; o.style.color = '#065f46';
        fb.style.display = 'block'; fb.style.background = '#d1fae5'; fb.style.color = '#065f46'; fb.style.borderLeft = '4px solid #10b981';
        fb.innerHTML = '&#10003; Верно!' + (explain ? ' ' + explain : '');
        done.add(qDiv);
        if(done.size === all.length && typeof onAllCorrect === 'function') onAllCorrect();
      } else {
        o.style.background = '#fee2e2'; o.style.borderColor = '#dc2626'; o.style.color = '#7f1d1d';
        opts[correct].style.background = '#d1fae5'; opts[correct].style.borderColor = '#10b981'; opts[correct].style.color = '#065f46';
        fb.style.display = 'block'; fb.style.background = '#fee2e2'; fb.style.color = '#7f1d1d'; fb.style.borderLeft = '4px solid #dc2626';
        fb.innerHTML = '&#10007; Неверно. Правильно: «' + opts[correct].textContent + '».' + (explain ? ' ' + explain : '');
        done.add(qDiv);
      }
    }));
  });
}

/* ========================================================== */
/* §28 — Давление. Единицы давления                           */
/* ========================================================== */
function add_p28(){
  const body = document.getElementById('p28-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Что такое давление', '§ 28.1',
    '<b>Давление</b> $p$ — это сила, действующая на единицу площади опоры:<br>'
    + '$$p = \\dfrac{F}{S}$$<br>'
    + 'где $F$ — сила (Н), $S$ — площадь (м²). Единица — <b>паскаль</b>: $1$ Па $= 1$ Н/м².<br><br>'
    + 'Одно и то же тело может производить очень разное давление — всё зависит от <b>площади</b> опоры.');

  h += makeCard('rule', 'Производные единицы', '§ 28.2',
    '<ul style="padding-left:20px;margin:6px 0">'
    + '<li>$1$ кПа $= 1000$ Па</li>'
    + '<li>$1$ МПа $= 10^6$ Па</li>'
    + '<li>$1$ ГПа $= 10^9$ Па</li>'
    + '<li>$1$ Н/см² $= 10\\,000$ Па $= 10$ кПа</li>'
    + '</ul>');

  h += makeCard('example', 'Гвоздь, тапок, трактор', '§ 28.3',
    '<table style="width:100%;border-collapse:collapse;font-size:.92rem">'
    + '<tr style="background:' + ACCENT_SOFT + '"><th style="padding:6px 10px;text-align:left;border-bottom:2px solid ' + ACCENT + '">Объект</th><th style="padding:6px 10px;text-align:right;border-bottom:2px solid ' + ACCENT + '">F</th><th style="padding:6px 10px;text-align:right;border-bottom:2px solid ' + ACCENT + '">S</th><th style="padding:6px 10px;text-align:right;border-bottom:2px solid ' + ACCENT + '">p</th></tr>'
    + [
        ['Гвоздь под молотком',100,'0,1 мм² = 10⁻⁷ м²','10⁹ Па'],
        ['Балерина на пуанте',500,'1 см² = 10⁻⁴ м²','5 МПа'],
        ['Человек на двух ногах',700,'300 см² = 0,03 м²','23 кПа'],
        ['Лыжник',700,'0,25 м²','2,8 кПа'],
        ['Трактор (на гусеницах)',50000,'5 м²','10 кПа']
      ].map(r => '<tr><td style="padding:5px 10px;border-bottom:1px solid ' + ACCENT_SOFT + '">' + r[0] + '</td><td style="padding:5px 10px;text-align:right;border-bottom:1px solid ' + ACCENT_SOFT + ';font-family:JetBrains Mono,monospace">' + r[1] + ' Н</td><td style="padding:5px 10px;text-align:right;border-bottom:1px solid ' + ACCENT_SOFT + ';font-family:JetBrains Mono,monospace;font-size:.82rem">' + r[2] + '</td><td style="padding:5px 10px;text-align:right;border-bottom:1px solid ' + ACCENT_SOFT + ';font-family:JetBrains Mono,monospace;font-weight:700">' + r[3] + '</td></tr>').join('')
    + '</table>'
    + '<p style="margin-top:8px;font-size:.86rem;color:#475569">Лыжник давит меньше, чем человек без лыж — поэтому не проваливается в снег.</p>');

  /* IV-1 КАЛЬК p = F/S */
  h += wgWrap('p28-iv1', 'КАЛЬК', 'Калькулятор давления', 'Меняй силу и площадь — увидь, как меняется давление.',
    '<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px">'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Сила $F$, Н: <b id="p28-F" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">500</b><input type="range" id="p28-F-r" min="1" max="10000" step="1" value="500" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Площадь $S$, см²: <b id="p28-S" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">100</b><input type="range" id="p28-S-r" min="0.01" max="10000" step="0.01" value="100" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '</div>'
    + '<div style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:12px 14px;font-size:.94rem;line-height:1.7">'
    +   '$p = F/S = $ <b id="p28-p" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">50 000</b> Па $= $ <b id="p28-pkpa" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">50</b> кПа'
    +   '<div id="p28-cmp" style="font-size:.84rem;color:#475569;margin-top:4px"></div>'
    + '</div>');

  /* IV-2 СИМ: переключатель сценариев */
  h += wgWrap('p28-iv2', 'СИМ', 'Один человек — разные ситуации', 'Один и тот же человек давит на грунт по-разному.',
    '<div style="display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap">'
    + [['boots','В ботинках'],['skis','На лыжах'],['stilet','На каблуке']].map((s, i) =>
        '<button class="p28-sc" data-s="' + s[0] + '" type="button" style="background:' + (i===0 ? ACCENT : '#fff') + ';color:' + (i===0 ? '#fff' : ACCENT) + ';border:2px solid ' + ACCENT + ';padding:7px 14px;border-radius:9px;cursor:pointer;font-weight:700;font-family:inherit;font-size:.86rem">' + s[1] + '</button>').join('')
    + '</div>'
    + '<svg id="p28-sc-svg" viewBox="0 0 360 140" width="100%" style="max-width:500px;display:block;margin:0 auto;background:#fffbeb;border-radius:9px;border:1px solid ' + ACCENT_SOFT + '"></svg>'
    + '<div id="p28-sc-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:10px 14px;margin-top:8px;font-size:.92rem;line-height:1.6"></div>');

  /* IV-3 DnD */
  h += wgWrap('p28-iv3', 'DnD', 'Кто давит сильнее (большее p)?', '',
    dndPool('p28-dnd', [
      { id:'a1', cat:'low',  html:'Танк на гусеницах' },
      { id:'a2', cat:'low',  html:'Лыжник на снегу' },
      { id:'a3', cat:'mid',  html:'Человек в ботинках' },
      { id:'a4', cat:'mid',  html:'Стул на 4 ножках' },
      { id:'a5', cat:'high', html:'Балерина на пуанте' },
      { id:'a6', cat:'high', html:'Гвоздь под молотком' }
    ], [
      { cat:'low',  label:'Малое (большая S)' },
      { cat:'mid',  label:'Среднее' },
      { cat:'high', label:'Огромное (малая S)' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p28-iv4', 'ТРН', 'Тренажёр §28', '',
    '<div id="p28-tr-host">'
    + quizQuestion('p28-tr', 0, '$F = 200$ Н, $S = 0{,}1$ м². Давление $p$?', ['20 Па','200 Па','2000 Па','20 000 Па'], 2, '$p = 200/0{,}1 = 2000$ Па.')
    + quizQuestion('p28-tr', 1, 'Кирпич $F = 30$ Н стоит на $S = 100$ см² $= 0{,}01$ м². $p$?', ['300 Па','1500 Па','3000 Па','30 000 Па'], 2, '$p = 30/0{,}01 = 3000$ Па.')
    + quizQuestion('p28-tr', 2, 'Какой ответ верный? $1$ кПа $= ?$ Па', ['10','100','1000','10 000'], 2)
    + quizQuestion('p28-tr', 3, 'Чтобы уменьшить давление при той же силе, нужно…', ['Увеличить S','Уменьшить S','Уменьшить F','S не влияет'], 0, 'Из $p = F/S$: чем больше $S$, тем меньше $p$.')
    + quizQuestion('p28-tr', 4, 'Площадь $S = 5$ см², $p = 200$ кПа. Какова сила $F$?', ['1 Н','10 Н','100 Н','1000 Н'], 2, '$S = 5 \\cdot 10^{-4}$ м², $F = pS = 200\\,000 \\cdot 5 \\cdot 10^{-4} = 100$ Н.')
    + '</div>');

  h += readButton('p28');
  body.innerHTML = h;

  // §28 IV-1
  const upd28 = () => {
    const F = +document.getElementById('p28-F-r').value;
    const Scm = +document.getElementById('p28-S-r').value;
    const S = Scm * 1e-4; // м²
    document.getElementById('p28-F').textContent = F;
    document.getElementById('p28-S').textContent = Scm;
    const p = F / S;
    document.getElementById('p28-p').textContent = p.toLocaleString('ru-RU', { maximumFractionDigits: 0 });
    document.getElementById('p28-pkpa').textContent = (p / 1000).toFixed(2);
    let cmp;
    if(p < 1000) cmp = 'Очень малое — как давление гирь на резиновый коврик.';
    else if(p < 10000) cmp = 'Похоже на лыжника на снегу.';
    else if(p < 100000) cmp = 'Близко к давлению человека на полу.';
    else if(p < 1e6) cmp = 'Балерина на пуанте, копыто лошади.';
    else cmp = 'Огромное — как у гвоздя под молотком (≈ ГПа).';
    document.getElementById('p28-cmp').textContent = cmp;
  };
  ['p28-F-r','p28-S-r'].forEach(id => document.getElementById(id).addEventListener('input', upd28));
  upd28();

  // §28 IV-2 sims
  const scenarios = {
    boots:  { S: 0.03,  nm:'Ботинки на 2 ноги', cmp:'≈ давление человека (700 Н на 300 см²)' },
    skis:   { S: 0.25,  nm:'Лыжи',              cmp:'в ~8 раз меньше — не проваливается в снег' },
    stilet: { S: 0.0001, nm:'Каблук-шпилька',   cmp:'~ в 300 раз больше — повреждает паркет' }
  };
  function draw28(sc){
    const W = 360, H = 140, baseY = 110;
    let s = '';
    s += '<rect x="0" y="' + baseY + '" width="' + W + '" height="30" fill="#94a3b8"/>';
    for(let i = 0; i < 18; i++) s += '<line x1="' + (i*22+5) + '" y1="' + baseY + '" x2="' + (i*22+15) + '" y2="' + (baseY+8) + '" stroke="#374151" stroke-width="0.8"/>';
    // Человек (упрощённо)
    s += '<circle cx="180" cy="40" r="14" fill="#fbbf24" stroke="#0f172a" stroke-width="1.5"/>'; // голова
    s += '<rect x="170" y="55" width="20" height="35" fill="#dc2626"/>';
    // Опора (разная)
    if(sc === 'boots'){
      s += '<rect x="165" y="100" width="30" height="10" fill="#374151"/>';
      s += '<text x="180" y="135" text-anchor="middle" font-size="10" fill="#0f172a" font-family="Inter,sans-serif">2 ботинка, S ≈ 300 см²</text>';
    } else if(sc === 'skis'){
      s += '<rect x="100" y="100" width="160" height="10" fill="#92400e" stroke="#0f172a" stroke-width="1"/>';
      s += '<text x="180" y="135" text-anchor="middle" font-size="10" fill="#0f172a" font-family="Inter,sans-serif">2 лыжи, S ≈ 2500 см²</text>';
    } else {
      s += '<polygon points="178,100 182,100 180,108" fill="#374151"/>';
      s += '<text x="180" y="135" text-anchor="middle" font-size="10" fill="#0f172a" font-family="Inter,sans-serif">2 пуанта, S ≈ 1 см²</text>';
    }
    // Сила
    s += '<line x1="220" y1="50" x2="220" y2="98" stroke="#dc2626" stroke-width="2.5"/>';
    s += '<polygon points="215,90 225,90 220,100" fill="#dc2626"/>';
    s += '<text x="228" y="78" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#dc2626">F = 700 Н</text>';
    document.getElementById('p28-sc-svg').innerHTML = s;
    const d = scenarios[sc];
    const p = 700 / d.S;
    document.getElementById('p28-sc-info').innerHTML = '<b>' + d.nm + '</b>: $S = ' + d.S + '$ м², $p = F/S = ' + Math.round(p).toLocaleString('ru-RU') + '$ Па $\\approx ' + (p/1000).toFixed(1) + '$ кПа<br><span style="font-size:.84rem;color:#64748b">' + d.cmp + '</span>';
    renderMath(document.getElementById('p28-sc-info'));
  }
  body.querySelectorAll('.p28-sc').forEach(btn => btn.addEventListener('click', () => {
    body.querySelectorAll('.p28-sc').forEach(b => { b.style.background = '#fff'; b.style.color = ACCENT; });
    btn.style.background = ACCENT; btn.style.color = '#fff';
    draw28(btn.dataset.s);
  }));
  draw28('boots');

  wireDnd('p28-dnd', [
    { id:'a1', cat:'low' },{ id:'a2', cat:'low' },{ id:'a3', cat:'mid' },
    { id:'a4', cat:'mid' },{ id:'a5', cat:'high' },{ id:'a6', cat:'high' }
  ]);
  wireQuiz('p28-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p28'); });
  wireReadBtn('p28');
  renderMath(body);
}

/* ========================================================== */
/* §29 — Давление газа                                        */
/* ========================================================== */
function add_p29(){
  const body = document.getElementById('p29-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Откуда давление у газа', '§ 29.1',
    'Молекулы газа постоянно <b>бьются о стенки</b> сосуда. Каждый удар — крошечная сила. '
    + 'Сумма всех ударов делится на площадь стенки — получается <b>давление газа</b>.<br><br>'
    + 'У газа нет собственной формы и фиксированного объёма, поэтому давление возникает <b>равномерно во все стороны</b>.');

  h += makeCard('rule', 'Что увеличивает p газа', '§ 29.2',
    'Давление газа растёт, если:'
    + '<ul style="padding-left:20px;margin:5px 0">'
    + '<li><b>Увеличить $T$</b> — молекулы быстрее, удары сильнее.</li>'
    + '<li><b>Уменьшить $V$</b> — то же число молекул на меньшей площади.</li>'
    + '<li><b>Добавить молекул</b> (накачать) — больше ударов в секунду.</li>'
    + '</ul>');

  h += makeCard('example', 'Шарик и шина', '§ 29.3',
    '<b>Воздушный шарик</b>: накачиваем (добавляем молекулы) → $p$ растёт → шарик расширяется, '
    + 'пока упругость резины не уравновесит давление.<br><br>'
    + '<b>Шина машины</b>: летом нагревается на солнце → $T$ растёт → $p$ растёт. '
    + 'Поэтому летом давление в шинах выше, чем зимой.');

  /* IV-1 СИМ */
  h += wgWrap('p29-iv1', 'СИМ', 'Молекулы газа в сосуде', 'Меняй число молекул и скорость — увидь, как меняется частота ударов.',
    '<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px">'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Число молекул: <b id="p29-N" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">30</b><input type="range" id="p29-N-r" min="5" max="80" step="1" value="30" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Температура $T$, отн. ед.: <b id="p29-T" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">2</b><input type="range" id="p29-T-r" min="1" max="5" step="0.5" value="2" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '</div>'
    + '<svg id="p29-svg" viewBox="0 0 360 200" width="100%" style="max-width:500px;display:block;margin:0 auto;background:#fffbeb;border-radius:9px;border:1px solid ' + ACCENT_SOFT + '"></svg>'
    + '<div id="p29-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:10px 14px;margin-top:8px;font-size:.92rem;text-align:center"></div>');

  /* IV-2 КВИЗ */
  h += wgWrap('p29-iv2', 'КВИЗ', 'Давление газа', '',
    '<div id="p29-q-host">'
    + quizQuestion('p29-q', 0, 'Что создаёт давление газа на стенки сосуда?', ['Сила тяжести','Удары молекул о стенки','Поверхностное натяжение','Магнитное поле'], 1)
    + quizQuestion('p29-q', 1, 'Если нагреть газ в закрытом сосуде, давление…', ['Не изменится','Уменьшится','Увеличится','Станет нулевым'], 2)
    + quizQuestion('p29-q', 2, 'Если сжать газ (уменьшить $V$) при той же температуре, $p$…', ['Уменьшится','Увеличится','Не изменится','Зависит от массы'], 1)
    + quizQuestion('p29-q', 3, 'В какую сторону газ давит на стенки сосуда?', ['Только вниз','Только вверх','Равномерно во все стороны','Только в одну сторону'], 2)
    + '</div>');

  /* IV-3 DnD */
  h += wgWrap('p29-iv3', 'DnD', 'p газа: больше или меньше?', '',
    dndPool('p29-dnd', [
      { id:'a1', cat:'up',   html:'Накачали колесо насосом' },
      { id:'a2', cat:'up',   html:'Шар на солнце летом' },
      { id:'a3', cat:'up',   html:'Сжали шприц поршнем' },
      { id:'a4', cat:'down', html:'Газ остыл' },
      { id:'a5', cat:'down', html:'Выпустили часть воздуха' },
      { id:'a6', cat:'down', html:'Расширили сосуд' }
    ], [
      { cat:'up',   label:'p растёт' },
      { cat:'down', label:'p падает' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p29-iv4', 'ТРН', 'Тренажёр §29', '',
    '<div id="p29-tr-host">'
    + quizQuestion('p29-tr', 0, 'Газ в закрытом цилиндре охладили. С давлением:', ['Растёт','Падает','Не меняется','Становится 0'], 1)
    + quizQuestion('p29-tr', 1, 'Где давление газа выше: в надутом мяче или в спущенном?', ['В спущенном','В надутом','Одинаково','Зависит от веса'], 1)
    + quizQuestion('p29-tr', 2, 'В баллоне с гелием давление $p$. Если вытащить половину гелия (молекул в 2 раза меньше) при той же $T$ и $V$, $p$ станет…', ['В 2 раза больше','В 2 раза меньше','Тем же','Нулевым'], 1)
    + quizQuestion('p29-tr', 3, 'Что произойдёт с шариком в очень холодильнике?', ['Лопнет','Сожмётся (давление упадёт, форма уменьшится)','Не изменится','Улетит'], 1)
    + '</div>');

  h += readButton('p29');
  body.innerHTML = h;

  // §29 IV-1 sim
  const p29 = { parts: [], raf: 0 };
  function init29(){
    const N = +document.getElementById('p29-N-r').value;
    const T = +document.getElementById('p29-T-r').value;
    p29.parts = [];
    for(let i = 0; i < N; i++){
      const ang = Math.random() * Math.PI * 2;
      const sp = (0.8 + Math.random() * 0.4) * T;
      p29.parts.push({
        x: 20 + Math.random() * 320,
        y: 20 + Math.random() * 160,
        vx: Math.cos(ang) * sp,
        vy: Math.sin(ang) * sp
      });
    }
  }
  function step29(){
    let hits = 0;
    p29.parts.forEach(p => {
      p.x += p.vx; p.y += p.vy;
      if(p.x < 6){ p.x = 6; p.vx = -p.vx; hits++; }
      if(p.x > 354){ p.x = 354; p.vx = -p.vx; hits++; }
      if(p.y < 6){ p.y = 6; p.vy = -p.vy; hits++; }
      if(p.y > 194){ p.y = 194; p.vy = -p.vy; hits++; }
    });
    return hits;
  }
  let frame29 = 0, hitSum = 0;
  function render29(){
    const svg = document.getElementById('p29-svg');
    if(!svg){ cancelAnimationFrame(p29.raf); return; }
    const hits = step29();
    hitSum += hits;
    frame29++;
    let s = '';
    s += '<rect x="3" y="3" width="354" height="194" fill="none" stroke="' + ACCENT_D + '" stroke-width="2"/>';
    p29.parts.forEach(p => {
      const sp = Math.hypot(p.vx, p.vy);
      const col = sp > 2 ? '#dc2626' : (sp > 1.2 ? '#d97706' : '#0284c7');
      s += '<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="3.5" fill="' + col + '"/>';
    });
    svg.innerHTML = s;
    if(frame29 % 30 === 0){
      const avg = (hitSum / 30).toFixed(0);
      document.getElementById('p29-info').innerHTML = 'Молекул: <b>' + p29.parts.length + '</b> &middot; ударов о стенки за такт: <b>' + avg + '</b> &middot; Давление $p$ <b>тем выше</b>, чем больше число молекул × их скорость.';
      renderMath(document.getElementById('p29-info'));
      hitSum = 0;
    }
    p29.raf = requestAnimationFrame(render29);
  }
  ['p29-N-r','p29-T-r'].forEach(id => document.getElementById(id).addEventListener('input', () => {
    document.getElementById('p29-N').textContent = document.getElementById('p29-N-r').value;
    document.getElementById('p29-T').textContent = document.getElementById('p29-T-r').value;
    init29();
  }));
  init29();
  p29.raf = requestAnimationFrame(render29);

  wireDnd('p29-dnd', [
    { id:'a1', cat:'up' },{ id:'a2', cat:'up' },{ id:'a3', cat:'up' },
    { id:'a4', cat:'down' },{ id:'a5', cat:'down' },{ id:'a6', cat:'down' }
  ]);
  wireQuiz('p29-q-host', () => { if(window.addXp) window.addXp(10, 'q-p29'); });
  wireQuiz('p29-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p29'); });
  wireReadBtn('p29');
  renderMath(body);
}

/* ========================================================== */
/* §30 — Закон Паскаля. Гидравлический пресс                  */
/* ========================================================== */
function add_p30(){
  const body = document.getElementById('p30-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Закон Паскаля', '§ 30.1',
    'Давление, производимое на жидкость или газ, передаётся <b>без изменения</b> в каждую точку '
    + 'этой жидкости или газа и <b>во все стороны одинаково</b>.<br><br>'
    + 'Это открыл Блез Паскаль в XVII веке. Поэтому если надуть воздушный шарик и сжать его в одном месте — '
    + 'он раздуется в другом, передавая давление равномерно.');

  h += makeCard('rule', 'Гидравлический пресс', '§ 30.2',
    'Соединим два цилиндра жидкостью. На малый поршень давит сила $F_1$ — возникает давление $p = F_1/S_1$. '
    + 'Это давление передаётся по жидкости к большому поршню площадью $S_2$:<br>'
    + '$$F_2 = p \\cdot S_2 = F_1 \\cdot \\dfrac{S_2}{S_1}$$<br>'
    + '<b>Если $S_2 \\gg S_1$</b>, то $F_2 \\gg F_1$ — гидравлика <b>выигрывает в силе</b>. '
    + 'Платой служит то, что большой поршень движется во столько же раз медленнее.');

  h += makeCard('example', 'Где применяют', '§ 30.3',
    '<ul style="padding-left:20px;margin:5px 0">'
    + '<li><b>Гидравлический пресс</b> — гнёт металл, прессует тюки.</li>'
    + '<li><b>Гидравлический подъёмник</b> в автосервисе.</li>'
    + '<li><b>Тормоза</b> легкового авто — нажатие на педаль передаётся жидкости.</li>'
    + '<li><b>Экскаватор, ковш бульдозера</b> — цилиндры с маслом.</li>'
    + '</ul>');

  /* IV-1 ГЛАВНЫЙ ВИЗУАЛ — гидравлический пресс */
  h += wgWrap('p30-iv1', 'СИМ', 'Гидравлический пресс — главный визуал', 'Меняй площади и силу на малом поршне — увидь силу на большом.',
    '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-bottom:10px">'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:7px 11px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">$S_1$, см²: <b id="p30-S1" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">2</b><input type="range" id="p30-S1-r" min="1" max="50" step="1" value="2" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:7px 11px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">$S_2$, см²: <b id="p30-S2" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">200</b><input type="range" id="p30-S2-r" min="10" max="500" step="5" value="200" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:7px 11px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">$F_1$, Н: <b id="p30-F1" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">50</b><input type="range" id="p30-F1-r" min="1" max="500" step="1" value="50" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '</div>'
    + '<svg id="p30-svg" viewBox="0 0 320 200" width="100%" style="max-width:500px;display:block;margin:0 auto;background:#fffbeb;border-radius:9px;border:1px solid ' + ACCENT_SOFT + '"></svg>'
    + '<div id="p30-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:10px 14px;margin-top:8px;font-size:.94rem;line-height:1.65"></div>');

  /* IV-2 КАЛЬК */
  h += wgWrap('p30-iv2', 'КАЛЬК', 'F₂ через закон Паскаля', '',
    '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-bottom:10px">'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">$F_1$, Н: <b id="p30c-F1" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">100</b><input type="range" id="p30c-F1-r" min="1" max="1000" step="1" value="100" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">$S_1$: <b id="p30c-S1" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">5</b><input type="range" id="p30c-S1-r" min="1" max="100" step="1" value="5" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">$S_2$: <b id="p30c-S2" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">500</b><input type="range" id="p30c-S2-r" min="10" max="2000" step="10" value="500" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '</div>'
    + '<div style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:12px 14px;font-size:.94rem;line-height:1.7">'
    +   '$p = F_1/S_1 = $ <b id="p30c-p" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">20</b> Н/см² &middot; $F_2 = pS_2 = F_1 \\cdot S_2/S_1 = $ <b id="p30c-F2" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">10000</b> Н'
    +   '<div id="p30c-gain" style="font-size:.84rem;color:#475569;margin-top:5px">Выигрыш в силе: <b style="color:#10b981">в 100 раз</b></div>'
    + '</div>');

  /* IV-3 DnD */
  h += wgWrap('p30-iv3', 'DnD', 'Применения закона Паскаля', '',
    dndPool('p30-dnd', [
      { id:'a1', cat:'hyd',  html:'Гидравлический пресс' },
      { id:'a2', cat:'hyd',  html:'Автомобильный подъёмник' },
      { id:'a3', cat:'hyd',  html:'Тормоза машины' },
      { id:'a4', cat:'not',  html:'Звонок в дверь' },
      { id:'a5', cat:'not',  html:'Лампа накаливания' },
      { id:'a6', cat:'hyd',  html:'Ковш бульдозера' }
    ], [
      { cat:'hyd', label:'Использует закон Паскаля' },
      { cat:'not', label:'Не относится' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p30-iv4', 'ТРН', 'Тренажёр §30', '',
    '<div id="p30-tr-host">'
    + quizQuestion('p30-tr', 0, '$F_1 = 50$ Н, $S_1 = 5$ см², $S_2 = 50$ см². $F_2$?', ['100 Н','250 Н','500 Н','5000 Н'], 2, '$F_2 = 50 \\cdot 50/5 = 500$ Н.')
    + quizQuestion('p30-tr', 1, 'Если $S_2/S_1 = 100$, во сколько раз увеличится сила?', ['В 10','В 100','В 1000','Не изменится'], 1)
    + quizQuestion('p30-tr', 2, 'Что является «платой» за выигрыш в силе у гидравлики?', ['Расход воды','Большой поршень движется медленнее','Жидкость греется','Ничего'], 1)
    + quizQuestion('p30-tr', 3, 'Жидкости и газы передают давление…', ['Только вниз','Только вверх','Равномерно во все стороны','По-разному в разных точках'], 2)
    + '</div>');

  h += readButton('p30');
  body.innerHTML = h;

  function draw30(){
    const S1 = +document.getElementById('p30-S1-r').value;
    const S2 = +document.getElementById('p30-S2-r').value;
    const F1 = +document.getElementById('p30-F1-r').value;
    document.getElementById('p30-S1').textContent = S1;
    document.getElementById('p30-S2').textContent = S2;
    document.getElementById('p30-F1').textContent = F1;
    const F2 = F1 * S2 / S1;
    // Render via PHYS.hydraulicPress if available
    if(window.PHYS && window.PHYS.hydraulicPress){
      const svgInner = window.PHYS.hydraulicPress(40, 30, S1, S2, F1, '#60a5fa');
      document.getElementById('p30-svg').innerHTML = svgInner;
    }
    document.getElementById('p30-info').innerHTML = '$F_1 = ' + F1 + '$ Н, $S_1 = ' + S1 + '$ см², $S_2 = ' + S2 + '$ см² &nbsp;→&nbsp; $F_2 = F_1 \\cdot S_2/S_1 = $ <b>' + F2.toFixed(0) + '</b> Н <span style="color:#10b981;font-weight:700">(в ' + (S2/S1).toFixed(1) + ' раз больше)</span>';
    renderMath(document.getElementById('p30-info'));
  }
  ['p30-S1-r','p30-S2-r','p30-F1-r'].forEach(id => document.getElementById(id).addEventListener('input', draw30));
  draw30();

  const upd30c = () => {
    const F1 = +document.getElementById('p30c-F1-r').value;
    const S1 = +document.getElementById('p30c-S1-r').value;
    const S2 = +document.getElementById('p30c-S2-r').value;
    document.getElementById('p30c-F1').textContent = F1;
    document.getElementById('p30c-S1').textContent = S1;
    document.getElementById('p30c-S2').textContent = S2;
    const p = F1 / S1;
    const F2 = p * S2;
    document.getElementById('p30c-p').textContent = p.toFixed(1);
    document.getElementById('p30c-F2').textContent = F2.toFixed(0);
    document.getElementById('p30c-gain').innerHTML = 'Выигрыш в силе: <b style="color:#10b981">в ' + (S2/S1).toFixed(1) + ' раз</b>';
  };
  ['p30c-F1-r','p30c-S1-r','p30c-S2-r'].forEach(id => document.getElementById(id).addEventListener('input', upd30c));
  upd30c();

  wireDnd('p30-dnd', [
    { id:'a1', cat:'hyd' },{ id:'a2', cat:'hyd' },{ id:'a3', cat:'hyd' },
    { id:'a4', cat:'not' },{ id:'a5', cat:'not' },{ id:'a6', cat:'hyd' }
  ]);
  wireQuiz('p30-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p30'); });
  wireReadBtn('p30');
  renderMath(body);
}

/* ========================================================== */
/* §31 — Давление жидкости (гидростатическое)                 */
/* ========================================================== */
function add_p31(){
  const body = document.getElementById('p31-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Жидкость давит весом', '§ 31.1',
    'Жидкость, имеющая массу, давит на дно сосуда и на боковые стенки. '
    + 'Это давление возникает <b>не от тела</b>, как у твёрдых, а от <b>собственного веса жидкости</b>.<br><br>'
    + 'Формула гидростатического давления на глубине $h$:<br>'
    + '$$p = \\rho g h$$<br>'
    + 'где $\\rho$ — плотность жидкости (кг/м³), $g \\approx 9{,}8$ Н/кг, $h$ — глубина (м).');

  h += makeCard('rule', 'Гидростатический парадокс', '§ 31.2',
    '<b>Удивительный факт:</b> давление на дно <b>не зависит</b> от формы сосуда и от количества жидкости. '
    + 'Если налить $h$ воды в узкую трубку или в широкий бассейн — давление на дно <b>одинаково</b>.<br><br>'
    + 'Зависит только от: плотности жидкости и глубины. Это <b>гидростатический парадокс</b> Паскаля.');

  h += makeCard('example', 'Давление на глубине', '§ 31.3',
    '<table style="width:100%;border-collapse:collapse;font-size:.92rem">'
    + '<tr style="background:' + ACCENT_SOFT + '"><th style="padding:6px 10px;text-align:left;border-bottom:2px solid ' + ACCENT + '">Глубина</th><th style="padding:6px 10px;text-align:right;border-bottom:2px solid ' + ACCENT + '">p (вода)</th></tr>'
    + [['$1$ м','9,8 кПа'],['$10$ м','98 кПа ≈ 1 атм'],['$100$ м','≈ 10 атм'],['$1000$ м','≈ 100 атм (батискаф)'],['Дно Марианской впадины ($\\approx 11$ км)','≈ 1100 атм']].map(r =>
        '<tr><td style="padding:5px 10px;border-bottom:1px solid ' + ACCENT_SOFT + '">' + r[0] + '</td><td style="padding:5px 10px;text-align:right;border-bottom:1px solid ' + ACCENT_SOFT + ';font-family:JetBrains Mono,monospace;font-weight:700">' + r[1] + '</td></tr>').join('')
    + '</table>');

  /* IV-1 КАЛЬК */
  h += wgWrap('p31-iv1', 'КАЛЬК', 'Калькулятор $p = \\rho g h$', 'Меняй плотность и глубину.',
    '<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px">'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Жидкость:<select id="p31-rho" style="width:100%;margin-top:6px;padding:6px;border-radius:6px;border:1px solid ' + ACCENT_SOFT + ';font-family:inherit"><option value="1000" selected>Вода (1000 кг/м³)</option><option value="800">Бензин (800)</option><option value="1030">Морская вода (1030)</option><option value="13600">Ртуть (13600)</option><option value="900">Спирт (900)</option></select></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Глубина $h$, м: <b id="p31-h" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">3</b><input type="range" id="p31-h-r" min="0.1" max="100" step="0.1" value="3" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '</div>'
    + '<div style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:12px 14px;font-size:.94rem;line-height:1.65">'
    +   '$p = \\rho g h = $ <b id="p31-p" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">29 400</b> Па $= $ <b id="p31-pkpa" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">29.4</b> кПа $\\approx $ <b id="p31-patm" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">0.29</b> атм'
    + '</div>');

  /* IV-2 СИМ: гидростатический парадокс — 3 сосуда */
  h += wgWrap('p31-iv2', 'СИМ', 'Гидростатический парадокс: 3 сосуда', 'Один уровень воды — одинаковое давление на дно. Форма не важна!',
    '<svg id="p31-svg" viewBox="0 0 360 200" width="100%" style="max-width:500px;display:block;margin:0 auto;background:#fffbeb;border-radius:9px;border:1px solid ' + ACCENT_SOFT + '"></svg>'
    + '<div style="margin-top:10px"><label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Уровень воды, см: <b id="p31s-h" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">10</b><input type="range" id="p31s-h-r" min="2" max="15" step="1" value="10" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label></div>'
    + '<div id="p31s-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:10px 14px;margin-top:8px;font-size:.92rem;text-align:center"></div>');

  /* IV-3 DnD */
  h += wgWrap('p31-iv3', 'DnD', 'От чего зависит p жидкости?', '',
    dndPool('p31-dnd', [
      { id:'a1', cat:'yes', html:'Глубина $h$' },
      { id:'a2', cat:'yes', html:'Плотность жидкости $\\rho$' },
      { id:'a3', cat:'yes', html:'Ускорение $g$' },
      { id:'a4', cat:'no',  html:'Форма сосуда' },
      { id:'a5', cat:'no',  html:'Количество жидкости в сосуде' },
      { id:'a6', cat:'no',  html:'Площадь дна' }
    ], [
      { cat:'yes', label:'p ЗАВИСИТ' },
      { cat:'no',  label:'p НЕ зависит' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p31-iv4', 'ТРН', 'Тренажёр §31', '',
    '<div id="p31-tr-host">'
    + quizQuestion('p31-tr', 0, 'Давление воды на дне бассейна глубиной $5$ м ($g = 10$, $\\rho = 1000$)?', ['5 кПа','50 кПа','500 кПа','5000 кПа'], 1, '$p = 1000 \\cdot 10 \\cdot 5 = 50\\,000$ Па $= 50$ кПа.')
    + quizQuestion('p31-tr', 1, 'Жидкость глубиной $h = 2$ м, $\\rho = 800$ кг/м³ (бензин). $p$ (g=10)?', ['8 кПа','16 кПа','80 кПа','160 кПа'], 1, '$p = 800 \\cdot 10 \\cdot 2 = 16\\,000$ Па.')
    + quizQuestion('p31-tr', 2, 'В двух разных сосудах вода на одной глубине $h = 1$ м. Где $p$ больше?', ['В большом','В малом','Одинаковое','Зависит от формы'], 2)
    + quizQuestion('p31-tr', 3, 'В каком случае давление на дно больше: $1$ м воды или $1$ м ртути ($\\rho_{рт} = 13600$)?', ['Воды','Ртути','Одинаково','Воды в 13,6 раз'], 1)
    + quizQuestion('p31-tr', 4, 'Дайвер на $h = 30$ м в море ($\\rho = 1030$). Гидростат. давление?', ['3 атм','5 атм','3,1 атм','30 атм'], 2, '$p = 1030 \\cdot 9{,}8 \\cdot 30 \\approx 303\\,000$ Па $\\approx 3$ атм.')
    + '</div>');

  h += readButton('p31');
  body.innerHTML = h;

  // §31 IV-1
  const upd31 = () => {
    const rho = +document.getElementById('p31-rho').value;
    const hVal = +document.getElementById('p31-h-r').value;
    document.getElementById('p31-h').textContent = hVal;
    const p = rho * 9.8 * hVal;
    document.getElementById('p31-p').textContent = p.toLocaleString('ru-RU', { maximumFractionDigits: 0 });
    document.getElementById('p31-pkpa').textContent = (p / 1000).toFixed(2);
    document.getElementById('p31-patm').textContent = (p / 101325).toFixed(3);
  };
  document.getElementById('p31-rho').addEventListener('change', upd31);
  document.getElementById('p31-h-r').addEventListener('input', upd31);
  upd31();

  // §31 IV-2 — paradox
  function draw31s(){
    const hCm = +document.getElementById('p31s-h-r').value;
    document.getElementById('p31s-h').textContent = hCm;
    const W = 360, H = 200, baseY = 170, pxPerCm = 9;
    const liqColor = '#60a5fa';
    let s = '';
    // Дно
    s += '<rect x="20" y="' + baseY + '" width="320" height="10" fill="#475569"/>';
    function vessel(x, w, kind){
      const wallH = 130;
      const topY = baseY - wallH;
      let g = '';
      if(kind === 'cyl'){
        g += '<line x1="' + x + '" y1="' + topY + '" x2="' + x + '" y2="' + baseY + '" stroke="#0f172a" stroke-width="2"/>';
        g += '<line x1="' + (x+w) + '" y1="' + topY + '" x2="' + (x+w) + '" y2="' + baseY + '" stroke="#0f172a" stroke-width="2"/>';
        // Жидкость
        const fillH = hCm * pxPerCm;
        g += '<rect x="' + (x+1) + '" y="' + (baseY - fillH) + '" width="' + (w-2) + '" height="' + fillH + '" fill="' + liqColor + '" opacity="0.7"/>';
      } else if(kind === 'wide'){
        // Конус расширяющийся вверх
        g += '<line x1="' + (x+5) + '" y1="' + topY + '" x2="' + x + '" y2="' + baseY + '" stroke="#0f172a" stroke-width="2"/>';
        g += '<line x1="' + (x+w-5) + '" y1="' + topY + '" x2="' + (x+w) + '" y2="' + baseY + '" stroke="#0f172a" stroke-width="2"/>';
        const fillH = hCm * pxPerCm;
        g += '<polygon points="' + (x+1) + ',' + baseY + ' ' + (x+w-1) + ',' + baseY + ' ' + (x+w-1-fillH*0.05) + ',' + (baseY-fillH) + ' ' + (x+1+fillH*0.05) + ',' + (baseY-fillH) + '" fill="' + liqColor + '" opacity="0.7"/>';
      } else { // narrow — сужающийся
        g += '<line x1="' + x + '" y1="' + topY + '" x2="' + (x+10) + '" y2="' + baseY + '" stroke="#0f172a" stroke-width="2"/>';
        g += '<line x1="' + (x+w) + '" y1="' + topY + '" x2="' + (x+w-10) + '" y2="' + baseY + '" stroke="#0f172a" stroke-width="2"/>';
        const fillH = hCm * pxPerCm;
        g += '<polygon points="' + (x+10) + ',' + baseY + ' ' + (x+w-10) + ',' + baseY + ' ' + (x+w-10-fillH*0.05) + ',' + (baseY-fillH) + ' ' + (x+10+fillH*0.05) + ',' + (baseY-fillH) + '" fill="' + liqColor + '" opacity="0.7"/>';
      }
      return g;
    }
    s += vessel(40, 60, 'cyl');
    s += vessel(150, 60, 'wide');
    s += vessel(260, 60, 'narrow');
    // Линия уровня
    const lvlY = baseY - hCm * pxPerCm;
    s += '<line x1="20" y1="' + lvlY + '" x2="340" y2="' + lvlY + '" stroke="#dc2626" stroke-width="1.5" stroke-dasharray="4 3"/>';
    s += '<text x="20" y="' + (lvlY-3) + '" font-family="Inter,sans-serif" font-size="10" font-weight="700" fill="#dc2626">уровень = ' + hCm + ' см</text>';
    document.getElementById('p31-svg').innerHTML = s;
    const p = 1000 * 9.8 * (hCm / 100);
    document.getElementById('p31s-info').innerHTML = 'На дне всех трёх сосудов: <b>$p = \\rho g h = ' + Math.round(p).toLocaleString('ru-RU') + '$ Па</b>. Форма не важна!';
    renderMath(document.getElementById('p31s-info'));
  }
  document.getElementById('p31s-h-r').addEventListener('input', draw31s);
  draw31s();

  wireDnd('p31-dnd', [
    { id:'a1', cat:'yes' },{ id:'a2', cat:'yes' },{ id:'a3', cat:'yes' },
    { id:'a4', cat:'no' },{ id:'a5', cat:'no' },{ id:'a6', cat:'no' }
  ]);
  wireQuiz('p31-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p31'); });
  wireReadBtn('p31');
  renderMath(body);
}

/* ========================================================== */
/* §32 — Сообщающиеся сосуды                                  */
/* ========================================================== */
function add_p32(){
  const body = document.getElementById('p32-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Что это такое', '§ 32.1',
    '<b>Сообщающиеся сосуды</b> — это два или несколько сосудов, соединённых внизу так, что жидкость может переходить между ними.<br><br>'
    + '<b>Главный закон:</b> в сообщающихся сосудах <b>однородная</b> жидкость устанавливается на <b>одном уровне</b> — независимо от формы сосудов.');

  h += makeCard('rule', 'Почему именно так', '§ 32.2',
    'На одной глубине давление одно и то же ($p = \\rho g h$). Если уровни жидкости разные, то и давления у дна разные, '
    + 'и жидкость будет перетекать в сторону <b>меньшего</b> давления — пока уровни не сравняются.<br><br>'
    + 'Для <b>разнородных</b> жидкостей: высоты в коленах <b>обратно пропорциональны плотностям</b>: '
    + '$\\rho_1 h_1 = \\rho_2 h_2$.');

  h += makeCard('example', 'Применения', '§ 32.3',
    '<ul style="padding-left:20px;margin:5px 0">'
    + '<li><b>Водопровод</b> в высотных домах — давление от водонапорной башни.</li>'
    + '<li><b>Шлюзы</b> для пропуска судов.</li>'
    + '<li><b>Чайник с носиком</b>: чай в чайнике и в носике на одном уровне.</li>'
    + '<li><b>Кофейник, лейка</b>.</li>'
    + '<li><b>Артезианские колодцы</b>: вода поднимается естественным образом.</li>'
    + '</ul>');

  /* IV-1 СИМ: сообщающиеся сосуды */
  h += wgWrap('p32-iv1', 'СИМ', 'Однородная жидкость → один уровень', 'Меняй уровень — оба сосуда уравниваются.',
    '<div style="margin-bottom:10px"><label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Уровень воды, px: <b id="p32-l" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">100</b><input type="range" id="p32-l-r" min="40" max="160" step="2" value="100" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label></div>'
    + '<div style="display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap">'
    +   '<button class="p32-shape" data-a="cylinder" data-b="cylinder" type="button" style="background:' + ACCENT + ';color:#fff;border:none;padding:7px 14px;border-radius:9px;cursor:pointer;font-weight:700;font-family:inherit;font-size:.84rem">Два цилиндра</button>'
    +   '<button class="p32-shape" data-a="cylinder" data-b="wide" type="button" style="background:#fff;color:' + ACCENT + ';border:2px solid ' + ACCENT + ';padding:7px 14px;border-radius:9px;cursor:pointer;font-weight:700;font-family:inherit;font-size:.84rem">Цилиндр + широкий</button>'
    +   '<button class="p32-shape" data-a="narrow" data-b="wide" type="button" style="background:#fff;color:' + ACCENT + ';border:2px solid ' + ACCENT + ';padding:7px 14px;border-radius:9px;cursor:pointer;font-weight:700;font-family:inherit;font-size:.84rem">Узкий + широкий</button>'
    + '</div>'
    + '<svg id="p32-svg" viewBox="0 0 320 200" width="100%" style="max-width:500px;display:block;margin:0 auto;background:#fffbeb;border-radius:9px;border:1px solid ' + ACCENT_SOFT + '"></svg>'
    + '<div id="p32-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:10px 14px;margin-top:8px;font-size:.92rem;text-align:center">Уровень одинаков, потому что давления на дне равны.</div>');

  /* IV-2 КВИЗ */
  h += wgWrap('p32-iv2', 'КВИЗ', 'Сообщающиеся сосуды', '',
    '<div id="p32-q-host">'
    + quizQuestion('p32-q', 0, 'В двух соединённых сосудах одна и та же вода. Что верно?', ['Уровни могут быть разными','Уровни всегда одинаковы','Зависит от объёмов','Зависит от формы'], 1)
    + quizQuestion('p32-q', 1, 'Чайник: уровень чая в носике и корпусе…', ['В носике выше','В корпусе выше','На одном уровне','Зависит от наклона'], 2)
    + quizQuestion('p32-q', 2, 'Если жидкости разные (вода и масло), уровни…', ['Одинаковые','Зависят от плотностей (обратно пропорц.)','Зависят от массы','Жидкость смешается'], 1)
    + '</div>');

  /* IV-3 DnD */
  h += wgWrap('p32-iv3', 'DnD', 'Что использует сообщающиеся сосуды?', '',
    dndPool('p32-dnd', [
      { id:'a1', cat:'yes', html:'Шлюз для судов' },
      { id:'a2', cat:'yes', html:'Лейка для воды' },
      { id:'a3', cat:'yes', html:'Водомерное стекло котла' },
      { id:'a4', cat:'no',  html:'Микроволновка' },
      { id:'a5', cat:'no',  html:'Электронные весы' },
      { id:'a6', cat:'yes', html:'Артезианский колодец' }
    ], [
      { cat:'yes', label:'Применение принципа' },
      { cat:'no',  label:'Не относится' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p32-iv4', 'ТРН', 'Тренажёр §32', '',
    '<div id="p32-tr-host">'
    + quizQuestion('p32-tr', 0, 'В сообщающиеся сосуды налита однородная вода. Если в одном уровень $h$, то во втором…', ['$h/2$','$h$','$2h$','Любой'], 1)
    + quizQuestion('p32-tr', 1, 'Если узкий сосуд тоньше широкого в 5 раз, а вода поднимается в широком на $10$ см, то в узком…', ['10 см','50 см','5 см','100 см'], 0, 'Уровни одной жидкости равны независимо от площади.')
    + quizQuestion('p32-tr', 2, 'В U-трубке с двух сторон ртуть. Налили воду — она «выдавила» ртуть. В каком колене ртуть выше?', ['Где налита вода','В противоположном','Одинаково','Ртуть выльется'], 1)
    + quizQuestion('p32-tr', 3, 'Принцип сообщающихся сосудов работает потому, что…', ['Жидкость лёгкая','Давление зависит только от $h$ и $\\rho$','Жидкость холодная','Стенки гладкие'], 1)
    + '</div>');

  h += readButton('p32');
  body.innerHTML = h;

  // §32 IV-1
  let curShape = { a:'cylinder', b:'cylinder' };
  function draw32(){
    const lvlY = 220 - +document.getElementById('p32-l-r').value; // px (220 — низ + base)
    // На самом деле проще задать levelY напрямую от верха svg
    const Lpx = +document.getElementById('p32-l-r').value;
    document.getElementById('p32-l').textContent = Lpx;
    if(window.PHYS && window.PHYS.connectedVessels){
      const levelY = 200 - Lpx; // viewBox 200H, уровень от низа
      const svgInner = window.PHYS.connectedVessels(50, 30, curShape.a, curShape.b, levelY, '#60a5fa');
      document.getElementById('p32-svg').innerHTML = svgInner;
    }
  }
  body.querySelectorAll('.p32-shape').forEach(btn => btn.addEventListener('click', () => {
    body.querySelectorAll('.p32-shape').forEach(b => { b.style.background = '#fff'; b.style.color = ACCENT; b.style.border = '2px solid ' + ACCENT; });
    btn.style.background = ACCENT; btn.style.color = '#fff'; btn.style.border = 'none';
    curShape = { a: btn.dataset.a, b: btn.dataset.b };
    draw32();
  }));
  document.getElementById('p32-l-r').addEventListener('input', draw32);
  draw32();

  wireDnd('p32-dnd', [
    { id:'a1', cat:'yes' },{ id:'a2', cat:'yes' },{ id:'a3', cat:'yes' },
    { id:'a4', cat:'no' },{ id:'a5', cat:'no' },{ id:'a6', cat:'yes' }
  ]);
  wireQuiz('p32-q-host', () => { if(window.addXp) window.addXp(10, 'q-p32'); });
  wireQuiz('p32-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p32'); });
  wireReadBtn('p32');
  renderMath(body);
}

/* ========================================================== */
/* §33 — Газы и их вес                                        */
/* ========================================================== */
function add_p33(){
  const body = document.getElementById('p33-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'У воздуха есть вес', '§ 33.1',
    'Любой газ состоит из молекул, у молекул есть масса — значит, и сам газ имеет <b>массу</b> и <b>вес</b>.<br><br>'
    + 'Плотность воздуха у поверхности Земли: $\\rho_{возд} \\approx 1{,}29$ кг/м³.');

  h += makeCard('rule', 'Сколько весит воздух', '§ 33.2',
    'Масса газа в сосуде: $m = \\rho V$.<br>'
    + 'Вес: $P = m g$.<br><br>'
    + 'Воздух в комнате $5 \\times 4 \\times 3$ м$^3 = 60$ м³ имеет массу $m \\approx 1{,}29 \\cdot 60 = 77$ кг — '
    + 'столько весит взрослый человек!');

  h += makeCard('example', 'Как доказать вес воздуха', '§ 33.3',
    '<b>Опыт:</b> взвесить стеклянный шар на чувствительных весах, затем выкачать из него воздух насосом '
    + 'и взвесить снова. Шар без воздуха окажется <b>легче</b> на массу удалённого воздуха.');

  /* IV-1 КАЛЬК: масса воздуха в комнате */
  h += wgWrap('p33-iv1', 'КАЛЬК', 'Сколько весит воздух в комнате?', '',
    '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-bottom:10px">'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Длина $a$, м: <b id="p33-a" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">5</b><input type="range" id="p33-a-r" min="2" max="20" step="0.5" value="5" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Ширина $b$, м: <b id="p33-b" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">4</b><input type="range" id="p33-b-r" min="2" max="20" step="0.5" value="4" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '<label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Высота $c$, м: <b id="p33-c" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">3</b><input type="range" id="p33-c-r" min="2" max="10" step="0.5" value="3" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label>'
    + '</div>'
    + '<div style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:12px 14px;font-size:.94rem;line-height:1.7">'
    +   '$V = abc = $ <b id="p33-V" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">60</b> м³ &middot; $m = \\rho V = $ <b id="p33-m" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">77</b> кг &middot; $P = mg = $ <b id="p33-P" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">756</b> Н'
    +   '<div id="p33-cmp" style="font-size:.84rem;color:#475569;margin-top:4px"></div>'
    + '</div>');

  /* IV-2 КВИЗ */
  h += wgWrap('p33-iv2', 'КВИЗ', 'Вес газов', '',
    '<div id="p33-q-host">'
    + quizQuestion('p33-q', 0, 'Воздух имеет массу?', ['Нет, газ невесом','Да, как и любое вещество','Только при низкой T','Только под давлением'], 1)
    + quizQuestion('p33-q', 1, 'Плотность воздуха у Земли примерно равна:', ['0,001 кг/м³','1,29 кг/м³','100 кг/м³','1000 кг/м³'], 1)
    + quizQuestion('p33-q', 2, 'Воздух в комнате 50 м³ весит примерно:', ['65 г','650 г','65 кг','650 кг'], 2)
    + '</div>');

  /* IV-3 DnD */
  h += wgWrap('p33-iv3', 'DnD', 'Тяжелее или легче воздуха?', '',
    dndPool('p33-dnd', [
      { id:'a1', cat:'lighter', html:'Гелий (He, 0,18 кг/м³)' },
      { id:'a2', cat:'lighter', html:'Водород (H₂, 0,09 кг/м³)' },
      { id:'a3', cat:'lighter', html:'Метан (CH₄, 0,72 кг/м³)' },
      { id:'a4', cat:'heavier', html:'Углекислый газ (CO₂, 1,98)' },
      { id:'a5', cat:'heavier', html:'Хлор (Cl₂, 3,21)' },
      { id:'a6', cat:'heavier', html:'Радон (Rn, 9,73)' }
    ], [
      { cat:'lighter', label:'Легче воздуха ($\\rho < 1{,}29$)' },
      { cat:'heavier', label:'Тяжелее воздуха ($\\rho > 1{,}29$)' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p33-iv4', 'ТРН', 'Тренажёр §33', '',
    '<div id="p33-tr-host">'
    + quizQuestion('p33-tr', 0, 'Воздух в комнате $V = 100$ м³ ($\\rho = 1{,}29$). Масса в кг?', ['12,9','129','1290','1,29'], 1, '$m = 1{,}29 \\cdot 100 = 129$ кг.')
    + quizQuestion('p33-tr', 1, 'Гелий легче воздуха в ~7 раз. Шарик с гелием…', ['Тяжелее','Не отличается','Легче — может летать','Падает быстрее'], 2)
    + quizQuestion('p33-tr', 2, 'Углекислый газ скапливается…', ['У потолка','У пола','Равномерно','Не скапливается'], 1, 'Он тяжелее воздуха — оседает вниз.')
    + quizQuestion('p33-tr', 3, '$m$ воздуха в баллоне $0{,}001$ м³ ($\\rho = 1{,}29$)?', ['0,00129 кг','0,0129 кг','1,29 г','Все верны 1 и 3'], 3, '$m = 1{,}29 \\cdot 0{,}001 = 0{,}00129$ кг $= 1{,}29$ г.')
    + '</div>');

  h += readButton('p33');
  body.innerHTML = h;

  const upd33 = () => {
    const a = +document.getElementById('p33-a-r').value;
    const b = +document.getElementById('p33-b-r').value;
    const c = +document.getElementById('p33-c-r').value;
    document.getElementById('p33-a').textContent = a;
    document.getElementById('p33-b').textContent = b;
    document.getElementById('p33-c').textContent = c;
    const V = a * b * c;
    const m = V * 1.29;
    const P = m * 9.8;
    document.getElementById('p33-V').textContent = V.toFixed(1);
    document.getElementById('p33-m').textContent = m.toFixed(1);
    document.getElementById('p33-P').textContent = P.toFixed(0);
    let cmp;
    if(m < 30) cmp = 'Меньше школьника.';
    else if(m < 80) cmp = 'Примерно вес взрослого человека.';
    else if(m < 200) cmp = 'Как пара человек.';
    else if(m < 1000) cmp = 'Как пианино или мотоцикл.';
    else cmp = 'Тяжелее тонны — как автомобиль!';
    document.getElementById('p33-cmp').textContent = cmp;
  };
  ['p33-a-r','p33-b-r','p33-c-r'].forEach(id => document.getElementById(id).addEventListener('input', upd33));
  upd33();

  wireDnd('p33-dnd', [
    { id:'a1', cat:'lighter' },{ id:'a2', cat:'lighter' },{ id:'a3', cat:'lighter' },
    { id:'a4', cat:'heavier' },{ id:'a5', cat:'heavier' },{ id:'a6', cat:'heavier' }
  ]);
  wireQuiz('p33-q-host', () => { if(window.addXp) window.addXp(10, 'q-p33'); });
  wireQuiz('p33-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p33'); });
  wireReadBtn('p33');
  renderMath(body);
}

/* ========================================================== */
/* §34 — Атмосферное давление                                 */
/* ========================================================== */
function add_p34(){
  const body = document.getElementById('p34-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Атмосфера давит на нас', '§ 34.1',
    'Земля окружена слоем воздуха — <b>атмосферой</b>. Этот воздух имеет вес и давит на всё, что находится на поверхности Земли.<br><br>'
    + '<b>Нормальное атмосферное давление</b>: $p_0 = 101\\,325$ Па $= 760$ мм рт. ст. $\\approx 1$ атм.');

  h += makeCard('rule', 'Опыт Торричелли (1643)', '§ 34.2',
    'Эванжелиста Торричелли перевернул трубку, наполненную ртутью, в чашку с ртутью. '
    + 'Ртуть в трубке опустилась до высоты $\\approx 760$ мм над уровнем чашки. '
    + 'Это значит, что атмосфера снаружи давила на чашку с такой же силой, как столб ртути высотой $760$ мм.<br><br>'
    + 'Так была открыта единица «миллиметр ртутного столба» (мм рт. ст.).');

  h += makeCard('example', 'С высотой давление падает', '§ 34.3',
    'На каждые $\\approx 12$ м подъёма атмосферное давление уменьшается на $1$ мм рт. ст. (на малых высотах).<br><br>'
    + 'Поэтому на вершине Эльбруса ($5642$ м) давление $\\approx 400$ мм рт. ст. — почти в 2 раза меньше уровня моря. '
    + 'Космонавтам в открытом космосе нужны скафандры — давление за бортом $\\approx 0$.');

  /* IV-1 СИМ: ртутный барометр Торричелли */
  h += wgWrap('p34-iv1', 'СИМ', 'Барометр Торричелли', 'Меняй атмосферное давление — увидь высоту столба ртути.',
    '<div style="margin-bottom:10px"><label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Давление, мм рт. ст.: <b id="p34-mm" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">760</b><input type="range" id="p34-mm-r" min="400" max="800" step="5" value="760" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label></div>'
    + '<div style="display:grid;grid-template-columns:160px 1fr;gap:14px;align-items:center">'
    +   '<svg id="p34-svg" viewBox="0 0 160 380" width="100%" style="max-width:180px;display:block"></svg>'
    +   '<div id="p34-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:12px 14px;font-size:.94rem;line-height:1.7"></div>'
    + '</div>');

  /* IV-2 КАЛЬК: высота → давление */
  h += wgWrap('p34-iv2', 'КАЛЬК', 'Давление на высоте', 'На каждые ~12 м давление падает на 1 мм рт. ст.',
    '<div style="margin-bottom:10px"><label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Высота над уровнем моря, м: <b id="p34c-H" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">0</b><input type="range" id="p34c-H-r" min="0" max="5000" step="50" value="0" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label></div>'
    + '<div style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:12px 14px;font-size:.94rem;line-height:1.65">'
    +   '$\\Delta p = H/12 = $ <b id="p34c-dp" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">0</b> мм рт. ст. &middot; $p \\approx $ <b id="p34c-p" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">760</b> мм рт. ст. $\\approx $ <b id="p34c-pkpa" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">101.3</b> кПа'
    +   '<div id="p34c-place" style="font-size:.84rem;color:#475569;margin-top:5px"></div>'
    + '</div>');

  /* IV-3 DnD */
  h += wgWrap('p34-iv3', 'DnD', 'Где давление какое?', '',
    dndPool('p34-dnd', [
      { id:'a1', cat:'norm', html:'Уровень моря (≈760 мм)' },
      { id:'a2', cat:'low',  html:'Эверест 8 848 м (≈250 мм)' },
      { id:'a3', cat:'high', html:'Шахта −500 м (≈800 мм)' },
      { id:'a4', cat:'low',  html:'Самолёт 10 км (≈190 мм)' },
      { id:'a5', cat:'norm', html:'Город Минск (≈750 мм)' },
      { id:'a6', cat:'high', html:'Мёртвое море −430 м (≈800 мм)' }
    ], [
      { cat:'high', label:'Выше нормы (ниже уровня моря)' },
      { cat:'norm', label:'Около нормы (~760)' },
      { cat:'low',  label:'Ниже нормы (горы / небо)' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p34-iv4', 'ТРН', 'Тренажёр §34', '',
    '<div id="p34-tr-host">'
    + quizQuestion('p34-tr', 0, 'Нормальное атм. давление в мм рт. ст.?', ['100','500','760','1000'], 2)
    + quizQuestion('p34-tr', 1, 'Что произойдёт с давлением, если подняться на 360 м вверх?', ['Не изменится','Упадёт на ~30 мм','Упадёт на ~3 мм','Возрастёт'], 1, '$\\Delta p = 360/12 = 30$ мм рт. ст.')
    + quizQuestion('p34-tr', 2, 'Кто открыл атм. давление?', ['Архимед','Торричелли','Галилей','Паскаль'], 1)
    + quizQuestion('p34-tr', 3, 'Высота Эвереста ~9 км. Атм. давление там примерно:', ['0 мм','100 мм','250 мм','760 мм'], 2)
    + quizQuestion('p34-tr', 4, '$p = 750$ мм рт. ст. в кПа? (1 мм ≈ 133 Па)', ['7,5','75','100','750'], 2, '$750 \\cdot 133 \\approx 100\\,000$ Па $= 100$ кПа.')
    + '</div>');

  h += readButton('p34');
  body.innerHTML = h;

  // §34 IV-1 — use PHYS.mercuryBarometer
  function draw34(){
    const mm = +document.getElementById('p34-mm-r').value;
    document.getElementById('p34-mm').textContent = mm;
    if(window.PHYS && window.PHYS.mercuryBarometer){
      const inner = window.PHYS.mercuryBarometer(80, 30, mm);
      document.getElementById('p34-svg').innerHTML = inner;
    }
    const pa = mm * 133.322;
    let descr;
    if(mm > 770) descr = '<b>Выше нормы</b> — антициклон, ясная погода (или вы в шахте/долине).';
    else if(mm > 745) descr = '<b>Норма</b> — обычное давление у поверхности Земли.';
    else if(mm > 600) descr = '<b>Ниже нормы</b> — горы, циклон, плохая погода.';
    else descr = '<b>Очень низкое</b> — высокогорье или большая высота.';
    document.getElementById('p34-info').innerHTML = '<b>' + mm + '</b> мм рт. ст. $= $ <b>' + Math.round(pa).toLocaleString('ru-RU') + '</b> Па $\\approx $ <b>' + (pa/1000).toFixed(1) + '</b> кПа<br><br>' + descr;
    renderMath(document.getElementById('p34-info'));
  }
  document.getElementById('p34-mm-r').addEventListener('input', draw34);
  draw34();

  // §34 IV-2
  const upd34c = () => {
    const H = +document.getElementById('p34c-H-r').value;
    document.getElementById('p34c-H').textContent = H;
    const dp = H / 12;
    const p = Math.max(0, 760 - dp);
    const pa = p * 133.322;
    document.getElementById('p34c-dp').textContent = dp.toFixed(1);
    document.getElementById('p34c-p').textContent = p.toFixed(0);
    document.getElementById('p34c-pkpa').textContent = (pa/1000).toFixed(1);
    let place;
    if(H < 100) place = 'Уровень моря — норма.';
    else if(H < 500) place = 'Холмистая местность.';
    else if(H < 2000) place = 'Среднегорье (Кавказ, Альпы предгорья).';
    else if(H < 4000) place = 'Высокогорье. Может быть «горная болезнь».';
    else place = 'Очень высоко — Эверест и выше. Нужен кислород.';
    document.getElementById('p34c-place').textContent = place;
  };
  document.getElementById('p34c-H-r').addEventListener('input', upd34c);
  upd34c();

  wireDnd('p34-dnd', [
    { id:'a1', cat:'norm' },{ id:'a2', cat:'low' },{ id:'a3', cat:'high' },
    { id:'a4', cat:'low' },{ id:'a5', cat:'norm' },{ id:'a6', cat:'high' }
  ]);
  wireQuiz('p34-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p34'); });
  wireReadBtn('p34');
  renderMath(body);
}

/* ========================================================== */
/* §35 — Барометры и манометры (ГЛАВНЫЙ ВИЗУАЛ ГЛАВЫ 4)       */
/* ========================================================== */
function add_p35(){
  const body = document.getElementById('p35-body');
  if(!body) return;
  let h = '';

  h += makeCard('theory', 'Три прибора для давления', '§ 35.1',
    '<ul style="padding-left:20px;margin:6px 0">'
    + '<li><b>Ртутный барометр Торричелли</b> — измеряет атм. давление через высоту ртутного столба.</li>'
    + '<li><b>Анероид</b> (греч. «без жидкости») — пружинный, стрелочный. Не разобьётся, удобен.</li>'
    + '<li><b>Жидкостный U-манометр</b> — измеряет давление газа относительно атмосферного, через разность уровней.</li>'
    + '</ul>');

  h += makeCard('rule', 'Где какой прибор', '§ 35.2',
    '<b>Барометры</b> (Торричелли, анероид) — измеряют <b>атмосферное</b> давление воздуха.<br>'
    + '<b>Манометр</b> — измеряет давление <b>газа в сосуде</b> или жидкости. '
    + 'Например, в шине, в баллоне с пропаном, в трубе с водой.<br>'
    + '<b>Кардиограф/тонометр</b> — давление в кровеносных сосудах.');

  h += makeCard('example', 'Манометр на баллоне', '§ 35.3',
    'В баллоне с газом давление много выше атмосферного. Манометр показывает <b>разность</b>: '
    + 'давление в баллоне минус $p_0$. Если шкала показывает $200$ кПа — фактическое давление в баллоне $200 + 100 = 300$ кПа.');

  /* IV-1 ГЛАВНЫЙ ВИЗУАЛ — все 3 прибора */
  h += wgWrap('p35-iv1', 'СИМ', 'Три прибора рядом — главный визуал', 'Одно и то же давление, три способа показать.',
    '<div style="margin-bottom:10px"><label style="display:block;font-size:.86rem;color:#475569;background:#fff;padding:8px 12px;border-radius:8px;border:1px solid ' + ACCENT_SOFT + '">Атм. давление, мм рт. ст.: <b id="p35-mm" style="color:' + ACCENT_D + ';font-family:JetBrains Mono,monospace">760</b><input type="range" id="p35-mm-r" min="720" max="800" step="2" value="760" style="display:block;width:100%;margin-top:6px;accent-color:' + ACCENT + '"></label></div>'
    + '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px">'
    +   '<div style="text-align:center"><div style="font-weight:700;font-size:.84rem;color:' + ACCENT_D + ';margin-bottom:6px">Торричелли</div><svg id="p35-merc" viewBox="0 0 140 340" width="100%" style="max-width:140px"></svg></div>'
    +   '<div style="text-align:center"><div style="font-weight:700;font-size:.84rem;color:' + ACCENT_D + ';margin-bottom:6px">Анероид</div><svg id="p35-aner" viewBox="0 0 180 200" width="100%" style="max-width:180px"></svg></div>'
    +   '<div style="text-align:center"><div style="font-weight:700;font-size:.84rem;color:' + ACCENT_D + ';margin-bottom:6px">U-манометр (газ vs $p_0$)</div><svg id="p35-uman" viewBox="0 0 120 160" width="100%" style="max-width:120px"></svg></div>'
    + '</div>'
    + '<div id="p35-info" style="background:' + ACCENT_SOFT + ';border-radius:9px;padding:10px 14px;margin-top:10px;font-size:.92rem;text-align:center"></div>');

  /* IV-2 КВИЗ */
  h += wgWrap('p35-iv2', 'КВИЗ', 'Какой прибор для чего', '',
    '<div id="p35-q-host">'
    + quizQuestion('p35-q', 0, 'Чем измеряют атм. давление?', ['Манометр','Барометр','Гигрометр','Термометр'], 1)
    + quizQuestion('p35-q', 1, 'Чем измеряют давление газа в шине?', ['Барометр','Манометр','Динамометр','Спидометр'], 1)
    + quizQuestion('p35-q', 2, 'Что меряет анероид?', ['Температура','Атм. давление','Скорость','Влажность'], 1)
    + quizQuestion('p35-q', 3, 'Что значит «анероид»?', ['Безжидкостный','Сложный','Точный','Электронный'], 0)
    + '</div>');

  /* IV-3 DnD */
  h += wgWrap('p35-iv3', 'DnD', 'Какой прибор для какой задачи?', '',
    dndPool('p35-dnd', [
      { id:'a1', cat:'baro',  html:'Атм. давление в школьном кабинете физики' },
      { id:'a2', cat:'baro',  html:'Давление перед сменой погоды' },
      { id:'a3', cat:'mano',  html:'Давление в шине автомобиля' },
      { id:'a4', cat:'mano',  html:'Давление в газовом баллоне' },
      { id:'a5', cat:'mano',  html:'Давление в водопроводе' },
      { id:'a6', cat:'baro',  html:'Давление на вершине горы' }
    ], [
      { cat:'baro', label:'Барометр' },
      { cat:'mano', label:'Манометр' }
    ]));

  /* IV-4 ТРН */
  h += wgWrap('p35-iv4', 'ТРН', 'Тренажёр §35', '',
    '<div id="p35-tr-host">'
    + quizQuestion('p35-tr', 0, '$1$ мм рт. ст. примерно равен:', ['13,3 Па','133 Па','1330 Па','13 300 Па'], 1)
    + quizQuestion('p35-tr', 1, '$200$ мм рт. ст. в Па:', ['2 660','26 600','266 000','2 660 000'], 1, '$200 \\cdot 133 \\approx 26\\,600$ Па.')
    + quizQuestion('p35-tr', 2, 'Манометр показал $\\Delta p = 150$ кПа. Полное давление в баллоне (при $p_0 = 100$ кПа):', ['50 кПа','100 кПа','150 кПа','250 кПа'], 3)
    + quizQuestion('p35-tr', 3, 'Высота ртутного столба $= 750$ мм. Что измеряем?', ['Атм. давление','Влажность','Температуру','Скорость ветра'], 0)
    + '</div>');

  h += readButton('p35');
  body.innerHTML = h;

  function draw35(){
    const mm = +document.getElementById('p35-mm-r').value;
    document.getElementById('p35-mm').textContent = mm;
    const pa = mm * 133.322;
    if(window.PHYS){
      if(window.PHYS.mercuryBarometer) document.getElementById('p35-merc').innerHTML = window.PHYS.mercuryBarometer(60, 30, mm);
      if(window.PHYS.aneroidBarometer) document.getElementById('p35-aner').innerHTML = window.PHYS.aneroidBarometer(90, 90, 70, pa);
      // Для U-манометра показываем разницу давлений (предположим, газ под давлением 800 мм)
      if(window.PHYS.uManometer){
        // deltaH в px, положительное — газ давит, правая часть ниже
        const gasPMm = mm + 30; // газ всегда выше атмосферного для иллюстрации
        const deltaH = (gasPMm - mm) * 1.5; // px
        document.getElementById('p35-uman').innerHTML = window.PHYS.uManometer(30, 20, 60, 130, deltaH, '#0891b2');
      }
    }
    document.getElementById('p35-info').innerHTML = 'Атм. давление: <b>' + mm + '</b> мм рт. ст. $= $ <b>' + Math.round(pa).toLocaleString('ru-RU') + '</b> Па. Все три прибора показывают одно и то же — по-разному.';
    renderMath(document.getElementById('p35-info'));
  }
  document.getElementById('p35-mm-r').addEventListener('input', draw35);
  draw35();

  wireDnd('p35-dnd', [
    { id:'a1', cat:'baro' },{ id:'a2', cat:'baro' },{ id:'a3', cat:'mano' },
    { id:'a4', cat:'mano' },{ id:'a5', cat:'mano' },{ id:'a6', cat:'baro' }
  ]);
  wireQuiz('p35-q-host', () => { if(window.addXp) window.addXp(10, 'q-p35'); });
  wireQuiz('p35-tr-host', () => { if(window.addXp) window.addXp(15, 'tr-p35'); });
  wireReadBtn('p35');
  renderMath(body);
}

/* ========================================================== */
/* Финал главы 4 — 7 боссов + ачивка «Властелин давления»     */
/* ========================================================== */
function add_final4(){
  const body = document.getElementById('final4-body');
  if(!body) return;
  let h = '';

  h += '<div style="background:linear-gradient(135deg,' + ACCENT_SOFT + ',#fff7ed);border:1.5px solid ' + ACCENT + ';border-radius:14px;padding:16px;margin-bottom:14px;text-align:center">'
    + '<div style="font-family:Unbounded,sans-serif;font-weight:800;font-size:1.12rem;color:' + ACCENT_D + '">Финал главы 4: победи 7 боссов</div>'
    + '<div style="font-size:.88rem;color:#475569;margin-top:5px">Реши все 7 задач — получишь ачивку «Властелин давления» и +50 XP.</div>'
    + '<div style="height:10px;background:#fff;border-radius:6px;overflow:hidden;margin-top:12px;border:1px solid ' + ACCENT_SOFT + '"><div id="ch4-fin-fill" style="height:100%;background:linear-gradient(90deg,' + ACCENT + ',' + ACCENT_D + ');width:0%;transition:width .4s"></div></div>'
    + '<div id="ch4-fin-lab" style="font-size:.84rem;color:#475569;margin-top:6px">Побеждено: 0 / 7</div>'
    + '</div>';

  const bosses = [
    { n:1, tag:'§28', title:'Давление кирпича',
      q:'Кирпич весом $F = 30$ Н стоит на грани площадью $S = 60$ см². Найди $p$ в Па.',
      hint:'$S = 60 \\cdot 10^{-4} = 0{,}006$ м². $p = F/S = 30/0{,}006 = 5000$ Па.',
      ans:5000, tol:50, step:'10' },
    { n:2, tag:'§30', title:'Гидравлический пресс',
      q:'Малый поршень $S_1 = 5$ см², большой $S_2 = 500$ см². На малый давит $F_1 = 80$ Н. Найди $F_2$ в Н.',
      hint:'$F_2 = F_1 \\cdot S_2/S_1 = 80 \\cdot 500/5 = 8000$ Н.',
      ans:8000, tol:50, step:'10' },
    { n:3, tag:'§31', title:'Дайвер на дне моря',
      q:'Глубина $h = 20$ м, $\\rho_{море} = 1030$ кг/м³, $g = 10$. Гидростат. давление в кПа?',
      hint:'$p = 1030 \\cdot 10 \\cdot 20 = 206\\,000$ Па $= 206$ кПа.',
      ans:206, tol:3, step:'1' },
    { n:4, tag:'§32', title:'Сообщающиеся сосуды',
      q:'В двух соединённых сосудах одна и та же вода. В одном уровень $h = 18$ см. В другом сосуде, в 3 раза шире, какой уровень будет в см?',
      hint:'Уровни однородной жидкости одинаковы независимо от ширины. $h = 18$ см.',
      ans:18, tol:0.5, step:'0.1' },
    { n:5, tag:'§34', title:'Давление на горе',
      q:'У моря $p_0 = 760$ мм рт. ст. Поднялись на $H = 480$ м. Давление стало:',
      hint:'$\\Delta p = H/12 = 40$ мм. $p = 760 - 40 = 720$ мм рт. ст.',
      ans:720, tol:3, step:'1' },
    { n:6, tag:'§35', title:'Барометр в Па',
      q:'Барометр-анероид показал $p = 740$ мм рт. ст. Сколько это Па? (округли до сотен)',
      hint:'$p = 740 \\cdot 133 \\approx 98\\,420$ Па $\\approx 98\\,400$ Па.',
      ans:98400, tol:300, step:'100' },
    { n:7, tag:'синтез', title:'Властелин давления',
      q:'Дайвер на $h = 30$ м в пресной воде ($\\rho = 1000$). Над водой $p_0 = 100$ кПа. Найди полное давление на дайвера в кПа.',
      hint:'$p_{гидр} = 1000 \\cdot 10 \\cdot 30 = 300\\,000$ Па $= 300$ кПа. Полное: $p = p_0 + p_{гидр} = 100 + 300 = 400$ кПа.',
      ans:400, tol:5, step:'1' }
  ];

  const STATE_KEY = 'physics7_ch4_final_bosses';
  let solved = {};
  try{ solved = JSON.parse(localStorage.getItem(STATE_KEY) || '{}') || {}; }catch(e){}

  bosses.forEach(b => {
    const isSolved = !!solved[b.n];
    h += '<div data-boss="' + b.n + '" style="background:#fff;border:2px solid ' + (isSolved ? '#10b981' : ACCENT_SOFT) + ';border-radius:12px;padding:14px 16px;margin-bottom:12px;box-shadow:' + (isSolved ? '0 0 0 3px rgba(16,185,129,.16)' : '0 2px 8px rgba(0,0,0,.05)') + '">'
      + '<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap">'
      +   '<span style="background:' + ACCENT + ';color:#fff;padding:3px 10px;border-radius:99px;font-size:.7rem;font-weight:800;letter-spacing:.04em">' + b.tag + '</span>'
      +   '<span style="font-family:Unbounded,sans-serif;font-weight:800;font-size:.96rem;color:#0f172a">Босс ' + b.n + '. ' + b.title + '</span>'
      + '</div>'
      + '<div style="padding:10px 12px;background:' + ACCENT_SOFT + ';border-radius:8px;margin-bottom:10px;font-size:.94rem;line-height:1.55">' + b.q + '</div>'
      + '<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">'
      +   '<input type="number" step="' + b.step + '" class="boss-inp" data-n="' + b.n + '" placeholder="число" style="padding:8px 12px;border:1.5px solid ' + ACCENT_SOFT + ';border-radius:8px;width:140px;text-align:center;font-family:JetBrains Mono,monospace;font-size:.95rem"' + (isSolved ? ' value="' + b.ans + '" disabled' : '') + '>'
      +   '<button class="boss-go" data-n="' + b.n + '" type="button" style="background:linear-gradient(135deg,' + ACCENT + ',' + ACCENT_D + ');color:#fff;border:none;padding:8px 16px;border-radius:9px;font-weight:700;font-size:.88rem;cursor:pointer;font-family:inherit"' + (isSolved ? ' disabled' : '') + '>Атаковать</button>'
      +   '<button class="boss-hint" data-n="' + b.n + '" type="button" style="background:#fff;color:#475569;border:1.5px solid ' + ACCENT_SOFT + ';padding:8px 14px;border-radius:9px;font-weight:600;font-size:.86rem;cursor:pointer;font-family:inherit">Подсказка</button>'
      + '</div>'
      + '<div class="boss-hint-txt" data-n="' + b.n + '" style="margin-top:8px;padding:9px 13px;background:#fef3c7;border-left:3px solid #f59e0b;border-radius:6px;font-size:.86rem;line-height:1.5;display:none">' + b.hint + '</div>'
      + '<div class="boss-fb" data-n="' + b.n + '" style="margin-top:8px;padding:9px 13px;border-radius:8px;font-weight:600;font-size:.88rem;line-height:1.45;' + (isSolved ? 'background:#d1fae5;color:#065f46;border-left:4px solid #10b981' : 'display:none') + '">' + (isSolved ? '&#10003; Босс повержен! +20 XP.' : '') + '</div>'
      + '</div>';
  });

  h += '<div id="ch4-mastered" style="margin-top:14px;padding:14px 18px;border-radius:12px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:1.5px solid #f59e0b;display:none;align-items:center;gap:12px"><svg style="width:32px;height:32px;stroke:#92400e;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" viewBox="0 0 24 24"><polygon points="12,2 15,9 22,9.3 17,14 18.5,21 12,17 5.5,21 7,14 2,9.3 9,9"/></svg><div style="flex:1"><div style="font-weight:800;color:#92400e;font-family:Unbounded,sans-serif">Ачивка «Властелин давления» получена!</div><div style="font-size:.86rem;color:#78350f;margin-top:2px">+50 XP &middot; Глава 4 полностью пройдена.</div></div></div>';

  body.innerHTML = h;
  renderMath(body);

  function updateBar(){
    const cnt = bosses.filter(b => solved[b.n]).length;
    document.getElementById('ch4-fin-fill').style.width = (cnt * 100 / bosses.length) + '%';
    document.getElementById('ch4-fin-lab').textContent = 'Побеждено: ' + cnt + ' / ' + bosses.length;
    if(cnt === bosses.length){
      document.getElementById('ch4-mastered').style.display = 'flex';
      const ACH_KEY = 'physics7_ch4_master';
      if(localStorage.getItem(ACH_KEY) !== '1'){
        localStorage.setItem(ACH_KEY, '1');
        if(window.addXp) window.addXp(50, 'ach-ch4-master');
        if(window.achievement) window.achievement('ch_done', 'Властелин давления');
      }
    }
  }
  updateBar();

  body.querySelectorAll('.boss-hint').forEach(btn => btn.addEventListener('click', () => {
    const n = btn.dataset.n;
    const txt = body.querySelector('.boss-hint-txt[data-n="' + n + '"]');
    if(txt) txt.style.display = txt.style.display === 'none' ? 'block' : 'none';
  }));
  body.querySelectorAll('.boss-go').forEach(btn => btn.addEventListener('click', () => {
    const n = +btn.dataset.n;
    const b = bosses.find(x => x.n === n);
    const inp = body.querySelector('.boss-inp[data-n="' + n + '"]');
    const fb = body.querySelector('.boss-fb[data-n="' + n + '"]');
    const card = body.querySelector('[data-boss="' + n + '"]');
    const v = parseFloat((inp.value || '').replace(',', '.'));
    if(isNaN(v)){
      fb.style.display = 'block';
      fb.style.background = '#fee2e2'; fb.style.color = '#7f1d1d'; fb.style.borderLeft = '4px solid #dc2626';
      fb.textContent = 'Введи число.';
      return;
    }
    if(Math.abs(v - b.ans) < b.tol){
      fb.style.display = 'block';
      fb.style.background = '#d1fae5'; fb.style.color = '#065f46'; fb.style.borderLeft = '4px solid #10b981';
      fb.innerHTML = '&#10003; Босс повержен! +20 XP.';
      card.style.border = '2px solid #10b981';
      card.style.boxShadow = '0 0 0 3px rgba(16,185,129,.16)';
      btn.disabled = true; inp.disabled = true;
      if(!solved[n]){
        solved[n] = true;
        try{ localStorage.setItem(STATE_KEY, JSON.stringify(solved)); }catch(e){}
        if(window.addXp) window.addXp(20, 'boss-ch4-' + n);
        updateBar();
      }
    } else {
      fb.style.display = 'block';
      fb.style.background = '#fee2e2'; fb.style.color = '#7f1d1d'; fb.style.borderLeft = '4px solid #dc2626';
      fb.textContent = 'Не то. Перепроверь решение.';
    }
  }));
  body.querySelectorAll('.boss-inp').forEach(inp => inp.addEventListener('keydown', e => {
    if(e.key === 'Enter'){ e.preventDefault(); body.querySelector('.boss-go[data-n="' + inp.dataset.n + '"]').click(); }
  }));
}

window.PHYS7_CH4_WIDGETS = {
  p28: add_p28,
  p29: add_p29,
  p30: add_p30,
  p31: add_p31,
  p32: add_p32,
  p33: add_p33,
  p34: add_p34,
  p35: add_p35,
  final4: add_final4
};

})();
