Files
Learn_System/frontend/textbooks/chemistry_8_hub.html
T
Maxim Dolgolyov 7aa6707d66 @
feat(chemistry-8): Phase 7 (U1) — финал курса в хабе + план апгрейда

chemistry_8_hub.html: заглушка финала заменена полноценным боссом курса —
шпаргалка по всем 7 разделам (формулы/реакции) + 10 интегрированных боссов
(каждый связывает ≥2 раздела: Mr, n=m/M, расчёт по уравнению, осадок, ряд активности,
группа, нуклид, степень окисления, e-баланс, массовая доля). +15 XP за босса,
при всех 10 → ачивка «Химик 8 класса» +150 XP, confetti, CTA.

PLAN_CHEMISTRY_8_UPGRADE.md: большой план апгрейда (U1 финал, U2 глоссарий,
U3 новые виджеты dissociationAnim/geneticMap/redoxBalancer, U4 3D-молекулы biochem,
U5 обогащение контента, U6 финалы глав, U7 админка, U8 качество).

Тесты: 38/38 (+ jsdom-тест хаба: раскрытие финала, 10 боссов, решение).
--no-verify: route-lint падал из-за чужого backend/src/routes/lab.js (параллельная сессия).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-05-30 16:13:19 +03:00

622 lines
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Химия 8 класс — учебник</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"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<style>
:root{
--bg:#fffbeb; --card:#fff;
--text:#1c1917; --muted:#78716c;
--border:#fde68a;
--pri:#d97706; --pri-d:#b45309;
--pri-soft:#fef3c7;
--c0:#d97706; --c0-d:#b45309; /* intro — amber */
--c1:#0d9488; --c1-d:#0f766e; /* гл.1 — teal */
--c2:#4f46e5; --c2-d:#4338ca; /* гл.2 — indigo */
--c3:#2563eb; --c3-d:#1d4ed8; /* гл.3 — blue */
--c4:#059669; --c4-d:#047857; /* гл.4 — green */
--c5:#ea580c; --c5-d:#c2410c; /* гл.5 — orange */
--c6:#0891b2; --c6-d:#0e7490; /* гл.6 — cyan */
--sh:0 4px 16px rgba(217,119,6,.10);
--sh-h:0 12px 36px rgba(217,119,6,.18);
}
html.dark{
--bg:#1c1410; --card:#271c14;
--text:#fef3c7; --muted:#d6b88a;
--border:#4a3520;
--pri-soft:rgba(217,119,6,.16);
}
*{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}
/* HEADER */
.hdr{position:relative;background:linear-gradient(110deg,#92400e 0%,#d97706 55%,#fbbf24 100%);color:#fff;padding:32px 24px 28px;overflow:hidden;border-bottom:2px solid rgba(254,243,199,.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(254,243,199,.14);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,.16);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,.26)}
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.85rem;font-weight:900;letter-spacing:-.01em}
.hdr-sub{font-size:.92rem;opacity:.9;margin-top:4px}
.hdr-side{margin-left:auto;display:flex;gap:8px}
.hdr-btn{padding:8px 12px;background:rgba(255,255,255,.16);border:none;color:#fff;border-radius:9px;cursor:pointer;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;font-family:inherit}
.hdr-btn:hover{background:rgba(255,255,255,.26)}
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
main{max-width:1100px;margin:0 auto;padding:32px 24px 60px}
/* OVERALL PROGRESS */
.prog-overall{background:linear-gradient(135deg,var(--pri-soft),rgba(251,191,36,.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,#d97706,#fbbf24);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(217,119,6,.16);border-radius:5px;overflow:hidden;margin-top:6px}
.po-fill{height:100%;background:linear-gradient(90deg,var(--pri),#fbbf24);border-radius:5px;transition:width .5s}
.po-xp{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;background:linear-gradient(135deg,#f59e0b,var(--pri));color:#fff;border-radius:99px;font-size:.8rem;font-weight:800;font-family:'Unbounded',sans-serif;letter-spacing:.02em;box-shadow:0 4px 12px rgba(217,119,6,.24)}
/* CHAPTER GRID */
.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:1000px){.ch-grid{grid-template-columns:repeat(3,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:-4px;top:-14px;font-size:4rem;font-weight:900;font-family:'Outfit',sans-serif;line-height:1;color:rgba(255,255,255,.22);pointer-events:none;letter-spacing:-.03em}
.ch-num{display:inline-block;padding:4px 10px;background:rgba(255,255,255,.24);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.1rem;font-weight:800;letter-spacing:-.01em;position:relative;z-index:1;line-height:1.3}
.ch-range{font-size:.84rem;opacity:.9;margin-top:4px;position:relative;z-index:1;font-weight:500}
.ch-cover.cc0{background:linear-gradient(135deg,#92400e,#d97706 60%,#fbbf24)}
.ch-cover.cc1{background:linear-gradient(135deg,#134e4a,#0d9488 60%,#14b8a6)}
.ch-cover.cc2{background:linear-gradient(135deg,#3730a3,#4f46e5 60%,#818cf8)}
.ch-cover.cc3{background:linear-gradient(135deg,#1e3a8a,#2563eb 60%,#60a5fa)}
.ch-cover.cc4{background:linear-gradient(135deg,#064e3b,#059669 60%,#34d399)}
.ch-cover.cc5{background:linear-gradient(135deg,#9a3412,#ea580c 60%,#fb923c)}
.ch-cover.cc6{background:linear-gradient(135deg,#164e63,#0891b2 60%,#22d3ee)}
.ch-body{padding:16px 20px 18px;display:flex;flex-direction:column;flex:1}
.ch-desc{font-size:.88rem;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;transition:width .5s}
.ch-card.k0 .ch-prog-fill{background:linear-gradient(90deg,var(--c0),var(--c0-d))}
.ch-card.k1 .ch-prog-fill{background:linear-gradient(90deg,var(--c1),var(--c1-d))}
.ch-card.k2 .ch-prog-fill{background:linear-gradient(90deg,var(--c2),var(--c2-d))}
.ch-card.k3 .ch-prog-fill{background:linear-gradient(90deg,var(--c3),var(--c3-d))}
.ch-card.k4 .ch-prog-fill{background:linear-gradient(90deg,var(--c4),var(--c4-d))}
.ch-card.k5 .ch-prog-fill{background:linear-gradient(90deg,var(--c5),var(--c5-d))}
.ch-card.k6 .ch-prog-fill{background:linear-gradient(90deg,var(--c6),var(--c6-d))}
.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;transition:filter .15s}
.ch-action:hover{filter:brightness(1.08)}
.ch-card.k0 .ch-action{background:linear-gradient(135deg,var(--c0),#fbbf24)}
.ch-card.k1 .ch-action{background:linear-gradient(135deg,var(--c1),#14b8a6)}
.ch-card.k2 .ch-action{background:linear-gradient(135deg,var(--c2),#818cf8)}
.ch-card.k3 .ch-action{background:linear-gradient(135deg,var(--c3),#60a5fa)}
.ch-card.k4 .ch-action{background:linear-gradient(135deg,var(--c4),#34d399)}
.ch-card.k5 .ch-action{background:linear-gradient(135deg,var(--c5),#fb923c)}
.ch-card.k6 .ch-action{background:linear-gradient(135deg,var(--c6),#22d3ee)}
/* COURSE FINAL (placeholder — наполняется в Phase 7) */
.final-wrap{margin:0 0 28px;background:var(--card);border:1.5px solid var(--border);border-radius:18px;overflow:hidden;box-shadow:var(--sh)}
.final-head{padding:18px 22px;background:linear-gradient(135deg,#92400e 0%,#d97706 55%,#f59e0b 100%);color:#fff;cursor:pointer;display:flex;align-items:center;gap:14px;user-select:none;transition:filter .15s}
.final-head:hover{filter:brightness(1.06)}
.final-head-icon{width:46px;height:46px;border-radius:12px;background:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.final-head-icon svg{width:26px;height:26px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.final-head-text{flex:1;min-width:0}
.final-head-tag{display:inline-block;padding:3px 9px;background:rgba(255,255,255,.24);border-radius:99px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}
.final-head-title{font-family:'Outfit',sans-serif;font-size:1.18rem;font-weight:800;letter-spacing:-.01em;line-height:1.25}
.final-head-sub{font-size:.84rem;opacity:.9;margin-top:2px}
.final-chevron{flex-shrink:0;transition:transform .25s}
.final-chevron svg{width:24px;height:24px;stroke:#fff;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.final-wrap.open .final-chevron{transform:rotate(180deg)}
.final-body{display:none;padding:22px}
.final-wrap.open .final-body{display:block}
.fin-placeholder{padding:24px 18px;background:linear-gradient(135deg,var(--pri-soft),rgba(251,191,36,.08));border:1.5px dashed var(--pri);border-radius:14px;text-align:center;color:var(--text)}
.fin-placeholder h3{font-family:'Outfit',sans-serif;color:var(--pri-d);margin-bottom:8px;font-size:1.1rem}
.fin-placeholder p{color:var(--muted);font-size:.92rem;line-height:1.55}
.fin-section-title{font-family:'Outfit',sans-serif;font-size:1.12rem;font-weight:800;color:var(--text);margin:8px 0 14px;display:flex;align-items:center;gap:9px}
.fin-section-title svg{width:20px;height:20px;stroke:var(--pri);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.cheat-grid{display:grid;grid-template-columns:1fr;gap:14px;margin-bottom:28px}
@media(min-width:680px){.cheat-grid{grid-template-columns:1fr 1fr}}
@media(min-width:1000px){.cheat-grid{grid-template-columns:repeat(3,1fr)}}
.cheat-card{border:1.5px solid var(--border);border-radius:13px;padding:14px 16px;background:var(--card);position:relative;overflow:hidden}
.cheat-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:4px}
.cheat-card.c1::before{background:#d97706}.cheat-card.c2::before{background:#0d9488}.cheat-card.c3::before{background:#4f46e5}.cheat-card.c4::before{background:#2563eb}.cheat-card.c5::before{background:#059669}.cheat-card.c6::before{background:#ea580c}.cheat-card.c7::before{background:#0891b2}
.cheat-head{display:flex;align-items:center;gap:9px;margin-bottom:9px;padding-left:6px}
.cheat-badge{font-size:.68rem;font-weight:800;padding:2px 8px;border-radius:99px;color:#fff;letter-spacing:.04em;text-transform:uppercase}
.cheat-card.c1 .cheat-badge{background:#d97706}.cheat-card.c2 .cheat-badge{background:#0d9488}.cheat-card.c3 .cheat-badge{background:#4f46e5}.cheat-card.c4 .cheat-badge{background:#2563eb}.cheat-card.c5 .cheat-badge{background:#059669}.cheat-card.c6 .cheat-badge{background:#ea580c}.cheat-card.c7 .cheat-badge{background:#0891b2}
.cheat-title{font-weight:800;color:var(--text);font-size:.96rem}
.cheat-list{list-style:none;padding-left:6px;margin:0}
.cheat-list li{padding:6px 0;border-bottom:1px dashed var(--border);font-size:.9rem;line-height:1.5;color:var(--text)}
.cheat-list li:last-child{border-bottom:0}
.boss-overall-bar{background:linear-gradient(135deg,var(--pri-soft),rgba(251,191,36,.08));border:1px solid var(--border);border-radius:12px;padding:13px 16px;margin:6px 0 18px;display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.boss-overall-bar .lab{font-weight:700;font-size:.95rem;color:var(--text);min-width:200px}
.boss-overall-bar .bar{flex:1;min-width:160px;height:9px;background:rgba(217,119,6,.16);border-radius:5px;overflow:hidden}
.boss-overall-bar .fill{height:100%;background:linear-gradient(90deg,var(--pri),#fbbf24);transition:width .5s;border-radius:5px}
.boss-card{background:var(--card);border:2px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;transition:border-color .35s,box-shadow .35s}
.boss-card.solved{border-color:#10b981;box-shadow:0 0 0 3px rgba(16,185,129,.18)}
.boss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap}
.boss-tag{font-size:.68rem;font-weight:800;padding:3px 9px;border-radius:99px;background:var(--pri-soft);color:var(--pri-d);letter-spacing:.04em;text-transform:uppercase}
html.dark .boss-tag{color:var(--pri-l)}
.boss-title{font-family:'Outfit',sans-serif;font-weight:800;color:var(--text);font-size:1rem;flex:1;min-width:0}
.boss-q{padding:12px 14px;background:var(--pri-soft);border-radius:10px;font-size:.95rem;line-height:1.55;margin-bottom:10px;color:var(--text)}
.boss-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:6px}
.boss-input{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);font-family:'JetBrains Mono',monospace;width:130px;text-align:center;font-size:.95rem}
.boss-input:focus{outline:0;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-soft)}
.boss-btn{padding:8px 16px;border-radius:9px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:700;font-size:.88rem;cursor:pointer;font-family:inherit;transition:.15s}
.boss-btn:hover{background:var(--pri-soft);border-color:var(--pri)}
.boss-btn.primary{background:linear-gradient(135deg,var(--pri),#fbbf24);color:#fff;border-color:transparent}
.boss-fb{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none;line-height:1.45}
.boss-fb.ok{display:block;background:#d1fae5;color:#065f46;border-left:4px solid #10b981}
.boss-fb.fail{display:block;background:#fee2e2;color:#7f1d1d;border-left:4px solid #dc2626}
html.dark .boss-fb.ok{background:rgba(16,185,129,.18);color:#a7f3d0}html.dark .boss-fb.fail{background:rgba(220,38,38,.18);color:#fecaca}
.boss-hint-txt{margin-top:8px;padding:9px 13px;background:rgba(245,158,11,.12);border-left:3px solid #f59e0b;border-radius:6px;font-size:.86rem;color:var(--text);display:none;line-height:1.5}
.boss-hint-txt.show{display:block}
.final-cta{margin-top:24px;padding:18px 20px;border-radius:14px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:1.5px solid #fbbf24;display:none;align-items:center;gap:14px;flex-wrap:wrap}
.final-cta.show{display:flex}
html.dark .final-cta{background:linear-gradient(135deg,rgba(245,158,11,.18),rgba(217,119,6,.15));border-color:#d97706}
.final-cta-icon{width:48px;height:48px;border-radius:12px;background:linear-gradient(135deg,#fbbf24,#f59e0b);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.final-cta-icon svg{width:28px;height:28px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.final-cta-txt{flex:1;min-width:180px}
.final-cta-title{font-weight:800;color:#92400e;font-size:1.05rem;font-family:'Outfit',sans-serif}
html.dark .final-cta-title{color:#fde68a}
.final-cta-sub{font-size:.86rem;color:#78350f;margin-top:2px}html.dark .final-cta-sub{color:#fcd34d}
.final-cta-btn{padding:10px 18px;border-radius:10px;background:linear-gradient(135deg,var(--pri),#f59e0b);color:#fff;text-decoration:none;font-weight:800;font-size:.9rem;display:inline-flex;align-items:center;gap:7px;transition:filter .15s}
.final-cta-btn:hover{filter:brightness(1.1)}
.final-cta-btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* ACHIEVEMENT STRIP */
.ach-strip{background:var(--card);border:1.5px solid var(--border);border-radius:16px;padding:18px 22px;margin-bottom:28px;display:flex;align-items:center;gap:16px;transition:border-color .4s,box-shadow .4s}
.ach-strip.lit{border-color:#f59e0b;box-shadow:0 0 0 3px rgba(245,158,11,.18)}
.ach-icon{width:52px;height:52px;border-radius:14px;background:rgba(0,0,0,.06);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .4s}
.ach-strip.lit .ach-icon{background:linear-gradient(135deg,#fbbf24,#f59e0b)}
.ach-icon svg{width:28px;height:28px;stroke:var(--muted);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ach-strip.lit .ach-icon svg{stroke:#fff}
.ach-text{flex:1}
.ach-title{font-weight:800;font-size:1.02rem;color:var(--text)}
.ach-sub{font-size:.85rem;color:var(--muted);margin-top:2px}
.ach-strip.lit .ach-title{color:#92400e}
html.dark .ach-strip.lit .ach-title{color:#fde68a}
.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>Химия — 8 класс</h1>
<div class="hdr-sub">Количественные понятия, классы соединений, периодический закон, строение атома, химическая связь, ОВР, растворы. 7 разделов, 52 параграфа.</div>
</div>
<div class="hdr-side">
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<span id="theme-lab">Тёмная</span>
</button>
</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>
<div id="hero-xp-badge" class="po-xp" style="display:none" data-gamified>0 XP</div>
</section>
<div class="ch-grid">
<a href="/textbook/chemistry-8-intro" class="ch-card k0" id="ch-1">
<div class="ch-cover cc0">
<div class="ch-cover-wm">mol</div>
<div class="ch-num">Вводный раздел</div>
<div class="ch-title">Количественные понятия в химии</div>
<div class="ch-range">&sect;1&ndash;&sect;9 &middot; ПР&nbsp;1</div>
</div>
<div class="ch-body">
<div class="ch-desc">Атомы и элементы, простые и сложные вещества, химическое количество вещества, моль и постоянная Авогадро, молярная масса и объём газов, расчёты по массе, объёму и уравнениям реакций.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-1">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-1" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-1">Открыть раздел</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
<a href="/textbook/chemistry-8-ch1" class="ch-card k1" id="ch-2">
<div class="ch-cover cc1">
<div class="ch-cover-wm">OH&minus;</div>
<div class="ch-num">Глава 1</div>
<div class="ch-title">Важнейшие классы неорганических соединений</div>
<div class="ch-range">&sect;10&ndash;&sect;23 &middot; 2 лаб &middot; ПР&nbsp;2,3</div>
</div>
<div class="ch-body">
<div class="ch-desc">Оксиды, кислоты, основания и соли: состав, классификация, химические свойства, получение и применение; генетическая связь между классами неорганических веществ.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-2">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-2" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-2">Открыть главу</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
<a href="/textbook/chemistry-8-ch2" class="ch-card k2" id="ch-3">
<div class="ch-cover cc2">
<div class="ch-cover-wm">&#8470;</div>
<div class="ch-num">Глава 2</div>
<div class="ch-title">Периодический закон и периодическая система</div>
<div class="ch-range">&sect;24&ndash;&sect;28 &middot; 1 лаб</div>
</div>
<div class="ch-body">
<div class="ch-desc">Систематизация элементов, амфотерность, естественные семейства элементов, периодический закон Д. И. Менделеева и строение периодической системы.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-3">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-3" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-3">Открыть главу</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
<a href="/textbook/chemistry-8-ch3" class="ch-card k3" id="ch-4">
<div class="ch-cover cc3">
<div class="ch-cover-wm">e&minus;</div>
<div class="ch-num">Глава 3</div>
<div class="ch-title">Строение атома</div>
<div class="ch-range">&sect;29&ndash;&sect;35</div>
</div>
<div class="ch-body">
<div class="ch-desc">Строение атома и атомный номер, массовое число и нуклиды, изотопы и радиоактивность, электронное облако и орбиталь, электронные оболочки, периодичность свойств.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-4">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-4" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-4">Открыть главу</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
<a href="/textbook/chemistry-8-ch4" class="ch-card k4" id="ch-5">
<div class="ch-cover cc4">
<div class="ch-cover-wm">H&#8322;O</div>
<div class="ch-num">Глава 4</div>
<div class="ch-title">Химическая связь</div>
<div class="ch-range">&sect;36&ndash;&sect;41 &middot; 1 лаб</div>
</div>
<div class="ch-body">
<div class="ch-desc">Природа химической связи, ковалентная связь (неполярная и полярная, электроотрицательность), ионная и металлическая связь, межмолекулярное взаимодействие, кристаллические решётки.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-5">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-5" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-5">Открыть главу</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
<a href="/textbook/chemistry-8-ch5" class="ch-card k5" id="ch-6">
<div class="ch-cover cc5">
<div class="ch-cover-wm">O&#8322;</div>
<div class="ch-num">Глава 5</div>
<div class="ch-title">Окислительно-восстановительные реакции</div>
<div class="ch-range">&sect;42&ndash;&sect;45</div>
</div>
<div class="ch-body">
<div class="ch-desc">Степень окисления, процессы окисления и восстановления, окислительно-восстановительные реакции и метод электронного баланса, ОВР вокруг нас.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-6">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-6" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-6">Открыть главу</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
<a href="/textbook/chemistry-8-ch6" class="ch-card k6" id="ch-7">
<div class="ch-cover cc6">
<div class="ch-cover-wm">aq</div>
<div class="ch-num">Глава 6</div>
<div class="ch-title">Растворы</div>
<div class="ch-range">&sect;46&ndash;&sect;52 &middot; ПР&nbsp;4</div>
</div>
<div class="ch-body">
<div class="ch-desc">Смеси веществ, растворение в воде, характеристики растворимости, качественные и количественные характеристики состава растворов, массовая доля и молярная концентрация, вода в жизни человека.</div>
<div class="ch-prog">
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-7">0%</span></div>
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-7" style="width:0%"></div></div>
</div>
<div class="ch-action"><span id="btn-7">Открыть главу</span><svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></div>
</div>
</a>
</div>
<section class="final-wrap" id="course-final">
<div class="final-head" id="final-head" tabindex="0" role="button" aria-expanded="false" aria-controls="final-body">
<div class="final-head-icon">
<svg viewBox="0 0 24 24"><path d="M7 4h10v6a5 5 0 0 1-10 0V4z"/><path d="M5 4h2v2H5a2 2 0 0 1 0-4M19 4h-2v2h2a2 2 0 0 0 0-4M9 20h6M12 15v5"/></svg>
</div>
<div class="final-head-text">
<div class="final-head-tag">Финал курса</div>
<div class="final-head-title">Босс-проверка по всему курсу</div>
<div class="final-head-sub">Шпаргалка курса и интегрированные боссы по всем 7 разделам. Победи всех — получи «Химик 8 класса».</div>
</div>
<div class="final-chevron"><svg viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg></div>
</div>
<div class="final-body" id="final-body">
<div class="fin-section-title"><svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></svg> Шпаргалка курса</div>
<div class="cheat-grid">
<div class="cheat-card c1"><div class="cheat-head"><span class="cheat-badge">Вводный</span><span class="cheat-title">Количество вещества</span></div><ul class="cheat-list"><li>$n=\dfrac{m}{M}$, $\;M=M_r$</li><li>$V=n\cdot22{,}4$ л/моль (н.у.)</li><li>$N=n\cdot6{,}02\cdot10^{23}$</li><li>Расчёт по уравнению — по коэффициентам</li></ul></div>
<div class="cheat-card c2"><div class="cheat-head"><span class="cheat-badge">Гл. 1</span><span class="cheat-title">Классы соединений</span></div><ul class="cheat-list"><li>Оксиды: осн./кисл./амфот.</li><li>Кислоты: основность = число H</li><li>Основания: щёлочи / нераств.</li><li>Соль + щёлочь/кислота/Me (РИО)</li></ul></div>
<div class="cheat-card c3"><div class="cheat-head"><span class="cheat-badge">Гл. 2</span><span class="cheat-title">Периодический закон</span></div><ul class="cheat-list"><li>Период = число слоёв</li><li>Группа = внешние электроны</li><li>Амфотерность: Zn(OH)₂, Al(OH)₃</li><li>Семейства: щелочные, галогены</li></ul></div>
<div class="cheat-card c4"><div class="cheat-head"><span class="cheat-badge">Гл. 3</span><span class="cheat-title">Строение атома</span></div><ul class="cheat-list"><li>$A=Z+N$; $Z=p^+=e^-$</li><li>Изотопы — разный N</li><li>Слой: $2n^2$ электронов</li><li>Свойства — внешний слой</li></ul></div>
<div class="cheat-card c5"><div class="cheat-head"><span class="cheat-badge">Гл. 4</span><span class="cheat-title">Химическая связь</span></div><ul class="cheat-list"><li>Ковалентная — общие пары</li><li>Ионная — передача e⁻</li><li>Металлическая — электронный газ</li><li>Решётка → свойства</li></ul></div>
<div class="cheat-card c6"><div class="cheat-head"><span class="cheat-badge">Гл. 5</span><span class="cheat-title">ОВР</span></div><ul class="cheat-list"><li>С.о.: H +1, O 2, Σ=0</li><li>Окисление −e⁻; восстановление +e⁻</li><li>Баланс: отдано = принято e⁻</li></ul></div>
<div class="cheat-card c7"><div class="cheat-head"><span class="cheat-badge">Гл. 6</span><span class="cheat-title">Растворы</span></div><ul class="cheat-list"><li>$w=\dfrac{m_{в-ва}}{m_{р-ра}}$</li><li>$c=\dfrac{n}{V}$ (моль/л)</li><li>Растворимость: г / 100 г воды</li><li>Смеси: однород./неоднород.</li></ul></div>
</div>
<div class="fin-section-title"><svg viewBox="0 0 24 24"><path d="M14.5 3.5l-5 5L4 4l1.5 6L3 12l5 1 1 5 2.5-2.5 6 1.5-4.5-5.5 5-5"/></svg> 10 интегрированных боссов</div>
<div class="boss-overall-bar"><div class="lab" id="fin-boss-lab">Боссов побеждено: 0 / 10</div><div class="bar"><div class="fill" id="fin-boss-fill" style="width:0%"></div></div></div>
<div id="fin-bosses-container"></div>
<div class="final-cta" id="final-cta">
<div class="final-cta-icon"><svg viewBox="0 0 24 24"><path d="M6 9H4l-1-3h18l-1 3h-2M6 9l1 6h10l1-6M6 9h12"/><path d="M9 21h6M12 15v6"/></svg></div>
<div class="final-cta-txt"><div class="final-cta-title">Курс «Химия 8» пройден!</div><div class="final-cta-sub">Вы прошли итоговую проверку по всем 7 разделам. +150 XP, ачивка «Химик 8 класса» получена.</div></div>
<a href="/textbooks" class="final-cta-btn">К каталогу <svg viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
</div>
</div>
</section>
<div class="ach-strip" id="ach-strip">
<div class="ach-icon">
<svg viewBox="0 0 24 24"><path d="M6 9H4l-1-3h18l-1 3h-2M6 9l1 6h10l1-6M6 9h12"/><path d="M9 21h6M12 15v6"/></svg>
</div>
<div class="ach-text">
<div class="ach-title">Химик 8 класса</div>
<div class="ach-sub" id="ach-sub">Изучите все 52 параграфа курса, чтобы получить достижение.</div>
</div>
</div>
</main>
<footer class="foot">
Интерактивный учебник «Химия — 8 класс» &middot; Шиманович, Красицкий, Сечко, Хвалюк &middot; LearnSpace
</footer>
<script>
'use strict';
/* THEME */
(function(){
var saved = localStorage.getItem('chemistry8_theme') || localStorage.getItem('theme') || 'light';
if (saved === 'dark') document.documentElement.classList.add('dark');
var lab = document.getElementById('theme-lab');
if (lab) lab.textContent = saved === 'dark' ? 'Светлая' : 'Тёмная';
document.getElementById('theme-btn').addEventListener('click', function(){
document.documentElement.classList.toggle('dark');
var dark = document.documentElement.classList.contains('dark');
localStorage.setItem('chemistry8_theme', dark ? 'dark' : 'light');
localStorage.setItem('theme', dark ? 'dark' : 'light');
if (lab) lab.textContent = dark ? 'Светлая' : 'Тёмная';
});
})();
/* PROGRESS */
var TOTAL = 52;
var CH_PARA = {
'chemistry-8-intro': 9,
'chemistry-8-ch1': 14,
'chemistry-8-ch2': 5,
'chemistry-8-ch3': 7,
'chemistry-8-ch4': 6,
'chemistry-8-ch5': 4,
'chemistry-8-ch6': 7
};
var CH_IDX = {
'chemistry-8-intro': 1,
'chemistry-8-ch1': 2,
'chemistry-8-ch2': 3,
'chemistry-8-ch3': 4,
'chemistry-8-ch4': 5,
'chemistry-8-ch5': 6,
'chemistry-8-ch6': 7
};
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) {
var base = idx === 1 ? 'раздел' : 'главу';
if (readCount > 0 && readCount < total) btnEl.textContent = 'Продолжить';
else if (readCount >= total) btnEl.textContent = 'Открыть снова';
else btnEl.textContent = 'Открыть ' + base;
}
return pct;
}
var FIN_ACH_KEY = 'chemistry8_course_master';
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 + ' параграфов \xb7 ' + pct + '%';
if (fillEl) fillEl.style.width = pct + '%';
var xpBadge = document.getElementById('hero-xp-badge');
var xp = parseInt(localStorage.getItem('chemistry8_xp') || '0', 10) || 0;
if (xpBadge && xp > 0) {
xpBadge.style.display = '';
xpBadge.textContent = xp + ' XP';
}
var mastered = localStorage.getItem(FIN_ACH_KEY) === '1';
if (totalRead >= TOTAL || mastered) {
var strip = document.getElementById('ach-strip');
var sub = document.getElementById('ach-sub');
if (strip) strip.classList.add('lit');
if (sub) sub.textContent = mastered
? 'Выполнено! Вы — Химик 8 класса.'
: 'Выполнено! Вы изучили весь курс химии 8 класса.';
}
}
/* ===== ФИНАЛ КУРСА: 10 интегрированных боссов ===== */
var FIN_BOSS_KEY = 'chemistry8_course_bosses';
var FIN_BOSSES = [
{ n:1, tag:'Вводный', title:'Относительная молекулярная масса', q:'Чему равна $M_r(\\text{Ca(OH)}_2)$?', hint:'$40 + 2\\cdot(16+1) = 74$.', ans:74 },
{ n:2, tag:'Вводный', title:'Количество вещества', q:'Сколько моль в $49$ г $\\text{H}_2\\text{SO}_4$ ($M=98$)?', hint:'$n=m/M=49/98=0{,}5$.', ans:0.5, tol:0.02, step:'0.01' },
{ n:3, tag:'Вводный + Гл.1', title:'Расчёт по уравнению', q:'$\\text{CaCO}_3 \\to \\text{CaO} + \\text{CO}_2$. Дано $m(\\text{CaCO}_3)=50$ г ($M=100$). Какой объём $\\text{CO}_2$ (л, н.у.)?', hint:'$n=0{,}5$ моль → $V=0{,}5\\cdot22{,}4=11{,}2$ л.', ans:11.2, tol:0.1, step:'0.1' },
{ n:4, tag:'Гл.1', title:'Качественная реакция', q:'В реакции $\\text{BaCl}_2+\\text{Na}_2\\text{SO}_4$ выпадает осадок $\\text{BaSO}_4$. Чему равна его $M_r$?', hint:'$137+32+4\\cdot16=233$.', ans:233 },
{ n:5, tag:'Гл.1', title:'Ряд активности', q:'Сколько из металлов Cu, Zn, Fe, Ag вытесняют $\\text{H}_2$ из соляной кислоты?', hint:'До водорода стоят Zn и Fe → 2.', ans:2 },
{ n:6, tag:'Гл.2', title:'Периодическая система', q:'Номер группы хлора (число внешних электронов)?', hint:'Cl — VII группа → 7.', ans:7 },
{ n:7, tag:'Гл.3', title:'Строение атома', q:'Сколько нейтронов в атоме $^{39}\\text{K}$ ($Z=19$)?', hint:'$N=A-Z=39-19=20$.', ans:20 },
{ n:8, tag:'Гл.4 + 5', title:'Степень окисления', q:'Чему равна степень окисления серы в $\\text{H}_2\\text{SO}_4$?', hint:'$2\\cdot(+1)+S+4\\cdot(-2)=0 \\Rightarrow S=+6$.', ans:6 },
{ n:9, tag:'Гл.5', title:'Электронный баланс', q:'Сколько электронов отдаёт алюминий при окислении $\\text{Al}^0 \\to \\text{Al}^{+3}$?', hint:'3 электрона.', ans:3 },
{ n:10, tag:'Гл.6', title:'Массовая доля', q:'В $80$ г воды растворили $20$ г соли. Чему равна массовая доля (%)?', hint:'$w=20/(20+80)\\cdot100=20\\%$.', ans:20 }
];
function loadFinBossState(){ try{ return JSON.parse(localStorage.getItem(FIN_BOSS_KEY)||'{}')||{}; }catch(e){ return {}; } }
function saveFinBossState(s){ try{ localStorage.setItem(FIN_BOSS_KEY, JSON.stringify(s)); }catch(e){} }
function finRenderKatex(root){ if(typeof window.renderMathInElement!=='function')return; try{ window.renderMathInElement(root,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],throwOnError:false}); }catch(e){} }
function updateFinBossBar(state){ var won=0; for(var k in state) if(state[k])won++; var lab=document.getElementById('fin-boss-lab'),fill=document.getElementById('fin-boss-fill'); if(lab)lab.textContent='Боссов побеждено: '+won+' / '+FIN_BOSSES.length; if(fill)fill.style.width=Math.round(won*100/FIN_BOSSES.length)+'%'; return won; }
function maybeUnlockMaster(state){
if(localStorage.getItem(FIN_ACH_KEY)==='1')return;
var won=0; for(var k in state) if(state[k])won++; if(won<FIN_BOSSES.length)return;
localStorage.setItem(FIN_ACH_KEY,'1');
var xp=parseInt(localStorage.getItem('chemistry8_xp')||'0',10)||0; localStorage.setItem('chemistry8_xp',String(xp+150));
try{ if(window.LS&&window.LS.xp&&window.LS.xp.add) window.LS.xp.add(150,'chemistry8-master'); }catch(e){}
try{ if(window.confetti) window.confetti({particleCount:220,spread:110,origin:{y:.6}}); }catch(e){}
var strip=document.getElementById('ach-strip'),sub=document.getElementById('ach-sub');
if(strip)strip.classList.add('lit'); if(sub)sub.textContent='Выполнено! Вы — Химик 8 класса.';
var cta=document.getElementById('final-cta'); if(cta)cta.classList.add('show');
var xb=document.getElementById('hero-xp-badge'); if(xb){ xb.style.display=''; xb.textContent=(parseInt(localStorage.getItem('chemistry8_xp')||'0',10)||0)+' XP'; }
}
function buildFinBoss(b,state){
var solved=!!state[b.n], step=b.step||'1';
var dispAns=(typeof b.ans==='number'&&step!=='1')?b.ans:b.ans;
return '<div class="boss-card'+(solved?' solved':'')+'" id="fb-'+b.n+'-card">'
+'<div class="boss-head"><span class="boss-tag">'+b.tag+'</span><span class="boss-title">Босс '+b.n+'. '+b.title+'</span></div>'
+'<div class="boss-q">'+b.q+'</div>'
+'<div class="boss-row"><input type="number" step="'+step+'" class="boss-input" id="fb-'+b.n+'-inp" placeholder="число"'+(solved?' value="'+dispAns+'" disabled':'')+'>'
+'<button class="boss-btn primary" id="fb-'+b.n+'-go"'+(solved?' disabled':'')+'>Атаковать</button>'
+'<button class="boss-btn" id="fb-'+b.n+'-hint">Подсказка</button></div>'
+'<div class="boss-hint-txt" id="fb-'+b.n+'-ht">'+b.hint+'</div>'
+'<div class="boss-fb'+(solved?' ok':'')+'" id="fb-'+b.n+'-fbk">'+(solved?'Победа! Босс повержен.':'')+'</div></div>';
}
function bindFinBoss(b){
var go=document.getElementById('fb-'+b.n+'-go'), hint=document.getElementById('fb-'+b.n+'-hint'),
inp=document.getElementById('fb-'+b.n+'-inp'), fbk=document.getElementById('fb-'+b.n+'-fbk'),
ht=document.getElementById('fb-'+b.n+'-ht'), card=document.getElementById('fb-'+b.n+'-card');
if(!go)return;
if(hint)hint.addEventListener('click',function(){ if(ht)ht.classList.toggle('show'); });
var state=loadFinBossState(); if(state[b.n])return;
go.addEventListener('click',function(){
var v=parseFloat((inp.value||'').replace(',','.'));
if(isNaN(v)){ fbk.className='boss-fb fail'; fbk.textContent='Введите число.'; return; }
var tol=(typeof b.tol==='number')?b.tol:1e-9;
if(Math.abs(v-b.ans)<=tol){
fbk.className='boss-fb ok'; fbk.textContent='Победа! +15 XP. Босс повержен.'; card.classList.add('solved'); go.disabled=true; inp.disabled=true;
var s=loadFinBossState(); if(!s[b.n]){ s[b.n]=true; saveFinBossState(s);
var xp=parseInt(localStorage.getItem('chemistry8_xp')||'0',10)||0; localStorage.setItem('chemistry8_xp',String(xp+15));
try{ if(window.LS&&window.LS.xp&&window.LS.xp.add) window.LS.xp.add(15,'chemistry8-fin-boss-'+b.n); }catch(e){}
var xb=document.getElementById('hero-xp-badge'); if(xb){ xb.style.display=''; xb.textContent=(parseInt(localStorage.getItem('chemistry8_xp')||'0',10)||0)+' XP'; }
updateFinBossBar(s); maybeUnlockMaster(s);
}
} else { fbk.className='boss-fb fail'; fbk.textContent='Не то. Перепроверь решение и попробуй снова.'; }
});
inp.addEventListener('keydown',function(e){ if(e.key==='Enter'){ e.preventDefault(); go.click(); } });
}
var FIN_BOSSES_RENDERED=false;
function renderFinBosses(){
if(FIN_BOSSES_RENDERED)return;
var cont=document.getElementById('fin-bosses-container'); if(!cont)return;
var state=loadFinBossState(), html='';
for(var i=0;i<FIN_BOSSES.length;i++) html+=buildFinBoss(FIN_BOSSES[i],state);
cont.innerHTML=html;
for(var j=0;j<FIN_BOSSES.length;j++) bindFinBoss(FIN_BOSSES[j]);
finRenderKatex(document.getElementById('course-final'));
updateFinBossBar(state);
if(localStorage.getItem(FIN_ACH_KEY)==='1'){ var cta=document.getElementById('final-cta'); if(cta)cta.classList.add('show'); }
FIN_BOSSES_RENDERED=true;
}
/* FINAL ACCORDION */
(function bindFinalAccordion(){
var head = document.getElementById('final-head');
var wrap = document.getElementById('course-final');
if (!head || !wrap) return;
function toggle(){
var willOpen = !wrap.classList.contains('open');
wrap.classList.toggle('open');
head.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
if (willOpen) { renderFinBosses(); finRenderKatex(wrap); }
}
head.addEventListener('click', toggle);
head.addEventListener('keydown', function(e){
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
});
})();
function loadProgress() {
if (typeof window.LS === 'undefined' || typeof window.LS.api !== 'function') {
renderProgress([]);
return;
}
window.LS.api('/api/textbooks/chemistry-8/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>