feat(alg10 W5): Финал главы 1 — 6 интегрированных боссов + ачивка

Глава 1 'Тригонометрия' полностью завершена.

buildFinal1():
- Hero card с градиентом teal→violet, водяной знак ★ и 3 плашки-метки
  (★ 6 боссов / + до 300 XP / ★ Финальная ачивка)
- Общий прогресс-бар 'X / 6 побеждено' с градиентной заливкой
- 6 boss-card по теме отдельных параграфов
- Celebration-card 'МАГИСТР ТРИГОНОМЕТРИИ' (скрыта пока не все
  6 боссов повержены) с ачивкой, кнопкой возврата на хаб
- Своё состояние в localStorage (algebra10_ch1_final1_state)

6 боссов (5 этапов каждый, 30 вопросов всего):
- Босс 1 (teal, §1-§4): окружность, sin/cos/tg/ctg, тождества
- Босс 2 (cyan, §5-§7): графики, обратные функции
- Босс 3 (red, §8): уравнения (метод интервалов, замена, разложение)
- Босс 4 (dark teal, §9): формулы приведения (правило двух шагов)
- Босс 5 (deep teal, §10-§11): сложение, разность, двойной аргумент
- Босс 6 (violet, §12+): синтез — сумма→произведение, проверка
  на отождествление углов отличающихся на 2πn

XP:
- 5 XP за каждый правильный этап (30 правильных = 150 XP)
- 25 XP за победу над каждым боссом (6 × 25 = 150 XP)
- Бонус +150 XP за финальную ачивку 'trig_master'
- Итого до 450 XP за финал

Добавлены:
- ACH_LABELS.trig_master: 'Магистр тригонометрии! +150 XP'
- SIDEBARS.final1 + TIPS.final1
- BUILDERS.final1 теперь buildFinal1() (вместо stub)

