Files
Learn_System/frontend/textbooks/algebra_9_ch4.html
T

2581 lines
164 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>Алгебра 9 · Глава 4 · Прогрессии</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 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:#0891b2; --pri2:#0e7490; --pri-soft:#cffafe;
--acc:#22d3ee; --acc2:#0891b2; --acc-soft:#ecfeff;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#04141a; --card:#0a1b22; --card-soft:#0d2229; --text:#e0fcff; --ink:#e0fcff; --muted:#7aa8b3; --border:#163842}
*{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}
.hdr{position:relative;background:linear-gradient(110deg,#164e63 0%,#0891b2 55%,#22d3ee 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(165,243,252,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 4';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(209,250,255,.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{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{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ₙ';position:absolute;right:0;top:-30px;font-size:clamp(2rem,12vw,8rem);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(0,0,0,.18)}
.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(0,0,0,.12);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(0,0,0,.18);font-family:'Unbounded',sans-serif}
.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:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(0,0,0,.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)}
.sec[id="sec-p14"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p15"]{ --sec-acc:#06b6d4; --sec-acc-d:#0891b2; --sec-acc-soft:#cffafe; }
.sec[id="sec-p16"]{ --sec-acc:#2563eb; --sec-acc-d:#1d4ed8; --sec-acc-soft:#dbeafe; }
.sec[id="sec-p17"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p18"]{ --sec-acc:#db2777; --sec-acc-d:#9d174d; --sec-acc-soft:#fce7f3; }
.sec[id="sec-p19"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-final4"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.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.6rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
.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(0,0,0,.04);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(0,0,0,.08)}
.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 .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}
.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))}
.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)}
.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}
.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))}
.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}
.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}
.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{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(0,0,0,.10);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}
.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}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,var(--pri),var(--acc));color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(0,0,0,.32);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
.ach-popup.show{display:flex}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
.col-side-backdrop.show{display:block}
@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}
}
.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}
.gloss-tip b{color:var(--sec-acc-d,var(--pri2));font-size:.92rem}
.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}
.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: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}
.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}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Алгебра 9 · Глава 4</h1>
<div class="hdr-sub">Последовательности · арифметическая · геометрическая</div>
</div>
<div class="hdr-side">
<a href="/textbook/algebra-9" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К алгебре 9</a>
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
<button id="sidebar-btn" class="hdr-btn"><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"><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> $(a_n = a_1 + (n-1)d)$ и <b>геометрическую прогрессию</b> $(b_n = b_1 q^{n-1})$, формулы сумм $n$ первых членов и <b>сумму бесконечно убывающей</b> геометрической прогрессии $S = \dfrac{b_1}{1 - q}$.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p14')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 14</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"></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p14" class="sec" data-watermark="aₙ"><div class="sec-header"><span class="sec-num">§ 14</span><h2 class="sec-h">Числовая последовательность</h2></div><div id="p14-body"></div></section>
<section id="sec-p15" class="sec" data-watermark="+d"><div class="sec-header"><span class="sec-num">§ 15</span><h2 class="sec-h">Арифметическая прогрессия</h2></div><div id="p15-body"></div></section>
<section id="sec-p16" class="sec" data-watermark="Σ"><div class="sec-header"><span class="sec-num">§ 16</span><h2 class="sec-h">Сумма арифм. прогрессии</h2></div><div id="p16-body"></div></section>
<section id="sec-p17" class="sec" data-watermark="·q"><div class="sec-header"><span class="sec-num">§ 17</span><h2 class="sec-h">Геометрическая прогрессия</h2></div><div id="p17-body"></div></section>
<section id="sec-p18" class="sec" data-watermark="Σ"><div class="sec-header"><span class="sec-num">§ 18</span><h2 class="sec-h">Сумма геом. прогрессии</h2></div><div id="p18-body"></div></section>
<section id="sec-p19" class="sec" data-watermark="∞"><div class="sec-header"><span class="sec-num">§ 19</span><h2 class="sec-h">Бесконечно убывающая</h2></div><div id="p19-body"></div></section>
<section id="sec-final4" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#0891b2,#22d3ee)">Финал главы</span><h2 class="sec-h">Итоги. 6 боссов главы 4</h2></div><div id="final4-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">Интерактивный учебник «Алгебра 9» · Глава 4 · Прогрессии · 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">
<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>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE = { current:'p14', progress:{p14:0,p15:0,p16:0,p17:0,p18:0,p19:0,final4:0}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 7;
const _TB_SLUG = 'algebra-9-ch4';
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:'Начало главы 4!',
p15_done:'Арифметическая прогрессия освоена!',
p17_done:'Геометрическая прогрессия освоена!',
p19_done:'Бесконечно убывающая освоена!',
ch4_done:'Глава 4 пройдена! Алгебра 9 — финал!'
};
function loadProgress(){
try{
const s=localStorage.getItem('algebra9_ch4_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('algebra9_ch4_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('algebra9_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('algebra9_ch4_progress', JSON.stringify(STATE.progress));
localStorage.setItem('algebra9_ch4_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('algebra9_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);
}
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,'algebra9-ch4-'+(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); }
}
}
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);
}
const PARAS = [
{ id:'p14', num:'§ 14', name:'Числовая последовательность', sub:'$a_1, a_2, \dots, a_n$' },
{ id:'p15', num:'§ 15', name:'Арифметическая прогрессия', sub:'$a_n = a_1 + (n-1)d$' },
{ id:'p16', num:'§ 16', name:'Сумма арифм. прогрессии', sub:'$S_n = \tfrac{a_1 + a_n}{2} n$' },
{ id:'p17', num:'§ 17', name:'Геометрическая прогрессия', sub:'$b_n = b_1 q^{n-1}$' },
{ id:'p18', num:'§ 18', name:'Сумма геом. прогрессии', sub:'$S_n = \tfrac{b_1(q^n - 1)}{q - 1}$' },
{ id:'p19', num:'§ 19', name:'Бесконечно убывающая', sub:'$S = \tfrac{b_1}{1 - q}$' },
{ id:'final4', num:'★', name:'Финал главы', sub:'Итоги · 6 боссов', 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 = { p14:()=>buildP14(), p15:()=>buildP15(), p16:()=>buildP16(), p17:()=>buildP17(), p18:()=>buildP18(), p19:()=>buildP19(), final4:()=>buildFinal4() };
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);
markLastPara(id);
}
const SIDEBARS = {
p14:{title:'Шпаргалка \xA714',rows:[['Послед-сть','$(a_n)$, $n \in \mathbb{N}$'],['Способы','формула $n$-го члена, реккурентно, словесно'],['Член','$a_n$ — $n$-й член']]},
p15:{title:'Шпаргалка \xA715',rows:[['Опр.','$a_{n+1} - a_n = d$'],['Форм.','$a_n = a_1 + (n - 1) d$'],['Свойство','$a_n = \tfrac{a_{n-1} + a_{n+1}}{2}$']]},
p16:{title:'Шпаргалка \xA716',rows:[['Формула 1','$S_n = \tfrac{a_1 + a_n}{2} n$'],['Формула 2','$S_n = \tfrac{2 a_1 + (n - 1) d}{2} n$']]},
p17:{title:'Шпаргалка \xA717',rows:[['Опр.','$\dfrac{b_{n+1}}{b_n} = q$, $b_1 \ne 0$, $q \ne 0$'],['Форм.','$b_n = b_1 q^{n-1}$'],['Свойство','$b_n^2 = b_{n-1} b_{n+1}$']]},
p18:{title:'Шпаргалка \xA718',rows:[['$q \ne 1$','$S_n = \tfrac{b_1(q^n - 1)}{q - 1}$'],['$q = 1$','$S_n = n \cdot b_1$']]},
p19:{title:'Шпаргалка \xA719',rows:[['Условие','$|q| < 1$'],['Сумма','$S = \tfrac{b_1}{1 - q}$']]},
final4:{title:'Финал главы',rows:[['§§1419','теория главы 4'],['Боссов','6'],['Награда','+100 XP'],['Алгебра 9','полностью пройдена!']]}
};
const TIPS=[
{sec:'p14',html:'Числовая последовательность — это функция натурального аргумента: $a: \mathbb{N} \to \mathbb{R}$.'},
{sec:'p15',html:'В арифметической прогрессии разность $d = a_{n+1} - a_n$ — постоянна.'},
{sec:'p16',html:'$S_n = \dfrac{(a_1 + a_n) n}{2} = \dfrac{(2 a_1 + (n - 1) d) n}{2}$.'},
{sec:'p17',html:'В геометрической прогрессии знаменатель $q = \dfrac{b_{n+1}}{b_n}$ — постоянен.'},
{sec:'p18',html:'$S_n = \dfrac{b_1 (q^n - 1)}{q - 1}$ при $q \ne 1$.'},
{sec:'p19',html:'При $|q| < 1$: $S = \dfrac{b_1}{1 - q}$.'},
{sec:'final4',html:'6 боссов главы 4. После — вся Алгебра 9 в твоём арсенале!'}
];
function buildSidebar(id){
const box=document.getElementById('sidebar-content');
const sb=SIDEBARS[id]||SIDEBARS['p14'];
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"><div class="xp-card-title"><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];
if(tip){
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)">&#10003; '+text+'</div>'; });
html+='</div>';
}
box.innerHTML=html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
function initTheme(){
const t=localStorage.getItem('algebra9_ch4_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('algebra9_ch4_theme', dark?'dark':'light');
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
});
}
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(); }
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 secNav(prev, next){
const NAMES={p14:'\xA714',p15:'\xA715',p16:'\xA716',p17:'\xA717',p18:'\xA718',p19:'\xA719',final4:'Финал'};
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;
}
function readButton(paraId){
return '<div style="margin-top:18px;display:flex;justify-content:center">'
+'<button class="btn primary" id="'+paraId+'-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>'
+' Я прочитал — '+(paraId.startsWith('final')?'финал':'\xA7'+paraId.replace('p',''))+' (+10 XP)'
+'</button></div>';
}
function wireReadBtn(paraId){
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
btn.addEventListener('click', ()=>{
addXp(10, paraId+'-read'); bumpProgress(paraId, 100);
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
if(paraId==='final4') achievement('ch4_done');
});
}
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
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 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(); }};
}
/* Координатная плоскость в SVG */
function axes2D(W, H, pad, xmin, xmax, ymin, ymax){
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (ymax - ymin);
const toX = v => pad + (v - xmin) * ux;
const toY = v => H - pad - (v - ymin) * uy;
let g = '';
g += '<g stroke="#e5e7eb" stroke-width="1">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
g += '<line x1="'+toX(x)+'" y1="'+pad+'" x2="'+toX(x)+'" y2="'+(H-pad)+'"/>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
g += '<line x1="'+pad+'" y1="'+toY(y)+'" x2="'+(W-pad)+'" y2="'+toY(y)+'"/>';
}
g += '</g>';
const y0 = toY(0), x0 = toX(0);
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+x0+'" y1="'+pad+'" x2="'+x0+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(W-pad+2)+'" y="'+(y0-4)+'" font-size="11" fill="#0f172a">x</text>';
g += '<text x="'+(x0+4)+'" y="'+(pad-2)+'" font-size="11" fill="#0f172a">y</text>';
g += '<g font-size="10" fill="#64748b">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
if (x !== 0) g += '<text x="'+(toX(x)-3)+'" y="'+(y0+12)+'">'+x+'</text>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
if (y !== 0) g += '<text x="'+(x0+4)+'" y="'+(toY(y)+3)+'">'+y+'</text>';
}
g += '<text x="'+(x0+4)+'" y="'+(y0+12)+'">0</text>';
g += '</g>';
return { content: g, toX, toY, ux, uy };
}
/* График функции y=f(x) — возвращает строку <path ...> */
function plotFunc(f, xmin, xmax, toX, toY, color, N){
N = N || 200;
let d = '';
let prevValid = false;
for (let i = 0; i <= N; i++){
const x = xmin + (xmax - xmin) * i / N;
let y;
try { y = f(x); } catch(e){ y = NaN; }
if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; }
d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2);
prevValid = true;
}
return '<path d="'+d+'" stroke="'+color+'" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';
}
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
function buildP14(){
const box = document.getElementById('p14-body');
let html = '';
html += makeCard('theory', 'Определение', '14.1', `
<p><b>Числовая последовательность</b> — это функция, заданная на множестве натуральных чисел $\\mathbb{N}$.</p>
<p>Записывают: $a_1,\\ a_2,\\ a_3,\\ \\ldots,\\ a_n,\\ \\ldots$ — это <b>члены последовательности</b>.</p>
<ul style="padding-left:22px;line-height:1.9">
<li>$a_n$ — <b>общий член</b> (член с номером $n$);</li>
<li>$n$ — <b>номер</b> члена ($n \\in \\mathbb{N}$);</li>
<li>$a_1$ — первый член, $a_2$ — второй, и т. д.</li>
</ul>
<p>Кратко последовательность обозначают $(a_n)$. Это значит: $a \\colon \\mathbb{N} \\to \\mathbb{R}$.</p>`);
html += makeCard('rule', 'Три способа задания', '14.2', `
<p>Последовательность можно задать одним из трёх способов:</p>
<ol style="padding-left:22px;line-height:2">
<li><b>Формулой $n$-го члена:</b> $a_n = 2n + 1$ → последовательность $3, 5, 7, 9, \\ldots$ Чтобы найти любой член, подставь его номер $n$.</li>
<li><b>Рекуррентно:</b> задают $a_1$ и формулу, выражающую следующий член через предыдущий. Например: $a_1 = 1,\\ a_{n+1} = a_n + 3$ → $1, 4, 7, 10, \\ldots$</li>
<li><b>Перечислением (словесно):</b> $2, 4, 8, 16, \\ldots$ — если из записи понятен алгоритм (удвоение).</li>
</ol>`);
html += makeCard('example', 'Конечные/бесконечные · возрастающие/убывающие', '14.3', `
<p><b>Конечная</b> последовательность: $1, 2, 3, \\ldots, 100$ — заканчивается на $a_{100}$.</p>
<p><b>Бесконечная</b>: $1, 2, 4, 8, \\ldots$ — продолжается без конца.</p>
<p>Последовательность $(a_n)$ — <b>возрастающая</b>, если $a_{n+1} > a_n$ для всех $n$.<br>
<b>Убывающая</b> — если $a_{n+1} < a_n$ для всех $n$.</p>
<p><b>Примеры:</b></p>
<ul style="padding-left:22px;line-height:1.9">
<li>$a_n = -n$ → $-1, -2, -3, \\ldots$ — убывающая;</li>
<li>$a_n = \\dfrac{1}{n}$ → $1, \\dfrac{1}{2}, \\dfrac{1}{3}, \\ldots$ — убывающая, но положительная;</li>
<li>$a_n = n^2$ → $1, 4, 9, 16, \\ldots$ — возрастающая;</li>
<li>$a_n = (-1)^n$ → $-1, 1, -1, 1, \\ldots$ — ни возрастает, ни убывает.</li>
</ul>`);
/* INTERACTIVE 1 — конструктор последовательности */
html += `<div class="wg" id="p14-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор последовательности</div></div>
<div class="wg-help">Выбери одну из пяти классических последовательностей и посмотри её точечную диаграмму $(n, a_n)$ на координатной плоскости.</div>
<div class="sliders">
<label>Тип №<b id="p14-iv1-ti">1</b> / 5<input type="range" id="p14-iv1-fn" min="1" max="5" step="1" value="1"></label>
</div>
<div id="p14-iv1-formula" style="text-align:center;font-size:1.1rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:10px"></div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p14-iv1-svg" viewBox="0 0 420 280" style="width:100%;max-width:520px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p14-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;text-align:center"></div>
<div id="p14-iv1-mono" style="margin-top:8px;padding:8px 12px;background:var(--card);border-radius:8px;font-size:.88rem;text-align:center;color:var(--sec-acc-d,var(--pri2));font-weight:600"></div>
</div>`;
/* INTERACTIVE 2 — вычисли a_n */
html += `<div class="wg" id="p14-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Вычисли $a_n$</div></div>
<div class="wg-help">Подставь номер $n$ в формулу и вычисли значение $a_n$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p14-iv2-idx">1</b> / 6 &middot; Очки: <b id="p14-iv2-sc">0</b></div>
<div id="p14-iv2-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">$a_n$ =</span>
<input type="number" id="p14-iv2-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p14-iv2-go">Проверить</button>
</div>
<div class="feedback" id="p14-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — возрастает/убывает/нет */
html += `<div class="wg" id="p14-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возрастает или убывает?</div></div>
<div class="wg-help">Дана формула $a_n$. Определи: возрастает, убывает или ни то ни другое.</div>
<div class="score-display">Задача: <b id="p14-iv3-idx">1</b> / 6 &middot; Очки: <b id="p14-iv3-sc">0</b></div>
<div id="p14-iv3-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p14-iv3-up">Возрастает</button>
<button class="btn" id="p14-iv3-dn">Убывает</button>
<button class="btn" id="p14-iv3-no">Ни то ни другое</button>
</div>
<div class="feedback" id="p14-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — сортер: рекуррентно или формулой */
html += `<div class="wg" id="p14-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Рекуррентно или формулой?</div></div>
<div class="wg-help">Перетащи каждый способ задания в нужный ящик.</div>
<div id="p14-iv4-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<div class="drop-box"><h5>Рекуррентное правило</h5><div class="drop-items" data-cat="rec"></div></div>
<div class="drop-box"><h5>Формула $n$-го члена</h5><div class="drop-items" data-cat="fml"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p14-iv4-check">Проверить</button><button class="btn" id="p14-iv4-reset">Сбросить</button></div>
<div class="feedback" id="p14-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav(null, 'p15') + readButton('p14');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const types = [
{ tex:'a_n = 2n - 1', f:n=>2*n-1, seq:'1, 3, 5, 7, 9, \\ldots', mono:'возрастает' },
{ tex:'a_n = n^2', f:n=>n*n, seq:'1, 4, 9, 16, 25, \\ldots', mono:'возрастает' },
{ tex:'a_n = (-1)^n', f:n=>Math.pow(-1,n), seq:'-1, 1, -1, 1, \\ldots', mono:'ни возрастает, ни убывает' },
{ tex:'a_n = \\dfrac{1}{n}', f:n=>1/n, seq:'1, \\dfrac{1}{2}, \\dfrac{1}{3}, \\dfrac{1}{4}, \\ldots', mono:'убывает' },
{ tex:'a_n = n(n+1)', f:n=>n*(n+1), seq:'2, 6, 12, 20, 30, \\ldots', mono:'возрастает' }
];
const svg = document.getElementById('p14-iv1-svg');
const fnSl = document.getElementById('p14-iv1-fn');
const ti = document.getElementById('p14-iv1-ti');
const formula = document.getElementById('p14-iv1-formula');
const out = document.getElementById('p14-iv1-out');
const mono = document.getElementById('p14-iv1-mono');
let bumped = false;
function redraw(){
const idx = (+fnSl.value)-1;
const t = types[idx];
ti.textContent = (idx+1);
// вычисляем 10 точек
const pts = [];
for (let n=1;n<=10;n++){ const y=t.f(n); if (isFinite(y)) pts.push([n,y]); }
let ymin = Math.min(...pts.map(p=>p[1]), 0);
let ymax = Math.max(...pts.map(p=>p[1]), 0);
// ограничим, чтобы график не «улетал»
if (ymax > 30) ymax = 30;
if (ymin < -5) ymin = -5;
const pad = Math.max(1, (ymax-ymin)*0.1);
ymin = Math.floor(ymin - pad);
ymax = Math.ceil(ymax + pad);
const ax = axes2D(420, 280, 32, 0, 11, ymin, ymax);
let g = ax.content;
// точки + соединяющие отрезки (пунктир)
let path = '';
pts.forEach((p,i)=>{
const x = ax.toX(p[0]); const y = ax.toY(Math.min(ymax,Math.max(ymin,p[1])));
path += (i===0?'M':' L')+x.toFixed(1)+','+y.toFixed(1);
});
g += '<path d="'+path+'" stroke="#0891b2" stroke-width="1.2" fill="none" stroke-dasharray="3 3" opacity=".55"/>';
pts.forEach(p=>{
const x = ax.toX(p[0]); const y = ax.toY(Math.min(ymax,Math.max(ymin,p[1])));
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>';
});
svg.innerHTML = g;
formula.innerHTML = '$' + t.tex + '$';
out.innerHTML = '<b>Первые 10 членов:</b> $' + t.seq + '$';
mono.innerHTML = 'Эта последовательность <b>' + t.mono + '</b>';
renderMath(formula); renderMath(out); renderMath(mono);
if (!bumped){ bumped = true; bumpProgress('p14', 15); addXp(10,'p14-iv1'); }
}
fnSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const items = [
{ q:'$a_n = 3n + 1$, найти $a_5$', ans: 16 },
{ q:'$a_n = n^2 - 2$, найти $a_4$', ans: 14 },
{ q:'$a_n = 2^n$, найти $a_6$', ans: 64 },
{ q:'$a_n = \\dfrac{6}{n}$, найти $a_3$', ans: 2 },
{ q:'$a_n = (-1)^n \\cdot n$, найти $a_7$', ans: -7 },
{ q:'$a_n = n^3 - n$, найти $a_3$', ans: 24 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p14-iv2-idx');
const scEl = document.getElementById('p14-iv2-sc');
const qEl = document.getElementById('p14-iv2-q');
const inp = document.getElementById('p14-iv2-ans');
const btn = document.getElementById('p14-iv2-go');
const fb = document.getElementById('p14-iv2-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p14', 15); addXp(10,'p14-iv2'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно. $a_n = ' + it.ans + '$' : '&#10007; Неверно. Правильный ответ: $a_n = ' + it.ans + '$');
i++;
setTimeout(render, 1000);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$a_n = 2n$', ans:'up', hint:'$a_{n+1} - a_n = 2 > 0$.' },
{ q:'$a_n = 10 - n$', ans:'dn', hint:'$a_{n+1} - a_n = -1 < 0$.' },
{ q:'$a_n = (-1)^n$', ans:'no', hint:'Чередуется $-1, 1, -1, 1, \\ldots$' },
{ q:'$a_n = n^2$', ans:'up', hint:'$1, 4, 9, 16, \\ldots$ — растёт.' },
{ q:'$a_n = \\dfrac{1}{n}$', ans:'dn', hint:'$1, \\tfrac{1}{2}, \\tfrac{1}{3}, \\ldots$ — убывает.' },
{ q:'$a_n = n - n^2$', ans:'dn', hint:'$0, -2, -6, -12, \\ldots$ — убывает с $n=1$.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p14-iv3-idx');
const scEl = document.getElementById('p14-iv3-sc');
const qEl = document.getElementById('p14-iv3-q');
const fb = document.getElementById('p14-iv3-fb');
const upB = document.getElementById('p14-iv3-up');
const dnB = document.getElementById('p14-iv3-dn');
const noB = document.getElementById('p14-iv3-no');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
[upB,dnB,noB].forEach(b=>{ b.disabled=true; b.style.opacity=.5; });
if (!bumped){ bumped = true; bumpProgress('p14', 25); addXp(15,'p14-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1000);
}
upB.addEventListener('click', ()=>answer('up'));
dnB.addEventListener('click', ()=>answer('dn'));
noB.addEventListener('click', ()=>answer('no'));
render();
})();
/* ===== IV4 wiring — DnD ===== */
(function(){
const items = [
{ id:'a', html:'$a_n = 5n - 3$', cat:'fml' },
{ id:'b', html:'$a_1 = 2,\\ a_{n+1} = a_n + 5$', cat:'rec' },
{ id:'c', html:'$a_n = n^2$', cat:'fml' },
{ id:'d', html:'$a_1 = 1,\\ a_{n+1} = 2 a_n$', cat:'rec' },
{ id:'e', html:'$a_n = \\dfrac{n}{n+1}$', cat:'fml' },
{ id:'f', html:'$a_1 = 3,\\ a_{n+1} = a_n - 2$', cat:'rec' }
];
const sorter = setupSorter({
poolId: 'p14-iv4-pool',
scopeSelector: '#p14-iv4',
cats: ['rec','fml'],
items: items
});
let bumped = false;
document.getElementById('p14-iv4-check').addEventListener('click', ()=>{
const fb = document.getElementById('p14-iv4-fb');
const total = items.length;
let correct = 0, placed = 0;
items.forEach(it=>{ if(sorter.placed[it.id]){ placed++; if(sorter.placed[it.id]===it.cat) correct++; } });
if (placed < total){
feedback(fb, false, 'Размещены не все: ' + placed + ' / ' + total + '.');
return;
}
const ok = (correct === total);
feedback(fb, ok, ok ? '&#10003; Все верно! ' + correct + ' / ' + total : '&#10007; Правильно: ' + correct + ' / ' + total);
if (ok && !bumped){ bumped = true; bumpProgress('p14', 25); addXp(15,'p14-iv4'); }
});
document.getElementById('p14-iv4-reset').addEventListener('click', ()=>{
sorter.reset();
const fb = document.getElementById('p14-iv4-fb'); fb.style.display='none';
});
})();
wireReadBtn('p14');
}
function buildP15(){
const box = document.getElementById('p15-body');
let html = '';
html += makeCard('theory', 'Определение и формула $n$-го члена', '15.1', `
<p><b>Арифметическая прогрессия</b> — числовая последовательность, в которой каждый следующий член отличается от предыдущего на одно и то же число $d$ — <b>разность прогрессии</b>.</p>
<p>$$a_{n+1} - a_n = d \\quad \\text{для всех } n \\in \\mathbb{N}.$$</p>
<p><b>Формула $n$-го члена:</b></p>
<p>$$a_n = a_1 + (n - 1)\\, d.$$</p>
<p><b>Пример.</b> $5, 8, 11, 14, \\ldots$ — арифметическая прогрессия с $a_1 = 5$, $d = 3$. Тогда
$a_{10} = 5 + 9 \\cdot 3 = 32$.</p>`);
html += makeCard('rule', 'Характеристическое свойство', '15.2', `
<p>В арифметической прогрессии любой <b>средний</b> член — это <b>среднее арифметическое</b> своих соседей:</p>
<p>$$a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}\\quad (n \\ge 2).$$</p>
<p>Это свойство <b>полностью характеризует</b> арифметическую прогрессию: если оно выполняется для всех $n \\ge 2$, то $(a_n)$ — арифметическая прогрессия.</p>
<p><b>Пример.</b> Между $4$ и $16$ в арифметической прогрессии стоит число $\\dfrac{4 + 16}{2} = 10$.</p>`);
html += makeCard('example', 'Применение формулы', '15.3', `
<p><b>а)</b> Найти $a_{20}$, если $a_1 = 3$, $d = 4$:<br>
$a_{20} = a_1 + 19 d = 3 + 19 \\cdot 4 = 3 + 76 = \\mathbf{79}.$</p>
<p><b>б)</b> Найти $d$, если $a_1 = 2$, $a_{10} = 29$:<br>
$29 = 2 + 9 d \\Rightarrow 9 d = 27 \\Rightarrow \\mathbf{d = 3}.$</p>
<p><b>в)</b> Найти $a_1$, если $d = -2$, $a_5 = 10$:<br>
$10 = a_1 + 4 \\cdot (-2) = a_1 - 8 \\Rightarrow \\mathbf{a_1 = 18}.$</p>`);
/* INTERACTIVE 1 — конструктор арифм. прогрессии */
html += `<div class="wg" id="p15-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор арифм. прогрессии</div></div>
<div class="wg-help">Двигай ползунки $a_1$ и $d$. Точки $(n; a_n)$ ложатся на прямую $y = a_1 + (x - 1) d$ — пунктирная линия.</div>
<div class="sliders">
<label>$a_1$ =<b id="p15-iv1-av">0</b><input type="range" id="p15-iv1-a1" min="-10" max="10" step="1" value="2"></label>
<label>$d$ =<b id="p15-iv1-dv">0</b><input type="range" id="p15-iv1-d" min="-5" max="5" step="1" value="2"></label>
</div>
<div id="p15-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:10px"></div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p15-iv1-svg" viewBox="0 0 460 300" style="width:100%;max-width:560px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p15-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор a_n и d */
html += `<div class="wg" id="p15-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $a_n$ и $d$</div></div>
<div class="wg-help">Две формы. <b>Слева</b> — найди $a_n$ по $a_1$, $d$, $n$. <b>Справа</b> — найди $d$ по $a_1$, $a_n$, $n$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:8px">
<div style="background:var(--card);border-radius:11px;padding:14px">
<div style="font-weight:700;color:var(--sec-acc-d,var(--pri2));margin-bottom:10px">Найти $a_n$</div>
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$a_1$ =</span><input type="number" id="p15-iv2a-a1" class="tinp" value="3" step="1">
<span>$d$ =</span><input type="number" id="p15-iv2a-d" class="tinp" value="4" step="1">
<span>$n$ =</span><input type="number" id="p15-iv2a-n" class="tinp" value="10" step="1" min="1">
</div>
<button class="btn primary" id="p15-iv2a-go" style="width:100%">Найти $a_n$</button>
<div id="p15-iv2a-out" style="margin-top:10px;padding:8px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.92rem;min-height:36px"></div>
</div>
<div style="background:var(--card);border-radius:11px;padding:14px">
<div style="font-weight:700;color:var(--sec-acc-d,var(--pri2));margin-bottom:10px">Найти $d$</div>
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$a_1$ =</span><input type="number" id="p15-iv2b-a1" class="tinp" value="2" step="1">
<span>$a_n$ =</span><input type="number" id="p15-iv2b-an" class="tinp" value="29" step="1">
<span>$n$ =</span><input type="number" id="p15-iv2b-n" class="tinp" value="10" step="1" min="2">
</div>
<button class="btn primary" id="p15-iv2b-go" style="width:100%">Найти $d$</button>
<div id="p15-iv2b-out" style="margin-top:10px;padding:8px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.92rem;min-height:36px"></div>
</div>
</div>
</div>`;
/* INTERACTIVE 3 — арифм. или нет */
html += `<div class="wg" id="p15-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Является ли арифметической?</div></div>
<div class="wg-help">Даны первые члены последовательности. Если разность $a_{n+1} - a_n$ постоянна — это арифметическая прогрессия.</div>
<div class="score-display">Задача: <b id="p15-iv3-idx">1</b> / 6 &middot; Очки: <b id="p15-iv3-sc">0</b></div>
<div id="p15-iv3-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p15-iv3-y">Да, арифм.</button>
<button class="btn" id="p15-iv3-n">Нет</button>
</div>
<div class="feedback" id="p15-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p15-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр прогрессии</div></div>
<div class="wg-help">Используй формулы $a_n = a_1 + (n - 1) d$ и характеристическое свойство $a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p15-iv4-idx">1</b> / 6 &middot; Очки: <b id="p15-iv4-sc">0</b></div>
<div id="p15-iv4-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p15-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p15-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p15-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p14', 'p16') + readButton('p15');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const svg = document.getElementById('p15-iv1-svg');
const a1Sl = document.getElementById('p15-iv1-a1');
const dSl = document.getElementById('p15-iv1-d');
const av = document.getElementById('p15-iv1-av');
const dv = document.getElementById('p15-iv1-dv');
const formula = document.getElementById('p15-iv1-formula');
const out = document.getElementById('p15-iv1-out');
let bumped = false;
function redraw(){
const a1 = +a1Sl.value, d = +dSl.value;
av.textContent = a1; dv.textContent = d;
// вычисляем 15 точек
const N = 15;
const pts = [];
for (let n=1;n<=N;n++) pts.push([n, a1 + (n-1)*d]);
const ys = pts.map(p=>p[1]);
let ymin = Math.min(...ys, 0), ymax = Math.max(...ys, 0);
const pad = Math.max(2, (ymax-ymin)*0.12);
ymin = Math.floor(ymin - pad);
ymax = Math.ceil(ymax + pad);
const ax = axes2D(460, 300, 34, 0, N+1, ymin, ymax);
let g = ax.content;
// пунктирная прямая y = a1 + (x-1) d
const xL = 0, xR = N + 1;
const yL = a1 + (xL - 1)*d;
const yR = a1 + (xR - 1)*d;
// зажмём в пределах ymin/ymax
function clip(x, y){
if (y > ymax){ return [ x + (ymax - y) / (d || 1e-9) * 1, ymax ]; }
if (y < ymin){ return [ x + (ymin - y) / (d || 1e-9) * 1, ymin ]; }
return [x, y];
}
const cL = clip(xL, yL), cR = clip(xR, yR);
g += '<line x1="'+ax.toX(cL[0])+'" y1="'+ax.toY(cL[1])+'" x2="'+ax.toX(cR[0])+'" y2="'+ax.toY(cR[1])+'" stroke="#06b6d4" stroke-width="1.5" stroke-dasharray="5 4" opacity=".7"/>';
// точки
pts.forEach(p=>{
if (p[1] < ymin || p[1] > ymax) return;
const x = ax.toX(p[0]); const y = ax.toY(p[1]);
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>';
});
svg.innerHTML = g;
// формула
const dPart = (d>=0 ? '+ ' + d : '- ' + (-d));
formula.innerHTML = '$a_n = ' + a1 + ' ' + dPart + ' \\cdot (n - 1)$';
// первые 8 членов
const first8 = pts.slice(0,8).map(p=>p[1]).join(',\\ ');
out.innerHTML = '<b>Первые 8 членов:</b> $' + first8 + ',\\ \\ldots$';
renderMath(formula); renderMath(out);
if (!bumped){ bumped = true; bumpProgress('p15', 15); addXp(10,'p15-iv1'); }
}
a1Sl.addEventListener('input', redraw);
dSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
// Найти a_n
const outA = document.getElementById('p15-iv2a-out');
let bumpedA = false;
document.getElementById('p15-iv2a-go').addEventListener('click', ()=>{
const a1 = +document.getElementById('p15-iv2a-a1').value;
const d = +document.getElementById('p15-iv2a-d').value;
const n = +document.getElementById('p15-iv2a-n').value;
if (!Number.isInteger(n) || n < 1){ outA.innerHTML = 'Номер $n$ должен быть натуральным.'; return; }
const an = a1 + (n - 1) * d;
const dPart = (d>=0 ? '+ ' + d : '- ' + (-d));
outA.innerHTML = '$a_{'+n+'} = '+a1+' '+dPart+' \\cdot ('+n+' - 1) = \\mathbf{'+fmt(an)+'}$';
renderMath(outA);
if (!bumpedA){ bumpedA = true; bumpProgress('p15', 8); addXp(5,'p15-iv2a'); }
});
// Найти d
const outB = document.getElementById('p15-iv2b-out');
let bumpedB = false;
document.getElementById('p15-iv2b-go').addEventListener('click', ()=>{
const a1 = +document.getElementById('p15-iv2b-a1').value;
const an = +document.getElementById('p15-iv2b-an').value;
const n = +document.getElementById('p15-iv2b-n').value;
if (!Number.isInteger(n) || n < 2){ outB.innerHTML = '$n$ должно быть целым и $\\ge 2$.'; renderMath(outB); return; }
const d = (an - a1) / (n - 1);
outB.innerHTML = '$d = \\dfrac{a_n - a_1}{n - 1} = \\dfrac{'+an+' - '+a1+'}{'+n+' - 1} = \\mathbf{'+fmt(d)+'}$';
renderMath(outB);
if (!bumpedB){ bumpedB = true; bumpProgress('p15', 7); addXp(5,'p15-iv2b'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$1,\\ 4,\\ 7,\\ 10,\\ \\ldots$', ans:true, hint:'Разность $d = 3$ постоянна.' },
{ q:'$2,\\ 4,\\ 8,\\ 16,\\ \\ldots$', ans:false, hint:'Это геометрическая ($\\cdot 2$), не арифметическая.' },
{ q:'$5,\\ 5,\\ 5,\\ 5,\\ \\ldots$', ans:true, hint:'Это арифм. прогрессия с $d = 0$.' },
{ q:'$10,\\ 7,\\ 4,\\ 1,\\ \\ldots$', ans:true, hint:'$d = -3$ постоянна.' },
{ q:'$1,\\ 3,\\ 6,\\ 10,\\ \\ldots$', ans:false, hint:'Разности $2, 3, 4, \\ldots$ — не равны.' },
{ q:'$-2,\\ 1,\\ 4,\\ 7,\\ \\ldots$', ans:true, hint:'$d = 3$ постоянна.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p15-iv3-idx');
const scEl = document.getElementById('p15-iv3-sc');
const qEl = document.getElementById('p15-iv3-q');
const fb = document.getElementById('p15-iv3-fb');
const yBtn = document.getElementById('p15-iv3-y');
const nBtn = document.getElementById('p15-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p15', 25); addXp(15,'p15-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1000);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring — тренажёр ===== */
(function(){
const items = [
{ q:'$a_1 = 2,\\ d = 3$. Найти $a_5$.', ans: 14 },
{ q:'$a_1 = 10,\\ d = -2$. Найти $a_{10}$.', ans: -8 },
{ q:'$a_1 = 5,\\ a_4 = 11$. Найти $d$.', ans: 2 },
{ q:'$a_3 = 7,\\ d = 3$. Найти $a_1$.', ans: 1 },
{ q:'$a_n = 3 + 2(n - 1)$. Найти $a_{50}$.', ans: 101 },
{ q:'Найти число, стоящее между $4$ и $16$ в арифм. прогрессии (т.е. среднее арифметическое).', ans: 10 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p15-iv4-idx');
const scEl = document.getElementById('p15-iv4-sc');
const qEl = document.getElementById('p15-iv4-q');
const inp = document.getElementById('p15-iv4-ans');
const btn = document.getElementById('p15-iv4-go');
const fb = document.getElementById('p15-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p15', 25); addXp(15,'p15-iv4'); achievement('p15_done'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно: $'+it.ans+'$' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$');
i++;
setTimeout(render, 1000);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p15');
}
function buildP16(){
const box = document.getElementById('p16-body');
let html = '';
html += makeCard('theory', 'Формула суммы (через крайние члены)', '16.1', `
<p>Сумма $n$ первых членов арифметической прогрессии равна <b>полусумме первого и последнего</b> члена, умноженной на число членов:</p>
<p>$$S_n = \\dfrac{a_1 + a_n}{2} \\cdot n.$$</p>
<p>Удобно применять, когда известны <b>$a_1$, $a_n$ и $n$</b> (или их легко найти).</p>`);
html += makeCard('rule', 'Формула суммы (через $a_1$ и $d$)', '16.2', `
<p>Подставив $a_n = a_1 + (n - 1) d$ в первую формулу, получим:</p>
<p>$$S_n = \\dfrac{2 a_1 + (n - 1) d}{2} \\cdot n.$$</p>
<p>Эта формула удобна, когда заданы <b>$a_1$, $d$ и $n$</b>. Обе формулы эквивалентны и дают один и тот же результат.</p>`);
html += makeCard('example', 'Идея вывода (Гаусс) и примеры', '16.3', `
<p><b>Идея вывода.</b> Пусть $S = 1 + 2 + 3 + \\ldots + 100$. Сложим пары крайних членов:
$(1 + 100) + (2 + 99) + \\ldots + (50 + 51)$. Каждая пара даёт $101$, пар $50$, поэтому
$S = 50 \\cdot 101 = \\mathbf{5050}$.</p>
<p>Аналогично для арифм. прогрессии: пар $n/2$, каждая равна $a_1 + a_n$, итого $S_n = \\dfrac{(a_1 + a_n) \\cdot n}{2}$.</p>
<p><b>а)</b> Сумма первых $20$ членов прогрессии $3, 7, 11, \\ldots$: $a_1 = 3$, $d = 4$, $a_{20} = 3 + 19 \\cdot 4 = 79$. Тогда $S_{20} = \\dfrac{3 + 79}{2} \\cdot 20 = \\mathbf{820}.$</p>
<p><b>б)</b> Сумма первых $100$ натуральных чисел: $S = \\dfrac{1 + 100}{2} \\cdot 100 = \\mathbf{5050}.$</p>`);
/* INTERACTIVE 1 — конструктор суммы (Гаусс-пары) */
html += `<div class="wg" id="p16-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор суммы (приём Гаусса)</div></div>
<div class="wg-help">Двигай ползунки $a_1$, $d$, $n$. Будут показаны все $n$ членов и подсвечены пары крайних — каждая пара даёт одно и то же число $a_1 + a_n$.</div>
<div class="sliders">
<label>$a_1$ =<b id="p16-iv1-av">0</b><input type="range" id="p16-iv1-a1" min="-10" max="10" step="1" value="3"></label>
<label>$d$ =<b id="p16-iv1-dv">0</b><input type="range" id="p16-iv1-d" min="-5" max="5" step="1" value="4"></label>
<label>$n$ =<b id="p16-iv1-nv">0</b><input type="range" id="p16-iv1-n" min="2" max="20" step="1" value="8"></label>
</div>
<div id="p16-iv1-terms" style="padding:12px;background:var(--card);border-radius:10px;margin-bottom:10px;font-family:'JetBrains Mono',monospace;font-size:.95rem;line-height:1.9;text-align:center;overflow-x:auto"></div>
<div id="p16-iv1-pairs" style="padding:10px 12px;background:var(--sec-acc-soft);border-radius:9px;margin-bottom:10px;font-size:.92rem;text-align:center"></div>
<div id="p16-iv1-formula" style="padding:12px;background:var(--card);border-radius:10px;font-size:1rem;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор суммы */
html += `<div class="wg" id="p16-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор суммы $S_n$</div></div>
<div class="wg-help">Введи $a_1$, $d$, $n$ — получишь $a_n$ и $S_n$ с подстановкой.</div>
<div style="background:var(--card);border-radius:11px;padding:14px;max-width:480px;margin:0 auto">
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$a_1$ =</span><input type="number" id="p16-iv2-a1" class="tinp" value="3" step="1">
<span>$d$ =</span><input type="number" id="p16-iv2-d" class="tinp" value="4" step="1">
<span>$n$ =</span><input type="number" id="p16-iv2-n" class="tinp" value="20" step="1" min="1">
</div>
<button class="btn primary" id="p16-iv2-go" style="width:100%">Вычислить $S_n$</button>
<div id="p16-iv2-out" style="margin-top:10px;padding:10px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.95rem;min-height:36px;line-height:1.7"></div>
</div>
</div>`;
/* INTERACTIVE 3 — найди ошибку в формуле */
html += `<div class="wg" id="p16-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">Задача: <b id="p16-iv3-idx">1</b> / 6 &middot; Очки: <b id="p16-iv3-sc">0</b></div>
<div id="p16-iv3-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:70px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p16-iv3-y">Верно</button>
<button class="btn" id="p16-iv3-n">Ошибка</button>
</div>
<div class="feedback" id="p16-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр сумм */
html += `<div class="wg" id="p16-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр сумм</div></div>
<div class="wg-help">Применяй $S_n = \\dfrac{a_1 + a_n}{2} \\cdot n$ или $S_n = \\dfrac{2 a_1 + (n - 1) d}{2} \\cdot n$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p16-iv4-idx">1</b> / 6 &middot; Очки: <b id="p16-iv4-sc">0</b></div>
<div id="p16-iv4-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p16-iv4-ans" class="tinp" style="width:140px;text-align:center" step="1">
<button class="btn primary" id="p16-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p16-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p15', 'p17') + readButton('p16');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const a1Sl = document.getElementById('p16-iv1-a1');
const dSl = document.getElementById('p16-iv1-d');
const nSl = document.getElementById('p16-iv1-n');
const av = document.getElementById('p16-iv1-av');
const dv = document.getElementById('p16-iv1-dv');
const nv = document.getElementById('p16-iv1-nv');
const termsEl = document.getElementById('p16-iv1-terms');
const pairsEl = document.getElementById('p16-iv1-pairs');
const formEl = document.getElementById('p16-iv1-formula');
const PAIR_COLORS = ['#ef4444','#0891b2','#16a34a','#a855f7','#f59e0b','#ec4899','#6366f1','#14b8a6','#f97316','#84cc16'];
let bumped = false;
function redraw(){
const a1 = +a1Sl.value, d = +dSl.value, n = +nSl.value;
av.textContent = a1; dv.textContent = d; nv.textContent = n;
// массив членов
const arr = [];
for (let i=0;i<n;i++) arr.push(a1 + i*d);
const an = arr[n-1];
// раскраска пар: i и n-1-i получают один цвет, средний (если n нечётно) — серый
const colors = new Array(n).fill('#94a3b8');
const halfPairs = Math.floor(n/2);
for (let p=0;p<halfPairs;p++){
const c = PAIR_COLORS[p % PAIR_COLORS.length];
colors[p] = c;
colors[n-1-p] = c;
}
// отрисовка членов с цветами
const parts = arr.map((v,i)=>'<span style="display:inline-block;padding:3px 9px;margin:2px;border-radius:7px;background:'+colors[i]+'15;color:'+colors[i]+';font-weight:700;border:1.5px solid '+colors[i]+'55">'+v+'</span>');
termsEl.innerHTML = parts.join(' ');
// описание пар
const pairSum = a1 + an;
let pairsTxt = '';
if (halfPairs > 0){
const pairsList = [];
for (let p=0;p<Math.min(halfPairs,3);p++){
pairsList.push('('+arr[p]+'\\, +\\, '+arr[n-1-p]+')');
}
if (halfPairs > 3) pairsList.push('\\ldots');
pairsTxt = '<b>Пары:</b> $' + pairsList.join('\\, +\\, ') + ' = ' + halfPairs + ' \\cdot ' + pairSum + ' = ' + (halfPairs*pairSum) + '$';
if (n % 2 === 1){
const mid = arr[(n-1)/2];
pairsTxt += ' &nbsp;+&nbsp; <span style="color:#64748b">средний $' + mid + '$</span>';
}
} else {
pairsTxt = '<b>n = 1</b>: $S_1 = a_1 = ' + a1 + '$';
}
pairsEl.innerHTML = pairsTxt;
// итог
const Sn = (a1 + an) * n / 2;
const dPart = (d>=0 ? '+ ' + d : '- ' + (-d));
formEl.innerHTML = '$a_{'+n+'} = '+a1+' '+dPart+' \\cdot ('+n+' - 1) = '+an+'$<br>'+
'$S_{'+n+'} = \\dfrac{'+a1+' + '+an+'}{2} \\cdot '+n+' = \\mathbf{'+Sn+'}$';
renderMath(pairsEl); renderMath(formEl);
if (!bumped){ bumped = true; bumpProgress('p16', 15); addXp(10,'p16-iv1'); }
}
a1Sl.addEventListener('input', redraw);
dSl.addEventListener('input', redraw);
nSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const out = document.getElementById('p16-iv2-out');
let bumped = false;
document.getElementById('p16-iv2-go').addEventListener('click', ()=>{
const a1 = +document.getElementById('p16-iv2-a1').value;
const d = +document.getElementById('p16-iv2-d').value;
const n = +document.getElementById('p16-iv2-n').value;
if (!Number.isInteger(n) || n < 1){ out.innerHTML = 'Номер $n$ должен быть натуральным.'; renderMath(out); return; }
const an = a1 + (n - 1) * d;
const Sn = (a1 + an) * n / 2;
const dPart = (d>=0 ? '+ ' + d : '- ' + (-d));
out.innerHTML =
'$a_{'+n+'} = '+a1+' '+dPart+' \\cdot ('+n+' - 1) = '+fmt(an)+'$<br>'+
'$S_{'+n+'} = \\dfrac{'+a1+' + '+fmt(an)+'}{2} \\cdot '+n+' = \\mathbf{'+fmt(Sn)+'}$';
renderMath(out);
if (!bumped){ bumped = true; bumpProgress('p16', 15); addXp(10,'p16-iv2'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'Для прогрессии $1, 3, 5, 7, 9$: $S_5 = \\dfrac{1 + 9}{2} \\cdot 5 = 25$.', ans:true, hint:'$\\dfrac{10}{2} \\cdot 5 = 25$.' },
{ q:'$S_n = a_1 + (n - 1) d$.', ans:false, hint:'Это формула $a_n$, а не суммы. У суммы есть множитель $n/2$.' },
{ q:'$S_n = \\dfrac{a_1 + a_n}{2} \\cdot n$.', ans:true, hint:'Классическая формула суммы через крайние члены.' },
{ q:'Для $1, 3, 5, \\ldots, 19$ (10 членов): $S_{10} = \\dfrac{1 + 19}{2} \\cdot 10 = 100$.', ans:true, hint:'$\\dfrac{20}{2} \\cdot 10 = 100$.' },
{ q:'$S_n = n \\cdot a_1 + d$.', ans:false, hint:'Слагаемых с $d$ должно быть много, а здесь только одно. Это не сумма.' },
{ q:'Сумма первых $50$ натуральных чисел: $\\dfrac{50 \\cdot 51}{2} = 1275$.', ans:true, hint:'$S = \\dfrac{1 + 50}{2} \\cdot 50 = 1275$.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p16-iv3-idx');
const scEl = document.getElementById('p16-iv3-sc');
const qEl = document.getElementById('p16-iv3-q');
const fb = document.getElementById('p16-iv3-fb');
const yBtn = document.getElementById('p16-iv3-y');
const nBtn = document.getElementById('p16-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p16', 25); addXp(15,'p16-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1100);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring ===== */
(function(){
const items = [
{ q:'Сумма первых 10 нечётных чисел: $1 + 3 + 5 + \\ldots + 19 = ?$', ans: 100 },
{ q:'$a_1 = 5,\\ d = 2,\\ n = 6$. Найти $S_6$.', ans: 60 },
{ q:'$a_1 = -3,\\ d = 4,\\ n = 10$. Найти $S_{10}$.', ans: 150 },
{ q:'Сумма первых $100$ натуральных чисел.', ans: 5050 },
{ q:'$a_1 = 2,\\ a_{20} = 40$. Найти $S_{20}$.', ans: 420 },
{ q:'Прогрессия $7, 11, 15, \\ldots, 47$. Найти сумму всех её членов.', ans: 297 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p16-iv4-idx');
const scEl = document.getElementById('p16-iv4-sc');
const qEl = document.getElementById('p16-iv4-q');
const inp = document.getElementById('p16-iv4-ans');
const btn = document.getElementById('p16-iv4-go');
const fb = document.getElementById('p16-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p16', 25); addXp(15,'p16-iv4'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно: $'+it.ans+'$' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$');
i++;
setTimeout(render, 1100);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p16');
}
function buildP17(){
const box = document.getElementById('p17-body');
let html = '';
html += makeCard('theory', 'Определение и формула $n$-го члена', '17.1', `
<p><b>Геометрическая прогрессия</b> — числовая последовательность, в которой каждый следующий член равен предыдущему, умноженному на одно и то же число $q \\ne 0$ — <b>знаменатель прогрессии</b>.</p>
<p>$$b_{n+1} = b_n \\cdot q,\\quad b_1 \\ne 0.$$</p>
<p><b>Формула $n$-го члена:</b></p>
<p>$$b_n = b_1 \\cdot q^{n - 1}.$$</p>
<p><b>Пример.</b> $2, 6, 18, 54, \\ldots$ — геометрическая прогрессия с $b_1 = 2$, $q = 3$. Тогда $b_5 = 2 \\cdot 3^4 = 162$.</p>`);
html += makeCard('rule', 'Характеристическое свойство', '17.2', `
<p>В геометрической прогрессии с положительными членами (или с одним знаком) <b>средний</b> член — это <b>среднее геометрическое</b> своих соседей:</p>
<p>$$b_n^2 = b_{n-1} \\cdot b_{n+1}\\quad (n \\ge 2),$$</p>
<p>то есть $|b_n| = \\sqrt{b_{n-1} \\cdot b_{n+1}}$.</p>
<p><b>Пример.</b> Между $2$ и $8$ в геом. прогрессии стоит $\\sqrt{2 \\cdot 8} = \\sqrt{16} = 4$.</p>`);
html += makeCard('example', 'Применение формулы и поведение', '17.3', `
<p><b>а)</b> $b_1 = 3$, $q = 2$: $b_5 = 3 \\cdot 2^4 = 3 \\cdot 16 = \\mathbf{48}.$</p>
<p><b>б)</b> $b_1 = 64$, $q = \\dfrac{1}{2}$: $b_5 = 64 \\cdot \\left(\\dfrac{1}{2}\\right)^4 = 64 \\cdot \\dfrac{1}{16} = \\mathbf{4}.$ Прогрессия убывает по модулю.</p>
<p><b>Поведение в зависимости от $q$:</b></p>
<ul style="margin-left:18px">
<li>$|q| > 1$ — модули членов <b>растут</b>;</li>
<li>$|q| < 1$ — модули <b>убывают</b> к $0$;</li>
<li>$|q| = 1$ — все члены равны по модулю;</li>
<li>$q < 0$ — знаки членов <b>чередуются</b>.</li>
</ul>`);
/* INTERACTIVE 1 — конструктор геометрической */
html += `<div class="wg" id="p17-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор геом. прогрессии</div></div>
<div class="wg-help">Двигай ползунки $b_1$ и $q$. Точки $(n; b_n)$ показывают, как ведёт себя прогрессия: рост / убывание / чередование знаков.</div>
<div class="sliders">
<label>$b_1$ =<b id="p17-iv1-bv">0</b><input type="range" id="p17-iv1-b1" min="-5" max="10" step="1" value="2"></label>
<label>$q$ =<b id="p17-iv1-qv">0</b><input type="range" id="p17-iv1-q" min="-2" max="2" step="0.25" value="1.5"></label>
</div>
<div id="p17-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:10px"></div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p17-iv1-svg" viewBox="0 0 460 300" style="width:100%;max-width:560px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p17-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;text-align:center"></div>
<div id="p17-iv1-desc" style="margin-top:8px;padding:8px 12px;background:var(--card);border-radius:8px;font-size:.9rem;text-align:center;color:var(--muted)"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор b_n и q */
html += `<div class="wg" id="p17-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $b_n$ и $q$</div></div>
<div class="wg-help">Две формы. <b>Слева</b> — найди $b_n$ по $b_1$, $q$, $n$. <b>Справа</b> — найди $q$ по $b_1$, $b_n$, $n$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:8px">
<div style="background:var(--card);border-radius:11px;padding:14px">
<div style="font-weight:700;color:var(--sec-acc-d,var(--pri2));margin-bottom:10px">Найти $b_n$</div>
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$b_1$ =</span><input type="number" id="p17-iv2a-b1" class="tinp" value="2" step="1">
<span>$q$ =</span><input type="number" id="p17-iv2a-q" class="tinp" value="3" step="0.5">
<span>$n$ =</span><input type="number" id="p17-iv2a-n" class="tinp" value="5" step="1" min="1">
</div>
<button class="btn primary" id="p17-iv2a-go" style="width:100%">Найти $b_n$</button>
<div id="p17-iv2a-out" style="margin-top:10px;padding:8px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.92rem;min-height:36px"></div>
</div>
<div style="background:var(--card);border-radius:11px;padding:14px">
<div style="font-weight:700;color:var(--sec-acc-d,var(--pri2));margin-bottom:10px">Найти $q$</div>
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$b_1$ =</span><input type="number" id="p17-iv2b-b1" class="tinp" value="2" step="1">
<span>$b_n$ =</span><input type="number" id="p17-iv2b-bn" class="tinp" value="162" step="1">
<span>$n$ =</span><input type="number" id="p17-iv2b-n" class="tinp" value="5" step="1" min="2">
</div>
<button class="btn primary" id="p17-iv2b-go" style="width:100%">Найти $q$</button>
<div id="p17-iv2b-out" style="margin-top:10px;padding:8px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.92rem;min-height:36px"></div>
</div>
</div>
</div>`;
/* INTERACTIVE 3 — геометрическая или нет */
html += `<div class="wg" id="p17-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Является ли геометрической?</div></div>
<div class="wg-help">Даны первые члены последовательности. Если отношение $b_{n+1}/b_n$ постоянно — это геометрическая прогрессия.</div>
<div class="score-display">Задача: <b id="p17-iv3-idx">1</b> / 6 &middot; Очки: <b id="p17-iv3-sc">0</b></div>
<div id="p17-iv3-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p17-iv3-y">Да, геом.</button>
<button class="btn" id="p17-iv3-n">Нет</button>
</div>
<div class="feedback" id="p17-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p17-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр геом. прогрессии</div></div>
<div class="wg-help">Применяй $b_n = b_1 \\cdot q^{n - 1}$ и характеристическое свойство $b_n^2 = b_{n-1} \\cdot b_{n+1}$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p17-iv4-idx">1</b> / 6 &middot; Очки: <b id="p17-iv4-sc">0</b></div>
<div id="p17-iv4-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p17-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p17-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p17-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p16', 'p18') + readButton('p17');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const svg = document.getElementById('p17-iv1-svg');
const b1Sl = document.getElementById('p17-iv1-b1');
const qSl = document.getElementById('p17-iv1-q');
const bv = document.getElementById('p17-iv1-bv');
const qv = document.getElementById('p17-iv1-qv');
const formula = document.getElementById('p17-iv1-formula');
const out = document.getElementById('p17-iv1-out');
const desc = document.getElementById('p17-iv1-desc');
let bumped = false;
function fmtQ(q){
// 1/2, 1/4 — для красоты
if (Math.abs(q - 0.5) < 1e-9) return '\\dfrac{1}{2}';
if (Math.abs(q + 0.5) < 1e-9) return '-\\dfrac{1}{2}';
if (Math.abs(q - 0.25) < 1e-9) return '\\dfrac{1}{4}';
if (Math.abs(q + 0.25) < 1e-9) return '-\\dfrac{1}{4}';
if (Math.abs(q - 0.75) < 1e-9) return '\\dfrac{3}{4}';
if (Math.abs(q + 0.75) < 1e-9) return '-\\dfrac{3}{4}';
if (Math.abs(q - 1.5) < 1e-9) return '\\dfrac{3}{2}';
if (Math.abs(q + 1.5) < 1e-9) return '-\\dfrac{3}{2}';
return fmt(q);
}
function redraw(){
const b1 = +b1Sl.value;
const q = +qSl.value;
bv.textContent = b1; qv.textContent = q;
const N = 10;
const pts = [];
for (let n=1;n<=N;n++) pts.push([n, b1 * Math.pow(q, n-1)]);
// обрезаем чрезмерные значения для оси Y
const ABS_LIMIT = 200;
const ys = pts.map(p=>p[1]).filter(v=>Math.abs(v)<=ABS_LIMIT*5);
let ymin = Math.min(...ys, 0), ymax = Math.max(...ys, 0);
if (!isFinite(ymin) || !isFinite(ymax) || ymax === ymin){ ymin = -1; ymax = 1; }
const pad = Math.max(2, (ymax - ymin) * 0.15);
ymin = Math.floor(ymin - pad);
ymax = Math.ceil(ymax + pad);
const ax = axes2D(460, 300, 34, 0, N+1, ymin, ymax);
let g = ax.content;
// соединяющая ломаная (без хвостов вне зоны)
let path = '';
let prevIn = false;
pts.forEach(p=>{
const inZone = (p[1] >= ymin && p[1] <= ymax);
if (inZone){
path += (prevIn ? ' L' : ' M') + ax.toX(p[0]).toFixed(1) + ',' + ax.toY(p[1]).toFixed(1);
}
prevIn = inZone;
});
g += '<path d="'+path+'" stroke="#0891b2" stroke-width="1.5" fill="none" opacity=".55" stroke-dasharray="4 3"/>';
// точки
pts.forEach(p=>{
if (p[1] < ymin || p[1] > ymax) return;
const x = ax.toX(p[0]); const y = ax.toY(p[1]);
const color = (p[1] >= 0) ? '#0891b2' : '#ef4444';
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="5" fill="'+color+'" stroke="#fff" stroke-width="2"/>';
});
svg.innerHTML = g;
// формула
formula.innerHTML = '$b_n = ' + b1 + ' \\cdot (' + fmtQ(q) + ')^{n - 1}$';
// первые 6 членов
const first6 = pts.slice(0,6).map(p=>fmt(+p[1].toFixed(4))).join(',\\ ');
out.innerHTML = '<b>Первые 6 членов:</b> $' + first6 + ',\\ \\ldots$';
// описание поведения
let txt = '';
const aq = Math.abs(q);
if (q === 0){ txt = 'При $q = 0$ прогрессия не определена (со 2-го члена все нули).'; }
else if (aq > 1) txt = 'Модули растут (|q| > 1). ';
else if (aq < 1) txt = 'Модули убывают к 0 (|q| < 1). ';
else txt = 'Все члены по модулю равны $|b_1|$ (|q| = 1). ';
if (q < 0) txt += 'Знаки чередуются (q < 0).';
else if (q > 0) txt += 'Все члены одного знака (q > 0).';
desc.textContent = txt;
renderMath(formula); renderMath(out);
if (!bumped){ bumped = true; bumpProgress('p17', 15); addXp(10,'p17-iv1'); }
}
b1Sl.addEventListener('input', redraw);
qSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
// Найти b_n
const outA = document.getElementById('p17-iv2a-out');
let bumpedA = false;
document.getElementById('p17-iv2a-go').addEventListener('click', ()=>{
const b1 = +document.getElementById('p17-iv2a-b1').value;
const q = +document.getElementById('p17-iv2a-q').value;
const n = +document.getElementById('p17-iv2a-n').value;
if (!Number.isInteger(n) || n < 1){ outA.innerHTML = 'Номер $n$ должен быть натуральным.'; renderMath(outA); return; }
if (q === 0){ outA.innerHTML = 'Знаменатель $q$ должен быть $\\ne 0$.'; renderMath(outA); return; }
const bn = b1 * Math.pow(q, n - 1);
outA.innerHTML = '$b_{'+n+'} = '+b1+' \\cdot ('+fmt(q)+')^{'+n+' - 1} = '+b1+' \\cdot ('+fmt(q)+')^{'+(n-1)+'} = \\mathbf{'+fmt(+bn.toFixed(6))+'}$';
renderMath(outA);
if (!bumpedA){ bumpedA = true; bumpProgress('p17', 8); addXp(5,'p17-iv2a'); }
});
// Найти q
const outB = document.getElementById('p17-iv2b-out');
let bumpedB = false;
document.getElementById('p17-iv2b-go').addEventListener('click', ()=>{
const b1 = +document.getElementById('p17-iv2b-b1').value;
const bn = +document.getElementById('p17-iv2b-bn').value;
const n = +document.getElementById('p17-iv2b-n').value;
if (!Number.isInteger(n) || n < 2){ outB.innerHTML = '$n$ должно быть целым и $\\ge 2$.'; renderMath(outB); return; }
if (b1 === 0){ outB.innerHTML = 'Должно быть $b_1 \\ne 0$.'; renderMath(outB); return; }
const ratio = bn / b1;
const k = n - 1;
// действительный корень (положительный)
let qPos;
if (ratio >= 0) qPos = Math.pow(ratio, 1/k);
else if (k % 2 === 1) qPos = -Math.pow(-ratio, 1/k);
else qPos = NaN;
if (!isFinite(qPos) || isNaN(qPos)){
outB.innerHTML = '$\\dfrac{b_n}{b_1} = '+fmt(ratio)+' < 0$, а степень $n - 1 = '+k+'$ чётная — действительного $q$ нет.';
renderMath(outB); return;
}
outB.innerHTML = '$q^{'+k+'} = \\dfrac{b_n}{b_1} = \\dfrac{'+bn+'}{'+b1+'} = '+fmt(ratio)+'$ &nbsp; $\\Rightarrow$ &nbsp; $q = \\sqrt['+k+']{'+fmt(ratio)+'} = \\mathbf{'+fmt(+qPos.toFixed(6))+'}$';
renderMath(outB);
if (!bumpedB){ bumpedB = true; bumpProgress('p17', 7); addXp(5,'p17-iv2b'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$1,\\ 2,\\ 4,\\ 8,\\ \\ldots$', ans:true, hint:'Отношение $q = 2$ постоянно.' },
{ q:'$3,\\ 6,\\ 9,\\ 12,\\ \\ldots$', ans:false, hint:'Отношения $2, 1.5, \\tfrac{4}{3}$ — разные. Это арифметическая ($d = 3$).' },
{ q:'$5,\\ 5,\\ 5,\\ 5,\\ \\ldots$', ans:true, hint:'Это геом. прогрессия с $q = 1$ (и одновременно арифм. с $d = 0$).' },
{ q:'$81,\\ -27,\\ 9,\\ -3,\\ \\ldots$', ans:true, hint:'$q = -\\dfrac{1}{3}$ постоянна — знаки чередуются.' },
{ q:'$2,\\ 4,\\ 6,\\ 8,\\ \\ldots$', ans:false, hint:'Отношения $2, 1.5, \\tfrac{4}{3}$ — не равны. Это арифм. с $d = 2$.' },
{ q:'$1,\\ \\dfrac{1}{2},\\ \\dfrac{1}{4},\\ \\dfrac{1}{8},\\ \\ldots$', ans:true, hint:'$q = \\dfrac{1}{2}$ постоянна.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p17-iv3-idx');
const scEl = document.getElementById('p17-iv3-sc');
const qEl = document.getElementById('p17-iv3-q');
const fb = document.getElementById('p17-iv3-fb');
const yBtn = document.getElementById('p17-iv3-y');
const nBtn = document.getElementById('p17-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p17', 25); addXp(15,'p17-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1100);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring — тренажёр ===== */
(function(){
const items = [
{ q:'$b_1 = 2,\\ q = 3$. Найти $b_4$.', ans: 54 },
{ q:'$b_1 = 64,\\ q = \\dfrac{1}{2}$. Найти $b_5$.', ans: 4 },
{ q:'$b_1 = 5,\\ b_3 = 45$. Найти $q$ (положительный).', ans: 3 },
{ q:'$b_2 = 6,\\ b_4 = 24$. Найти $q$ (положительный).', ans: 2 },
{ q:'Найти среднее геометрическое чисел $2$ и $8$ (положительное).', ans: 4 },
{ q:'$b_1 = 3,\\ q = -2$. Найти $b_4$.', ans: -24 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p17-iv4-idx');
const scEl = document.getElementById('p17-iv4-sc');
const qEl = document.getElementById('p17-iv4-q');
const inp = document.getElementById('p17-iv4-ans');
const btn = document.getElementById('p17-iv4-go');
const fb = document.getElementById('p17-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p17', 25); addXp(15,'p17-iv4'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно: $'+it.ans+'$' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$');
i++;
setTimeout(render, 1100);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p17');
}
function buildP18(){
const box = document.getElementById('p18-body');
let html = '';
html += makeCard('theory', 'Формула суммы $n$ членов', '18.1', `
<p><b>Сумма первых $n$ членов</b> геометрической прогрессии:</p>
<p>$$S_n = \\dfrac{b_1\\,(q^n - 1)}{q - 1},\\quad q \\ne 1.$$</p>
<p>Если $q = 1$ — все члены равны $b_1$, тогда $S_n = n \\cdot b_1$.</p>
<p>Также удобная форма (через $b_n$):</p>
<p>$$S_n = \\dfrac{b_n \\cdot q - b_1}{q - 1},\\quad q \\ne 1.$$</p>`);
html += makeCard('rule', 'Вывод формулы', '18.2', `
<p>Запишем сумму:</p>
<p>$$S_n = b_1 + b_1 q + b_1 q^2 + \\ldots + b_1 q^{n - 1}. \\quad (1)$$</p>
<p>Умножим обе части на $q$:</p>
<p>$$q\\,S_n = b_1 q + b_1 q^2 + b_1 q^3 + \\ldots + b_1 q^n. \\quad (2)$$</p>
<p>Вычтем $(1)$ из $(2)$ — почти все слагаемые сократятся:</p>
<p>$$q\\,S_n - S_n = b_1 q^n - b_1\\ \\Rightarrow\\ S_n(q - 1) = b_1(q^n - 1).$$</p>
<p>При $q \\ne 1$ делим на $q - 1$ и получаем формулу.</p>`);
html += makeCard('example', 'Примеры применения', '18.3', `
<p><b>а)</b> Сумма первых 5 членов прогрессии $2, 4, 8, 16, 32$ ($b_1 = 2,\\ q = 2$):</p>
<p>$$S_5 = \\dfrac{2\\,(2^5 - 1)}{2 - 1} = 2 \\cdot 31 = \\mathbf{62}.$$</p>
<p><b>б)</b> Сумма $1 + 3 + 9 + 27 + \\ldots + 3^{10}$ — это $b_1 = 1,\\ q = 3,\\ n = 11$:</p>
<p>$$S_{11} = \\dfrac{1 \\cdot (3^{11} - 1)}{3 - 1} = \\dfrac{177146}{2} = \\mathbf{88\\,573}.$$</p>
<p><b>в)</b> Особый случай $q = -1$ для $b_1 = 1$: ряд $1, -1, 1, -1, \\ldots$ даёт $S_n = 0$ при чётном $n$ и $S_n = 1$ при нечётном.</p>`);
/* INTERACTIVE 1 — конструктор суммы */
html += `<div class="wg" id="p18-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор суммы $S_n$</div></div>
<div class="wg-help">Двигай $b_1$, $q$, $n$ — увидишь первые члены, подстановку в формулу и итог. Особый случай $q = 1$ обработан.</div>
<div class="sliders">
<label>$b_1$ =<b id="p18-iv1-bv">2</b><input type="range" id="p18-iv1-b1" min="-5" max="10" step="1" value="2"></label>
<label>$q$ =<b id="p18-iv1-qv">2</b><input type="range" id="p18-iv1-q" min="-2" max="2" step="0.5" value="2"></label>
<label>$n$ =<b id="p18-iv1-nv">5</b><input type="range" id="p18-iv1-n" min="2" max="10" step="1" value="5"></label>
</div>
<div id="p18-iv1-terms" style="text-align:center;font-size:1.0rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:8px"></div>
<div id="p18-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:8px"></div>
<div id="p18-iv1-out" style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1.0rem;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор S_n */
html += `<div class="wg" id="p18-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $S_n$</div></div>
<div class="wg-help">Введи $b_1$, $q$, $n$ — получишь $q^n$ и полную подстановку в формулу.</div>
<div style="background:var(--card);border-radius:11px;padding:14px;max-width:380px;margin:0 auto 10px">
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$b_1$ =</span><input type="number" id="p18-iv2-b1" class="tinp" value="2" step="1">
<span>$q$ =</span><input type="number" id="p18-iv2-q" class="tinp" value="3" step="0.5">
<span>$n$ =</span><input type="number" id="p18-iv2-n" class="tinp" value="5" step="1" min="1">
</div>
<button class="btn primary" id="p18-iv2-go" style="width:100%">Вычислить $S_n$</button>
</div>
<div id="p18-iv2-out" style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:8px;font-size:.95rem;min-height:40px;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — сравни формулы */
html += `<div class="wg" id="p18-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Сравни формулы: верно или ошибка?</div></div>
<div class="wg-help">Перед тобой утверждение. Реши, верное оно или содержит ошибку в применении формулы $S_n = \\dfrac{b_1(q^n - 1)}{q - 1}$.</div>
<div class="score-display">Задача: <b id="p18-iv3-idx">1</b> / 6 &middot; Очки: <b id="p18-iv3-sc">0</b></div>
<div id="p18-iv3-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p18-iv3-y">Верно</button>
<button class="btn" id="p18-iv3-n">Ошибка</button>
</div>
<div class="feedback" id="p18-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p18-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр на $S_n$</div></div>
<div class="wg-help">Вычисляй суммы по формуле $S_n = \\dfrac{b_1(q^n - 1)}{q - 1}$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p18-iv4-idx">1</b> / 6 &middot; Очки: <b id="p18-iv4-sc">0</b></div>
<div id="p18-iv4-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p18-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p18-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p18-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p17', 'p19') + readButton('p18');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const b1Sl = document.getElementById('p18-iv1-b1');
const qSl = document.getElementById('p18-iv1-q');
const nSl = document.getElementById('p18-iv1-n');
const bv = document.getElementById('p18-iv1-bv');
const qv = document.getElementById('p18-iv1-qv');
const nv = document.getElementById('p18-iv1-nv');
const termsEl = document.getElementById('p18-iv1-terms');
const fEl = document.getElementById('p18-iv1-formula');
const outEl = document.getElementById('p18-iv1-out');
let bumped = false;
function fmtQ(q){
if (Math.abs(q - 0.5) < 1e-9) return '\\dfrac{1}{2}';
if (Math.abs(q + 0.5) < 1e-9) return '-\\dfrac{1}{2}';
if (Math.abs(q - 1.5) < 1e-9) return '\\dfrac{3}{2}';
if (Math.abs(q + 1.5) < 1e-9) return '-\\dfrac{3}{2}';
return fmt(q);
}
function redraw(){
const b1 = +b1Sl.value, q = +qSl.value, n = +nSl.value;
bv.textContent = b1; qv.textContent = q; nv.textContent = n;
// первые n членов
const terms = [];
for (let i = 0; i < n; i++) terms.push(b1 * Math.pow(q, i));
const sum = terms.reduce((a,b)=>a+b, 0);
const termsStr = terms.map(t => fmt(+t.toFixed(4))).join('\\ +\\ ');
termsEl.innerHTML = '<b>Члены:</b> $' + termsStr + '$';
if (Math.abs(q - 1) < 1e-9){
fEl.innerHTML = '<b>Особый случай $q = 1$:</b> $S_n = n \\cdot b_1 = ' + n + ' \\cdot ' + b1 + ' = \\mathbf{' + (n*b1) + '}$.';
outEl.innerHTML = '$S_{' + n + '} = \\mathbf{' + (n*b1) + '}$';
} else {
const qN = Math.pow(q, n);
fEl.innerHTML = '$S_{' + n + '} = \\dfrac{b_1(q^n - 1)}{q - 1} = \\dfrac{' + b1 + '\\,((' + fmtQ(q) + ')^{' + n + '} - 1)}{' + fmtQ(q) + ' - 1}$';
outEl.innerHTML = '$q^n = ' + fmt(+qN.toFixed(4)) + ',\\quad S_{' + n + '} = \\mathbf{' + fmt(+sum.toFixed(4)) + '}$';
}
renderMath(termsEl); renderMath(fEl); renderMath(outEl);
if (!bumped){ bumped = true; bumpProgress('p18', 15); addXp(10,'p18-iv1'); }
}
b1Sl.addEventListener('input', redraw);
qSl.addEventListener('input', redraw);
nSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const out = document.getElementById('p18-iv2-out');
let bumped = false;
document.getElementById('p18-iv2-go').addEventListener('click', ()=>{
const b1 = +document.getElementById('p18-iv2-b1').value;
const q = +document.getElementById('p18-iv2-q').value;
const n = +document.getElementById('p18-iv2-n').value;
if (!Number.isInteger(n) || n < 1){ out.innerHTML = 'Число членов $n$ должно быть натуральным.'; renderMath(out); return; }
if (b1 === 0){ out.innerHTML = 'Должно быть $b_1 \\ne 0$.'; renderMath(out); return; }
if (q === 0){ out.innerHTML = 'Знаменатель $q$ должен быть $\\ne 0$.'; renderMath(out); return; }
if (Math.abs(q - 1) < 1e-9){
out.innerHTML = '$q = 1$: $S_n = n \\cdot b_1 = ' + n + ' \\cdot ' + b1 + ' = \\mathbf{' + (n*b1) + '}$';
renderMath(out);
} else {
const qN = Math.pow(q, n);
const S = b1 * (qN - 1) / (q - 1);
out.innerHTML = '$q^n = ' + fmt(q) + '^{' + n + '} = ' + fmt(+qN.toFixed(6))
+ '$<br/>$S_n = \\dfrac{b_1(q^n - 1)}{q - 1} = \\dfrac{' + b1 + '\\,(' + fmt(+qN.toFixed(6)) + ' - 1)}{' + fmt(q) + ' - 1} = \\mathbf{' + fmt(+S.toFixed(6)) + '}$';
renderMath(out);
}
if (!bumped){ bumped = true; bumpProgress('p18', 15); addXp(10,'p18-iv2'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$1 + 2 + 4 + 8 = \\dfrac{1 \\cdot (2^4 - 1)}{2 - 1} = 15$',
ans:true, hint:'$b_1 = 1,\\ q = 2,\\ n = 4$: $\\dfrac{16 - 1}{1} = 15$. Верно.' },
{ q:'$1 + 3 + 9 + 27 = \\dfrac{1 \\cdot (3^4 - 1)}{2} = 40$',
ans:true, hint:'$b_1 = 1,\\ q = 3,\\ n = 4$: $\\dfrac{81 - 1}{2} = 40$. Верно.' },
{ q:'Для $q \\ne 1$ верно $S_n = \\dfrac{b_1\\,q^n}{q - 1}$',
ans:false, hint:'Ошибка: в числителе должно быть $q^n - 1$, а не просто $q^n$.' },
{ q:'$5 + 5 + 5 + 5 + 5 = 5 \\cdot 5 = 25$ (при $q = 1$)',
ans:true, hint:'При $q = 1$ формула $S_n = n \\cdot b_1 = 5 \\cdot 5 = 25$. Верно.' },
{ q:'Для $q \\ne 1$ верно $S_3 = b_1 + b_1 q + b_1 q^2 = 3 b_1$',
ans:false, hint:'Сумма равна $3 b_1$ только при $q = 1$. В общем случае это $b_1(1 + q + q^2)$.' },
{ q:'$1 - 2 + 4 - 8 + 16 = \\dfrac{1 \\cdot ((-2)^5 - 1)}{-2 - 1} = \\dfrac{-33}{-3} = 11$',
ans:true, hint:'$b_1 = 1,\\ q = -2,\\ n = 5$: $(-2)^5 = -32$, $\\dfrac{-33}{-3} = 11$. Верно.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p18-iv3-idx');
const scEl = document.getElementById('p18-iv3-sc');
const qEl = document.getElementById('p18-iv3-q');
const fb = document.getElementById('p18-iv3-fb');
const yBtn = document.getElementById('p18-iv3-y');
const nBtn = document.getElementById('p18-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p18', 25); addXp(15,'p18-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1200);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring ===== */
(function(){
const items = [
{ q:'Найди $1 + 2 + 4 + 8 + 16$.', ans: 31 },
{ q:'$b_1 = 3,\\ q = 2$. Найти $S_5$.', ans: 93 },
{ q:'Найди $1 + 3 + 9 + 27$.', ans: 40 },
{ q:'$b_1 = 4,\\ q = -1$. Найти $S_5$.', ans: 4 },
{ q:'$b_1 = 8,\\ q = \\dfrac{1}{2}$. Найти $S_4$.', ans: 15 },
{ q:'Геом. прогрессия $1, 4, 16, \\ldots, 256$. Найти сумму всех её членов.', ans: 341 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p18-iv4-idx');
const scEl = document.getElementById('p18-iv4-sc');
const qEl = document.getElementById('p18-iv4-q');
const inp = document.getElementById('p18-iv4-ans');
const btn = document.getElementById('p18-iv4-go');
const fb = document.getElementById('p18-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p18', 25); addXp(15,'p18-iv4'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно: $'+it.ans+'$' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$');
i++;
setTimeout(render, 1100);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p18');
}
function buildP19(){
const box = document.getElementById('p19-body');
let html = '';
html += makeCard('theory', 'Когда прогрессия бесконечно убывающая', '19.1', `
<p>Геометрическая прогрессия называется <b>бесконечно убывающей</b>, если её знаменатель удовлетворяет:</p>
<p>$$|q| < 1\\quad\\text{и}\\quad q \\ne 0.$$</p>
<p>При $|q| < 1$ члены $b_n = b_1 q^{n - 1}$ по модулю <b>стремятся к нулю</b> с ростом $n$.</p>
<p><b>Пример.</b> Прогрессия $1, \\dfrac{1}{2}, \\dfrac{1}{4}, \\dfrac{1}{8}, \\ldots$ имеет $q = \\dfrac{1}{2}$, $|q| < 1$ — бесконечно убывающая.</p>`);
html += makeCard('rule', 'Формула суммы $S$', '19.2', `
<p><b>Сумма всех членов</b> бесконечно убывающей геом. прогрессии:</p>
<p>$$S = \\dfrac{b_1}{1 - q},\\quad |q| < 1.$$</p>
<p><b>Откуда это?</b> Это <b>предел</b> $S_n$ при $n \\to \\infty$. Действительно,</p>
<p>$$S_n = \\dfrac{b_1\\,(q^n - 1)}{q - 1}.$$</p>
<p>При $|q| < 1$ имеем $q^n \\to 0$, поэтому</p>
<p>$$S = \\lim_{n \\to \\infty} S_n = \\dfrac{b_1 \\cdot (0 - 1)}{q - 1} = \\dfrac{-b_1}{q - 1} = \\dfrac{b_1}{1 - q}.$$</p>`);
html += makeCard('example', 'Примеры', '19.3', `
<p><b>а)</b> $1 + \\dfrac{1}{2} + \\dfrac{1}{4} + \\dfrac{1}{8} + \\ldots = \\dfrac{1}{1 - \\tfrac{1}{2}} = \\mathbf{2}.$</p>
<p><b>б)</b> $1 - \\dfrac{1}{3} + \\dfrac{1}{9} - \\dfrac{1}{27} + \\ldots = \\dfrac{1}{1 - (-\\tfrac{1}{3})} = \\dfrac{1}{\\tfrac{4}{3}} = \\mathbf{\\dfrac{3}{4}}.$</p>
<p><b>в) Знаменитое равенство $0{,}(9) = 1$.</b> Действительно:</p>
<p>$$0{,}999\\ldots = 0{,}9 + 0{,}09 + 0{,}009 + \\ldots = \\dfrac{0{,}9}{1 - 0{,}1} = \\dfrac{0{,}9}{0{,}9} = \\mathbf{1}.$$</p>
<p><b>г) Превращение периодической дроби в обыкновенную:</b></p>
<p>$$0{,}(3) = 0{,}3 + 0{,}03 + 0{,}003 + \\ldots = \\dfrac{0{,}3}{1 - 0{,}1} = \\dfrac{0{,}3}{0{,}9} = \\mathbf{\\dfrac{1}{3}}.$$</p>`);
/* INTERACTIVE 1 — визуализация сходимости */
html += `<div class="wg" id="p19-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Визуализация сходимости</div></div>
<div class="wg-help">Двигай $b_1$ и $q$ (при $|q| < 1$). На диаграмме — столбики первых 15 членов и нарастающая сумма, которая стремится к $S = \\dfrac{b_1}{1 - q}$.</div>
<div class="sliders">
<label>$b_1$ =<b id="p19-iv1-bv">4</b><input type="range" id="p19-iv1-b1" min="-10" max="10" step="1" value="4"></label>
<label>$q$ =<b id="p19-iv1-qv">0.5</b><input type="range" id="p19-iv1-q" min="-0.95" max="0.95" step="0.05" value="0.5"></label>
</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p19-iv1-svg" viewBox="0 0 480 280" style="width:100%;max-width:600px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p19-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-top:8px"></div>
<div id="p19-iv1-out" style="margin-top:8px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор S */
html += `<div class="wg" id="p19-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $S = \\dfrac{b_1}{1 - q}$</div></div>
<div class="wg-help">$q$ задаётся обыкновенной дробью — введи числитель и знаменатель отдельно.</div>
<div style="background:var(--card);border-radius:11px;padding:14px;max-width:380px;margin:0 auto 10px">
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$b_1$ =</span><input type="number" id="p19-iv2-b1" class="tinp" value="1" step="1">
<span>$q$ числитель =</span><input type="number" id="p19-iv2-qn" class="tinp" value="1" step="1">
<span>$q$ знаменатель =</span><input type="number" id="p19-iv2-qd" class="tinp" value="2" step="1">
</div>
<button class="btn primary" id="p19-iv2-go" style="width:100%">Найти $S$</button>
</div>
<div id="p19-iv2-out" style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:8px;font-size:.95rem;min-height:40px;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — можно ли применить формулу */
html += `<div class="wg" id="p19-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Можно ли применить формулу $\\dfrac{b_1}{1 - q}$?</div></div>
<div class="wg-help">Формула работает только при $|q| < 1$ и $q \\ne 0$. Реши, подходит ли данная прогрессия.</div>
<div class="score-display">Задача: <b id="p19-iv3-idx">1</b> / 6 &middot; Очки: <b id="p19-iv3-sc">0</b></div>
<div id="p19-iv3-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p19-iv3-y">Да, $|q| < 1$</button>
<button class="btn" id="p19-iv3-n">Нет</button>
</div>
<div class="feedback" id="p19-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p19-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр сумм $S = \\dfrac{b_1}{1 - q}$</div></div>
<div class="wg-help">В каждой задаче указано, что вводить (целое значение, или числитель / знаменатель ответа).</div>
<div class="score-display">Задача: <b id="p19-iv4-idx">1</b> / 6 &middot; Очки: <b id="p19-iv4-sc">0</b></div>
<div id="p19-iv4-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p19-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p19-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p19-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p18', 'final4') + readButton('p19');
renderMath(box);
/* ===== IV1 wiring — визуализация ===== */
(function(){
const svg = document.getElementById('p19-iv1-svg');
const b1Sl = document.getElementById('p19-iv1-b1');
const qSl = document.getElementById('p19-iv1-q');
const bv = document.getElementById('p19-iv1-bv');
const qv = document.getElementById('p19-iv1-qv');
const fEl = document.getElementById('p19-iv1-formula');
const outEl = document.getElementById('p19-iv1-out');
let bumped = false;
function redraw(){
const b1 = +b1Sl.value, q = +qSl.value;
bv.textContent = b1; qv.textContent = q.toFixed(2);
const N = 15;
const terms = [];
const partial = [];
let s = 0;
for (let i = 0; i < N; i++){
const t = b1 * Math.pow(q, i);
terms.push(t); s += t; partial.push(s);
}
const Sinf = (Math.abs(q) < 1 - 1e-9) ? b1 / (1 - q) : NaN;
// SVG: верх — столбики членов, низ — частичные суммы
const W = 480, H = 280, pad = 22;
const barAreaH = 130;
const partAreaTop = 160;
const partAreaH = 100;
const ux = (W - 2*pad) / N;
// вертикальный масштаб для членов
const tmax = Math.max(...terms.map(Math.abs), 1e-6);
const tScale = (barAreaH / 2 - 6) / tmax;
const tMid = pad + barAreaH / 2;
// масштаб для частичных
const allP = partial.concat([Sinf||0, 0]);
let pMin = Math.min(...allP), pMax = Math.max(...allP);
if (pMax === pMin){ pMin -= 1; pMax += 1; }
const padP = (pMax - pMin) * 0.15;
pMin -= padP; pMax += padP;
const pScale = (partAreaH - 12) / (pMax - pMin);
const pToY = v => partAreaTop + 6 + (pMax - v) * pScale;
let g = '';
// фон областей
g += '<rect x="'+pad+'" y="'+pad+'" width="'+(W-2*pad)+'" height="'+barAreaH+'" fill="#f8fafc" stroke="#e5e7eb"/>';
g += '<rect x="'+pad+'" y="'+partAreaTop+'" width="'+(W-2*pad)+'" height="'+partAreaH+'" fill="#f8fafc" stroke="#e5e7eb"/>';
// средняя линия для членов (ось 0)
g += '<line x1="'+pad+'" y1="'+tMid+'" x2="'+(W-pad)+'" y2="'+tMid+'" stroke="#94a3b8" stroke-width="1"/>';
g += '<text x="'+(pad+4)+'" y="'+(pad+12)+'" font-size="10" fill="#64748b">Члены $b_n$</text>';
g += '<text x="'+(pad+4)+'" y="'+(partAreaTop+12)+'" font-size="10" fill="#64748b">Частичные суммы $S_n$</text>';
// столбики членов
const barW = ux * 0.7;
for (let i = 0; i < N; i++){
const x = pad + ux * (i + 0.15);
const h = Math.abs(terms[i]) * tScale;
const y = terms[i] >= 0 ? tMid - h : tMid;
const color = terms[i] >= 0 ? '#0891b2' : '#ef4444';
g += '<rect x="'+x.toFixed(1)+'" y="'+y.toFixed(1)+'" width="'+barW.toFixed(1)+'" height="'+h.toFixed(1)+'" fill="'+color+'" opacity=".75"/>';
}
// линия Sinf
if (isFinite(Sinf)){
const ySinf = pToY(Sinf);
g += '<line x1="'+pad+'" y1="'+ySinf+'" x2="'+(W-pad)+'" y2="'+ySinf+'" stroke="#16a34a" stroke-width="1.5" stroke-dasharray="5 3"/>';
g += '<text x="'+(W-pad-4)+'" y="'+(ySinf-4)+'" font-size="10" fill="#16a34a" text-anchor="end">$S = '+fmt(+Sinf.toFixed(4))+'$</text>';
}
// линия 0 в нижней области
const yZero = pToY(0);
if (yZero >= partAreaTop && yZero <= partAreaTop + partAreaH){
g += '<line x1="'+pad+'" y1="'+yZero+'" x2="'+(W-pad)+'" y2="'+yZero+'" stroke="#94a3b8" stroke-width="1"/>';
}
// точки и ломаная частичных сумм
let path = '';
for (let i = 0; i < N; i++){
const x = pad + ux * (i + 0.5);
const y = pToY(partial[i]);
path += (i === 0 ? 'M' : ' L') + x.toFixed(1) + ',' + y.toFixed(1);
}
g += '<path d="'+path+'" stroke="#7c3aed" stroke-width="2" fill="none"/>';
for (let i = 0; i < N; i++){
const x = pad + ux * (i + 0.5);
const y = pToY(partial[i]);
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="3" fill="#7c3aed"/>';
}
svg.innerHTML = g;
// формула
if (isFinite(Sinf)){
fEl.innerHTML = '$S = \\dfrac{b_1}{1 - q} = \\dfrac{' + b1 + '}{1 - (' + q.toFixed(2) + ')} = \\mathbf{' + fmt(+Sinf.toFixed(4)) + '}$';
// частичные S_1..S_10
const ps = partial.slice(0, 10).map((s,i)=>'$S_{'+(i+1)+'}\\!=\\!'+fmt(+s.toFixed(3))+'$').join(',\\ ');
outEl.innerHTML = '<b>Частичные суммы:</b> ' + ps + ' &mdash; стремятся к $S$.';
} else {
fEl.innerHTML = '<b>$|q| \\ge 1$:</b> прогрессия не является бесконечно убывающей.';
outEl.innerHTML = '<b>Частичные суммы расходятся</b> — формула $\\dfrac{b_1}{1 - q}$ не применима.';
}
renderMath(fEl); renderMath(outEl);
if (!bumped){ bumped = true; bumpProgress('p19', 15); addXp(10,'p19-iv1'); }
}
b1Sl.addEventListener('input', redraw);
qSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const out = document.getElementById('p19-iv2-out');
let bumped = false;
document.getElementById('p19-iv2-go').addEventListener('click', ()=>{
const b1 = +document.getElementById('p19-iv2-b1').value;
const qn = +document.getElementById('p19-iv2-qn').value;
const qd = +document.getElementById('p19-iv2-qd').value;
if (qd === 0){ out.innerHTML = 'Знаменатель $q$ не может быть нулём.'; renderMath(out); return; }
if (b1 === 0){ out.innerHTML = 'Должно быть $b_1 \\ne 0$.'; renderMath(out); return; }
const q = qn / qd;
if (q === 0){ out.innerHTML = '$q$ должен быть отличен от $0$.'; renderMath(out); return; }
if (Math.abs(q) >= 1){
out.innerHTML = '$q = \\dfrac{'+qn+'}{'+qd+'} = '+fmt(q)+'$. <b>$|q| \\ge 1$</b> — прогрессия не бесконечно убывающая!';
renderMath(out); return;
}
// S = b1 / (1 - q) = b1 * qd / (qd - qn)
const num = b1 * qd;
const den = qd - qn;
const g = gcd(num, den);
const sNum = num / g, sDen = den / g;
// знак в числитель
let nn = sNum, dd = sDen;
if (dd < 0){ nn = -nn; dd = -dd; }
const S = b1 / (1 - q);
let frac;
if (dd === 1) frac = '' + nn;
else frac = '\\dfrac{' + nn + '}{' + dd + '}';
out.innerHTML = '$q = \\dfrac{'+qn+'}{'+qd+'},\\ |q| < 1$ &nbsp;&#10003;<br/>$S = \\dfrac{b_1}{1 - q} = \\dfrac{'+b1+'}{1 - \\tfrac{'+qn+'}{'+qd+'}} = \\dfrac{'+b1+' \\cdot '+qd+'}{'+qd+' - ('+qn+')} = \\mathbf{'+frac+'} \\approx '+fmt(+S.toFixed(4))+'$';
renderMath(out);
if (!bumped){ bumped = true; bumpProgress('p19', 15); addXp(10,'p19-iv2'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$b_1 = 1,\\ q = \\dfrac{1}{2}$', ans:true, hint:'$|q| = \\tfrac{1}{2} < 1$ — формула применима.' },
{ q:'$b_1 = 1,\\ q = 2$', ans:false, hint:'$|q| = 2 > 1$ — прогрессия расходится, формула неприменима.' },
{ q:'$b_1 = 5,\\ q = -\\dfrac{1}{3}$', ans:true, hint:'$|q| = \\tfrac{1}{3} < 1$ — формула применима.' },
{ q:'$b_1 = 100,\\ q = 0{,}99$', ans:true, hint:'$|q| = 0{,}99 < 1$ — формула применима (сходимость медленная, но есть).' },
{ q:'$b_1 = 1,\\ q = -1$', ans:false, hint:'$|q| = 1$ — граница, формула неприменима ($1 - 1 + 1 - 1 + \\ldots$ не сходится).' },
{ q:'$b_1 = 1,\\ q = 1$', ans:false, hint:'$|q| = 1$ — это вообще не убывающая прогрессия ($1 + 1 + 1 + \\ldots = \\infty$).' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p19-iv3-idx');
const scEl = document.getElementById('p19-iv3-sc');
const qEl = document.getElementById('p19-iv3-q');
const fb = document.getElementById('p19-iv3-fb');
const yBtn = document.getElementById('p19-iv3-y');
const nBtn = document.getElementById('p19-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p19', 25); addXp(15,'p19-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1200);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring ===== */
(function(){
const items = [
{ q:'$1 + \\dfrac{1}{2} + \\dfrac{1}{4} + \\ldots = \\ ?$ Введи целое значение $S$.', ans: 2, expl:'$S = \\dfrac{1}{1 - 1/2} = 2$.' },
{ q:'$4 + 2 + 1 + \\dfrac{1}{2} + \\ldots = \\ ?$ Введи целое значение $S$.', ans: 8, expl:'$b_1 = 4,\\ q = \\tfrac{1}{2}$: $S = \\dfrac{4}{1 - 1/2} = 8$.' },
{ q:'$1 - \\dfrac{1}{2} + \\dfrac{1}{4} - \\ldots = \\dfrac{2}{?}$ Введи знаменатель.', ans: 3, expl:'$S = \\dfrac{1}{1 + 1/2} = \\dfrac{2}{3}$.' },
{ q:'$0{,}(6) = 0{,}666\\ldots = \\dfrac{2}{?}$ Введи знаменатель.', ans: 3, expl:'$S = \\dfrac{0{,}6}{1 - 0{,}1} = \\dfrac{0{,}6}{0{,}9} = \\dfrac{2}{3}$.' },
{ q:'$9 + 3 + 1 + \\dfrac{1}{3} + \\ldots = \\dfrac{?}{2}$ Введи числитель.', ans: 27, expl:'$S = \\dfrac{9}{1 - 1/3} = \\dfrac{9 \\cdot 3}{2} = \\dfrac{27}{2}$.' },
{ q:'$0{,}(12) = 0{,}1212\\ldots = \\dfrac{4}{?}$ Введи знаменатель.', ans: 33, expl:'$S = \\dfrac{0{,}12}{1 - 0{,}01} = \\dfrac{12}{99} = \\dfrac{4}{33}$.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p19-iv4-idx');
const scEl = document.getElementById('p19-iv4-sc');
const qEl = document.getElementById('p19-iv4-q');
const inp = document.getElementById('p19-iv4-ans');
const btn = document.getElementById('p19-iv4-go');
const fb = document.getElementById('p19-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p19', 25); addXp(15,'p19-iv4'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok ? '&#10003; Верно: $'+it.ans+'$. ' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$. ') + it.expl);
i++;
setTimeout(render, 1300);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p19');
}
function buildFinal4(){
const box = document.getElementById('final4-body');
let html = '';
/* Часть А — Шпаргалка главы (6 mini-карточек) */
html += `<div class="card">
<div class="card-header">
<span class="card-icon theory">${ICONS.theory}</span>
<span class="card-title">Шпаргалка главы 4</span>
<span class="card-num">Итог</span>
</div>
<div class="card-body">
<p>Все ключевые формулы главы — в одном месте. Просмотри перед боссами!</p>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:10px">
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><circle cx="6" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="18" cy="12" r="2"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 14 · Последовательность</div>
</div>
<div style="font-size:.95rem">$a_n = f(n)$. Способы: формула, рекуррентно, таблица. Возрастает / убывает / ни то, ни другое.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><path d="M3 17l4-4 4 4 4-8 6 8"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 15 · Арифм. прогрессия</div>
</div>
<div style="font-size:.95rem">$a_n = a_1 + (n-1)d$. Разность $d = a_{n+1} - a_n$. Среднее: $a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}$.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><path d="M4 20V8M10 20V4M16 20V12M22 20H2"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 16 · Сумма арифм.</div>
</div>
<div style="font-size:.95rem">$S_n = \\dfrac{a_1 + a_n}{2} \\cdot n = \\dfrac{2a_1 + (n-1)d}{2} \\cdot n$. <b>Идея Гаусса</b>: пары с одинаковой суммой.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><path d="M3 21c2-6 6-12 18-18"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 17 · Геом. прогрессия</div>
</div>
<div style="font-size:.95rem">$b_n = b_1 q^{n-1}$. Знаменатель $q = \\dfrac{b_{n+1}}{b_n}$. Среднее: $b_n^2 = b_{n-1} \\cdot b_{n+1}$.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><path d="M4 4h16M4 10h16M4 16h10"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 18 · Сумма геом.</div>
</div>
<div style="font-size:.95rem">$S_n = \\dfrac{b_1(q^n - 1)}{q - 1}$ при $q \\ne 1$. При $q = 1$: $S_n = n \\cdot b_1$.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><path d="M3 12c4-8 14-8 18 0M3 12c4 8 14 8 18 0"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 19 · Беск. убывающая</div>
</div>
<div style="font-size:.95rem">При $|q| < 1$: $S = \\dfrac{b_1}{1 - q}$. Превращает периодические дроби в обыкновенные!</div>
</div>
</div>
</div>
</div>`;
/* Часть Б — 5 боссов */
html += `<div class="card">
<div class="card-header">
<span class="card-icon rule">${ICONS.rule}</span>
<span class="card-title">Боссы главы 4</span>
<span class="card-num">5</span>
</div>
<div class="card-body">
<p>5 интегрированных задач. Каждая комбинирует несколько тем главы 4. За каждого побеждённого босса — <b>+10 XP</b>. Победишь всех — <b>+50 XP бонус</b> и ачивка «Магистр прогрессий»!</p>
</div>
</div>`;
html += '<div id="ch4-bosses-container"></div>';
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch4-final-summary">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
<div id="ch4-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>
<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">
<div id="ch4-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#7c3aed,#a78bfa);transition:width .35s"></div>
</div>
<div id="ch4-final-reward" style="margin-top:14px;display:none;padding:16px;background:var(--card);border-radius:11px;border:2px solid #f59e0b">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:#92400e;font-size:1.15rem;margin-bottom:6px">Магистр прогрессий</div>
<div style="font-size:.95rem;margin-bottom:8px"><b>Алгебра 9 пройдена!</b> Все 4 главы изучены. Готовы к Геометрии 9!</div>
<div style="font-size:.9rem;color:var(--muted);margin-bottom:12px">+50 XP бонус начислены.</div>
<a class="btn primary" href="/textbook/algebra-9" style="text-decoration:none">К хабу Алгебры 9 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
</div>
</div>`;
html += secNav('p19', null);
box.innerHTML = html;
renderMath(box);
/* Боссы */
const BOSSES = [
{
n:1, color:'#10b981',
title:'Гном Арифметики',
tag:'§ 15 + § 16',
q:'В арифметической прогрессии $a_1 = 3$, $d = 4$. Найди $S_{10}$.',
ans:210,
hint:'$a_{10} = a_1 + 9d = 3 + 9 \\cdot 4 = 39$. Тогда $S_{10} = \\dfrac{a_1 + a_{10}}{2} \\cdot 10 = \\dfrac{3 + 39}{2} \\cdot 10 = \\mathbf{210}$.'
},
{
n:2, color:'#0891b2',
title:'Эльф Геометрии',
tag:'§ 17 + § 18',
q:'В геометрической прогрессии $b_1 = 2$, $q = 3$. Найди $S_5$.',
ans:242,
hint:'$S_5 = \\dfrac{b_1(q^5 - 1)}{q - 1} = \\dfrac{2(243 - 1)}{3 - 1} = \\dfrac{2 \\cdot 242}{2} = \\mathbf{242}$.'
},
{
n:3, color:'#7c3aed',
title:'Орк Сходимости',
tag:'§ 19 — синтез',
q:'Преврати периодическую дробь $0{,}\\overline{18}$ в обыкновенную и введи <b>знаменатель</b> несократимой дроби.',
ans:11,
hint:'$0{,}\\overline{18} = \\dfrac{0{,}18}{1 - 0{,}01} = \\dfrac{18}{99} = \\dfrac{2}{11}$. Знаменатель: $\\mathbf{11}$.'
},
{
n:4, color:'#dc2626',
title:'Маг Двух Прогрессий',
tag:'§ 15 + § 17',
q:'Числа $x, y, z$ — арифм. прогрессия; числа $x, y-1, z$ — геом. прогрессия. Если $x + y + z = 12$, найди $y$.',
ans:4,
hint:'Из арифм.: $y = \\dfrac{x + z}{2} \\Rightarrow x + z = 2y$. Тогда $x + y + z = 3y = 12 \\Rightarrow y = \\mathbf{4}$. (Проверка геом.: $(y-1)^2 = xz$, т.е. $9 = xz$ с $x+z = 8$ — корни $t^2 - 8t + 9 = 0$, всё ок.)'
},
{
n:5, color:'#f59e0b',
title:'Архимаг Прогрессий',
tag:'§§ 1419 — синтез',
q:'В геометрической прогрессии $b_1 = 16$, $q = \\dfrac{1}{2}$. Найди <b>сумму</b> бесконечно убывающей прогрессии.',
ans:32,
hint:'$|q| = \\dfrac{1}{2} < 1$ — формула работает. $S = \\dfrac{b_1}{1 - q} = \\dfrac{16}{1 - 1/2} = \\dfrac{16}{1/2} = \\mathbf{32}$.'
},
];
const cont = document.getElementById('ch4-bosses-container');
const STATE_KEY = 'algebra9_ch4_bosses';
const BOSS_STATE = (function(){
try{ const s = localStorage.getItem(STATE_KEY); if(s) return JSON.parse(s); }catch(e){}
return BOSSES.map(()=>({defeated:false}));
})();
function saveBosses(){ try{ localStorage.setItem(STATE_KEY, JSON.stringify(BOSS_STATE)); }catch(e){} }
cont.innerHTML = BOSSES.map((b, idx)=>{
return '<div class="boss-card" id="boss4-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
+'<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
+'</div>'
+'<div id="boss4-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
+'<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
+'<input type="number" id="boss4-'+b.n+'-ans" class="tinp" style="width:120px;text-align:center" placeholder="число">'
+'<button class="btn primary" id="boss4-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
+'<button class="btn" id="boss4-'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="feedback" id="boss4-'+b.n+'-fb"></div>'
+'</div>';
}).join('');
renderMath(cont);
function refreshOverall(){
const won = BOSS_STATE.filter(s => s.defeated).length;
const txt = document.getElementById('ch4-boss-overall');
const fill = document.getElementById('ch4-boss-overall-fill');
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
if(won >= BOSSES.length){
const reward = document.getElementById('ch4-final-reward');
if(reward && reward.style.display === 'none'){
reward.style.display = 'block';
if(!STATE.achievements.has('ch4_done')){
achievement('ch4_done','Магистр прогрессий');
addXp(50, 'ch4-bonus');
bumpProgress('final4', 30);
if(window.confetti){ try{ confetti(); }catch(e){} }
}
}
}
}
BOSSES.forEach((b, idx)=>{
const card = document.getElementById('boss4-'+b.n+'-card');
const goBtn = document.getElementById('boss4-'+b.n+'-go');
const hintBtn = document.getElementById('boss4-'+b.n+'-hint');
const ansInp = document.getElementById('boss4-'+b.n+'-ans');
if(BOSS_STATE[idx].defeated){
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
ansInp.disabled = true;
}
goBtn.addEventListener('click', ()=>{
if(BOSS_STATE[idx].defeated) return;
const fb = document.getElementById('boss4-'+b.n+'-fb');
const val = parseInt(ansInp.value, 10);
if(isNaN(val)){ feedback(fb, false, '&#10007; Введи целое число.'); return; }
if(val === b.ans){
BOSS_STATE[idx].defeated = true; saveBosses();
feedback(fb, true, '&#10003; Босс '+b.n+' повержен! +10 XP. '+b.hint);
addXp(10, 'boss-ch4-'+b.n);
bumpProgress('final4', 18);
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
ansInp.disabled = true;
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
refreshOverall();
} else {
feedback(fb, false, '&#10007; Промах. Попробуй ещё. Подсказка доступна.');
}
});
hintBtn.addEventListener('click', ()=>{
const fb = document.getElementById('boss4-'+b.n+'-fb');
fb.className = 'feedback ok';
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
fb.style.display = 'block';
fb.style.background = 'var(--warn-bg)';
fb.style.color = '#92400e';
fb.style.borderLeftColor = 'var(--warn)';
renderMath(fb);
});
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
});
refreshOverall();
}
/* ===== Search ===== */
const SEARCH_INDEX = (function(){
const arr=[];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
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(); } });
}
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(); });
}
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo('p14');
setTimeout(()=>achievement('start'), 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);
</script>
</body>
</html>