Files
Learn_System/frontend/textbooks/algebra_8_ch2.html
T
Maxim Dolgolyov 2c8eb84c65 feat(algebra-8): Chapter 2 Wave 1 — skeleton + §7 + §8
§7 «Квадратные уравнения. Неполные»:
- Теория, правила, алгоритм, примеры
- INTERACT 1: Конструктор уравнения (3 слайдера a/b/c, live-расчёт типа и корней)
- INTERACT 2: Сортировка 8 уравнений по 3 типам (полное/неполное/не квадратное)
- INTERACT 3: Пошаговый решатель неполных (2 типа: bc=0, ac=0)
- INTERACT 4: Задача про страницу книги (165 см²)
- INTERACT 5: Тренажёр 10 неполных с таймером
- INTERACT 6: «Имеет ли корни?» — 8 раундов на знаки

§8 «Формулы корней. Дискриминант»:
- Вывод формулы, таблица 3 случаев, алгоритм 5 шагов
- INTERACT 1: Калькулятор дискриминанта (пошагово)
- INTERACT 2: SVG-парабола (a, b, c слайдеры) + точки пересечения с OX
- INTERACT 3: 3 случая D (>0, =0, <0) с мини-графиками
- INTERACT 4: Тренажёр «сколько корней?» 10 уравнений
- INTERACT 5: Пошаговый решатель полного квадратного
- INTERACT 6: «Угадай знак D» только по графику параболы

Скелет: 6 параграфов (§7-§12) + финал, цвета по секциям, LocalStorage,
12 достижений, hero-прогресс, KaTeX, тёмная тема.

§§9-12 + final — stubs до следующих волн.
2026-05-27 14:42:07 +03:00