Файл вырос с 221 KB до 240 KB (2998 → 3252 строки).
Глава 1 готова на 100% — 12 § + Финал.
This commit is contained in:
Maxim Dolgolyov
2026-05-29 11:30:24 +03:00
parent 18fadcba9f
commit 77bfdb4331
+254 -1
View File
@@ -290,6 +290,7 @@ const ACH_LABELS = {
p11_done:'Двойной аргумент — лёгкость!',
p12_done:'Преобразование суммы — освоено!',
ch1_done:'Глава 1 — Тригонометрия пройдена!',
trig_master:'Магистр тригонометрии! +150 XP',
};
function loadProgress(){
@@ -402,7 +403,7 @@ const BUILT=new Set();
const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), p3:()=>buildP3(), p4:()=>buildP4(),
p5:()=>buildP5(), p6:()=>buildP6(), p7:()=>buildP7(), p8:()=>buildP8(),
p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildP11(), p12:()=>buildP12(),
final1:()=>buildStub('final1','Финал главы 1 — 6 боссов')
final1:()=>buildFinal1()
};
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
@@ -529,6 +530,16 @@ const SIDEBARS = {
['Польза','решение уравнений'],
['Пример','$\\sin 3x + \\sin x$ → произведение'],
]},
final1:{title:'Финал главы 1',rows:[
['Боссов','6 — все темы главы'],
['Босс 1','§1-§4 окружность, sin/cos/tg/ctg'],
['Босс 2','§5-§7 графики, обратные'],
['Босс 3','§8 уравнения'],
['Босс 4','§9 приведение'],
['Босс 5','§10-§11 сложение и двойной'],
['Босс 6','§12+ синтез всех тем'],
['Награда','+150 XP + «Магистр тригонометрии»'],
]},
};
const TIPS=[
@@ -544,6 +555,7 @@ const TIPS=[
{sec:'p10',html:'Запомнить помогает мнемоника: <b>знаки повторяются в sin</b> (sin·cos + cos·sin) и <b>чередуются в cos</b> (cos·cos sin·sin). Для разности — наоборот.'},
{sec:'p11',html:'Три формы $\\cos 2\\alpha$ нужны для разных задач: $\\cos^2 - \\sin^2$ — для упрощения; $1 - 2\\sin^2$ — когда задан $\\sin\\alpha$; $2\\cos^2 - 1$ — когда задан $\\cos\\alpha$.'},
{sec:'p12',html:'4 формулы — все имеют вид «2 · функция полусуммы · функция полуразности». Знаки и тип функции в правой части смотри по таблице (или по правилу для каждой формулы).'},
{sec:'final1',html:'<b>6 интегрированных боссов</b> проверяют синтез знаний всей главы. После победы над всеми — ачивка <b>«Магистр тригонометрии»</b> и +150 XP. Не торопись, используй шпаргалки.'},
];
function buildSidebar(id){
@@ -2862,6 +2874,247 @@ function buildP12(){
wireReadBtn('p12');
}
/* ============================================================
ФИНАЛ ГЛАВЫ 1 — 6 интегрированных боссов
============================================================ */
const FINAL1_BOSSES = [
{
n:1, tag:'§ 14',
title:'Окружность, sin/cos, tg/ctg, тождества',
color:'#0d9488',
steps:[
{ q:'Преобразуй $240°$ в радианы. Введи как 4pi/3.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='4pi/3'||s==='4π/3';}, hint:'$240 \\cdot \\pi / 180 = 4\\pi/3$.' },
{ q:'Найди $\\sin\\dfrac{\\pi}{4} \\cdot \\cos\\dfrac{\\pi}{4}$. Введи как 1/2.', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1/2'||+v===0.5;}, hint:'$(\\sqrt{2}/2)^2 = 1/2$.' },
{ q:'В какой четверти $\\tg\\alpha > 0$ <b>и</b> $\\cos\\alpha < 0$? (1/2/3/4)', verify:(v)=>+v===3, hint:'tg > 0: I или III; cos < 0: II или III. Пересечение — III.' },
{ q:'$\\sin^2 30° + \\cos^2 60° = ?$ Введи 1/2.', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1/2'||+v===0.5;}, hint:'$(1/2)^2 + (1/2)^2 = 1/4 + 1/4$.' },
{ q:'$\\sin\\alpha = -3/5$, $\\alpha \\in (\\pi;\\,3\\pi/2)$. Найди $\\tg\\alpha$ (введи 3/4).', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='3/4'||+v===0.75;}, hint:'cos = -4/5 (III четв.), tg = sin/cos = (-3/5)/(-4/5) = 3/4.' },
]
},
{
n:2, tag:'§ 57',
title:'Графики, обратные функции',
color:'#0891b2',
steps:[
{ q:'Период функции $y = \\sin 3x$. Введи как 2pi/n.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='2pi/3'||s==='2π/3';}, hint:'$T = 2\\pi / 3$.' },
{ q:'$y = \\cos x$ — чётная или нечётная? Введи слово.', verify:(v)=>String(v).trim().toLowerCase().startsWith('чёт')||String(v).trim().toLowerCase().startsWith('чет'), hint:'$\\cos(-x) = \\cos x$.' },
{ q:'Наибольшее значение функции $y = 3\\sin x - 1$?', verify:(v)=>+v===2, hint:'При $\\sin x = 1$: $3 - 1 = 2$.' },
{ q:'$\\arcsin(-1) = ?$ Введи как -pi/2.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='-pi/2'||s==='-π/2';}, hint:'$\\sin(-\\pi/2) = -1$.' },
{ q:'$\\arccos\\dfrac{\\sqrt{2}}{2} = ?$ Введи pi/4.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='pi/4'||s==='π/4';}, hint:'$\\cos(\\pi/4) = \\sqrt{2}/2$.' },
]
},
{
n:3, tag:'§ 8',
title:'Тригонометрические уравнения',
color:'#dc2626',
steps:[
{ q:'Сколько корней у $\\sin x = 0{,}5$ на $[0;\\,2\\pi]$?', verify:(v)=>+v===2, hint:'$\\pi/6$ и $5\\pi/6$.' },
{ q:'Реши $\\cos x = -1$. Корень в $[0;\\,2\\pi]$? Введи pi.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='pi'||s==='π';}, hint:'$\\cos\\pi = -1$.' },
{ q:'$2\\sin^2 x - \\sin x - 1 = 0$. Сколько корней в $[0;\\,2\\pi)$?', verify:(v)=>+v===3, hint:'$t = \\sin x$: $2t^2 - t - 1 = 0$, $t = 1$ или $t = -1/2$. sin = 1: π/2 (1 корень). sin = -1/2: 7π/6, 11π/6 (2 корня). Итого 3.' },
{ q:'$\\tg x = \\sqrt{3}$. Наименьший положительный корень? Введи pi/n.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='pi/3'||s==='π/3';}, hint:'$\\arctg\\sqrt{3} = \\pi/3$.' },
{ q:'$\\sin x \\cdot \\cos x = 0$. Сколько корней в $[0;\\,2\\pi)$?', verify:(v)=>+v===4, hint:'$\\sin x = 0$: 0, π. $\\cos x = 0$: π/2, 3π/2. Все различны — 4.' },
]
},
{
n:4, tag:'§ 9',
title:'Формулы приведения',
color:'#0e7490',
steps:[
{ q:'$\\sin(\\pi - \\alpha) = ?$ Введи: «sin a» или «-sin a».', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='sina'||s==='sin(a)'||s==='sinα';}, hint:'II четв., sin > 0; имя не меняется.' },
{ q:'$\\cos\\left(\\dfrac{\\pi}{2} + \\alpha\\right) = ?$ Введи «-sin a».', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='-sina'||s==='-sin(a)'||s==='-sinα';}, hint:'II четв., cos < 0; имя меняется cos → sin.' },
{ q:'Вычисли $\\sin 150°$. Введи 1/2.', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1/2'||+v===0.5;}, hint:'$\\sin(180° - 30°) = \\sin 30° = 1/2$.' },
{ q:'Вычисли $\\cos 210°$. Введи -sqrt3/2.', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='-sqrt3/2'||s==='-√3/2'||Math.abs(+v - (-Math.sqrt(3)/2))<0.02;}, hint:'$\\cos(180° + 30°) = -\\cos 30°$.' },
{ q:'$\\tg\\left(\\dfrac{3\\pi}{2} - \\alpha\\right) = ?$ Введи «ctg a», «-ctg a» или «tg a».', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='ctga'||s==='ctg(a)'||s==='ctgα';}, hint:'III четв., tg > 0; имя меняется tg → ctg.' },
]
},
{
n:5, tag:'§ 1011',
title:'Сумма, разность, двойной аргумент',
color:'#0f766e',
steps:[
{ q:'$\\sin 75° = \\dfrac{\\sqrt{6} + \\sqrt{2}}{?}$ — введи знаменатель.', verify:(v)=>+v===4, hint:'$\\sin(45° + 30°) = \\sin 45 \\cos 30 + \\cos 45 \\sin 30$.' },
{ q:'$\\sin\\alpha = 3/5$, $\\alpha$ в I четв. Найди $\\sin 2\\alpha$ (введи 24/25).', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='24/25'||+v===0.96;}, hint:'$\\cos\\alpha = 4/5$, $\\sin 2\\alpha = 2 \\cdot 3/5 \\cdot 4/5 = 24/25$.' },
{ q:'$\\cos^2 30° - \\sin^2 30° = ?$ Введи 1/2.', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1/2'||+v===0.5;}, hint:'Это $\\cos 60° = 1/2$.' },
{ q:'$\\tg(45° + 45°) = ?$ Введи 9999 (это $\\infty$).', verify:(v)=>+v===9999, hint:'$\\tg 90°$ не существует.' },
{ q:'$\\sin^2 22{,}5° = \\dfrac{1 - \\cos 45°}{?}$ — введи знаменатель.', verify:(v)=>+v===2, hint:'Формула понижения степени.' },
]
},
{
n:6, tag:'§ 12+',
title:'Финальный — синтез всех тем',
color:'#7c3aed',
steps:[
{ q:'$\\sin 3x + \\sin x = 2\\sin ?x \\cos x$. Введи коэф.', verify:(v)=>+v===2, hint:'$\\frac{3x+x}{2} = 2x$.' },
{ q:'$\\dfrac{\\sin 5x - \\sin x}{\\cos 5x + \\cos x} = \\tg ?x$. Введи коэф.', verify:(v)=>+v===2, hint:'Числитель: $2\\sin 2x \\cos 3x$. Знаменатель: $2\\cos 3x \\cos 2x$. Сокращаем — $\\tg 2x$.' },
{ q:'Сколько корней у $\\cos x = 0{,}5$ на $[-2\\pi;\\,2\\pi]$?', verify:(v)=>+v===4, hint:'$x = \\pm\\pi/3 + 2\\pi n$: $-5\\pi/3, -\\pi/3, \\pi/3, 5\\pi/3$ — 4 корня.' },
{ q:'$\\sin\\alpha = 0{,}6$, $\\alpha$ в I четв. Найди $\\cos 2\\alpha$ (введи 0.28).', verify:(v)=>Math.abs(+v - 0.28)<0.01, hint:'$\\cos 2\\alpha = 1 - 2 \\cdot 0{,}36 = 0{,}28$.' },
{ q:'Сколько <b>различных</b> точек $P_\\alpha$ на окружности при $\\alpha = \\dfrac{\\pi}{4}, \\dfrac{9\\pi}{4}, -\\dfrac{7\\pi}{4}, \\dfrac{17\\pi}{4}$?', verify:(v)=>+v===1, hint:'Все отличаются на $2\\pi k$, значит совпадают.' },
]
},
];
function buildFinal1(){
const box = document.getElementById('final1-body');
let html = '';
/* === Hero card === */
html += '<div style="background:linear-gradient(135deg,#0d9488,#7c3aed);color:#fff;border-radius:18px;padding:24px 22px;margin-bottom:24px;box-shadow:0 8px 28px rgba(13,148,136,.25);position:relative;overflow:hidden">'
+'<div style="position:absolute;right:-20px;top:-30px;font-size:8rem;font-weight:900;color:rgba(255,255,255,.1);font-family:Unbounded,sans-serif;line-height:1;pointer-events:none">★</div>'
+'<div style="position:relative;z-index:1">'
+'<div style="font-size:.78rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;opacity:.85;margin-bottom:6px">ФИНАЛ ГЛАВЫ 1</div>'
+'<h2 style="font-family:Unbounded,sans-serif;font-size:1.55rem;font-weight:800;margin-bottom:8px">6 интегрированных боссов</h2>'
+'<p style="font-size:.95rem;opacity:.92;margin-bottom:14px;max-width:580px">Каждый босс проверяет синтез нескольких параграфов главы. Победи всех — получи ачивку <b>«Магистр тригонометрии»</b> и +150 XP.</p>'
+'<div style="display:flex;gap:12px;flex-wrap:wrap;align-items:center">'
+'<div style="padding:8px 14px;background:rgba(255,255,255,.18);border-radius:99px;font-size:.82rem;font-weight:700">★ 6 боссов</div>'
+'<div style="padding:8px 14px;background:rgba(255,255,255,.18);border-radius:99px;font-size:.82rem;font-weight:700">+ до 300 XP</div>'
+'<div style="padding:8px 14px;background:rgba(255,255,255,.18);border-radius:99px;font-size:.82rem;font-weight:700">★ Финальная ачивка</div>'
+'</div></div></div>';
/* === Overall progress === */
html += '<div style="background:var(--card);border:1.5px solid var(--border);border-radius:14px;padding:16px 20px;margin-bottom:20px">'
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">'
+'<div style="font-family:Unbounded,sans-serif;font-size:.85rem;font-weight:800;color:var(--text);letter-spacing:.06em;text-transform:uppercase">Прогресс по боссам</div>'
+'<div id="final1-overall" style="font-size:.95rem;font-weight:700;color:var(--pri2)">0 / 6 побеждено</div>'
+'</div>'
+'<div style="height:12px;background:rgba(13,148,136,.12);border-radius:8px;overflow:hidden">'
+'<div id="final1-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#0d9488,#7c3aed);transition:width .6s cubic-bezier(.16,1,.3,1)"></div>'
+'</div></div>';
/* === Bosses container === */
html += '<div id="final1-bosses"></div>';
/* === Celebration === */
html += '<div id="final1-cel" style="display:none;margin:24px 0;padding:24px 22px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:2px solid #f59e0b;border-radius:18px;text-align:center;box-shadow:0 6px 22px rgba(245,158,11,.25)">'
+'<div style="font-size:3rem;margin-bottom:6px">★</div>'
+'<div style="font-family:Unbounded,sans-serif;font-size:1.4rem;font-weight:900;color:#92400e;margin-bottom:6px">МАГИСТР ТРИГОНОМЕТРИИ!</div>'
+'<div style="font-size:.95rem;color:#78350f;margin-bottom:14px">Ты победил всех 6 боссов главы 1 и освоил тригонометрию.<br>Получено: <b>+150 XP</b> и финальная ачивка.</div>'
+'<a href="/textbook/algebra-10" style="display:inline-flex;align-items:center;gap:8px;padding:11px 22px;background:linear-gradient(135deg,#0d9488,#7c3aed);color:#fff;border-radius:11px;font-weight:700;text-decoration:none">Вернуться к Алгебре 10 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><polyline points="9 18 15 12 9 6"/></svg></a>'
+'</div>';
html += secNav('p12', null) + readButton('final1');
box.innerHTML = html; renderMath(box);
/* === State === */
const SKEY = 'algebra10_ch1_final1_state';
let state = {};
try{ const s=localStorage.getItem(SKEY); if(s) state = JSON.parse(s); }catch(e){}
/* инициализация */
FINAL1_BOSSES.forEach(b => {
if(!state['b'+b.n]) state['b'+b.n] = {stage:0, defeated:false};
});
function save(){ try{ localStorage.setItem(SKEY, JSON.stringify(state)); }catch(e){} }
function refreshOverall(){
let won = 0;
FINAL1_BOSSES.forEach(b => { if(state['b'+b.n].defeated) won++; });
document.getElementById('final1-overall').textContent = won + ' / 6 побеждено';
document.getElementById('final1-overall-fill').style.width = (won*100/6) + '%';
if(won >= 6){
document.getElementById('final1-cel').style.display = 'block';
if(STATE.progress.final1 < 100){
bumpProgress('final1', 100);
}
if(!STATE.achievements.has('trig_master')){
achievement('trig_master');
/* Бонус 150 XP */
addXp(150, 'trig-master');
}
}
}
/* === Build boss cards === */
const cont = document.getElementById('final1-bosses');
cont.innerHTML = FINAL1_BOSSES.map(b => {
return '<div class="boss-card" id="bb-'+b.n+'-card" style="border-color:'+b.color+'">'
+'<div class="boss-head" style="flex-wrap:wrap">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px"><polygon points="12,2 22,20 2,20"/></svg>'
+'<div style="padding:3px 10px;background:'+b.color+'22;color:'+b.color+';border-radius:99px;font-family:Unbounded,sans-serif;font-size:.7rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase">'+b.tag+'</div>'
+'<div class="boss-title" style="color:'+b.color+';flex:1;min-width:0">Босс '+b.n+'. '+b.title+'</div>'
+'<div class="boss-stage" id="bb-'+b.n+'-stage">Этап 1 / '+b.steps.length+'</div>'
+'</div>'
+'<div class="hp-boss" style="border-color:'+b.color+'66;background:'+b.color+'1a"><div class="hp-boss-fill" id="bb-'+b.n+'-fill" style="width:0%;background:linear-gradient(90deg,'+b.color+',#f59e0b)"></div></div>'
+'<div class="boss-q" id="bb-'+b.n+'-q" style="border-color:'+b.color+'"></div>'
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
+'<input type="text" id="bb-'+b.n+'-input" class="tinp" placeholder="Ответ" style="width:160px;text-align:center">'
+'<button class="btn primary" id="bb-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атака</button>'
+'<button class="btn" id="bb-'+b.n+'-hint">Подсказка</button>'
+'<button class="btn" id="bb-'+b.n+'-restart">↻</button>'
+'</div>'
+'<div class="feedback" id="bb-'+b.n+'-fb"></div>'
+'</div>';
}).join('');
if(window.renderMathInElement) try{ renderMath(cont); }catch(e){}
/* === Bind handlers === */
FINAL1_BOSSES.forEach(b => {
const stKey = 'b' + b.n;
function show(){
const st = state[stKey];
const stageEl=document.getElementById('bb-'+b.n+'-stage');
const fill=document.getElementById('bb-'+b.n+'-fill');
const q=document.getElementById('bb-'+b.n+'-q');
const fb=document.getElementById('bb-'+b.n+'-fb');
if(st.defeated){
stageEl.textContent='✓ Побеждён';
fill.style.width='100%';
q.innerHTML='<b style="color:'+b.color+'">Босс повержен!</b>';
document.getElementById('bb-'+b.n+'-go').disabled=true;
document.getElementById('bb-'+b.n+'-go').style.opacity=.5;
return;
}
stageEl.textContent='Этап '+(st.stage+1)+' / '+b.steps.length;
fill.style.width=(st.stage*100/b.steps.length)+'%';
q.innerHTML=b.steps[st.stage].q;
document.getElementById('bb-'+b.n+'-input').value='';
fb.style.display='none';
renderMath(q);
}
document.getElementById('bb-'+b.n+'-go').addEventListener('click', ()=>{
const st = state[stKey];
if(st.defeated) return;
const step=b.steps[st.stage];
const val=document.getElementById('bb-'+b.n+'-input').value;
const fb=document.getElementById('bb-'+b.n+'-fb');
if(!val.trim()){ feedback(fb,false,'&#10007; Введи ответ.'); return; }
if(step.verify(val)){
st.stage++;
if(st.stage>=b.steps.length){
st.defeated=true; save();
feedback(fb,true,'&#10003; Босс '+b.n+' повержен! +25 XP');
addXp(25,'final1-b'+b.n);
refreshOverall();
setTimeout(show, 1400);
} else {
save();
feedback(fb,true,'&#10003; Верно! +5 XP');
addXp(5,'final1-b'+b.n+'-step');
setTimeout(show, 1100);
}
} else {
feedback(fb,false,'&#10007; Промах. Подумай ещё.');
}
});
document.getElementById('bb-'+b.n+'-hint').addEventListener('click', ()=>{
const st = state[stKey];
if(st.defeated) return;
const fb=document.getElementById('bb-'+b.n+'-fb');
fb.className='feedback ok';
fb.innerHTML='<span style="color:#92400e">\u{1F4A1} Подсказка:</span> '+b.steps[st.stage].hint;
fb.style.display='block';
fb.style.background='var(--warn-bg)'; fb.style.color='#92400e'; fb.style.borderLeftColor='var(--warn)';
renderMath(fb);
});
document.getElementById('bb-'+b.n+'-restart').addEventListener('click', ()=>{
state[stKey] = {stage:0, defeated:false}; save();
document.getElementById('bb-'+b.n+'-go').disabled=false;
document.getElementById('bb-'+b.n+'-go').style.opacity=1;
show(); refreshOverall();
});
show();
});
refreshOverall();
wireReadBtn('final1');
}
</script>
</body>