Files
Learn_System/frontend/textbooks/algebra_7_ch1.html
T
Maxim Dolgolyov 9d5a2959e1 fix(textbooks): кнопка «Шпаргалка» не открывала контент на desktop
На десктопе (>980px) .col-side уже видна как sticky-колонка справа в grid 1fr 280px.
Клик по кнопке #sidebar-btn добавлял .col-side-backdrop.show — backdrop с
z-index:9990 затемнял всю страницу, перекрывая sticky-aside. Со стороны
выглядело как «ничего не открылось» — на самом деле появлялась чёрная вуаль.

Фикс: @media(min-width:981px) скрывает #sidebar-btn и подавляет показ backdrop.
На мобайле (≤980px) кнопка и overlay работают как раньше.

Применено в 51 файле: physics 8/9/10 chN, algebra 7/9/10/11 chN + 8 ch2-3,
geometry 7/8/9/11 chN, geometry_10 r1-4.
2026-05-30 09:51:04 +03:00

1640 lines
115 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">
<title>Алгебра 7 · Глава 1 · Степени</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<link rel="stylesheet" href="/css/alg7-fx.css">
<script src="/js/alg7-fx.js" defer></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#d97706; --pri2:#b45309; --pri-soft:#fef3c7;
--acc:#f59e0b; --acc2:#d97706; --acc-soft:#fef9c3;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#0a0a0e; --card:#13120a; --card-soft:#18160a; --text:#fef9e7; --ink:#fef9e7; --muted:#a39070; --border:#2a2512}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
/* HEADER */
.hdr{position:relative;background:linear-gradient(110deg,#92400e 0%,#d97706 55%,#fbbf24 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(251,191,36,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 1';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,235,180,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
/* MAIN GRID */
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
/* HERO */
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero::before{content:'a&#8319;';position:absolute;right:-10px;top:-20px;font-size:clamp(2rem,8vw,5.5rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(217,119,6,.32)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(217,119,6,.18);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(217,119,6,.22);font-family:'Unbounded',sans-serif}
/* PARA SELECTOR */
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
.psel-name{font-size:.88rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(217,119,6,.10);border-radius:3px;overflow:hidden}
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
.psel-card.final{background:linear-gradient(135deg,#fff5e1,#fef3c7)}
.psel-card.final .psel-num{color:var(--warn)}
/* SECTION COLORS */
.sec[id="sec-p1"] { --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p2"] { --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p3"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-final1"]{ --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.7rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
/* CARDS */
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(217,119,6,.06);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(217,119,6,.12)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}.card-icon.class{background:#3b82f6}.card-icon.home{background:#f97316}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.card-body p:last-child{margin-bottom:0}
/* WIDGET */
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
/* BUTTONS */
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.btn.small{padding:5px 11px;font-size:.78rem}
/* INPUTS */
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
/* FEEDBACK */
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
/* SIDEBAR */
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
.sidecard-row:last-child{margin-bottom:0}
@media(max-width:980px){.col-side{position:static;max-height:none}}
/* XP card */
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
.xp-bar{height:9px;background:rgba(245,158,11,.15);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
/* SPOILER */
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
/* TABLES */
.tbl{width:100%;border-collapse:collapse;margin:12px 0;font-size:.88rem}
.tbl th,.tbl td{padding:7px 10px;border:1px solid var(--border);text-align:center}
.tbl th{background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));font-weight:700}
/* ACH popup */
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#d97706,#f59e0b);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(217,119,6,.45);z-index:1002;display:none;align-items:center;gap:8px;animation:achIn .45s cubic-bezier(.34,1.56,.64,1) forwards;max-width:340px}
.ach-popup.show{display:flex}
@keyframes achIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
/* DRAG & DROP */
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
/* HP-BAR for bosses */
.hp-boss{height:14px;background:rgba(220,38,38,.12);border-radius:9px;overflow:hidden;border:1px solid #fecaca;margin:8px 0}
.hp-boss-fill{height:100%;background:linear-gradient(90deg,#dc2626,#f59e0b);border-radius:9px;transition:width .5s cubic-bezier(.4,0,.2,1)}
/* SIDEBAR DRAWER */
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none;animation:fadeIn .18s ease}
.col-side-backdrop.show{display:block}
@media(min-width:981px){#sidebar-btn{display:none}.col-side-backdrop.show{display:none}}
@media(max-width:980px){
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
.col-side.open{transform:none}
}
/* GLOSSARY */
.gloss-term{border-bottom:1.5px dotted var(--sec-acc,var(--pri));cursor:help;color:var(--sec-acc-d,var(--pri2));font-weight:600;padding:0 1px}
.gloss-term:hover{background:var(--sec-acc-soft,var(--pri-soft));border-radius:3px}
.gloss-tip{position:fixed;max-width:320px;padding:11px 14px;background:var(--card);border:1.5px solid var(--sec-acc,var(--pri));border-radius:11px;font-size:.84rem;line-height:1.55;box-shadow:0 12px 32px rgba(0,0,0,.18);z-index:9994;display:none;pointer-events:none;color:var(--text)}
.gloss-tip.show{display:block;animation:tipIn .15s ease}
.gloss-tip b{color:var(--sec-acc-d,var(--pri2));font-size:.92rem}
@keyframes tipIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
/* SEARCH */
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
.search-modal.show{display:flex;animation:fadeIn .15s ease}
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
.search-results{flex:1;overflow-y:auto;padding:6px 0}
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border-left:0;border-right:0;border-top:0;width:100%;color:var(--text)}
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px;background:var(--card-soft,transparent)}
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
/* BOSS */
.boss-card{padding:16px;background:var(--card);border-radius:12px;border:2px solid var(--bad,#dc2626);margin-bottom:14px}
.boss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.boss-title{font-family:'Unbounded',sans-serif;font-weight:800;color:#7f1d1d;font-size:1.04rem;flex:1}
.boss-stage{font-size:.85rem;color:var(--muted)}
.boss-q{font-size:1rem;line-height:1.55;padding:11px 13px;background:var(--card-soft);border-radius:8px;margin-bottom:9px;border-left:3px solid var(--bad,#dc2626)}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Алгебра 7 · Глава 1</h1>
<div class="hdr-sub">Степень с натуральным и целым показателем · Стандартный вид числа</div>
</div>
<div class="hdr-side">
<a href="/textbook/algebra-7" class="hdr-btn" title="К Алгебре 7 — все главы">
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
К алгебре 7
</a>
<button id="search-btn" class="hdr-btn" title="Поиск (Ctrl+K)">
<svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg>
<span>Поиск</span>
</button>
<button id="sidebar-btn" class="hdr-btn" title="Шпаргалка">
<svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg>
Шпаргалка
</button>
<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 class="main">
<div class="col-main">
<section class="hero">
<h2>Степени: от натуральных к целым показателям</h2>
<p>В этой главе мы изучаем <b>степень с натуральным показателем</b> и её пять свойств, расширяем понятие до <b>целого показателя</b> (включая ноль и отрицательные), и осваиваем <b>стандартный вид числа</b> — мощный инструмент для работы с очень большими и очень маленькими числами в науке.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')">
<svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg>
Начать § 1
</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" title="Опыт" data-gamified></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="a&#8319;"><div class="sec-header"><span class="sec-num">§ 1</span><h2 class="sec-h">Степень с натуральным показателем и её свойства</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="a&#8313;"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Степень с целым показателем и её свойства</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="&times;10&#8319;"><div class="sec-header"><span class="sec-num">§ 3</span><h2 class="sec-h">Стандартный вид числа</h2></div><div id="p3-body"></div></section>
<section id="sec-final1" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#d97706,#f59e0b)">Финал главы</span><h2 class="sec-h">Итоги. Боссы главы 1</h2></div><div id="final1-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Алгебра 7» · Глава 1 · Степени · LearnSpace</footer>
<div id="ach-popup" class="ach-popup">
<svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg>
<span id="ach-text">Достижение!</span>
</div>
<div id="gloss-tip" class="gloss-tip"></div>
<div id="search-modal" class="search-modal" role="dialog" aria-label="Поиск по главе">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск: понятие, свойство, параграф…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot">
<span><kbd>&#8593;&#8595;</kbd> навигация</span>
<span><kbd>Enter</kbd> открыть</span>
<span><kbd>Esc</kbd> закрыть</span>
</div>
</div>
</div>
<script>
'use strict';
/* ============================================================
STATE + PROGRESS + XP
============================================================ */
const STATE = {
current: 'p1',
progress: { p1:0, p2:0, p3:0, final1:0 },
achievements: new Map(),
xp: 0,
level: 1,
};
const TOTAL_PARAS = 4;
const _TB_SLUG = 'algebra-7-ch1';
function calcLevel(xp){ return Math.floor(Math.sqrt((xp || 0) / 100)) + 1; }
function _xpForLevel(lv){ return (lv - 1) * (lv - 1) * 100; }
const ACH_LABELS = {
start: 'Начало главы 1!',
p1_done: 'Степени натуральные покорены!',
p2_done: 'Целые показатели — лёгкая задача!',
p3_done: 'Стандартный вид освоен!',
ch1_done: 'Глава 1 пройдена!',
};
function loadProgress(){
try{
const s = localStorage.getItem('algebra7_ch1_progress');
if(s) Object.assign(STATE.progress, JSON.parse(s));
const a = localStorage.getItem('algebra7_ch1_achievements');
if(a){
const p = JSON.parse(a);
if(Array.isArray(p)) p.forEach(id => STATE.achievements.set(id, ACH_LABELS[id] || id));
else if(p && typeof p === 'object'){
for(const [id, t] of Object.entries(p)) STATE.achievements.set(id, (t && t !== id) ? t : (ACH_LABELS[id] || id));
}
}
STATE.xp = +(localStorage.getItem('algebra7_xp') || 0);
STATE.level = calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('algebra7_ch1_progress', JSON.stringify(STATE.progress));
localStorage.setItem('algebra7_ch1_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('algebra7_xp', String(STATE.xp));
}catch(e){}
}
function bumpProgress(key, delta){
STATE.progress[key] = Math.max(0, Math.min(100, (STATE.progress[key]||0) + delta));
saveProgress();
refreshProgressUI();
if(STATE.progress[key] >= 50) markParaRead(key);
if(STATE.progress[key] >= 100){
if(key === 'p1') achievement('p1_done');
else if(key === 'p2') achievement('p2_done');
else if(key === 'p3') achievement('p3_done');
else if(key === 'final1') achievement('ch1_done');
}
}
const _markedRead = new Set();
let _pendingProgressBody = null, _progressTimer = null;
function _flushProgress(){
const body = _pendingProgressBody; _pendingProgressBody = null; if(!body) return;
const tok = (window.LS && LS.getToken) ? LS.getToken() : ''; if(!tok) return;
fetch('/api/textbooks/' + _TB_SLUG + '/progress', { method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok}, body:JSON.stringify(body), keepalive:true }).catch(()=>{});
}
function _queueProgress(patch){
_pendingProgressBody = Object.assign(_pendingProgressBody || {}, patch);
if(_progressTimer) clearTimeout(_progressTimer);
_progressTimer = setTimeout(_flushProgress, 600);
}
function markLastPara(id){ _queueProgress({ last_para: id }); }
function markParaRead(id){
if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({ mark_read: id });
}
window.addEventListener('beforeunload', _flushProgress);
function loadServerReadState(){
const tok = (window.LS && LS.getToken) ? LS.getToken() : ''; if(!tok) return;
fetch('/api/textbooks/' + _TB_SLUG, { headers:{'Authorization':'Bearer '+tok} })
.then(r => r.ok ? r.json() : null)
.then(d => {
if(!d || !d.progress) return;
(d.progress.read || []).forEach(k => { _markedRead.add(k); if((STATE.progress[k]||0) < 50) STATE.progress[k] = 100; });
saveProgress(); refreshProgressUI();
}).catch(()=>{});
}
function addXp(n, src){
if(!n) return;
const prev = STATE.level;
STATE.xp = Math.max(0, (STATE.xp||0) + n);
STATE.level = calcLevel(STATE.xp);
saveProgress(); refreshProgressUI();
if(window.LS && window.LS.xp) window.LS.xp.add(n, 'algebra7-ch1-' + (src||'misc'));
if(STATE.level > prev){
const pop = document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent = 'Уровень ' + STATE.level + '!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'), 2600); }
if(window.confetti) try{ confetti(); }catch(e){}
}
}
function refreshProgressUI(){
const total = Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0) / TOTAL_PARAS);
const f = document.getElementById('hero-hp-fill'); if(f) f.style.width = total + '%';
const t = document.getElementById('hero-hp-text'); if(t) t.textContent = total + '% пройдено';
document.querySelectorAll('[data-prog-card]').forEach(el=>{
const k = el.dataset.progCard; const fl = el.querySelector('.psel-prog-fill'); if(fl) fl.style.width = (STATE.progress[k]||0) + '%';
});
const xpBadge = document.getElementById('hero-xp-badge');
if(xpBadge){ xpBadge.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. ' + STATE.level + ' \xb7 ' + (STATE.xp||0) + ' XP'; }
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
}
function achievement(id, text){
if(STATE.achievements.has(id)) return;
STATE.achievements.set(id, text || ACH_LABELS[id] || id);
saveProgress();
const pop = document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent = text || ACH_LABELS[id] || id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'), 3300); }
addXp(20, 'ach-' + id);
}
/* ============================================================
PARAGRAPHS + NAVIGATION
============================================================ */
const PARAS = [
{ id:'p1', num:'§ 1', name:'Степень с натуральным показателем', sub:'Определение и 5 свойств' },
{ id:'p2', num:'§ 2', name:'Степень с целым показателем', sub:'a⁰ = 1, a⁻ⁿ = 1/aⁿ' },
{ id:'p3', num:'§ 3', name:'Стандартный вид числа', sub:'a \xB7 10ⁿ — порядок числа' },
{ id:'final1', num:'★', name:'Финал главы', sub:'Итоги \xB7 5 боссов', final:true },
];
function buildParaSelector(){
const g = document.getElementById('psel-grid'); g.innerHTML = '';
PARAS.forEach(p=>{
const card = document.createElement('div');
card.className = 'psel-card' + (p.final ? ' final' : '');
card.dataset.id = p.id; card.dataset.progCard = p.id;
card.innerHTML = '<div class="psel-num">' + p.num + '</div><div class="psel-name">' + p.name + '</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
card.addEventListener('click', ()=>goTo(p.id));
g.appendChild(card);
});
}
const BUILT = new Set();
const BUILDERS = {
p1:()=>buildP1(), p2:()=>buildP2(), p3:()=>buildP3(), final1:()=>buildFinal1(),
};
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn = BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current = id; ensureBuilt(id);
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
const el = document.getElementById('sec-' + id); if(el) el.classList.add('active');
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id === id));
buildSidebar(id);
window.scrollTo({top:0, behavior:'smooth'});
if((STATE.progress[id]||0) < 10) bumpProgress(id, 10);
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
setTimeout(()=>{ try{ wrapGlossary(el); }catch(e){} }, 60);
markLastPara(id);
}
/* SIDEBAR DATA */
const SIDEBARS = {
p1: { title:'Шпаргалка \xA7 1', rows:[
['Степень $a^n$','произведение $n$ одинаковых множителей $a$'],
['$a^1$','$= a$'],
['Свойство 1','$a^n \\cdot a^m = a^{n+m}$'],
['Свойство 2','$a^n : a^m = a^{n-m}$, $n>m$, $a\\ne 0$'],
['Свойство 3','$(a^n)^m = a^{nm}$'],
['Свойство 4','$(a:b)^n = a^n : b^n$, $b\\ne 0$'],
['Свойство 5','$(ab)^n = a^n \\cdot b^n$'],
]},
p2: { title:'Шпаргалка \xA7 2', rows:[
['$a^0$','$= 1$, $a\\ne 0$'],
['$a^{-n}$','$= \\dfrac{1}{a^n}$, $a\\ne 0$'],
['Знак','$(+)^k > 0$ всегда'],
['$(-)^{\\text{чёт}}$','$> 0$'],
['$(-)^{\\text{неч}}$','$< 0$'],
['Свойства','те же 5 для любых целых $m, n$'],
]},
p3: { title:'Шпаргалка \xA7 3', rows:[
['Стандартный вид','$b = a \\cdot 10^n$'],
['Условие','$1 \\le a < 10$'],
['Показатель','$n$ — целое'],
['$n$ — порядок','числа $b$'],
['Большое число','$n > 0$'],
['Малое число','$n < 0$'],
]},
final1: { title:'Финал главы', rows:[
['\xA71\xA73','теория глава 1'],
['Боссов','5 итоговых задач'],
['Награда','+50 XP за полное прохождение'],
]},
};
const TIPS = [
{ sec:'p1', html:'Запоминай свойства не зубрёжкой, а так: $a^n \\cdot a^m$ — это произведение $(n+m)$ одинаковых множителей $a$. Логика та же для $a^n : a^m$.' },
{ sec:'p2', html:'$a^{-n}$ — это <b>обратное</b> число к $a^n$. То есть $a^{-n} \\cdot a^n = 1$. Поэтому $a^{-n} = \\dfrac{1}{a^n}$.' },
{ sec:'p3', html:'В стандартном виде <b>обязательно</b> $1 \\le a < 10$. Если $a = 23{,}5 \\cdot 10^4$ — это <b>не</b> стандартный вид: нужно $2{,}35 \\cdot 10^5$.' },
{ sec:'final1', html:'Боссы проверяют все свойства степени и алгоритм стандартного вида. Не торопись — лучше внимательно прочитай условие.' },
];
function buildSidebar(id){
const box = document.getElementById('sidebar-content');
const sb = SIDEBARS[id] || SIDEBARS.p1;
let html = '';
const xpForLv = _xpForLevel(STATE.level), xpNext = _xpForLevel(STATE.level + 1);
const xpInLv = STATE.xp - xpForLv, xpRange = xpNext - xpForLv;
const xpPct = xpRange > 0 ? Math.round(xpInLv / xpRange * 100) : 100;
html += '<div class="xp-card" data-gamified><div class="xp-card-title" data-gamified><span>XP-прогресс</span><span class="xp-level">Ур. ' + STATE.level + '</span></div><div class="xp-bar"><div class="xp-fill" style="width:' + xpPct + '%"></div></div><div class="xp-nums"><span>' + STATE.xp + ' XP</span><span>' + xpNext + ' XP</span></div></div>';
html += '<div class="sidecard"><h4>' + sb.title + '</h4>';
sb.rows.forEach(([k,v])=>{ html += '<div class="sidecard-row"><b>' + k + '</b>' + (v ? ' — ' + v : '') + '</div>'; });
html += '</div>';
const tip = TIPS.find(t=>t.sec === id) || TIPS[0];
html += '<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">' + tip.html + '</div></div>';
if(STATE.achievements.size > 0){
html += '<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">' + STATE.achievements.size + '</span></h4>';
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html += '<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ ' + text + '</div>'; });
html += '</div>';
}
box.innerHTML = html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
/* THEME */
function initTheme(){
const t = localStorage.getItem('algebra7_ch1_theme') || 'light';
if(t === 'dark') document.documentElement.classList.add('dark');
document.getElementById('theme-lab').textContent = t === 'dark' ? 'Светлая' : 'Тёмная';
document.getElementById('theme-btn').addEventListener('click', ()=>{
document.documentElement.classList.toggle('dark');
const dark = document.documentElement.classList.contains('dark');
localStorage.setItem('algebra7_ch1_theme', dark ? 'dark' : 'light');
document.getElementById('theme-lab').textContent = dark ? 'Светлая' : 'Тёмная';
});
}
/* HELPERS */
function renderMath(root){
if(window.renderMathInElement){
try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){}
}
}
function feedback(elm, ok, text){ if(!elm)return; elm.className = 'feedback ' + (ok ? 'ok' : 'fail'); elm.innerHTML = text || (ok ? '&#10003; Верно!' : '&#10007; Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n - Math.round(n)) < 1e-9 ? String(Math.round(n)) : (+n.toFixed(6)).toString(); }
function ipow(base, exp){ let r=1; for(let i=0;i<Math.abs(exp);i++) r*=base; return exp<0 ? 1/r : r; }
const ICONS = {
repeat: '<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
theory: '<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
algo: '<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
rule: '<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
example: '<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
oral: '<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
};
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
return '<div class="card"><div class="card-header"><div class="card-icon ' + kind + '">' + ICONS[kind] + '</div><div class="card-title">' + (labels[kind]||'') + (title && title !== labels[kind] ? ' \xb7 ' + title : '') + '</div>' + (num ? '<div class="card-num">' + num + '</div>' : '') + '</div><div class="card-body">' + body + '</div></div>';
}
function secNav(prev, next){
const NAMES = {p1:'\xA71',p2:'\xA72',p3:'\xA73',final1:'Финал'};
let h = '<div class="sec-nav">';
h += prev ? '<button class="btn" onclick="goTo(\'' + prev + '\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> ' + NAMES[prev] + '</button>' : '<span></span>';
h += next ? '<button class="btn primary" onclick="goTo(\'' + next + '\')">' + NAMES[next] + ' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>' : '<span></span>';
h += '</div>'; return h;
}
/* CONFETTI */
let _confettiCanvas = null, _confettiParticles = [], _confettiRaf = null;
function confetti(){
if(!_confettiCanvas){ _confettiCanvas = document.createElement('canvas'); _confettiCanvas.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999'; document.body.appendChild(_confettiCanvas); }
const c = _confettiCanvas; c.width = window.innerWidth; c.height = window.innerHeight;
const ctx = c.getContext('2d');
const colors = ['#d97706','#f59e0b','#fbbf24','#10b981','#8b5cf6'];
for(let i = 0; i < 80; i++){ _confettiParticles.push({ x:window.innerWidth/2+(Math.random()-.5)*200, y:window.innerHeight/2, vx:(Math.random()-.5)*14, vy:-10-Math.random()*10, g:.4, life:100, color:colors[i%colors.length], r:4+Math.random()*4, rot:0, vRot:(Math.random()-.5)*.3 }); }
if(_confettiRaf) cancelAnimationFrame(_confettiRaf);
function frame(){ ctx.clearRect(0,0,c.width,c.height); _confettiParticles=_confettiParticles.filter(p=>{ p.x+=p.vx;p.y+=p.vy;p.vy+=p.g;p.life--;p.rot+=p.vRot;ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);ctx.fillStyle=p.color;ctx.fillRect(-p.r,-p.r/2,p.r*2,p.r);ctx.restore();return p.life>0&&p.y<c.height+50; }); if(_confettiParticles.length>0) _confettiRaf=requestAnimationFrame(frame); else{ ctx.clearRect(0,0,c.width,c.height);_confettiRaf=null; } }
frame();
}
/* DnD SORTER */
function setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
/* GLOSSARY */
const GLOSSARY = [
{ term:'степень', def:'$a^n$ — произведение $n$ одинаковых множителей, равных $a$ (для натурального $n > 1$).', sec:'p1', aliases:['степень','степени','степенью','степеней','степенями'] },
{ term:'основание степени', def:'Число $a$ в записи $a^n$.', sec:'p1', aliases:['основание степени','основанием степени','основания степени'] },
{ term:'показатель степени', def:'Число $n$ в записи $a^n$ — сколько раз основание повторяется множителем.', sec:'p1', aliases:['показатель степени','показателем степени','показателя степени','показатели степени'] },
{ term:'квадрат числа', def:'Вторая степень: $a^2 = a \\cdot a$.', sec:'p1', aliases:['квадрат числа','квадрата числа','квадрату числа'] },
{ term:'куб числа', def:'Третья степень: $a^3 = a \\cdot a \\cdot a$.', sec:'p1', aliases:['куб числа','куба числа','кубу числа'] },
{ term:'степень с нулевым показателем', def:'$a^0 = 1$ для любого $a \\ne 0$.', sec:'p2', aliases:['степень с нулевым показателем','нулевая степень','нулевой степени'] },
{ term:'степень с отрицательным показателем', def:'$a^{-n} = \\dfrac{1}{a^n}$ — число, обратное $a^n$ ($a \\ne 0$).', sec:'p2', aliases:['степень с отрицательным показателем','отрицательная степень','отрицательной степени','отрицательным показателем'] },
{ term:'стандартный вид числа', def:'Запись $b = a \\cdot 10^n$, где $1 \\le a < 10$, а $n$ — целое.', sec:'p3', aliases:['стандартный вид числа','стандартном виде','стандартный вид','стандартного вида'] },
{ term:'порядок числа', def:'Показатель $n$ в записи $b = a \\cdot 10^n$ (стандартный вид).', sec:'p3', aliases:['порядок числа','порядка числа','порядком числа','порядки чисел'] },
{ term:'мантисса', def:'Множитель $a$ в записи $a \\cdot 10^n$ ($1 \\le a < 10$).', sec:'p3', aliases:['мантисса','мантиссы','мантиссой'] },
];
function wrapGlossary(root){
if(!root||root.__glossDone) return;
const allAliases = [];
GLOSSARY.forEach((g,i)=>g.aliases.forEach(a=>allAliases.push({a,i})));
allAliases.sort((x,y)=>y.a.length-x.a.length);
const re = new RegExp('(?<![\\w\\u0400-\\u04ff-])(' + allAliases.map(x=>x.a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('|') + ')(?![\\w\\u0400-\\u04ff-])', 'iu');
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode(node){ const p=node.parentElement; if(!p) return NodeFilter.FILTER_REJECT; if(p.closest('.katex,.gloss-term,button,input,select,.wg-badge,.card-icon,.sec-num,.psel-num,.hdr,.ach-popup,script,style,.search-modal,.sidecard,.gloss-tip')) return NodeFilter.FILTER_REJECT; if(!re.test(node.nodeValue)) return NodeFilter.FILTER_REJECT; return NodeFilter.FILTER_ACCEPT; } });
const nodes = []; let n; while((n=walker.nextNode())) nodes.push(n);
nodes.forEach(node=>{ const text=node.nodeValue; const out=document.createDocumentFragment(); let cursor=0; const global=new RegExp(re.source,'giu'); let m; while((m=global.exec(text))!==null){ if(m.index>cursor) out.appendChild(document.createTextNode(text.slice(cursor,m.index))); const found=m[0].toLowerCase(); const hit=allAliases.find(x=>x.a.toLowerCase()===found); const g=hit?GLOSSARY[hit.i]:null; const sp=document.createElement('span'); sp.className='gloss-term'; sp.dataset.gloss=g?g.term:''; sp.textContent=m[0]; out.appendChild(sp); cursor=m.index+m[0].length; } if(cursor<text.length) out.appendChild(document.createTextNode(text.slice(cursor))); node.parentNode.replaceChild(out,node); });
root.__glossDone = true;
}
function initGlossaryTip(){
const tip = document.getElementById('gloss-tip'); if(!tip) return;
let lockOpen = null;
function show(elm){ const g=GLOSSARY.find(x=>x.term===elm.dataset.gloss); if(!g) return; tip.innerHTML='<b>'+g.term[0].toUpperCase()+g.term.slice(1)+'</b><div style="margin-top:4px">'+g.def+'</div><div style="margin-top:6px;font-size:.72rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">См. \xA7 '+g.sec.replace('p','')+'</div>'; if(window.renderMathInElement) renderMath(tip); const r=elm.getBoundingClientRect(); tip.classList.add('show'); const tw=tip.offsetWidth,th=tip.offsetHeight; let left=r.left,top=r.bottom+8; if(left+tw>window.innerWidth-12) left=window.innerWidth-tw-12; if(top+th>window.innerHeight-12) top=r.top-th-8; tip.style.left=Math.max(8,left)+'px'; tip.style.top=Math.max(8,top)+'px'; }
function hide(){ tip.classList.remove('show'); }
document.addEventListener('mouseover',e=>{ const elm=e.target.closest&&e.target.closest('.gloss-term'); if(elm&&!lockOpen) show(elm); });
document.addEventListener('mouseout',e=>{ const elm=e.target.closest&&e.target.closest('.gloss-term'); if(elm&&!lockOpen) hide(); });
document.addEventListener('click',e=>{ const elm=e.target.closest&&e.target.closest('.gloss-term'); if(elm){ if(lockOpen===elm){lockOpen=null;hide();}else{lockOpen=elm;show(elm);} }else if(lockOpen&&!e.target.closest('.gloss-tip')){lockOpen=null;hide();} });
}
/* SEARCH */
const SEARCH_INDEX = (function(){
const arr = [];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
GLOSSARY.forEach(g=>arr.push({kind:'Понятие',title:g.term,desc:g.def.replace(/\$/g,''),sec:g.sec,gloss:g.term}));
[
['Формула','a^n \xB7 a^m = a^(n+m) — произведение','\xA71','p1'],
['Формула','a^n : a^m = a^(n-m) — частное','\xA71','p1'],
['Формула','(a^n)^m = a^(nm) — степень степени','\xA71','p1'],
['Формула','(ab)^n = a^n \xB7 b^n — степень произведения','\xA71','p1'],
['Формула','(a/b)^n = a^n / b^n — степень частного','\xA71','p1'],
['Формула','a^0 = 1 (при a ≠ 0)','\xA72','p2'],
['Формула','a^(-n) = 1/a^n (при a ≠ 0)','\xA72','p2'],
['Формула','b = a \xB7 10^n, 1 ≤ a < 10 — стандартный вид','\xA73','p3'],
].forEach(([k,t,d,s])=>arr.push({kind:k,title:t,desc:d,sec:s}));
return arr;
})();
function initSearch(){
const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');
if(!modal||!inp||!out) return;
let cur=0,rows=[];
function score(q,it){ const t=(it.title+' '+it.desc).toLowerCase(); if(t.includes(q)) return 100+(it.title.toLowerCase().startsWith(q)?50:0); let s=0; q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;}); return s; }
function rank(q){ q=q.trim().toLowerCase(); if(!q) return SEARCH_INDEX.slice(0,12); return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it); }
function render(){ cur=0; if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;} out.innerHTML=rows.map((r,i)=>'<button class="search-row'+(i===0?' active':'')+'" data-i="'+i+'"><div class="sr-kind">'+r.kind+'</div><div class="sr-title">'+r.title+'</div>'+(r.desc?'<div class="sr-desc">'+(r.desc.length>90?r.desc.slice(0,90)+'…':r.desc)+'</div>':'')+'</button>').join(''); out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();})); }
function pick(){ const r=rows[cur];if(!r)return; close(); goTo(r.sec); }
function move(d){ const items=out.querySelectorAll('.search-row');if(!items.length)return; items[cur]&&items[cur].classList.remove('active'); cur=(cur+d+items.length)%items.length; items[cur].classList.add('active');items[cur].scrollIntoView({block:'nearest'}); }
function open(){ modal.classList.add('show');inp.value='';rows=rank('');render();setTimeout(()=>inp.focus(),50); }
function close(){ modal.classList.remove('show'); }
btn&&btn.addEventListener('click',open);
modal.addEventListener('click',e=>{if(e.target===modal)close();});
inp.addEventListener('input',()=>{rows=rank(inp.value);render();});
inp.addEventListener('keydown',e=>{ if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();} });
document.addEventListener('keydown',e=>{ if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); if(modal.classList.contains('show'))close();else open(); } });
}
/* SIDEBAR TOGGLE */
function initSidebarToggle(){
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
if(!side||!btn) return;
function open(){ side.classList.add('open');back.classList.add('show'); }
function close(){ side.classList.remove('open');back.classList.remove('show'); }
btn.addEventListener('click',()=>{ if(side.classList.contains('open'))close();else open(); });
back.addEventListener('click',close);
document.addEventListener('keydown',e=>{ if(e.key==='Escape')close(); });
}
/* INIT */
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initGlossaryTip(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo('p1');
setTimeout(()=>achievement('start','Начало главы 1!'), 600);
if(window.LS && window.LS.xp){
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
}
}
document.addEventListener('DOMContentLoaded', init);
/* ============================================================
\xA7 1 — СТЕПЕНЬ С НАТУРАЛЬНЫМ ПОКАЗАТЕЛЕМ
============================================================ */
function buildP1(){
const box = document.getElementById('p1-body');
let html = '';
html += makeCard('theory', 'Определение степени', '1.1', `
<p><b>Степенью</b> числа $a$ с натуральным показателем $n$, бо́льшим 1, называется произведение $n$ множителей, каждый из которых равен $a$:</p>
\\[a^n = \\underbrace{a \\cdot a \\cdot a \\cdot \\ldots \\cdot a}_{n\\text{ раз}}\\]
<p>Если $n = 1$, то $a^1 = a$.</p>
<p>$a$ — <b>основание степени</b>, $n$ — <b>показатель степени</b>, $a^n$ — <b>значение степени</b>.</p>
<details class="spoiler"><summary>Примеры</summary><div class="spoiler-body">
$2^4 = 2 \\cdot 2 \\cdot 2 \\cdot 2 = 16$ &nbsp;&middot;&nbsp; $5^6 = 5 \\cdot 5 \\cdot 5 \\cdot 5 \\cdot 5 \\cdot 5 = 15625$ &nbsp;&middot;&nbsp; $\\left(\\dfrac{2}{3}\\right)^3 = \\dfrac{8}{27}$
</div></details>`);
html += makeCard('rule', 'Пять свойств степени', '1.2', `
<p>Для натуральных $m, n$:</p>
<ol style="padding-left:22px;line-height:2">
<li><b>Произведение</b> с одинаковыми основаниями: $\\;a^n \\cdot a^m = a^{n+m}$</li>
<li><b>Частное</b> с одинаковыми основаниями: $\\;a^n : a^m = a^{n-m}$, где $a \\ne 0,\\ n > m$</li>
<li><b>Степень степени</b>: $\\;(a^n)^m = a^{nm}$</li>
<li><b>Степень частного</b>: $\\;(a:b)^n = a^n : b^n$, где $b \\ne 0$</li>
<li><b>Степень произведения</b>: $\\;(a \\cdot b)^n = a^n \\cdot b^n$</li>
</ol>`);
html += makeCard('rule', 'Знак степени', '1.3', `
<p>Степень <b>положительного числа</b> при любом показателе — <b>положительна</b>: $3^5 > 0$, $3^{100} > 0$.</p>
<p>Степень <b>отрицательного числа</b>:</p>
<ul style="padding-left:22px;line-height:1.9">
<li>с <b>чётным</b> показателем — <b>положительна</b>: $(-2)^4 = 16$</li>
<li>с <b>нечётным</b> показателем — <b>отрицательна</b>: $(-2)^3 = -8$</li>
</ul>
<p>Запомни: $-3^2 \\ne (-3)^2$. В первом случае возводим $3$, а минус остаётся: $-3^2 = -9$. Во втором — возводим $-3$: $(-3)^2 = 9$.</p>`);
html += makeCard('example', 'Применение свойств', '1.4', `
<p><b>а)</b> $5^2 \\cdot 5^4 = 5^{2+4} = 5^6 = 15625$</p>
<p><b>б)</b> $a^{12} : a^8 = a^{12-8} = a^4$</p>
<p><b>в)</b> $(m^4)^6 = m^{4 \\cdot 6} = m^{24}$</p>
<p><b>г)</b> $(3 \\cdot 5)^3 = 3^3 \\cdot 5^3 = 27 \\cdot 125 = 3375$</p>
<p><b>д)</b> $\\left(\\dfrac{2}{5}\\right)^4 = \\dfrac{2^4}{5^4} = \\dfrac{16}{625}$</p>`);
/* INTERACTIVE 1 */
html += `<div class="wg" id="p1-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Степень-конструктор</div></div>
<div class="wg-help">Подвигай ползунки. Увидишь раскрытие степени и итоговое значение $a^n$.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p1-a-val">2</b><input type="range" id="p1-a-sl" min="-5" max="10" step="1" value="2"></label>
<label>Показатель $n$ = <b id="p1-n-val">4</b><input type="range" id="p1-n-sl" min="1" max="10" step="1" value="4"></label>
</div>
<div id="p1-iv1-out" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:.95rem;line-height:1.8;min-height:60px"></div>
</div>`;
/* INTERACTIVE 2 */
html += `<div class="wg" id="p1-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сопоставь свойство и формулу</div></div>
<div class="wg-help">Перетащи каждое словесное описание к нужной формуле (или нажми карточку, потом — на ящик).</div>
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 5 описаний — 5 ящиков</div>
<div id="p1-iv2-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="prod">$a^n \\cdot a^m$</h5><div class="drop-items" data-cat="prod"></div></div>
<div class="drop-box"><h5 data-cat="quot">$a^n : a^m$</h5><div class="drop-items" data-cat="quot"></div></div>
<div class="drop-box"><h5 data-cat="pow">$(a^n)^m$</h5><div class="drop-items" data-cat="pow"></div></div>
<div class="drop-box"><h5 data-cat="mulb">$(ab)^n$</h5><div class="drop-items" data-cat="mulb"></div></div>
<div class="drop-box"><h5 data-cat="divb">$(a:b)^n$</h5><div class="drop-items" data-cat="divb"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p1-iv2-check">Проверить</button><button class="btn" id="p1-iv2-reset">Сначала</button></div>
<div class="feedback" id="p1-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 */
html += `<div class="wg" id="p1-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Положительная или отрицательная?</div></div>
<div class="wg-help">Для каждого выражения ответь, какой знак у его значения. Без вычислений!</div>
<div class="score-display"><span>Задача <b id="p1-iv3-i">1</b> / 8</span><span>Очки: <b id="p1-iv3-s">0</b> / 8</span></div>
<div id="p1-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;text-align:center;margin-bottom:10px"></div>
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
<button class="btn primary" id="p1-iv3-pos" style="background:#10b981;border-color:#10b981">$> 0$ — положительно</button>
<button class="btn primary" id="p1-iv3-neg" style="background:#dc2626;border-color:#dc2626">$< 0$ — отрицательно</button>
</div>
<div class="feedback" id="p1-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 */
html += `<div class="wg" id="p1-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр свойств степени</div></div>
<div class="wg-help">Упрости выражение и запиши результат в виде $a^k$ — введи только показатель $k$.</div>
<div class="score-display"><span>Задача <b id="p1-iv4-i">1</b> / 6</span><span>Очки: <b id="p1-iv4-s">0</b> / 6</span></div>
<div id="p1-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace;font-size:1.1rem">$k$ =</span>
<input type="number" id="p1-iv4-ans" class="tinp" style="width:90px;text-align:center">
<button class="btn primary" id="p1-iv4-go">Проверить</button>
<button class="btn" id="p1-iv4-start">Заново</button>
</div>
<div class="feedback" id="p1-iv4-fb"></div>
</div>`;
html += secNav(null, 'p2');
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p1-read-btn">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал \xA71 (+10 XP)
</button>
</div>`;
box.innerHTML = html;
renderMath(box);
/* IV1 */
(function(){
const aSl = document.getElementById('p1-a-sl');
const nSl = document.getElementById('p1-n-sl');
const aV = document.getElementById('p1-a-val');
const nV = document.getElementById('p1-n-val');
const out = document.getElementById('p1-iv1-out');
function paren(x){ return x < 0 ? '(' + x + ')' : x; }
function update(){
const a = +aSl.value, n = +nSl.value;
aV.textContent = a; nV.textContent = n;
let parts = [];
for(let i=0;i<n;i++) parts.push(paren(a));
const val = ipow(a, n);
const sign = val > 0 ? '<span style="color:#10b981">положительное</span>' : (val < 0 ? '<span style="color:#dc2626">отрицательное</span>' : '<span>нулевое</span>');
out.innerHTML = '<div style="font-family:\'JetBrains Mono\',monospace">$' + paren(a) + '^{' + n + '} = ' + parts.join(' \\cdot ') + ' = ' + val + '$</div><div style="font-size:.85rem;color:var(--muted);margin-top:6px">Значение ' + sign + '</div>';
renderMath(out);
}
aSl.addEventListener('input', update);
nSl.addEventListener('input', update);
update();
})();
/* IV2 */
(function(){
const items = [
{ id:'i1', cat:'prod', html:'основания сохраняются, показатели <b>складываются</b>' },
{ id:'i2', cat:'quot', html:'основания сохраняются, показатели <b>вычитаются</b>' },
{ id:'i3', cat:'pow', html:'основание сохраняется, показатели <b>перемножаются</b>' },
{ id:'i4', cat:'mulb', html:'каждый множитель возводится в <b>ту же</b> степень' },
{ id:'i5', cat:'divb', html:'делимое и делитель возводятся в <b>ту же</b> степень' },
];
const sorter = setupSorter({
poolId:'p1-iv2-pool',
scopeSelector:'#p1-iv2',
items: items,
cats:['prod','quot','pow','mulb','divb'],
columnLayout:true,
});
document.getElementById('p1-iv2-check').addEventListener('click', ()=>{
const fb = document.getElementById('p1-iv2-fb');
const all = items.every(it => sorter.placed[it.id] === it.cat);
const placed = items.filter(it => sorter.placed[it.id]).length;
if(all){ feedback(fb, true, '&#10003; Все 5 свойств на месте! +10 XP'); addXp(10,'p1-iv2'); bumpProgress('p1', 20); }
else if(placed < items.length){ feedback(fb, false, '&#10007; Размести все 5 карточек по ящикам.'); }
else{ const correct = items.filter(it => sorter.placed[it.id] === it.cat).length; feedback(fb, false, '&#10007; Правильно ' + correct + ' из 5. Подумай ещё раз.'); }
});
document.getElementById('p1-iv2-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p1-iv2-fb').style.display='none'; });
})();
/* IV3 */
(function(){
const Q = [
{ expr:'$3^{100}$', pos:true },
{ expr:'$(-2)^4$', pos:true },
{ expr:'$(-5)^7$', pos:false },
{ expr:'$\\left(-\\dfrac{1}{3}\\right)^{10}$', pos:true },
{ expr:'$(-1)^{99}$', pos:false },
{ expr:'$-3^4$', pos:false },
{ expr:'$(-2)^{20}$', pos:true },
{ expr:'$-(-2)^5$', pos:true },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p1-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
renderMath(document.getElementById('p1-iv3-q'));
if(score === Q.length){ addXp(15,'p1-iv3'); bumpProgress('p1', 25); }
else if(score >= Q.length - 2){ addXp(8,'p1-iv3'); bumpProgress('p1', 15); }
return;
}
document.getElementById('p1-iv3-i').textContent = (i + 1);
document.getElementById('p1-iv3-s').textContent = score;
document.getElementById('p1-iv3-q').innerHTML = Q[i].expr;
renderMath(document.getElementById('p1-iv3-q'));
document.getElementById('p1-iv3-fb').style.display = 'none';
}
function answer(isPos){
if(i >= Q.length) return;
const fb = document.getElementById('p1-iv3-fb');
if(isPos === Q[i].pos){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: ' + (Q[i].pos ? '$> 0$' : '$< 0$') + '. Дальше ▶');
document.getElementById('p1-iv3-s').textContent = score;
i++;
setTimeout(show, 900);
}
document.getElementById('p1-iv3-pos').addEventListener('click', ()=>answer(true));
document.getElementById('p1-iv3-neg').addEventListener('click', ()=>answer(false));
show();
})();
/* IV4 */
(function(){
const Q = [
{ q:'$a^7 \\cdot a^3$', k:10 },
{ q:'$x^{15} : x^9$', k:6 },
{ q:'$(b^4)^5$', k:20 },
{ q:'$m^{12} \\cdot m^{8} : m^{6}$', k:14 },
{ q:'$(c^3)^4 \\cdot c^2$', k:14 },
{ q:'$(d^{10} : d^{4})^{2}$', k:12 },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p1-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p1-iv4'); bumpProgress('p1', 25); }
else if(score >= 4){ addXp(8,'p1-iv4'); bumpProgress('p1', 15); }
return;
}
document.getElementById('p1-iv4-i').textContent = (i + 1);
document.getElementById('p1-iv4-s').textContent = score;
document.getElementById('p1-iv4-q').innerHTML = 'Упрости: ' + Q[i].q;
document.getElementById('p1-iv4-ans').value = '';
renderMath(document.getElementById('p1-iv4-q'));
document.getElementById('p1-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p1-iv4-fb');
const ans = parseInt(document.getElementById('p1-iv4-ans').value, 10);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи целое число — показатель степени.'); return; }
if(ans === Q[i].k){ score++; feedback(fb, true, '&#10003; Верно! $a^{' + Q[i].k + '}$. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: $k = ' + Q[i].k + '$. Дальше ▶');
document.getElementById('p1-iv4-s').textContent = score;
i++;
setTimeout(show, 1100);
}
document.getElementById('p1-iv4-go').addEventListener('click', go);
document.getElementById('p1-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
document.getElementById('p1-iv4-start').addEventListener('click', ()=>{ i = 0; score = 0; show(); });
show();
})();
/* READ */
document.getElementById('p1-read-btn').addEventListener('click', ()=>{
addXp(10,'p1-read'); bumpProgress('p1', 30);
const b = document.getElementById('p1-read-btn');
b.textContent = 'Прочитано! +10 XP';
b.disabled = true; b.style.opacity = .6;
});
}
/* ============================================================
\xA7 2 — СТЕПЕНЬ С ЦЕЛЫМ ПОКАЗАТЕЛЕМ
============================================================ */
function buildP2(){
const box = document.getElementById('p2-body');
let html = '';
html += makeCard('theory', 'Нулевой показатель', '2.1', `
<p>Любое число $a \\ne 0$ в нулевой степени равно единице:</p>
\\[a^0 = 1, \\quad a \\ne 0\\]
<p><b>Почему?</b> Из частного $\\dfrac{a^m}{a^m} = 1$ (делим число само на себя). С другой стороны, по свойству степени $\\dfrac{a^m}{a^m} = a^{m-m} = a^0$. Значит $a^0 = 1$.</p>
<p>Обрати внимание: $\\boxed{0^0}$ — <b>не определено</b>.</p>`);
html += makeCard('theory', 'Отрицательный показатель', '2.2', `
<p><b>Степенью</b> числа с целым отрицательным показателем называется число, обратное степени с тем же основанием и противоположным показателем:</p>
\\[a^{-n} = \\dfrac{1}{a^n}, \\quad a \\ne 0\\]
<p>Так $a^{-n}$ и $a^n$ — взаимно обратные: $a^{-n} \\cdot a^n = 1$.</p>
<details class="spoiler"><summary>Примеры</summary><div class="spoiler-body">
$5^{-3} = \\dfrac{1}{5^3} = \\dfrac{1}{125}$ &nbsp;&middot;&nbsp;
$10^{-2} = \\dfrac{1}{100} = 0{,}01$ &nbsp;&middot;&nbsp;
$\\left(\\dfrac{2}{5}\\right)^{-3} = \\left(\\dfrac{5}{2}\\right)^3 = \\dfrac{125}{8}$
</div></details>`);
html += makeCard('rule', 'Знак степени с целым показателем', '2.3', `
<p>Степень <b>положительного</b> числа при любом целом показателе — <b>положительная</b>.</p>
<p>Степень <b>отрицательного</b> числа:</p>
<ul style="padding-left:22px;line-height:1.9">
<li>с <b>чётным</b> показателем — <b>положительная</b>: $(-3)^{-2} = \\dfrac{1}{(-3)^2} = \\dfrac{1}{9} > 0$</li>
<li>с <b>нечётным</b> показателем — <b>отрицательная</b>: $(-3)^{-5} = \\dfrac{1}{(-3)^5} = -\\dfrac{1}{243} < 0$</li>
</ul>`);
html += makeCard('rule', 'Свойства для целых показателей', '2.4', `
<p>Все пять свойств остаются справедливыми, если показатели — <b>любые целые</b> числа, а основания не равны нулю:</p>
<ol style="padding-left:22px;line-height:2">
<li>$a^m \\cdot a^n = a^{m+n}$</li>
<li>$a^m : a^n = a^{m-n}$</li>
<li>$(a^m)^n = a^{mn}$</li>
<li>$(a:b)^n = a^n : b^n$</li>
<li>$(a \\cdot b)^n = a^n \\cdot b^n$</li>
</ol>
<p>Полезное следствие: $\\dfrac{1}{a^{-n}} = a^n$.</p>`);
html += makeCard('example', 'Применение для целых', '2.5', `
<p><b>а)</b> $2^{-8} \\cdot 2^5 = 2^{-8+5} = 2^{-3} = \\dfrac{1}{8}$</p>
<p><b>б)</b> $3 : 3^{-3} = 3^{1-(-3)} = 3^4 = 81$</p>
<p><b>в)</b> $(5^{-3})^{-1} = 5^{-3 \\cdot (-1)} = 5^3 = 125$</p>
<p><b>г)</b> $5^{20} : 5^{-4} \\cdot 5^7 = 5^{20-(-4)+7} = 5^{31}$</p>`);
html += `<div class="wg" id="p2-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Чему равно $a^0$?</div></div>
<div class="wg-help">Подвигай ползунок — увидишь $a^0$ для разных оснований.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p2-a-val">5</b><input type="range" id="p2-a-sl" min="-10" max="10" step="1" value="5"></label>
</div>
<div id="p2-iv1-out" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;text-align:center;line-height:1.8"></div>
</div>`;
html += `<div class="wg" id="p2-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Конвертер $a^{-n} \\leftrightarrow \\dfrac{1}{a^n}$</div></div>
<div class="wg-help">Подвигай ползунки — увидишь обе формы записи и точное значение.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p2-b-val">3</b><input type="range" id="p2-b-sl" min="2" max="10" step="1" value="3"></label>
<label>Показатель $-n$ = <b id="p2-c-val">-2</b><input type="range" id="p2-c-sl" min="-6" max="-1" step="1" value="-2"></label>
</div>
<div id="p2-iv2-out" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1rem;line-height:2;text-align:center"></div>
</div>`;
html += `<div class="wg" id="p2-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Положительные / отрицательные / равные 1</div></div>
<div class="wg-help">Перетащи каждую степень в нужный ящик.</div>
<div id="p2-iv3-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="pos">$> 0$</h5><div class="drop-items" data-cat="pos"></div></div>
<div class="drop-box"><h5 data-cat="neg">$< 0$</h5><div class="drop-items" data-cat="neg"></div></div>
<div class="drop-box"><h5 data-cat="one">$= 1$</h5><div class="drop-items" data-cat="one"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p2-iv3-check">Проверить</button><button class="btn" id="p2-iv3-reset">Сначала</button></div>
<div class="feedback" id="p2-iv3-fb"></div>
</div>`;
html += `<div class="wg" id="p2-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр свойств (целые показатели)</div></div>
<div class="wg-help">Упрости и введи показатель $k$. Может быть отрицательным или равным нулю!</div>
<div class="score-display"><span>Задача <b id="p2-iv4-i">1</b> / 6</span><span>Очки: <b id="p2-iv4-s">0</b> / 6</span></div>
<div id="p2-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace;font-size:1.1rem">$k$ =</span>
<input type="number" id="p2-iv4-ans" class="tinp" style="width:90px;text-align:center">
<button class="btn primary" id="p2-iv4-go">Проверить</button>
<button class="btn" id="p2-iv4-start">Заново</button>
</div>
<div class="feedback" id="p2-iv4-fb"></div>
</div>`;
html += secNav('p1', 'p3');
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p2-read-btn">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал \xA72 (+10 XP)
</button>
</div>`;
box.innerHTML = html;
renderMath(box);
(function(){
const sl = document.getElementById('p2-a-sl'), v = document.getElementById('p2-a-val'), out = document.getElementById('p2-iv1-out');
function paren(x){ return x < 0 ? '(' + x + ')' : x; }
function update(){
const a = +sl.value; v.textContent = a;
if(a === 0) out.innerHTML = '$0^0$ — <span style="color:#dc2626"><b>не определено</b></span><br><span style="font-size:.85rem;color:var(--muted)">Особый случай: возводить ноль в нулевую степень нельзя.</span>';
else out.innerHTML = '$' + paren(a) + '^0 = 1$<br><span style="font-size:.85rem;color:var(--muted)">Любое число, кроме нуля, в нулевой степени равно 1.</span>';
renderMath(out);
}
sl.addEventListener('input', update); update();
})();
(function(){
const bSl = document.getElementById('p2-b-sl'), cSl = document.getElementById('p2-c-sl');
const bV = document.getElementById('p2-b-val'), cV = document.getElementById('p2-c-val');
const out = document.getElementById('p2-iv2-out');
function update(){
const a = +bSl.value, n = -(+cSl.value);
bV.textContent = a; cV.textContent = (+cSl.value);
const an = Math.pow(a, n);
const dec = (1 / an);
let decStr = dec >= 0.0001 ? dec.toFixed(8).replace(/0+$/,'').replace(/\.$/,'') : dec.toExponential(3);
out.innerHTML = '$' + a + '^{-' + n + '} = \\dfrac{1}{' + a + '^{' + n + '}} = \\dfrac{1}{' + an + '} = ' + decStr + '$';
renderMath(out);
}
bSl.addEventListener('input', update); cSl.addEventListener('input', update); update();
})();
(function(){
const items = [
{ id:'q1', cat:'pos', html:'$2^{-5}$' },
{ id:'q2', cat:'neg', html:'$(-3)^{-3}$' },
{ id:'q3', cat:'pos', html:'$(-4)^{-2}$' },
{ id:'q4', cat:'one', html:'$(-7)^0$' },
{ id:'q5', cat:'pos', html:'$5^0$' },
{ id:'q6', cat:'neg', html:'$-5^0$' },
{ id:'q7', cat:'pos', html:'$\\left(\\dfrac{1}{2}\\right)^{-10}$' },
{ id:'q8', cat:'neg', html:'$(-1)^{99}$' },
];
const sorter = setupSorter({
poolId:'p2-iv3-pool', scopeSelector:'#p2-iv3', items,
cats:['pos','neg','one'],
});
document.getElementById('p2-iv3-check').addEventListener('click', ()=>{
const fb = document.getElementById('p2-iv3-fb');
const placed = items.filter(it => sorter.placed[it.id]).length;
if(placed < items.length){ feedback(fb, false, '&#10007; Размести все 8 карточек.'); return; }
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
if(correct === items.length){ feedback(fb, true, '&#10003; Все 8 правильно! +12 XP'); addXp(12,'p2-iv3'); bumpProgress('p2', 25); }
else feedback(fb, false, '&#10007; Правильно ' + correct + ' из ' + items.length + '. Подсказка: $-5^0 = -(5^0) = -1$, а $(-1)^{99}$ — нечётный показатель.');
});
document.getElementById('p2-iv3-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p2-iv3-fb').style.display='none'; });
})();
(function(){
const Q = [
{ q:'$x^{-3} \\cdot x^{7}$', k:4 },
{ q:'$a^{5} : a^{-2}$', k:7 },
{ q:'$(b^{-4})^{-2}$', k:8 },
{ q:'$m^{-5} \\cdot m^{5}$', k:0 },
{ q:'$(c^{2})^{-3} \\cdot c^{6}$', k:0 },
{ q:'$\\dfrac{y^{-2} \\cdot y^{5}}{y^{4}}$', k:-1 },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p2-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p2-iv4'); bumpProgress('p2', 25); }
else if(score >= 4){ addXp(8,'p2-iv4'); bumpProgress('p2', 15); }
return;
}
document.getElementById('p2-iv4-i').textContent = (i + 1);
document.getElementById('p2-iv4-s').textContent = score;
document.getElementById('p2-iv4-q').innerHTML = 'Упрости: ' + Q[i].q;
document.getElementById('p2-iv4-ans').value = '';
renderMath(document.getElementById('p2-iv4-q'));
document.getElementById('p2-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p2-iv4-fb');
const raw = document.getElementById('p2-iv4-ans').value.trim();
if(raw === '' || isNaN(+raw)){ feedback(fb, false, '&#10007; Введи целое число.'); return; }
const ans = parseInt(raw, 10);
if(ans === Q[i].k){ score++; feedback(fb, true, '&#10003; Верно! $k = ' + Q[i].k + '$. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: $k = ' + Q[i].k + '$. Дальше ▶');
document.getElementById('p2-iv4-s').textContent = score;
i++;
setTimeout(show, 1100);
}
document.getElementById('p2-iv4-go').addEventListener('click', go);
document.getElementById('p2-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
document.getElementById('p2-iv4-start').addEventListener('click', ()=>{ i = 0; score = 0; show(); });
show();
})();
document.getElementById('p2-read-btn').addEventListener('click', ()=>{
addXp(10,'p2-read'); bumpProgress('p2', 30);
const b = document.getElementById('p2-read-btn');
b.textContent = 'Прочитано! +10 XP';
b.disabled = true; b.style.opacity = .6;
});
}
/* ============================================================
\xA7 3 — СТАНДАРТНЫЙ ВИД ЧИСЛА
============================================================ */
function buildP3(){
const box = document.getElementById('p3-body');
let html = '';
html += makeCard('theory', 'Зачем стандартный вид?', '3.1', `
<p>Иногда нужно работать с очень <b>большими</b> или очень <b>маленькими</b> числами:</p>
<ul style="padding-left:22px;line-height:1.8">
<li>масса Земли $\\approx 6 \\cdot 10^{24}$ кг</li>
<li>масса Марса $\\approx 6{,}4 \\cdot 10^{23}$ кг</li>
<li>размер вирусов и наночастиц $\\sim 10^{-9}$ м</li>
</ul>
<p>Записывать длинную цепочку нулей неудобно — поэтому используют <b>стандартный вид</b>.</p>`);
html += makeCard('rule', 'Определение', '3.2', `
<p>Записать число $b$ в <b>стандартном виде</b> означает представить его как произведение:</p>
\\[b = a \\cdot 10^n\\]
<p>где $a$ — число, удовлетворяющее условию $1 \\le a < 10$, а $n$ — целое.</p>
<p>Показатель $n$ называется <b>порядком числа</b> $b$.</p>
<details class="spoiler"><summary>Примеры</summary><div class="spoiler-body">
$5 \\cdot 10^{18}$ — порядок $18$. &nbsp;&middot;&nbsp; $1{,}2547 \\cdot 10^{-21}$ — порядок $-21$.
</div></details>`);
html += makeCard('algo', 'Как записать в стандартном виде', '3.3', `
<p><b>Если число больше 10</b> — переноси запятую <b>влево</b> так, чтобы в целой части осталась одна ненулевая цифра. Запоминай число шагов $n$ — это положительный показатель.</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px">
$350\\,000 = 3{,}5 \\cdot 10^5$ &nbsp; (5 шагов влево)
</p>
<p><b>Если число меньше единицы</b> — переноси запятую <b>вправо</b>, считая шаги $n$ — это отрицательный показатель.</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px">
$0{,}000000052 = 5{,}2 \\cdot 10^{-8}$ &nbsp; (8 шагов вправо)
</p>`);
html += makeCard('rule', 'Сравнение чисел в стандартном виде', '3.4', `
<p>Чтобы сравнить два числа $a_1 \\cdot 10^{n_1}$ и $a_2 \\cdot 10^{n_2}$ в стандартном виде:</p>
<ol style="padding-left:22px;line-height:1.9">
<li>сначала сравни <b>порядки</b> $n_1$ и $n_2$ — у кого порядок больше, то число больше;</li>
<li>если порядки равны — сравни <b>мантиссы</b> $a_1$ и $a_2$.</li>
</ol>
<p><b>Пример:</b> $9{,}5687 \\cdot 10^{14}$ и $1{,}06 \\cdot 10^{15}$. Порядки $14 < 15$, поэтому первое число меньше второго.</p>`);
html += `<div class="wg" id="p3-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор $a \\cdot 10^n$</div></div>
<div class="wg-help">Подвигай мантиссу и порядок — увидишь обычную и стандартную запись.</div>
<div class="sliders">
<label>Мантисса $a$ = <b id="p3-a-val">3.50</b><input type="range" id="p3-a-sl" min="100" max="999" step="1" value="350"></label>
<label>Порядок $n$ = <b id="p3-n-val">5</b><input type="range" id="p3-n-sl" min="-10" max="10" step="1" value="5"></label>
</div>
<div id="p3-iv1-out" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;line-height:2;text-align:center"></div>
</div>`;
html += `<div class="wg" id="p3-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Запиши в стандартном виде</div></div>
<div class="wg-help">Прочитай число и введи мантиссу $a$ и порядок $n$ так, чтобы получилось $a \\cdot 10^n$.</div>
<div class="score-display"><span>Задача <b id="p3-iv2-i">1</b> / 6</span><span>Очки: <b id="p3-iv2-s">0</b> / 6</span></div>
<div id="p3-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;margin-bottom:10px;text-align:center"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:center">
<input type="text" id="p3-iv2-a" class="tinp" placeholder="a" style="width:90px;text-align:center">
<span style="font-family:'JetBrains Mono',monospace">\xB7 10^</span>
<input type="text" id="p3-iv2-n" class="tinp" placeholder="n" style="width:60px;text-align:center">
<button class="btn primary" id="p3-iv2-go">Проверить</button>
<button class="btn" id="p3-iv2-start">Заново</button>
</div>
<div class="feedback" id="p3-iv2-fb"></div>
</div>`;
html += `<div class="wg" id="p3-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какое число больше?</div></div>
<div class="wg-help">Сравни два числа в стандартном виде. Не считай — используй порядок и мантиссу!</div>
<div class="score-display"><span>Задача <b id="p3-iv3-i">1</b> / 6</span><span>Очки: <b id="p3-iv3-s">0</b> / 6</span></div>
<div id="p3-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;text-align:center;margin-bottom:10px"></div>
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
<button class="btn primary" id="p3-iv3-left" style="background:#7c3aed;border-color:#7c3aed">A &lt; B</button>
<button class="btn primary" id="p3-iv3-eq" style="background:#6b7280;border-color:#6b7280">A = B</button>
<button class="btn primary" id="p3-iv3-right" style="background:#d97706;border-color:#d97706">A &gt; B</button>
</div>
<div class="feedback" id="p3-iv3-fb"></div>
</div>`;
html += `<div class="wg" id="p3-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Действия со степенями десяти</div></div>
<div class="wg-help">Выполни действие и запиши результат в стандартном виде — введи мантиссу и порядок отдельно.</div>
<div class="score-display"><span>Задача <b id="p3-iv4-i">1</b> / 5</span><span>Очки: <b id="p3-iv4-s">0</b> / 5</span></div>
<div id="p3-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:center">
<input type="text" id="p3-iv4-a" class="tinp" placeholder="a" style="width:90px;text-align:center">
<span style="font-family:'JetBrains Mono',monospace">\xB7 10^</span>
<input type="text" id="p3-iv4-n" class="tinp" placeholder="n" style="width:60px;text-align:center">
<button class="btn primary" id="p3-iv4-go">Проверить</button>
<button class="btn" id="p3-iv4-start">Заново</button>
</div>
<div class="feedback" id="p3-iv4-fb"></div>
</div>`;
html += secNav('p2', 'final1');
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p3-read-btn">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал \xA73 (+10 XP)
</button>
</div>`;
box.innerHTML = html;
renderMath(box);
(function(){
const aSl = document.getElementById('p3-a-sl'), nSl = document.getElementById('p3-n-sl');
const aV = document.getElementById('p3-a-val'), nV = document.getElementById('p3-n-val');
const out = document.getElementById('p3-iv1-out');
function update(){
const aRaw = +aSl.value;
const a = aRaw / 100;
const n = +nSl.value;
aV.textContent = a.toFixed(2);
nV.textContent = n;
const val = a * Math.pow(10, n);
let plain;
if(n >= 0 && n <= 15) plain = (Math.round(val * 1e6) / 1e6).toString();
else if(n < 0 && n >= -10) plain = val.toFixed(Math.min(15, Math.abs(n) + 2)).replace(/0+$/,'').replace(/\.$/,'');
else plain = val.toExponential(4);
out.innerHTML = '<div>$' + a.toFixed(2) + ' \\cdot 10^{' + n + '} = ' + plain + '$</div><div style="font-size:.85rem;color:var(--muted);margin-top:6px">Порядок числа: <b>' + n + '</b></div>';
renderMath(out);
}
aSl.addEventListener('input', update); nSl.addEventListener('input', update); update();
})();
(function(){
const Q = [
{ num:'350 000', a:3.5, n:5 },
{ num:'0,0048', a:4.8, n:-3 },
{ num:'1 200 000', a:1.2, n:6 },
{ num:'0,00000045', a:4.5, n:-7 },
{ num:'785,4', a:7.854, n:2 },
{ num:'0,072', a:7.2, n:-2 },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p3-iv2-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p3-iv2'); bumpProgress('p3', 25); }
else if(score >= 4){ addXp(8,'p3-iv2'); bumpProgress('p3', 15); }
return;
}
document.getElementById('p3-iv2-i').textContent = (i + 1);
document.getElementById('p3-iv2-s').textContent = score;
document.getElementById('p3-iv2-q').innerHTML = 'Запиши в стандартном виде: <b>' + Q[i].num + '</b>';
document.getElementById('p3-iv2-a').value = ''; document.getElementById('p3-iv2-n').value = '';
document.getElementById('p3-iv2-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p3-iv2-fb');
const a = parseFloat(document.getElementById('p3-iv2-a').value.replace(',','.'));
const n = parseInt(document.getElementById('p3-iv2-n').value, 10);
if(isNaN(a) || isNaN(n)){ feedback(fb, false, '&#10007; Введи и мантиссу, и порядок.'); return; }
if(a < 1 || a >= 10){ feedback(fb, false, '&#10007; Мантисса должна удовлетворять $1 \\le a < 10$.'); return; }
const ok = Math.abs(a - Q[i].a) < 0.01 && n === Q[i].n;
if(ok){ score++; feedback(fb, true, '&#10003; Верно! $' + Q[i].num + ' = ' + Q[i].a + ' \\cdot 10^{' + Q[i].n + '}$. Дальше ▶'); }
else feedback(fb, false, '&#10007; Правильный ответ: $' + Q[i].a + ' \\cdot 10^{' + Q[i].n + '}$. Дальше ▶');
document.getElementById('p3-iv2-s').textContent = score;
i++;
setTimeout(show, 1300);
}
document.getElementById('p3-iv2-go').addEventListener('click', go);
document.getElementById('p3-iv2-n').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
document.getElementById('p3-iv2-start').addEventListener('click', ()=>{ i = 0; score = 0; show(); });
show();
})();
(function(){
const Q = [
{ A:'$2{,}1 \\cdot 10^{-4}$', B:'$3{,}235 \\cdot 10^{-3}$', ans:'lt' },
{ A:'$5{,}23 \\cdot 10^{8}$', B:'$5{,}061 \\cdot 10^{8}$', ans:'gt' },
{ A:'$9{,}5687 \\cdot 10^{14}$', B:'$1{,}06 \\cdot 10^{15}$', ans:'lt' },
{ A:'$3{,}3 \\cdot 10^{-4}$', B:'$3{,}5 \\cdot 10^{-4}$', ans:'lt' },
{ A:'$7 \\cdot 10^{0}$', B:'$0{,}7 \\cdot 10^{1}$', ans:'eq' },
{ A:'$8{,}1 \\cdot 10^{-15}$', B:'$1 \\cdot 10^{-14}$', ans:'lt' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p3-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p3-iv3'); bumpProgress('p3', 25); }
else if(score >= 4){ addXp(8,'p3-iv3'); bumpProgress('p3', 15); }
return;
}
document.getElementById('p3-iv3-i').textContent = (i + 1);
document.getElementById('p3-iv3-s').textContent = score;
document.getElementById('p3-iv3-q').innerHTML = 'A = ' + Q[i].A + ' &nbsp;&nbsp; B = ' + Q[i].B;
renderMath(document.getElementById('p3-iv3-q'));
document.getElementById('p3-iv3-fb').style.display = 'none';
}
function answer(choice){
if(i >= Q.length) return;
const fb = document.getElementById('p3-iv3-fb');
if(choice === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else{
const labels = {lt:'A &lt; B', eq:'A = B', gt:'A &gt; B'};
feedback(fb, false, '&#10007; Правильно: ' + labels[Q[i].ans] + '. Дальше ▶');
}
document.getElementById('p3-iv3-s').textContent = score;
i++;
setTimeout(show, 1300);
}
document.getElementById('p3-iv3-left').addEventListener('click', ()=>answer('lt'));
document.getElementById('p3-iv3-eq').addEventListener('click', ()=>answer('eq'));
document.getElementById('p3-iv3-right').addEventListener('click', ()=>answer('gt'));
show();
})();
(function(){
const Q = [
{ q:'$(4{,}2 \\cdot 10^{-3}) \\cdot (3 \\cdot 10^{-1})$', a:1.26, n:-3 },
{ q:'$(7{,}2 \\cdot 10^{7}) : (9 \\cdot 10^{10})$', a:8, n:-4 },
{ q:'$(1{,}2 \\cdot 10^{62}) \\cdot (4 \\cdot 10^{38}) : (5 \\cdot 10^{45})$', a:9.6, n:54 },
{ q:'$(3{,}6 \\cdot 10^{-5}) \\cdot (4 \\cdot 10^{2})$', a:1.44, n:-2 },
{ q:'$(6{,}4 \\cdot 10^{12}) : (8 \\cdot 10^{14})$', a:8, n:-3 },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p3-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(20,'p3-iv4'); bumpProgress('p3', 30); }
else if(score >= 3){ addXp(10,'p3-iv4'); bumpProgress('p3', 18); }
return;
}
document.getElementById('p3-iv4-i').textContent = (i + 1);
document.getElementById('p3-iv4-s').textContent = score;
document.getElementById('p3-iv4-q').innerHTML = 'Вычисли: ' + Q[i].q;
document.getElementById('p3-iv4-a').value = ''; document.getElementById('p3-iv4-n').value = '';
renderMath(document.getElementById('p3-iv4-q'));
document.getElementById('p3-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p3-iv4-fb');
const a = parseFloat(document.getElementById('p3-iv4-a').value.replace(',','.'));
const n = parseInt(document.getElementById('p3-iv4-n').value, 10);
if(isNaN(a) || isNaN(n)){ feedback(fb, false, '&#10007; Введи и мантиссу, и порядок.'); return; }
if(a < 1 || a >= 10){ feedback(fb, false, '&#10007; Мантисса должна быть $1 \\le a < 10$.'); return; }
const ok = Math.abs(a - Q[i].a) < 0.02 && n === Q[i].n;
if(ok){ score++; feedback(fb, true, '&#10003; Верно! Ответ: $' + Q[i].a + ' \\cdot 10^{' + Q[i].n + '}$. Дальше ▶'); }
else feedback(fb, false, '&#10007; Правильный ответ: $' + Q[i].a + ' \\cdot 10^{' + Q[i].n + '}$. Дальше ▶');
document.getElementById('p3-iv4-s').textContent = score;
i++;
setTimeout(show, 1300);
}
document.getElementById('p3-iv4-go').addEventListener('click', go);
document.getElementById('p3-iv4-n').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
document.getElementById('p3-iv4-start').addEventListener('click', ()=>{ i = 0; score = 0; show(); });
show();
})();
document.getElementById('p3-read-btn').addEventListener('click', ()=>{
addXp(10,'p3-read'); bumpProgress('p3', 30);
const b = document.getElementById('p3-read-btn');
b.textContent = 'Прочитано! +10 XP';
b.disabled = true; b.style.opacity = .6;
});
}
/* ============================================================
FINAL 1 — 5 БОССОВ
============================================================ */
const BOSSES = [
{
n:1, title:'Босс \xA71 — Свойства натуральных степеней', color:'#d97706',
steps:[
{ q:'Упрости $a^5 \\cdot a^3 \\cdot a^{12}$ и введи показатель.', verify:(v)=>+v === 20, hint:'Складывай показатели.' },
{ q:'Упрости $(x^4)^6$ и введи показатель.', verify:(v)=>+v === 24, hint:'Перемножай показатели.' },
{ q:'Упрости $b^{18} : b^{15}$ и введи показатель.', verify:(v)=>+v === 3, hint:'Вычитай показатели.' },
{ q:'Чему равно $(2 \\cdot 5)^3$? Введи число.', verify:(v)=>+v === 1000, hint:'$(ab)^n = a^n b^n$.' },
{ q:'Чему равно $\\left(\\dfrac{2}{3}\\right)^4$? Введи в виде дроби a/b.', verify:(v)=>{const m=String(v).match(/^(-?\d+)\s*\/\s*(-?\d+)$/); if(!m) return false; return +m[1] === 16 && +m[2] === 81;}, hint:'$a^n/b^n$. Получится 16/81.' },
]
},
{
n:2, title:'Босс \xA72 — Целые показатели и знак', color:'#059669',
steps:[
{ q:'Чему равно $5^0$? Введи число.', verify:(v)=>+v === 1, hint:'$a^0 = 1$ при $a \\ne 0$.' },
{ q:'Чему равно $2^{-3}$? Введи в виде дроби a/b.', verify:(v)=>{const m=String(v).match(/^(-?\d+)\s*\/\s*(-?\d+)$/); if(!m) return false; return +m[1] === 1 && +m[2] === 8;}, hint:'$2^{-3} = 1/2^3$.' },
{ q:'Упрости $a^{-7} \\cdot a^{4}$ и введи показатель.', verify:(v)=>+v === -3, hint:'Складывай показатели.' },
{ q:'Каков знак $(-2)^{-6}$? Введи + или .', verify:(v)=>{const t=String(v).trim();return t === '+' || t.toLowerCase() === 'плюс' || t === 'pos';}, hint:'Чётный показатель → знак $+$.' },
{ q:'Упрости $(b^{-2})^{-5}$ и введи показатель.', verify:(v)=>+v === 10, hint:'Перемножай: $(-2)\\cdot(-5)$.' },
]
},
{
n:3, title:'Босс \xA73 — Стандартный вид', color:'#7c3aed',
steps:[
{ q:'Запиши $480\\,000\\,000$ в виде $a \\cdot 10^n$. Введи только $n$.', verify:(v)=>+v === 8, hint:'8 шагов влево: $4{,}8 \\cdot 10^8$.' },
{ q:'Запиши $0{,}000021$ в виде $a \\cdot 10^n$. Введи только $n$.', verify:(v)=>+v === -5, hint:'5 шагов вправо.' },
{ q:'Каков порядок числа $1{,}067 \\cdot 10^{-4}$?', verify:(v)=>+v === -4, hint:'Порядок = показатель степени.' },
{ q:'Сравни: $9{,}48 \\cdot 10^{7}$ и $7{,}1 \\cdot 10^{8}$. Введи &lt;, &gt; или =.', verify:(v)=>String(v).trim() === '<', hint:'Порядки $7 < 8$.' },
{ q:'Вычисли $(6{,}4 \\cdot 10^{12}) : (8 \\cdot 10^{14})$. Введи мантиссу (например, 8).', verify:(v)=>Math.abs(parseFloat(String(v).replace(',','.')) - 8) < 0.05, hint:'$6{,}4 : 8 = 0{,}8$, итого $8 \\cdot 10^{-3}$.' },
]
},
{
n:4, title:'Босс — Космос. Применение стандартного вида', color:'#0891b2',
steps:[
{ q:'Масса Земли $\\approx 5{,}97 \\cdot 10^{24}$ кг. Каков порядок?', verify:(v)=>+v === 24, hint:'Показатель = порядок.' },
{ q:'Размер атома $\\approx 10^{-10}$ м. Это число $1 \\cdot 10^n$, чему равно $n$?', verify:(v)=>+v === -10, hint:'10 шагов вправо.' },
{ q:'1 парсек $\\approx 30{,}857 \\cdot 10^{12}$ км. В стандартном виде это $3{,}0857 \\cdot 10^n$. Чему $n$?', verify:(v)=>+v === 13, hint:'$30{,}857 = 3{,}0857 \\cdot 10$.' },
{ q:'Возраст Вселенной $\\approx 14 \\cdot 10^{9}$ лет. В стандартном виде $1{,}4 \\cdot 10^n$. Чему $n$?', verify:(v)=>+v === 10, hint:'$14 = 1{,}4 \\cdot 10$.' },
{ q:'Скорость света $\\approx 3 \\cdot 10^{8}$ м/с. За 1 сек свет пройдёт $a \\cdot 10^n$ км. Введи $n$.', verify:(v)=>+v === 5, hint:'Переведи м в км: делим на $10^3$.' },
]
},
{
n:5, title:'Финальный босс — Все приёмы вместе', color:'#dc2626',
steps:[
{ q:'Упрости $\\dfrac{a^5 \\cdot a^{-8}}{a^{-2}}$ и введи показатель.', verify:(v)=>+v === -1, hint:'$5 + (-8) - (-2) = -1$.' },
{ q:'Чему равно $\\left(\\dfrac{1}{3}\\right)^{-3}$? Введи число.', verify:(v)=>+v === 27, hint:'$(1/3)^{-3} = 3^3$.' },
{ q:'Найди $5^{-3} \\cdot 5^5$. Введи число.', verify:(v)=>+v === 25, hint:'$5^{-3+5} = 5^2 = 25$.' },
{ q:'Запиши $0{,}079 \\cdot 10^{-3}$ в стандартном виде. Введи $n$ (показатель ответа).', verify:(v)=>+v === -5, hint:'$0{,}079 = 7{,}9 \\cdot 10^{-2}$, итого $7{,}9 \\cdot 10^{-5}$.' },
{ q:'Вычисли $(2 \\cdot 10^{-5}) \\cdot (4 \\cdot 10^{8})$ и введи мантиссу.', verify:(v)=>Math.abs(parseFloat(String(v).replace(',','.')) - 8) < 0.05, hint:'$2 \\cdot 4 = 8$, $-5+8 = 3$, итого $8 \\cdot 10^{3}$.' },
]
},
];
function buildFinal1(){
const box = document.getElementById('final1-body');
let html = '';
html += makeCard('theory', 'Что мы изучили', 'Итог', `
<p>В этой главе мы:</p>
<ul style="padding-left:22px;line-height:1.85">
<li>определили <b>степень с натуральным показателем</b> и доказали <b>5 свойств</b>: произведение, частное, степень степени, степень произведения и частного;</li>
<li>расширили понятие на <b>целые показатели</b>: $a^0 = 1$, $a^{-n} = \\dfrac{1}{a^n}$ — все свойства остались справедливы;</li>
<li>научились записывать числа в <b>стандартном виде</b> $a \\cdot 10^n$, $1 \\le a < 10$, и работать с порядком — для очень больших и малых величин в науке.</li>
</ul>
<p>Теперь — <b>5 боссов</b>. Каждый — серия из 5 этапов. Пройди все 25 этапов, чтобы получить полную награду главы.</p>`);
html += '<div id="bosses-container"></div>';
html += `<div style="margin-top:22px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
<div id="boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>
<div class="hp-boss"><div class="hp-boss-fill" id="boss-overall-fill" style="width:0%;background:linear-gradient(90deg,#d97706,#f59e0b)"></div></div>
</div>`;
html += secNav('p3', null);
box.innerHTML = html;
renderMath(box);
const cont = document.getElementById('bosses-container');
const BOSS_STATE = (function(){
try{ const s = localStorage.getItem('algebra7_ch1_bosses'); if(s) return JSON.parse(s); }catch(e){}
return BOSSES.map(()=>({ stage:0, defeated:false }));
})();
function saveBosses(){ try{ localStorage.setItem('algebra7_ch1_bosses', JSON.stringify(BOSS_STATE)); }catch(e){} }
function refreshOverall(){
const won = BOSS_STATE.filter(b => b.defeated).length;
const txt = document.getElementById('boss-overall'); if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
const fill = document.getElementById('boss-overall-fill'); if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
if(won >= BOSSES.length){
bumpProgress('final1', 60);
achievement('ch1_done', 'Глава 1 пройдена!');
}
}
cont.innerHTML = BOSSES.map((b, idx) => {
return `<div class="boss-card" id="boss-card-${idx}" style="border-color:${b.color}">
<div class="boss-head">
<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 class="boss-title" style="color:${b.color}">${b.title}</div>
<div class="boss-stage" id="boss-${idx}-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="boss-${idx}-fill" style="width:0%;background:linear-gradient(90deg,${b.color},#f59e0b)"></div></div>
<div class="boss-q" id="boss-${idx}-q" style="border-color:${b.color}"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="text" id="boss-${idx}-input" class="tinp" placeholder="Ответ" style="width:160px;text-align:center">
<button class="btn primary" id="boss-${idx}-go" style="background:${b.color};border-color:${b.color}">Атака</button>
<button class="btn" id="boss-${idx}-hint">Подсказка</button>
<button class="btn" id="boss-${idx}-restart">↻</button>
</div>
<div class="feedback" id="boss-${idx}-fb"></div>
</div>`;
}).join('');
BOSSES.forEach((b, idx) => {
function show(){
const st = BOSS_STATE[idx];
const stageEl = document.getElementById('boss-' + idx + '-stage');
const fill = document.getElementById('boss-' + idx + '-fill');
const q = document.getElementById('boss-' + idx + '-q');
const fb = document.getElementById('boss-' + idx + '-fb');
if(st.defeated){
stageEl.textContent = '✓ Побеждён';
fill.style.width = '100%';
q.innerHTML = '<b style="color:' + b.color + '">Босс повержен!</b> Можно перезапустить, чтобы пройти ещё раз.';
document.getElementById('boss-' + idx + '-go').disabled = true;
document.getElementById('boss-' + idx + '-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('boss-' + idx + '-input').value = '';
fb.style.display = 'none';
renderMath(q);
}
document.getElementById('boss-' + idx + '-go').addEventListener('click', ()=>{
const st = BOSS_STATE[idx];
if(st.defeated) return;
const step = b.steps[st.stage];
const val = document.getElementById('boss-' + idx + '-input').value;
const fb = document.getElementById('boss-' + idx + '-fb');
if(!val.trim()){ feedback(fb, false, '&#10007; Введи ответ.'); return; }
if(step.verify(val)){
st.stage++;
if(st.stage >= b.steps.length){
st.defeated = true;
saveBosses();
feedback(fb, true, '&#10003; Босс ' + b.n + ' побеждён! +20 XP');
addXp(20, 'boss-' + b.n);
bumpProgress('final1', 18);
refreshOverall();
setTimeout(show, 1400);
if(window.confetti) try{ confetti(); }catch(e){}
}else{
saveBosses();
feedback(fb, true, '&#10003; Верно! Удар попал. +3 XP');
addXp(3, 'boss-step');
setTimeout(show, 1100);
}
}else{
feedback(fb, false, '&#10007; Промах. Попробуй ещё раз.');
}
});
document.getElementById('boss-' + idx + '-hint').addEventListener('click', ()=>{
const st = BOSS_STATE[idx]; if(st.defeated) return;
const fb = document.getElementById('boss-' + idx + '-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('boss-' + idx + '-restart').addEventListener('click', ()=>{
BOSS_STATE[idx] = { stage:0, defeated:false }; saveBosses();
document.getElementById('boss-' + idx + '-go').disabled = false;
document.getElementById('boss-' + idx + '-go').style.opacity = 1;
show(); refreshOverall();
});
show();
});
refreshOverall();
}
</script>
</body>
</html>