1440 lines
89 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 name="viewport" content="width=device-width,initial-scale=1.0">
<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>Алгебра 8 · Глава 2 · Квадратные уравнения</title>
<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>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=Manrope:wght@400;500;600;700;800&family=Unbounded:wght@400;700;800;900&display=swap" rel="stylesheet">
<style>
:root{
--pri:#e91e63; --pri2:#c2185b; --pri-soft:#fce7f3;
--acc:#03a9f4; --acc2:#0288d1; --acc-soft:#e0f4ff;
--ok:#10b981; --ok-bg:#d1fae5;
--fail:#ef4444; --fail-bg:#fee2e2;
--warn:#f59e0b; --warn-bg:#fef3c7;
--bg:#fdf2f8; --card:#fff;
--text:#1a1a2e; --muted:#6b5b73;
--border:#fce7f3;
--sh:0 2px 12px rgba(233,30,99,.07);
--sh2:0 6px 24px rgba(233,30,99,.10);
}
.dark{
--bg:#1a0f1a; --card:#2a1929;
--text:#f5e6f0; --muted:#b0a0b0;
--border:#5a2a5a; --pri-soft:#4a1a3a;
--acc-soft:#1a3a4a;
--sh:0 2px 12px rgba(0,0,0,.4);
--sh2:0 6px 24px rgba(0,0,0,.5);
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%}
body{font-family:'Inter','Manrope',system-ui,sans-serif;background:var(--bg);color:var(--text);display:flex;flex-direction:column;min-height:100vh;line-height:1.5;transition:background .25s,color .25s}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
input,select,textarea{font-family:inherit}
.ic{width:1em;height:1em;display:inline-block;vertical-align:-.125em;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* HEADER */
.hdr{position:relative;background:linear-gradient(110deg,#c2185b 0%,#e91e63 55%,#ec407a 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(255,180,210,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 2';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,220,235,.08);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}
.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 */
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
/* HERO */
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero::before{content:'ax² + bx + c = 0';position:absolute;right:-10px;top:-20px;font-family:'JetBrains Mono',monospace;font-size:clamp(2rem,8vw,5.5rem);font-weight:900;color:var(--pri);opacity:.08;line-height:1;pointer-events:none}
.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(233,30,99,.28)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.7rem;font-weight:700;color:var(--pri2);text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px;display:block}
.hp-bar{height:8px;background:rgba(233,30,99,.12);border-radius:6px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:6px;width:0%;transition:width .4s}
.hp-text{font-size:.72rem;color:var(--muted);margin-top:3px;display:block}
/* PARA SELECTOR */
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
.psel-name{font-size:.88rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(233,30,99,.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,#fce7f3)}
.dark .psel-card.final{background:linear-gradient(135deg,#3a2818,#4a1a3a)}
.psel-card.final .psel-num{color:var(--warn)}
/* SECTION COLORS */
.sec[id="sec-p7"] { --sec-acc:#f43f5e; --sec-acc-d:#be123c; --sec-acc-soft:#ffe4e6; }
.sec[id="sec-p8"] { --sec-acc:#06b6d4; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p9"] { --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p10"] { --sec-acc:#6366f1; --sec-acc-d:#4338ca; --sec-acc-soft:#e0e7ff; }
.sec[id="sec-p11"] { --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p12"] { --sec-acc:#a21caf; --sec-acc-d:#86198f; --sec-acc-soft:#fae8ff; }
.sec[id="sec-final2"] { --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
/* SECTIONS */
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.7rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
/* CARDS */
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(233,30,99,.06);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(233,30,99,.12)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff;outline:2px solid var(--sec-acc-soft,transparent);outline-offset:1px}
.card-icon.repeat{background:#0ea5e9}
.card-icon.theory{background:#8b5cf6}
.card-icon.algo{background:#f59e0b}
.card-icon.rule{background:#ec4899}
.card-icon.example{background:#10b981}
.card-icon.oral{background:#06b6d4}
.card-icon.class{background:#3b82f6}
.card-icon.home{background:#f97316}
.card-icon.prev{background:#6366f1}
.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:.95rem;line-height:1.65}
.card-body p{margin-bottom:10px}
.card-body p:last-child{margin-bottom:0}
.card-body b{color:var(--sec-acc-d,var(--pri2));font-weight:700}
.card-body ul,.card-body ol{padding-left:22px;margin:8px 0}
.card-body li{margin-bottom:4px}
.formula-box{background:var(--sec-acc-soft,var(--pri-soft));border-left:4px solid var(--sec-acc,var(--pri));border-radius:8px;padding:12px 16px;margin:10px 0;font-size:1.05rem}
.def-box{background:linear-gradient(135deg,var(--acc-soft),var(--card));border:1.5px solid var(--acc);border-radius:11px;padding:14px 16px;margin:12px 0}
.def-box-title{font-size:.78rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px;font-family:'Unbounded',sans-serif}
.note-warn{background:var(--warn-bg);border-left:4px solid var(--warn);border-radius:7px;padding:10px 14px;font-size:.9rem;margin:10px 0}
.dark .note-warn{background:rgba(245,158,11,.15);color:#fef3c7}
/* WIDGETS */
.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;transition:box-shadow .25s}
.wg:hover{box-shadow:0 4px 12px rgba(0,0,0,.08),0 16px 40px rgba(233,30,99,.18)}
.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:.78rem;color:var(--muted);font-style:italic;margin-bottom:10px;line-height:1.5}
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.btn.acc{background:var(--acc);color:#fff;border-color:var(--acc)}
.btn.ok{background:var(--ok);color:#fff;border-color:var(--ok)}
.btn.small{padding:5px 11px;font-size:.78rem}
.inp{padding:7px 12px;border-radius:8px;background:var(--card);border:1.5px solid var(--border);font-size:.92rem;color:var(--text);font-family:'JetBrains Mono',monospace;width:auto;min-width:60px}
.inp:focus{outline:none;border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.inp.num{width:100px;text-align:center}
.slider{appearance:none;-webkit-appearance:none;height:6px;background:rgba(233,30,99,.18);border-radius:5px;width:100%;outline:none}
.slider::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:18px;height:18px;background:var(--sec-acc,var(--pri));border-radius:50%;cursor:pointer;box-shadow:0 0 0 3px rgba(233,30,99,.25),0 2px 5px rgba(0,0,0,.18)}
.slider::-moz-range-thumb{width:18px;height:18px;background:var(--sec-acc,var(--pri));border-radius:50%;cursor:pointer;border:none}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:10px}
.row-c{display:flex;gap:10px;align-items:center;justify-content:center;flex-wrap:wrap;margin-bottom:10px}
.lab{font-size:.85rem;font-weight:600;color:var(--text);margin-right:4px}
.lab-mono{font-family:'JetBrains Mono',monospace;font-weight:700;color:var(--sec-acc-d,var(--pri2))}
.chip{display:inline-flex;align-items:center;gap:5px;padding:5px 10px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:7px;font-size:.84rem;font-weight:600;color:var(--sec-acc-d,var(--pri2))}
.chip.acc{background:var(--acc-soft);color:var(--acc2)}
.chip.ok{background:var(--ok-bg);color:#065f46}
.chip.fail{background:var(--fail-bg);color:#7f1d1d}
.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)}
/* DRAG-DROP */
.dz{min-height:60px;padding:12px;border:2px dashed var(--border);border-radius:10px;background:var(--card);display:flex;flex-wrap:wrap;gap:6px;align-content:flex-start;transition:border-color .15s,background .15s}
.dz.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.dz-label{font-size:.78rem;font-weight:700;color:var(--muted);margin-bottom:6px;text-transform:uppercase;letter-spacing:.06em}
.drag-item{padding:6px 12px;background:linear-gradient(135deg,var(--acc),var(--acc2));color:#fff;border-radius:8px;font-weight:600;font-size:.88rem;cursor:grab;user-select:none;font-family:'JetBrains Mono',monospace;box-shadow:var(--sh);transition:transform .12s}
.drag-item:hover{transform:translateY(-1px)}
.drag-item:active{cursor:grabbing}
.drag-item.dragging{opacity:.5}
.drag-item.selected{outline:3px solid #FFD166;outline-offset:2px}
/* SIDEBAR */
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
@media(max-width:980px){.col-side{position:static;max-height:none}}
/* ACHIEVEMENT POPUP */
.ach-popup{position:fixed;top:18px;right:18px;background:linear-gradient(135deg,#fcd34d,#f59e0b);color:#451a03;padding:13px 18px;border-radius:11px;font-weight:700;font-size:.9rem;font-family:'Unbounded',sans-serif;box-shadow:0 8px 32px rgba(245,158,11,.4);z-index:1000;display:none;align-items:center;gap:10px;animation:slideIn .35s ease}
.ach-popup.show{display:flex}
@keyframes slideIn{from{transform:translateX(110%);opacity:0}to{transform:none;opacity:1}}
.spoiler{margin:10px 0;border:1px dashed var(--sec-acc,var(--pri));border-radius:8px;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:''}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
/* QUIZ */
.quiz{background:var(--card);border:1.5px solid var(--border);border-radius:12px;padding:14px 16px;margin-bottom:12px}
.quiz-q{font-weight:600;margin-bottom:10px;font-size:.94rem}
.quiz-opts{display:flex;flex-wrap:wrap;gap:6px}
.quiz-opt{padding:6px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.86rem;font-weight:600;cursor:pointer;background:var(--card);transition:background .12s,border-color .12s}
.quiz-opt:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.quiz-opt.correct{background:var(--ok-bg);border-color:var(--ok);color:#065f46}
.quiz-opt.wrong{background:var(--fail-bg);border-color:var(--fail);color:#7f1d1d}
.tbl{width:100%;border-collapse:collapse;margin:12px 0;font-size:.88rem}
.tbl th,.tbl td{padding:7px 10px;border:1px solid var(--border);text-align:center;font-family:'JetBrains Mono',monospace}
.tbl th{background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));font-weight:700}
/* SPECIFIC WIDGETS — added per § */
.live-ind{display:inline-block;margin-left:6px;font-weight:900;font-size:1.1rem}
.live-ind.ok{color:var(--ok)}
.live-ind.fail{color:var(--fail)}
/* CONSTRUCTOR uravnenia (§7) */
.uconstr-display{font-family:'JetBrains Mono',monospace;font-size:1.8rem;font-weight:800;text-align:center;padding:18px;background:var(--card);border-radius:11px;border:1px solid var(--border);margin:14px 0;color:var(--sec-acc-d,var(--pri2))}
.uconstr-type{display:inline-block;padding:6px 14px;border-radius:99px;font-size:.85rem;font-weight:700;margin-top:8px}
.uconstr-type.full{background:var(--ok-bg);color:#065f46}
.uconstr-type.incomp{background:var(--warn-bg);color:#92400e}
.uconstr-type.notq{background:var(--fail-bg);color:#7f1d1d}
/* BOOK PAGE задача */
.bookpage-svg{display:block;margin:0 auto;max-width:280px}
/* PIPELINE — пошаговый решатель */
.pipe-step{margin:8px 0;padding:10px 14px;background:var(--card);border-left:3px solid var(--sec-acc,var(--pri));border-radius:7px;font-family:'JetBrains Mono',monospace;font-size:.95rem;opacity:0;transform:translateX(-12px);transition:opacity .35s,transform .35s}
.pipe-step.show{opacity:1;transform:none}
.pipe-step b{color:var(--sec-acc-d,var(--pri2));font-family:'Inter',sans-serif;display:block;font-size:.78rem;margin-bottom:4px}
/* PARABOLA (§8) */
.parab-wrap{background:var(--card);border-radius:11px;border:1px solid var(--border);padding:8px;margin:14px 0}
/* CASES (§8) */
.case-card{background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px;text-align:center;cursor:pointer;transition:transform .15s,box-shadow .15s,border-color .15s}
.case-card:hover{transform:translateY(-2px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.case-card.active{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:var(--sh2)}
.case-card h5{font-family:'Unbounded',sans-serif;font-size:.85rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:4px}
/* SCORE display */
.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}
/* SLIDERS / DROP / ACTIONS */
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:flex;flex-direction:column;gap:4px;font-size:.85rem;color:var(--muted);background:var(--card);padding:8px 10px;border-radius:8px;border:1px solid var(--border)}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2))}
.sliders input[type="range"]{width:100%;accent-color:var(--sec-acc,var(--pri))}
.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-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.drop-items .chip{cursor:pointer}
#p7s-pool .chip{cursor:pointer}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.eq-show{font-family:'JetBrains Mono',monospace}
.pipe-tabs .btn.active{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Алгебра 8 · Глава 2</h1>
<div class="hdr-sub">Квадратные уравнения</div>
</div>
<div class="hdr-side">
<a href="/textbook/algebra-8" class="hdr-btn" title="К Главе 1">
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
Глава 1
</a>
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<span id="theme-lab">Тёмная</span>
</button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Самый знаменитый тип уравнений</h2>
<p>Квадратные уравнения встречаются везде: от расчёта траектории брошенного камня до планировки сада. Эта глава покажет полный арсенал: <b>дискриминант</b>, <b>теорему Виета</b>, разложение на множители, решение через подстановку. После неё вы сможете решить любое квадратное уравнение.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p7')">
<svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg>
Начать § 7
</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>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p7" class="sec" data-watermark="ax²">
<div class="sec-header"><span class="sec-num">§ 7</span><h2 class="sec-h">Квадратные уравнения. Решение неполных квадратных уравнений</h2></div>
<div id="p7-body"></div>
</section>
<section id="sec-p8" class="sec" data-watermark="D">
<div class="sec-header"><span class="sec-num">§ 8</span><h2 class="sec-h">Формулы корней квадратного уравнения</h2></div>
<div id="p8-body"></div>
</section>
<section id="sec-p9" class="sec" data-watermark="Виета">
<div class="sec-header"><span class="sec-num">§ 9</span><h2 class="sec-h">Теорема Виета</h2></div>
<div id="p9-body"></div>
</section>
<section id="sec-p10" class="sec" data-watermark="( )( )">
<div class="sec-header"><span class="sec-num">§ 10</span><h2 class="sec-h">Квадратный трёхчлен. Разложение на множители</h2></div>
<div id="p10-body"></div>
</section>
<section id="sec-p11" class="sec" data-watermark="задачи">
<div class="sec-header"><span class="sec-num">§ 11</span><h2 class="sec-h">Решение текстовых задач с помощью квадратных уравнений</h2></div>
<div id="p11-body"></div>
</section>
<section id="sec-p12" class="sec" data-watermark="t = x²">
<div class="sec-header"><span class="sec-num">§ 12</span><h2 class="sec-h">Целые рациональные уравнения, сводящиеся к квадратным</h2></div>
<div id="p12-body"></div>
</section>
<section id="sec-final2" class="sec" data-watermark="★">
<div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#f59e0b,#ec4899)">Финал главы</span><h2 class="sec-h">Итоги. Практическая и увлекательная математика</h2></div>
<div id="final2-body"></div>
</section>
</div>
<aside class="col-side"><div id="sidebar-content"></div></aside>
</main>
<footer class="foot">Интерактивный учебник «Алгебра 8» · Глава 2 · LearnSpace · версия 1.0</footer>
<div id="ach-popup" class="ach-popup">
<svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><circle cx="12" cy="8" r="5"/><path d="M8 13l-2 8 6-4 6 4-2-8"/></svg>
<span id="ach-text">Достижение!</span>
</div>
<script>
'use strict';
/* STATE & PROGRESS */
const STATE = {
current: 'p7',
progress: { p7:0, p8:0, p9:0, p10:0, p11:0, p12:0, final2:0 },
achievements: new Map(),
xp: 0,
};
const ACH_LABELS = {
start: 'Начало главы 2!',
p7_constr: 'Конструктор уравнений',
p7_sort: 'Сортировка по типам',
p7_pipeline: 'Решил неполное по шагам',
p7_book: 'Задача про страницу',
p7_train: 'Тренажёр неполных',
p7_roots: 'Угадал есть ли корни',
p8_disc: 'Калькулятор дискриминанта',
p8_parab: 'Парабола и корни',
p8_cases: '3 случая дискриминанта',
p8_disc_train: 'Тренажёр дискриминанта',
p8_steps: 'Пошаговое решение',
p8_graph: 'Знак D по графику',
};
function loadProgress(){
try{
const s = localStorage.getItem('algebra8_ch2_progress');
if(s) Object.assign(STATE.progress, JSON.parse(s));
const a = localStorage.getItem('algebra8_ch2_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));
}
}
const xp = localStorage.getItem('algebra8_ch2_xp');
if(xp) STATE.xp = +xp;
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('algebra8_ch2_progress', JSON.stringify(STATE.progress));
localStorage.setItem('algebra8_ch2_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('algebra8_ch2_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();
}
function addXp(n, src){ STATE.xp = (STATE.xp||0) + n; saveProgress(); refreshProgressUI(); }
function refreshProgressUI(){
const total = Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0) / 7);
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) + '%';
});
}
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');
}
/* PARA SELECTOR */
const PARAS = [
{ id:'p7', num:'§ 7', name:'Квадратные уравнения', sub:'Неполные' },
{ id:'p8', num:'§ 8', name:'Формулы корней', sub:'Дискриминант' },
{ id:'p9', num:'§ 9', name:'Теорема Виета', sub:'Подбор корней' },
{ id:'p10', num:'§ 10', name:'Квадратный трёхчлен', sub:'Разложение' },
{ id:'p11', num:'§ 11', name:'Текстовые задачи', sub:'4 шага' },
{ id:'p12', num:'§ 12', name:'Сводящиеся к квадратным', sub:'Биквадратные' },
{ id:'final2', num:'★', name:'Финал главы', sub:'Итоги · Практика', 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 = {
p7:()=>buildP7(), p8:()=>buildP8(),
p9:()=>buildP9stub(), p10:()=>buildP10stub(),
p11:()=>buildP11stub(), p12:()=>buildP12stub(),
final2:()=>buildFinal2stub(),
};
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);
}
/* SIDEBAR */
const SIDEBARS = {
p7: { title:'Шпаргалка § 7', rows:[
['$ax^2+bx+c=0$','квадратное, $a \\neq 0$'],
['$ax^2+bx=0$','неполное: $x(ax+b)=0$'],
['$ax^2+c=0$','неполное: $x^2 = -c/a$'],
['$ax^2=0$','неполное: $x=0$'],
['Свойство','$ab=0 \\Leftrightarrow a=0$ или $b=0$'],
]},
p8: { title:'Шпаргалка § 8', rows:[
['$D = b^2 - 4ac$','дискриминант'],
['$D > 0$','два корня: $x_{1,2} = \\dfrac{-b \\pm \\sqrt{D}}{2a}$'],
['$D = 0$','один корень: $x = \\dfrac{-b}{2a}$'],
['$D < 0$','корней нет'],
]},
p9: { title:'§ 9 — скоро', rows:[['Теорема Виета','будет в Wave 2']]},
p10:{ title:'§ 10 — скоро', rows:[['Разложение','будет в Wave 2']]},
p11:{ title:'§ 11 — скоро', rows:[['Текстовые задачи','будет в Wave 3']]},
p12:{ title:'§ 12 — скоро', rows:[['Сводящиеся к квадратным','будет в Wave 3']]},
final2:{ title:'Финал', rows:[['Итоги главы','будет в Wave 4']]},
};
function buildSidebar(id){
const box = document.getElementById('sidebar-content');
const sb = SIDEBARS[id] || SIDEBARS.p7;
let 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>';
if(STATE.achievements.size > 0){
html += `<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">${STATE.achievements.size}</span></h4>`;
[...STATE.achievements.values()].slice(-4).forEach(text=>{
html += `<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ ${text}</div>`;
});
html += '</div>';
}
box.innerHTML = html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
/* THEME */
function initTheme(){
const t = localStorage.getItem('algebra8_ch2_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('algebra8_ch2_theme', dark ? 'dark' : 'light');
document.getElementById('theme-lab').textContent = dark ? 'Светлая' : 'Тёмная';
});
}
/* HELPERS */
function $(sel, root){ return (root||document).querySelector(sel); }
function $$(sel, root){ return [...(root||document).querySelectorAll(sel)]; }
function el(tag, attrs, html){
const e = document.createElement(tag);
if(attrs) Object.entries(attrs).forEach(([k,v])=>{
if(k === 'class') e.className = v;
else if(k === 'style') e.style.cssText = v;
else if(k.startsWith('on') && typeof v === 'function') e.addEventListener(k.slice(2), v);
else e.setAttribute(k, v);
});
if(html != null) e.innerHTML = html;
return e;
}
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){
elm.className = 'feedback ' + (ok ? 'ok' : 'fail');
elm.innerHTML = text || (ok ? '&#10003; Верно!' : '&#10007; Неверно');
}
function sleep(ms){ return new Promise(r => setTimeout(r, ms)); }
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(4)).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>',
class: '<svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="14" rx="1"/><line x1="3" y1="21" x2="21" y2="21"/><polyline points="7 14 10 11 13 14 17 10"/></svg>',
home: '<svg class="ic" viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>',
};
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно',class:'Класс',home:'Домашка'};
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] ? '· ' + title : ''}</div>
${num ? `<div class="card-num">${num}</div>` : ''}
</div>
<div class="card-body">${body}</div>
</div>`;
}
function widget(title, badge, helpText, body){
return `<div class="wg">
<div class="wg-header"><span class="wg-badge">${badge||'INTERACT'}</span><div class="wg-title">${title}</div></div>
${helpText ? `<div class="wg-help">${helpText}</div>` : ''}
${body}
</div>`;
}
function secNav(prev, next){
const NAMES = {p7:'§7',p8:'§8',p9:'§9',p10:'§10',p11:'§11',p12:'§12',final2:'Финал'};
let h = '<div class="sec-nav">';
h += prev ? `<button class="btn" onclick="goTo('${prev}')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> ${NAMES[prev]}</button>` : '<span></span>';
h += next ? `<button class="btn primary" onclick="goTo('${next}')">${NAMES[next]} <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>` : '<span></span>';
h += '</div>';
return h;
}
/* CONFETTI (simplified) */
let _confettiCanvas = null, _confettiParticles = [], _confettiRaf = null;
function confetti(){
if(!_confettiCanvas){
_confettiCanvas = document.createElement('canvas');
_confettiCanvas.id = 'confetti-canvas';
_confettiCanvas.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999';
document.body.appendChild(_confettiCanvas);
}
const c = _confettiCanvas;
c.width = window.innerWidth; c.height = window.innerHeight;
const ctx = c.getContext('2d');
const colors = ['#e91e63','#03a9f4','#f59e0b','#10b981','#a855f7'];
for(let i = 0; i < 80; i++){
_confettiParticles.push({
x: window.innerWidth/2 + (Math.random()-0.5)*200,
y: window.innerHeight/2,
vx: (Math.random()-0.5)*14,
vy: -10 - Math.random()*10,
g: 0.4, life: 100, color: colors[i%colors.length], r: 4+Math.random()*4, rot: 0, vRot: (Math.random()-0.5)*0.3,
});
}
if(_confettiRaf) cancelAnimationFrame(_confettiRaf);
function frame(){
ctx.clearRect(0,0,c.width,c.height);
_confettiParticles = _confettiParticles.filter(p=>{
p.x += p.vx; p.y += p.vy; p.vy += p.g; p.life--; p.rot += p.vRot;
ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.rot);
ctx.fillStyle = p.color;
ctx.fillRect(-p.r, -p.r/2, p.r*2, p.r);
ctx.restore();
return p.life > 0 && p.y < c.height + 50;
});
if(_confettiParticles.length > 0) _confettiRaf = requestAnimationFrame(frame);
else { ctx.clearRect(0,0,c.width,c.height); _confettiRaf = null; }
}
frame();
}
/* INIT */
function init(){
loadProgress();
initTheme();
buildParaSelector();
refreshProgressUI();
goTo('p7');
setTimeout(()=>achievement('start','Начало главы 2!'), 600);
}
document.addEventListener('DOMContentLoaded', init);
/* STUBS for paragraphs not yet implemented */
function buildP9stub(){ document.getElementById('p9-body').innerHTML = `<div class="card"><div class="card-body"><p style="text-align:center;padding:20px"><b>§ 9 — Теорема Виета</b><br><br>Этот параграф будет реализован в следующей волне обновлений.<br><br><small>Wave 2: § 9 + § 10</small></p></div></div>${secNav('p8','p10')}`; }
function buildP10stub(){ document.getElementById('p10-body').innerHTML = `<div class="card"><div class="card-body"><p style="text-align:center;padding:20px"><b>§ 10 — Квадратный трёхчлен. Разложение на множители</b><br><br>Скоро в Wave 2.</p></div></div>${secNav('p9','p11')}`; }
function buildP11stub(){ document.getElementById('p11-body').innerHTML = `<div class="card"><div class="card-body"><p style="text-align:center;padding:20px"><b>§ 11 — Текстовые задачи</b><br><br>Скоро в Wave 3.</p></div></div>${secNav('p10','p12')}`; }
function buildP12stub(){ document.getElementById('p12-body').innerHTML = `<div class="card"><div class="card-body"><p style="text-align:center;padding:20px"><b>§ 12 — Сводящиеся к квадратным</b><br><br>Скоро в Wave 3.</p></div></div>${secNav('p11','final2')}`; }
function buildFinal2stub(){ document.getElementById('final2-body').innerHTML = `<div class="card"><div class="card-body"><p style="text-align:center;padding:20px"><b>Финал главы</b><br><br>Итоговая самооценка, практика, увлекательная математика и финальный босс — в Wave 4.</p></div></div>${secNav('p12',null)}`; }
</script>
<script>
/* ============================================================
§ 7 — КВАДРАТНЫЕ УРАВНЕНИЯ. НЕПОЛНЫЕ
============================================================ */
function buildP7(){
const box = document.getElementById('p7-body');
let html = '';
/* Card: повторение */
html += makeCard('repeat','Повторение',null, `
<ul style="margin-left:18px;line-height:1.7">
<li><b>Линейное уравнение</b> $ax+b=0$: при $a \\neq 0$ один корень $x=-b/a$.</li>
<li><b>Раскрытие скобок:</b> $(a+b)^2 = a^2+2ab+b^2$, $(a-b)^2=a^2-2ab+b^2$.</li>
<li><b>Свойство нуля:</b> произведение равно нулю $\\Leftrightarrow$ хотя бы один множитель равен нулю.</li>
<li><b>Квадратный корень:</b> $\\sqrt{a}$ определён только для $a \\geq 0$.</li>
</ul>`);
/* Card: теория */
html += makeCard('theory','Что такое квадратное уравнение','7.1',`
<p style="margin-bottom:8px"><b>Определение.</b> Уравнение вида $ax^2 + bx + c = 0$, где $a \\neq 0$, называется <b>квадратным</b>. Числа $a$, $b$, $c$ — <b>коэффициенты</b>:</p>
<ul style="margin-left:18px;line-height:1.7">
<li>$a$ — старший (первый) коэффициент;</li>
<li>$b$ — второй коэффициент;</li>
<li>$c$ — свободный член.</li>
</ul>
<p style="margin-top:8px">Если $a=1$, уравнение называют <b>приведённым</b>. Если хотя бы один из коэффициентов $b$ или $c$ равен нулю — <b>неполным</b>.</p>`);
/* Card: правило */
html += makeCard('rule','Три типа неполных уравнений','7.2',`
<table class="tbl" style="width:100%;border-collapse:collapse;font-size:.95rem">
<thead><tr style="background:var(--sec-acc-soft,var(--pri-soft))"><th style="padding:8px;text-align:left">Вид</th><th style="padding:8px;text-align:left">Метод</th><th style="padding:8px;text-align:left">Корни</th></tr></thead>
<tbody>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)">$ax^2 + bx = 0$</td><td style="padding:8px;border-bottom:1px solid var(--border)">$x(ax+b)=0$</td><td style="padding:8px;border-bottom:1px solid var(--border)">$x_1=0,\\ x_2=-b/a$</td></tr>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)">$ax^2 + c = 0$</td><td style="padding:8px;border-bottom:1px solid var(--border)">$x^2 = -c/a$</td><td style="padding:8px;border-bottom:1px solid var(--border)">$x = \\pm\\sqrt{-c/a}$ или нет</td></tr>
<tr><td style="padding:8px">$ax^2 = 0$</td><td style="padding:8px">очевидно</td><td style="padding:8px">$x = 0$</td></tr>
</tbody>
</table>`);
/* Card: алгоритм */
html += makeCard('algo','Универсальная стратегия',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>Привести уравнение к виду $ax^2+bx+c=0$.</li>
<li>Определить, какие коэффициенты равны нулю.</li>
<li>Выбрать метод по таблице выше.</li>
<li>Проверить ответ подстановкой.</li>
</ol>`);
/* Card: пример */
html += makeCard('example','Решим вместе','7.3',`
<p><b>Пример 1.</b> $3x^2 - 12x = 0$. Выносим $x$ за скобки: $x(3x - 12) = 0$. Откуда $x_1 = 0$ или $3x-12 = 0 \\Rightarrow x_2 = 4$. Ответ: $\\{0;\\ 4\\}$.</p>
<p style="margin-top:6px"><b>Пример 2.</b> $2x^2 - 18 = 0$. Получаем $x^2 = 9 \\Rightarrow x = \\pm 3$.</p>
<p style="margin-top:6px"><b>Пример 3.</b> $5x^2 + 20 = 0 \\Rightarrow x^2 = -4$. Корней нет.</p>`);
/* ===== INTERACTIVE 1: Конструктор уравнения ===== */
html += widget('Конструктор квадратного уравнения','INTERACT 1','Перетягивайте слайдеры $a$, $b$, $c$. Смотрите, как уравнение оживает и меняет тип.', `
<div class="sliders">
<label>$a$ = <b id="p7c-a-val">1</b><input type="range" min="-5" max="5" step="1" value="1" id="p7c-a"></label>
<label>$b$ = <b id="p7c-b-val">0</b><input type="range" min="-9" max="9" step="1" value="0" id="p7c-b"></label>
<label>$c$ = <b id="p7c-c-val">-9</b><input type="range" min="-12" max="12" step="1" value="-9" id="p7c-c"></label>
</div>
<div class="eq-show" id="p7c-eq" style="text-align:center;font-size:1.4rem;margin:10px 0;padding:14px;background:var(--sec-acc-soft);border-radius:10px"></div>
<div id="p7c-type" style="text-align:center;font-weight:600;color:var(--sec-acc-d);margin-bottom:6px"></div>
<div id="p7c-roots" style="text-align:center;margin-bottom:6px"></div>`);
/* ===== INTERACTIVE 2: Drag-сортировка ===== */
html += widget('Сортировка уравнений по типу','INTERACT 2','Кликом отправьте каждое уравнение в нужный ящик. Серый — лишнее (не квадратное).',`
<div id="p7s-pool" style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px"></div>
<div class="drop-row" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px">
<div class="drop-box" data-type="full"><h5>Полное</h5><div class="drop-items" data-cat="full"></div></div>
<div class="drop-box" data-type="incomplete"><h5>Неполное</h5><div class="drop-items" data-cat="incomplete"></div></div>
<div class="drop-box" data-type="notquad"><h5>Не квадратное</h5><div class="drop-items" data-cat="notquad"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p7s-check">Проверить</button><button class="btn" id="p7s-reset">Сначала</button></div>
<div class="feedback" id="p7s-fb" style="display:none"></div>`);
/* ===== INTERACTIVE 3: Пошаговый решатель ===== */
html += widget('Пошаговый решатель неполных','INTERACT 3','Выберите тип, нажимайте «Дальше» и наблюдайте за решением пошагово.',`
<div class="pipe-tabs" style="display:flex;gap:6px;margin-bottom:10px">
<button class="btn small p7p-tab active" data-tp="bc0">$ax^2+bx=0$</button>
<button class="btn small p7p-tab" data-tp="ac0">$ax^2+c=0$</button>
</div>
<div id="p7p-stage" style="padding:14px;background:var(--card-soft);border-radius:10px;min-height:160px;font-size:1.05rem"></div>
<div class="actions" style="margin-top:10px"><button class="btn primary" id="p7p-next">Дальше</button><button class="btn" id="p7p-reset">Сначала</button></div>`);
/* ===== INTERACTIVE 4: Задача про страницу книги ===== */
html += widget('Задача про страницу книги','INTERACT 4','Длина страницы на 4 см больше ширины, а площадь — 165 см². Найдите ширину.',`
<p style="margin-bottom:10px">Пусть ширина $= x$, тогда длина $= x+4$. Уравнение: $x(x+4) = 165 \\Rightarrow x^2 + 4x - 165 = 0$.</p>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<label>Ваш ответ (ширина, см): <input type="number" id="p7b-inp" style="width:80px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
<button class="btn primary" id="p7b-check">Проверить</button>
</div>
<div class="feedback" id="p7b-fb" style="display:none;margin-top:10px"></div>
<svg id="p7b-svg" viewBox="0 0 320 200" style="width:100%;max-width:320px;margin-top:12px;display:block">
<rect x="40" y="40" width="240" height="120" fill="var(--sec-acc-soft)" stroke="var(--sec-acc)" stroke-width="2"/>
<text x="160" y="32" text-anchor="middle" font-size="14" fill="var(--sec-acc-d)">длина = x + 4</text>
<text x="22" y="105" font-size="14" fill="var(--sec-acc-d)" transform="rotate(-90 22 105)">x</text>
<text x="160" y="108" text-anchor="middle" font-size="20" fill="var(--ink)" id="p7b-area">S = 165</text>
</svg>`);
/* ===== INTERACTIVE 5: Тренажёр 10 неполных ===== */
html += widget('Тренажёр: 10 неполных','INTERACT 5','Решайте устно. Ответ — большее значение корня (через запятую: если корней два — оба, по возрастанию).',`
<div class="score-display"><span>Задача <b id="p7t-i">1</b> / 10</span><span>Очки: <b id="p7t-score">0</b></span><span>Время: <b id="p7t-time">0</b> c</span></div>
<div id="p7t-task" style="font-size:1.4rem;text-align:center;padding:18px;background:var(--sec-acc-soft);border-radius:10px;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap">
<input type="text" id="p7t-inp" placeholder="например 0; 4 или -3; 3 или нет" style="width:240px;padding:8px;border:1.5px solid var(--border);border-radius:8px">
<button class="btn primary" id="p7t-go">Ответ</button>
<button class="btn" id="p7t-skip">Пропустить</button>
</div>
<div class="feedback" id="p7t-fb" style="display:none;margin-top:10px"></div>
<button class="btn primary" id="p7t-start" style="margin-top:10px">Начать</button>`);
/* ===== INTERACTIVE 6: Есть ли корни у ax²+c=0 ===== */
html += widget('«Имеет ли корни?» — $ax^2 + c = 0$','INTERACT 6','Быстрый тренажёр на знаки. Нужно сообразить, имеет ли уравнение корни.',`
<div class="score-display"><span>Раунд <b id="p7r-i">1</b> / 8</span><span>Правильно: <b id="p7r-score">0</b></span></div>
<div id="p7r-task" style="font-size:1.5rem;text-align:center;padding:18px;background:var(--sec-acc-soft);border-radius:10px;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;justify-content:center">
<button class="btn primary" id="p7r-yes">Имеет корни</button>
<button class="btn" id="p7r-no">Корней нет</button>
</div>
<div class="feedback" id="p7r-fb" style="display:none;margin-top:10px"></div>
<button class="btn primary" id="p7r-start" style="margin-top:10px">Начать</button>`);
/* ===== Устно / Класс / Дом ===== */
html += makeCard('oral','Устные вопросы',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>Какое уравнение называется квадратным? Какое — неполным?</li>
<li>Может ли коэффициент $a$ быть равен нулю? Почему?</li>
<li>Сколько корней может быть у уравнения $ax^2+c=0$?</li>
<li>Назовите коэффициенты уравнения $5x^2 - 2x + 7 = 0$.</li>
</ol>`);
html += makeCard('class','Класс — решите в тетради',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>$4x^2 - 9 = 0$</li>
<li>$3x^2 + 6x = 0$</li>
<li>$x^2 + 25 = 0$</li>
<li>$2x^2 = 50$</li>
<li>$x(x-7) = 0$</li>
</ol>`);
html += makeCard('home','Домашка',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>$9x^2 - 16 = 0$</li>
<li>$5x^2 + 10x = 0$</li>
<li>$x^2 + 4 = 0$</li>
<li>Длина прямоугольника на 3 см больше ширины, а площадь равна 54 см². Найдите стороны.</li>
</ol>`);
html += secNav(null,'p8');
box.innerHTML = html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box), 0);
/* ===== INIT INTERACTIVE 1 ===== */
(function initConstructor(){
const aE = document.getElementById('p7c-a'), bE = document.getElementById('p7c-b'), cE = document.getElementById('p7c-c');
const eq = document.getElementById('p7c-eq'), tp = document.getElementById('p7c-type'), rt = document.getElementById('p7c-roots');
let done = false;
function termA(a){
if(a === 0) return '';
if(a === 1) return 'x^2';
if(a === -1) return '-x^2';
return a + 'x^2';
}
function termB(b){
if(b === 0) return '';
const s = b > 0 ? ' + ' : ' - ';
const v = Math.abs(b);
return s + (v === 1 ? '' : v) + 'x';
}
function termC(c){
if(c === 0) return '';
const s = c > 0 ? ' + ' : ' - ';
return s + Math.abs(c);
}
function refresh(){
const a = +aE.value, b = +bE.value, c = +cE.value;
document.getElementById('p7c-a-val').textContent = a;
document.getElementById('p7c-b-val').textContent = b;
document.getElementById('p7c-c-val').textContent = c;
if(a === 0){ eq.innerHTML = '$' + (termB(b) || '0') + termC(c) + ' = 0$ <span style="color:var(--bad);font-size:.85rem">не квадратное!</span>'; tp.textContent = 'Не квадратное (a=0)'; rt.textContent = ''; renderMath(eq); return; }
let s = termA(a) + termB(b) + termC(c) + ' = 0';
eq.innerHTML = '$' + s + '$';
let label = '';
if(b === 0 && c === 0) label = 'Неполное: $ax^2=0$ · корень $x=0$';
else if(b === 0) label = 'Неполное: $ax^2+c=0$';
else if(c === 0) label = 'Неполное: $ax^2+bx=0$ · корни $x_1=0,\\ x_2=' + fmt(-b/a) + '$';
else label = 'Полное квадратное · решаем через дискриминант';
tp.innerHTML = label;
let rs = '';
if(b === 0 && c === 0) rs = '$x=0$';
else if(c === 0) rs = '$x_1=0,\\ x_2=' + fmt(-b/a) + '$';
else if(b === 0){
const v = -c/a;
if(v > 0){ const r = Math.sqrt(v); rs = '$x = \\pm' + fmt(r) + '$'; }
else if(v === 0) rs = '$x=0$';
else rs = 'корней нет';
} else {
const D = b*b - 4*a*c;
if(D > 0){ const r1 = (-b - Math.sqrt(D))/(2*a), r2 = (-b + Math.sqrt(D))/(2*a); rs = '$x_1=' + fmt(r1) + ',\\ x_2=' + fmt(r2) + '$'; }
else if(D === 0) rs = '$x=' + fmt(-b/(2*a)) + '$';
else rs = 'корней нет';
}
rt.innerHTML = rs;
renderMath(eq); renderMath(tp); renderMath(rt);
if(!done){ done = true; setTimeout(()=>{ achievement('p7_constr'); bumpProgress('p7', 12); }, 300); }
}
aE.addEventListener('input', refresh); bE.addEventListener('input', refresh); cE.addEventListener('input', refresh);
refresh();
})();
/* ===== INIT INTERACTIVE 2 ===== */
(function initSort(){
const items = [
{ id:1, txt:'$2x^2 + 3x - 5 = 0$', cat:'full' },
{ id:2, txt:'$x^2 - 16 = 0$', cat:'incomplete' },
{ id:3, txt:'$3x^2 + 6x = 0$', cat:'incomplete' },
{ id:4, txt:'$x + 5 = 0$', cat:'notquad' },
{ id:5, txt:'$5x^2 - 2x + 1 = 0$', cat:'full' },
{ id:6, txt:'$7x^2 = 0$', cat:'incomplete' },
{ id:7, txt:'$x^3 - x = 0$', cat:'notquad' },
{ id:8, txt:'$x^2 + x + 1 = 0$', cat:'full' },
];
const pool = document.getElementById('p7s-pool');
const cats = ['full','incomplete','notquad'];
const labels = { full:'Полное', incomplete:'Неполное', notquad:'Не квадр.' };
let placed = {};
function makeChip(it, where){
const wrap = document.createElement('div');
wrap.className = 'chip-wrap';
wrap.style.cssText = 'display:inline-flex;align-items:center;gap:4px;background:var(--sec-acc-soft);border-radius:8px;padding:3px 6px;margin:2px';
const sp = document.createElement('span');
sp.className = 'chip';
sp.style.cssText = 'background:transparent;padding:2px 4px';
sp.innerHTML = it.txt;
wrap.appendChild(sp);
if(where === 'pool'){
cats.forEach(cat=>{
const b = document.createElement('button');
b.className = 'btn small';
b.textContent = labels[cat];
b.style.cssText = 'padding:3px 7px;font-size:.7rem';
b.addEventListener('click', ()=>{ placed[it.id] = cat; renderAll(); });
wrap.appendChild(b);
});
} else {
const b = document.createElement('button');
b.className = 'btn small';
b.textContent = '×';
b.style.cssText = 'padding:2px 7px';
b.addEventListener('click', ()=>{ delete placed[it.id]; renderAll(); });
wrap.appendChild(b);
}
return wrap;
}
function render(){
pool.innerHTML = '';
items.forEach(it=>{
if(placed[it.id]) return;
pool.appendChild(makeChip(it, 'pool'));
});
cats.forEach(cat=>{
const box = document.querySelector('.drop-items[data-cat="' + cat + '"]');
box.innerHTML = '';
items.forEach(it=>{
if(placed[it.id] !== cat) return;
box.appendChild(makeChip(it, 'placed'));
});
});
if(window.renderMathInElement) renderMath(document.getElementById('p7s-pool').parentElement);
}
function renderAll(){ render(); }
document.getElementById('p7s-check').addEventListener('click', ()=>{
const total = items.length;
const placedCount = Object.keys(placed).length;
if(placedCount < total){ feedback(document.getElementById('p7s-fb'), false, '&#9888; Разложите ВСЕ уравнения по ящикам.'); document.getElementById('p7s-fb').style.display='block'; return; }
let ok = 0; items.forEach(it=>{ if(placed[it.id] === it.cat) ok++; });
const fb = document.getElementById('p7s-fb');
fb.style.display = 'block';
if(ok === total){ feedback(fb, true, '&#10003; Идеально! Все ' + total + ' верно.'); achievement('p7_sort'); bumpProgress('p7', 14); confetti(); }
else feedback(fb, false, 'Верно ' + ok + ' из ' + total + '. Попробуйте перепроверить.');
});
document.getElementById('p7s-reset').addEventListener('click', ()=>{ placed = {}; document.getElementById('p7s-fb').style.display='none'; renderAll(); });
renderAll();
})();
/* ===== INIT INTERACTIVE 3 ===== */
(function initPipeline(){
const stages = {
bc0: [
'<b>Дано:</b> $2x^2 - 6x = 0$',
'<b>Шаг 1:</b> Вынесем $x$ за скобки: $x(2x - 6) = 0$',
'<b>Шаг 2:</b> Произведение равно нулю $\\Leftrightarrow$ один из множителей равен нулю.',
'<b>Шаг 3:</b> $x = 0$ или $2x - 6 = 0$',
'<b>Шаг 4:</b> $x_1 = 0,\\ x_2 = 3$',
'<b>Ответ:</b> $\\{0;\\ 3\\}$',
],
ac0: [
'<b>Дано:</b> $3x^2 - 27 = 0$',
'<b>Шаг 1:</b> Перенесём $-27$ вправо: $3x^2 = 27$',
'<b>Шаг 2:</b> Разделим на $3$: $x^2 = 9$',
'<b>Шаг 3:</b> Возьмём квадратный корень: $x = \\pm\\sqrt{9}$',
'<b>Шаг 4:</b> $x = \\pm 3$',
'<b>Ответ:</b> $\\{-3;\\ 3\\}$',
],
};
let mode = 'bc0', idx = 0;
const stage = document.getElementById('p7p-stage');
let solved = false;
function show(){
const arr = stages[mode];
stage.innerHTML = arr.slice(0, idx + 1).map(s => '<p style="margin:6px 0">' + s + '</p>').join('');
renderMath(stage);
if(idx >= arr.length - 1 && !solved){ solved = true; achievement('p7_pipeline'); bumpProgress('p7', 12); }
}
document.querySelectorAll('.p7p-tab').forEach(t=>{
t.addEventListener('click', ()=>{ document.querySelectorAll('.p7p-tab').forEach(x=>x.classList.remove('active')); t.classList.add('active'); mode = t.dataset.tp; idx = 0; show(); });
});
document.getElementById('p7p-next').addEventListener('click', ()=>{ if(idx < stages[mode].length - 1) idx++; show(); });
document.getElementById('p7p-reset').addEventListener('click', ()=>{ idx = 0; show(); });
show();
})();
/* ===== INIT INTERACTIVE 4 ===== */
(function initBook(){
document.getElementById('p7b-check').addEventListener('click', ()=>{
const v = +document.getElementById('p7b-inp').value;
const fb = document.getElementById('p7b-fb');
fb.style.display = 'block';
if(Math.abs(v - 11) < 1e-9){ feedback(fb, true, '&#10003; Верно! Ширина = 11 см, длина = 15 см. $11 \\cdot 15 = 165$.'); renderMath(fb); achievement('p7_book'); bumpProgress('p7', 14); confetti(); }
else feedback(fb, false, 'Не то. $x^2+4x-165=0$, корни $-15$ и $11$. Ширина не может быть отрицательной.');
});
})();
/* ===== INIT INTERACTIVE 5 ===== */
(function initTrain(){
function gen(){
const t = Math.floor(Math.random()*3);
if(t === 0){
const a = 1 + Math.floor(Math.random()*4);
const r2 = 1 + Math.floor(Math.random()*8);
return { eq: a + 'x^2 - ' + (a*r2) + 'x = 0', ans: [0, r2] };
}
if(t === 1){
const a = 1 + Math.floor(Math.random()*3);
const r = 1 + Math.floor(Math.random()*7);
return { eq: a + 'x^2 - ' + (a*r*r) + ' = 0', ans: [-r, r] };
}
const a = 1 + Math.floor(Math.random()*3);
const c = 1 + Math.floor(Math.random()*15);
return { eq: a + 'x^2 + ' + c + ' = 0', ans: null };
}
let cur = null, i = 1, score = 0, t0 = 0, timer = null;
function newOne(){
cur = gen();
document.getElementById('p7t-task').innerHTML = '$' + cur.eq + ' = 0$'; renderMath(document.getElementById('p7t-task'));
document.getElementById('p7t-i').textContent = i;
document.getElementById('p7t-inp').value = '';
document.getElementById('p7t-fb').style.display = 'none';
}
function parse(s){
s = s.trim().toLowerCase();
if(/нет|none/.test(s)) return null;
return s.replace(/,/g,';').split(/[;\s]+/).filter(Boolean).map(Number).sort((a,b)=>a-b);
}
function check(){
const u = parse(document.getElementById('p7t-inp').value);
const fb = document.getElementById('p7t-fb');
fb.style.display = 'block';
let ok = false;
if(cur.ans === null){ ok = u === null; }
else if(u !== null){ const a = [...cur.ans].sort((a,b)=>a-b); ok = a.length === u.length && a.every((x,k)=>x === u[k]); }
if(ok){ score++; feedback(fb, true, '&#10003;'); } else feedback(fb, false, 'Правильно: ' + (cur.ans === null ? 'нет' : cur.ans.join('; ')));
document.getElementById('p7t-score').textContent = score;
if(i >= 10){
setTimeout(()=>{ feedback(fb, score >= 7, 'Итог: ' + score + '/10 за ' + (((Date.now() - t0)/1000)|0) + ' c'); if(score >= 7){ achievement('p7_train'); bumpProgress('p7', 16); confetti(); } clearInterval(timer); }, 500);
} else { i++; setTimeout(newOne, 700); }
}
document.getElementById('p7t-start').addEventListener('click', ()=>{ i = 1; score = 0; document.getElementById('p7t-score').textContent = 0; t0 = Date.now(); if(timer) clearInterval(timer); timer = setInterval(()=>{ document.getElementById('p7t-time').textContent = ((Date.now() - t0)/1000)|0; }, 200); newOne(); });
document.getElementById('p7t-go').addEventListener('click', check);
document.getElementById('p7t-inp').addEventListener('keyup', e=>{ if(e.key === 'Enter') check(); });
document.getElementById('p7t-skip').addEventListener('click', ()=>{ if(i < 10){ i++; newOne(); } });
})();
/* ===== INIT INTERACTIVE 6 ===== */
(function initRoots(){
let i = 1, score = 0, cur = null;
function gen(){
const a = (Math.random() < 0.5 ? 1 : -1) * (1 + Math.floor(Math.random()*5));
const c = (Math.random() < 0.5 ? 1 : -1) * (1 + Math.floor(Math.random()*16));
const v = -c/a;
return { a, c, ok: v > 0, eq: (a === 1 ? '' : (a === -1 ? '-' : a)) + 'x^2 ' + (c >= 0 ? '+ ' + c : '- ' + Math.abs(c)) + ' = 0' };
}
function newOne(){
cur = gen();
document.getElementById('p7r-i').textContent = i;
document.getElementById('p7r-task').innerHTML = '$' + cur.eq + '$'; renderMath(document.getElementById('p7r-task'));
document.getElementById('p7r-fb').style.display = 'none';
}
function answer(yes){
const fb = document.getElementById('p7r-fb');
fb.style.display = 'block';
const ok = (cur.ok === yes);
if(ok){ score++; feedback(fb, true, '&#10003; Точно. $x^2 = ' + fmt(-cur.c/cur.a) + '$'); }
else feedback(fb, false, 'Нет. $x^2 = ' + fmt(-cur.c/cur.a) + '$ — ' + (cur.ok ? 'корни есть' : 'корней нет'));
renderMath(fb);
document.getElementById('p7r-score').textContent = score;
if(i >= 8){ setTimeout(()=>{ feedback(fb, score >= 6, 'Итог: ' + score + '/8'); if(score >= 6){ achievement('p7_roots'); bumpProgress('p7', 14); confetti(); } }, 600); }
else { i++; setTimeout(newOne, 800); }
}
document.getElementById('p7r-start').addEventListener('click', ()=>{ i = 1; score = 0; document.getElementById('p7r-score').textContent = 0; newOne(); });
document.getElementById('p7r-yes').addEventListener('click', ()=>answer(true));
document.getElementById('p7r-no').addEventListener('click', ()=>answer(false));
})();
}
</script>
<script>
/* ============================================================
§ 8 — ФОРМУЛЫ КОРНЕЙ. ДИСКРИМИНАНТ
============================================================ */
function buildP8(){
const box = document.getElementById('p8-body');
let html = '';
html += makeCard('repeat','Повторение из § 7',null,`
<ul style="margin-left:18px;line-height:1.7">
<li>Квадратное уравнение: $ax^2+bx+c=0,\\ a \\neq 0$.</li>
<li>Неполные уравнения решали без дискриминанта — через вынесение или прямой корень.</li>
<li>Свойство квадратного корня: $\\sqrt{D}$ существует только при $D \\geq 0$.</li>
</ul>`);
html += makeCard('theory','Универсальная формула','8.1',`
<p>Для уравнения $ax^2 + bx + c = 0$, $a \\neq 0$, выделим полный квадрат:</p>
<div style="background:var(--sec-acc-soft);border-radius:10px;padding:12px;margin:8px 0;text-align:center">$$a\\left(x + \\dfrac{b}{2a}\\right)^2 = \\dfrac{b^2 - 4ac}{4a}$$</div>
<p>Выражение $\\boxed{D = b^2 - 4ac}$ называется <b>дискриминантом</b>. От его знака зависит, есть ли корни.</p>`);
html += makeCard('rule','Главные формулы','8.2',`
<div style="background:var(--card-soft);border:1.5px solid var(--sec-acc);border-radius:10px;padding:12px;margin:6px 0;text-align:center;font-size:1.1rem">$$D = b^2 - 4ac \\qquad x_{1,2} = \\dfrac{-b \\pm \\sqrt{D}}{2a}$$</div>
<table class="tbl" style="width:100%;border-collapse:collapse;margin-top:8px">
<thead><tr style="background:var(--sec-acc-soft)"><th style="padding:8px;text-align:left">Знак $D$</th><th style="padding:8px;text-align:left">Сколько корней</th><th style="padding:8px;text-align:left">Формула</th></tr></thead>
<tbody>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)">$D > 0$</td><td style="padding:8px;border-bottom:1px solid var(--border)">Два различных</td><td style="padding:8px;border-bottom:1px solid var(--border)">$x_{1,2} = \\dfrac{-b \\pm \\sqrt{D}}{2a}$</td></tr>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)">$D = 0$</td><td style="padding:8px;border-bottom:1px solid var(--border)">Один (кратный)</td><td style="padding:8px;border-bottom:1px solid var(--border)">$x = \\dfrac{-b}{2a}$</td></tr>
<tr><td style="padding:8px">$D < 0$</td><td style="padding:8px">Корней нет</td><td style="padding:8px">—</td></tr>
</tbody>
</table>`);
html += makeCard('algo','Алгоритм решения','8.3',`
<ol style="margin-left:18px;line-height:1.8">
<li>Запишите уравнение в виде $ax^2+bx+c=0$, $a \\neq 0$.</li>
<li>Выпишите коэффициенты $a$, $b$, $c$ со знаками.</li>
<li>Вычислите $D = b^2 - 4ac$.</li>
<li>Сравните $D$ с нулём — определите число корней.</li>
<li>Если $D \\geq 0$, по формуле найдите $x_1$, $x_2$.</li>
</ol>`);
html += makeCard('example','Пример решения','8.4',`
<p><b>Решим:</b> $2x^2 - 5x + 2 = 0$. Здесь $a=2$, $b=-5$, $c=2$.</p>
<p>$D = (-5)^2 - 4 \\cdot 2 \\cdot 2 = 25 - 16 = 9$.</p>
<p>$\\sqrt{D} = 3$. $x_{1,2} = \\dfrac{5 \\pm 3}{4}$, поэтому $x_1 = \\dfrac{1}{2},\\ x_2 = 2$.</p>
<p><b>Проверка:</b> $2 \\cdot 4 - 10 + 2 = 0$. &#10003;</p>`);
/* INTERACTIVE 1: калькулятор дискриминанта */
html += widget('Калькулятор дискриминанта','INTERACT 1','Введите $a$, $b$, $c$ — мгновенно увидите $D$ и корни. Идёт пошаговое вычисление.',`
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label>$a$ = <input type="number" id="p8d-a" value="1" style="width:70px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
<label>$b$ = <input type="number" id="p8d-b" value="-5" style="width:70px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
<label>$c$ = <input type="number" id="p8d-c" value="6" style="width:70px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
</div>
<div id="p8d-out" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;line-height:1.8"></div>`);
/* INTERACTIVE 2: парабола */
html += widget('Парабола: $y = ax^2+bx+c$','INTERACT 2','Двигайте слайдеры — смотрите, как меняется график и сколько раз он пересекает ось OX.',`
<div class="sliders">
<label>$a$ = <b id="p8g-a-val">1</b><input type="range" min="-3" max="3" step="0.1" value="1" id="p8g-a"></label>
<label>$b$ = <b id="p8g-b-val">0</b><input type="range" min="-6" max="6" step="0.2" value="0" id="p8g-b"></label>
<label>$c$ = <b id="p8g-c-val">-4</b><input type="range" min="-8" max="8" step="0.2" value="-4" id="p8g-c"></label>
</div>
<svg id="p8g-svg" viewBox="-10 -10 220 160" style="width:100%;max-width:480px;display:block;margin:10px auto;background:#fafafa;border:1px solid var(--border);border-radius:8px"></svg>
<div id="p8g-info" style="text-align:center;font-weight:600;color:var(--sec-acc-d)"></div>`);
/* INTERACTIVE 3: 3 случая */
html += widget('Три случая дискриминанта','INTERACT 3','Кликните по карточке — увидите пример и график для каждого случая.',`
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:12px">
<div class="case-card" data-case="pos"><h5>$D > 0$</h5><div>Два корня</div></div>
<div class="case-card" data-case="zero"><h5>$D = 0$</h5><div>Один корень</div></div>
<div class="case-card" data-case="neg"><h5>$D < 0$</h5><div>Корней нет</div></div>
</div>
<div id="p8c-detail" style="padding:14px;background:var(--card-soft);border-radius:10px;min-height:140px;display:flex;gap:14px;flex-wrap:wrap;align-items:center"></div>`);
/* INTERACTIVE 4: тренажёр D */
html += widget('Тренажёр: «Сколько корней?»','INTERACT 4','По данному уравнению вычислите $D$ устно и ответьте: сколько корней?',`
<div class="score-display"><span>Задача <b id="p8t-i">1</b> / 10</span><span>Очки: <b id="p8t-score">0</b></span></div>
<div id="p8t-task" style="font-size:1.3rem;text-align:center;padding:16px;background:var(--sec-acc-soft);border-radius:10px;margin-bottom:10px"></div>
<div id="p8t-d-hint" style="text-align:center;color:var(--muted);margin-bottom:8px;font-size:.85rem"></div>
<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
<button class="btn" data-n="2" id="p8t-2">Два</button>
<button class="btn" data-n="1" id="p8t-1">Один</button>
<button class="btn" data-n="0" id="p8t-0">Корней нет</button>
</div>
<div class="feedback" id="p8t-fb" style="display:none;margin-top:10px"></div>
<button class="btn primary" id="p8t-start" style="margin-top:10px">Начать</button>`);
/* INTERACTIVE 5: пошаговый */
html += widget('Пошаговый решатель','INTERACT 5','Введите $a$, $b$, $c$ — система разложит решение по шагам.',`
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label>$a$ = <input type="number" id="p8s-a" value="1" style="width:70px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
<label>$b$ = <input type="number" id="p8s-b" value="-7" style="width:70px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
<label>$c$ = <input type="number" id="p8s-c" value="12" style="width:70px;padding:6px;border:1.5px solid var(--border);border-radius:6px"></label>
<button class="btn primary" id="p8s-go">Решить пошагово</button>
</div>
<div id="p8s-stage" style="padding:14px;background:var(--card-soft);border-radius:10px;min-height:100px"></div>`);
/* INTERACTIVE 6: угадай знак D по графику */
html += widget('«Угадай знак D» — только по графику','INTERACT 6','По параболе определите знак дискриминанта.',`
<div class="score-display"><span>Раунд <b id="p8r-i">1</b> / 8</span><span>Правильно: <b id="p8r-score">0</b></span></div>
<svg id="p8r-svg" viewBox="-10 -10 220 160" style="width:100%;max-width:380px;display:block;margin:10px auto;background:#fafafa;border:1px solid var(--border);border-radius:8px"></svg>
<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
<button class="btn" data-d="pos" id="p8r-pos">$D > 0$</button>
<button class="btn" data-d="zero" id="p8r-zero">$D = 0$</button>
<button class="btn" data-d="neg" id="p8r-neg">$D < 0$</button>
</div>
<div class="feedback" id="p8r-fb" style="display:none;margin-top:10px"></div>
<button class="btn primary" id="p8r-start" style="margin-top:10px">Начать</button>`);
/* Устно/класс/дом */
html += makeCard('oral','Устные вопросы',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>Что такое дискриминант? Какова его формула?</li>
<li>При каком знаке $D$ уравнение имеет два корня? Один? Ни одного?</li>
<li>Как изменится $D$, если уравнение умножить на $-1$?</li>
<li>Найдите $D$ для $x^2 + 2x + 1 = 0$.</li>
</ol>`);
html += makeCard('class','Класс — решите в тетради',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>$x^2 - 7x + 12 = 0$</li>
<li>$2x^2 + 3x - 2 = 0$</li>
<li>$x^2 - 6x + 9 = 0$</li>
<li>$3x^2 + x + 1 = 0$</li>
<li>$5x^2 - 4x - 1 = 0$</li>
</ol>`);
html += makeCard('home','Домашка',null,`
<ol style="margin-left:18px;line-height:1.8">
<li>$x^2 + 5x + 6 = 0$</li>
<li>$2x^2 - 7x + 3 = 0$</li>
<li>$4x^2 - 4x + 1 = 0$</li>
<li>$x^2 + x + 2 = 0$</li>
<li>При каких $m$ уравнение $x^2 - 4x + m = 0$ имеет один корень?</li>
</ol>`);
html += secNav('p7','p9');
box.innerHTML = html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box), 0);
/* INIT INTERACTIVE 1 */
(function initDisc(){
const aE = document.getElementById('p8d-a'), bE = document.getElementById('p8d-b'), cE = document.getElementById('p8d-c');
const out = document.getElementById('p8d-out');
let done = false;
function refresh(){
const a = +aE.value, b = +bE.value, c = +cE.value;
if(!isFinite(a) || a === 0){ out.innerHTML = 'Введите $a \\neq 0$ — иначе уравнение не квадратное.'; renderMath(out); return; }
const D = b*b - 4*a*c;
let s = '<div><b>Уравнение:</b> $' + (a === 1 ? '' : (a === -1 ? '-' : a)) + 'x^2 ' + (b >= 0 ? '+' + b : b) + 'x ' + (c >= 0 ? '+' + c : c) + ' = 0$</div>';
s += '<div><b>Шаг 1.</b> $D = b^2 - 4ac = (' + b + ')^2 - 4 \\cdot (' + a + ') \\cdot (' + c + ') = ' + (b*b) + ' - ' + (4*a*c) + ' = ' + D + '$</div>';
if(D > 0){ const r1 = (-b - Math.sqrt(D))/(2*a), r2 = (-b + Math.sqrt(D))/(2*a); s += '<div><b>Шаг 2.</b> $D > 0 \\Rightarrow$ два корня:</div><div>$x_{1,2} = \\dfrac{-(' + b + ') \\pm \\sqrt{' + D + '}}{2 \\cdot ' + a + '}$</div><div><b>Ответ:</b> $x_1 = ' + fmt(r1) + ',\\ x_2 = ' + fmt(r2) + '$</div>'; }
else if(D === 0){ s += '<div><b>Шаг 2.</b> $D = 0 \\Rightarrow$ один корень: $x = ' + fmt(-b/(2*a)) + '$</div>'; }
else s += '<div><b>Шаг 2.</b> $D < 0 \\Rightarrow$ корней нет.</div>';
out.innerHTML = s; renderMath(out);
if(!done){ done = true; setTimeout(()=>{ achievement('p8_disc'); bumpProgress('p8', 12); }, 300); }
}
[aE,bE,cE].forEach(e => e.addEventListener('input', refresh));
refresh();
})();
/* INIT INTERACTIVE 2 */
(function initParabola(){
const aE = document.getElementById('p8g-a'), bE = document.getElementById('p8g-b'), cE = document.getElementById('p8g-c');
const svg = document.getElementById('p8g-svg'), info = document.getElementById('p8g-info');
let done = false;
const W = 200, H = 140, x0 = W/2, y0 = H/2, sx = 14, sy = 14;
function refresh(){
const a = +aE.value, b = +bE.value, c = +cE.value;
document.getElementById('p8g-a-val').textContent = a.toFixed(1);
document.getElementById('p8g-b-val').textContent = b.toFixed(1);
document.getElementById('p8g-c-val').textContent = c.toFixed(1);
let path = '';
for(let i = 0; i <= 200; i++){
const x = -7 + i * 14 / 200;
const y = a*x*x + b*x + c;
const cx = x0 + x*sx, cy = y0 - y*sy;
path += (i === 0 ? 'M' : 'L') + cx.toFixed(2) + ',' + cy.toFixed(2) + ' ';
}
let svgC = '<line x1="0" y1="' + y0 + '" x2="' + W + '" y2="' + y0 + '" stroke="#999" stroke-width="0.5"/>';
svgC += '<line x1="' + x0 + '" y1="0" x2="' + x0 + '" y2="' + H + '" stroke="#999" stroke-width="0.5"/>';
svgC += '<path d="' + path + '" fill="none" stroke="var(--sec-acc)" stroke-width="1.8"/>';
if(a !== 0){
const D = b*b - 4*a*c;
if(D >= 0){
const r1 = (-b - Math.sqrt(D))/(2*a), r2 = (-b + Math.sqrt(D))/(2*a);
[r1, r2].forEach(r=>{ svgC += '<circle cx="' + (x0 + r*sx).toFixed(2) + '" cy="' + y0 + '" r="3" fill="#e91e63"/>'; });
info.innerHTML = 'D = ' + D.toFixed(2) + ' · ' + (D > 0 ? 'два корня: ' + fmt(r1) + ', ' + fmt(r2) : 'один корень: ' + fmt(r1));
} else info.innerHTML = 'D = ' + D.toFixed(2) + ' · корней нет (парабола не пересекает OX)';
}
svg.innerHTML = svgC;
if(!done){ done = true; setTimeout(()=>{ achievement('p8_parab'); bumpProgress('p8', 12); }, 400); }
}
[aE,bE,cE].forEach(e => e.addEventListener('input', refresh));
refresh();
})();
/* INIT INTERACTIVE 3 */
(function initCases(){
const data = {
pos: { eq: 'x^2 - 5x + 6 = 0', D: 1, roots: 'x_1 = 2,\\ x_2 = 3', svg: parab(1, -5, 6) },
zero:{ eq: 'x^2 - 4x + 4 = 0', D: 0, roots: 'x = 2', svg: parab(1, -4, 4) },
neg: { eq: 'x^2 + x + 1 = 0', D:-3, roots: 'корней нет', svg: parab(1, 1, 1) },
};
function parab(a, b, c){
const W = 180, H = 130, x0 = W/2, y0 = H/2, sx = 11, sy = 11;
let path = '';
for(let i = 0; i <= 200; i++){
const x = -8 + i * 16 / 200;
const y = a*x*x + b*x + c;
const cx = x0 + x*sx, cy = y0 - y*sy;
if(cy >= -20 && cy <= H + 20) path += (path ? 'L' : 'M') + cx.toFixed(2) + ',' + cy.toFixed(2) + ' ';
}
let s = '<svg viewBox="0 0 ' + W + ' ' + H + '" style="width:180px;height:130px;background:#fff;border:1px solid #ddd;border-radius:6px">';
s += '<line x1="0" y1="' + y0 + '" x2="' + W + '" y2="' + y0 + '" stroke="#999" stroke-width="0.5"/>';
s += '<line x1="' + x0 + '" y1="0" x2="' + x0 + '" y2="' + H + '" stroke="#999" stroke-width="0.5"/>';
s += '<path d="' + path + '" fill="none" stroke="#e91e63" stroke-width="1.5"/></svg>';
return s;
}
const detail = document.getElementById('p8c-detail');
let seen = new Set();
document.querySelectorAll('.case-card').forEach(c=>{
c.addEventListener('click', ()=>{
document.querySelectorAll('.case-card').forEach(x=>x.classList.remove('active'));
c.classList.add('active');
const d = data[c.dataset.case];
detail.innerHTML = '<div style="flex:1;min-width:180px"><div><b>Пример:</b> $' + d.eq + '$</div><div><b>$D$ =</b> ' + d.D + '</div><div><b>Корни:</b> $' + d.roots + '$</div></div><div>' + d.svg + '</div>';
renderMath(detail);
seen.add(c.dataset.case);
if(seen.size === 3){ achievement('p8_cases'); bumpProgress('p8', 12); }
});
});
document.querySelector('.case-card').click();
})();
/* INIT INTERACTIVE 4 */
(function initTrain(){
let cur = null, i = 1, score = 0;
function gen(){
const t = Math.floor(Math.random()*3);
if(t === 0){
const r1 = -3 + Math.floor(Math.random()*7), r2 = -3 + Math.floor(Math.random()*7);
if(r1 === r2) return gen();
return { a:1, b:-(r1+r2), c:r1*r2, n:2 };
}
if(t === 1){
const r = -4 + Math.floor(Math.random()*9);
return { a:1, b:-2*r, c:r*r, n:1 };
}
const b = -3 + Math.floor(Math.random()*7), c = 3 + Math.floor(Math.random()*8);
return { a:1, b, c, n:0 };
}
function newOne(){
cur = gen();
const a = cur.a, b = cur.b, c = cur.c;
const txt = 'x^2 ' + (b >= 0 ? '+ ' + b : '- ' + Math.abs(b)) + 'x ' + (c >= 0 ? '+ ' + c : '- ' + Math.abs(c)) + ' = 0';
document.getElementById('p8t-task').innerHTML = '$' + txt + '$';
document.getElementById('p8t-i').textContent = i;
document.getElementById('p8t-d-hint').textContent = 'Подскажу: $D = b^2 - 4ac$';
renderMath(document.getElementById('p8t-task'));
document.getElementById('p8t-fb').style.display = 'none';
}
function answer(n){
const D = cur.b*cur.b - 4*cur.a*cur.c;
const fb = document.getElementById('p8t-fb');
fb.style.display = 'block';
if(n === cur.n){ score++; feedback(fb, true, '&#10003; Верно! D = ' + D); }
else feedback(fb, false, 'Нет. D = ' + D + ' &rarr; ' + (cur.n === 2 ? '2 корня' : cur.n === 1 ? '1 корень' : 'корней нет'));
document.getElementById('p8t-score').textContent = score;
if(i >= 10){ setTimeout(()=>{ feedback(fb, score >= 7, 'Итог: ' + score + '/10'); if(score >= 7){ achievement('p8_disc_train'); bumpProgress('p8', 16); confetti(); } }, 600); }
else { i++; setTimeout(newOne, 800); }
}
document.getElementById('p8t-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p8t-score').textContent = 0; newOne(); });
document.getElementById('p8t-2').addEventListener('click', ()=>answer(2));
document.getElementById('p8t-1').addEventListener('click', ()=>answer(1));
document.getElementById('p8t-0').addEventListener('click', ()=>answer(0));
})();
/* INIT INTERACTIVE 5 */
(function initSteps(){
document.getElementById('p8s-go').addEventListener('click', ()=>{
const a = +document.getElementById('p8s-a').value;
const b = +document.getElementById('p8s-b').value;
const c = +document.getElementById('p8s-c').value;
const stage = document.getElementById('p8s-stage');
if(!a){ stage.innerHTML = '<p>$a$ не может быть нулём.</p>'; return; }
const D = b*b - 4*a*c;
let html = '<p><b>Шаг 1:</b> $a = ' + a + ',\\ b = ' + b + ',\\ c = ' + c + '$</p>';
html += '<p><b>Шаг 2:</b> $D = b^2 - 4ac = ' + b + '^2 - 4 \\cdot ' + a + ' \\cdot ' + c + ' = ' + (b*b) + ' - ' + (4*a*c) + ' = ' + D + '$</p>';
html += '<p><b>Шаг 3:</b> ' + (D > 0 ? '$D > 0$ &rarr; два корня' : D === 0 ? '$D = 0$ &rarr; один корень' : '$D < 0$ &rarr; корней нет') + '</p>';
if(D >= 0){
html += '<p><b>Шаг 4:</b> $\\sqrt{D} = ' + fmt(Math.sqrt(D)) + '$</p>';
if(D > 0){ const r1 = (-b - Math.sqrt(D))/(2*a), r2 = (-b + Math.sqrt(D))/(2*a); html += '<p><b>Шаг 5:</b> $x_1 = \\dfrac{' + (-b) + ' - ' + fmt(Math.sqrt(D)) + '}{' + (2*a) + '} = ' + fmt(r1) + ',\\ x_2 = ' + fmt(r2) + '$</p>'; }
else html += '<p><b>Шаг 5:</b> $x = \\dfrac{' + (-b) + '}{' + (2*a) + '} = ' + fmt(-b/(2*a)) + '$</p>';
}
stage.innerHTML = html; renderMath(stage);
achievement('p8_steps'); bumpProgress('p8', 12);
});
})();
/* INIT INTERACTIVE 6 */
(function initGraph(){
let cur = null, i = 1, score = 0;
function gen(){
const t = Math.floor(Math.random()*3);
const a = Math.random() < 0.5 ? 1 : -1;
if(t === 0){
const r1 = -3 + Math.floor(Math.random()*7), r2 = -3 + Math.floor(Math.random()*7);
if(r1 === r2) return gen();
return { a, b:-a*(r1+r2), c:a*r1*r2, kind:'pos' };
}
if(t === 1){
const r = -3 + Math.floor(Math.random()*7);
return { a, b:-2*a*r, c:a*r*r, kind:'zero' };
}
const v = 1 + Math.floor(Math.random()*4);
return { a:1, b:0, c:v, kind:'neg' };
}
function draw(){
const W = 200, H = 140, x0 = W/2, y0 = H/2, sx = 14, sy = 14;
let path = '';
for(let k = 0; k <= 200; k++){
const x = -7 + k * 14 / 200;
const y = cur.a*x*x + cur.b*x + cur.c;
const cx = x0 + x*sx, cy = y0 - y*sy;
if(cy >= -40 && cy <= H + 40) path += (path ? 'L' : 'M') + cx.toFixed(2) + ',' + cy.toFixed(2) + ' ';
}
let s = '<line x1="0" y1="' + y0 + '" x2="' + W + '" y2="' + y0 + '" stroke="#999" stroke-width="0.5"/>';
s += '<line x1="' + x0 + '" y1="0" x2="' + x0 + '" y2="' + H + '" stroke="#999" stroke-width="0.5"/>';
s += '<path d="' + path + '" fill="none" stroke="#e91e63" stroke-width="1.6"/>';
document.getElementById('p8r-svg').innerHTML = s;
}
function newOne(){
cur = gen();
document.getElementById('p8r-i').textContent = i;
document.getElementById('p8r-fb').style.display = 'none';
draw();
}
function answer(k){
const fb = document.getElementById('p8r-fb');
fb.style.display = 'block';
if(k === cur.kind){ score++; feedback(fb, true, '&#10003;'); }
else feedback(fb, false, 'Нет. Правильно: ' + (cur.kind === 'pos' ? 'D > 0' : cur.kind === 'zero' ? 'D = 0' : 'D < 0'));
document.getElementById('p8r-score').textContent = score;
if(i >= 8){ setTimeout(()=>{ feedback(fb, score >= 6, 'Итог: ' + score + '/8'); if(score >= 6){ achievement('p8_graph'); bumpProgress('p8', 14); confetti(); } }, 600); }
else { i++; setTimeout(newOne, 800); }
}
document.getElementById('p8r-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p8r-score').textContent = 0; newOne(); });
document.getElementById('p8r-pos').addEventListener('click', ()=>answer('pos'));
document.getElementById('p8r-zero').addEventListener('click', ()=>answer('zero'));
document.getElementById('p8r-neg').addEventListener('click', ()=>answer('neg'));
})();
}
</script>
</body>
</html>