Files
Learn_System/frontend/textbooks/geometry_8_ch3.html
T
Maxim Dolgolyov ac3aaeadb2 feat(geom8): Wave 2 Главы 3 — §4-§5 (параллельная стороне, первый признак ДД)
§4 Свойство параллельной прямой: SVG-треугольник со слайдером положения
MN (t=0..1), live коэффициент подобия k, подсветка △AMN; 5-шаговое
доказательство через соответственные углы; калькулятор AM,AB,BC→MN+k;
DnD пропорция верна/неверна; тренажёр; босс.

§5 Первый признак подобия (по двум углам): SVG двух треугольников
с 3 слайдерами (α, β, k), оба строятся через теорему синусов с
автоподобием; 5-шаговое доказательство через сумму углов 180° и
вспомогательное построение; DnD подобны/не подобны по углам; калькулятор
2 угла + сторона → соответствующая сторона; тренажёр; босс.

File: 1557 → 2338 LOC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:25:45 +03:00

2338 lines
200 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>Геометрия 8 · Глава 3 · Подобные треугольники</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#fafafa; --card:#fff; --card-soft:#faf5ff; --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:#7c3aed; --pri2:#6d28d9; --pri-soft:#ede9fe;
--acc:#8b5cf6; --acc2:#7c3aed; --acc-soft:#ddd6fe;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#08030e; --card:#10071a; --card-soft:#140820; --text:#f3e8ff; --ink:#f3e8ff; --muted:#9f7ec0; --border:#2a1040}
*{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,#3b0764 0%,#7c3aed 55%,#a78bfa 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(167,139,250,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 3';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(220,200,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 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(124,58,237,.32)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(124,58,237,.18);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(124,58,237,.22);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(160px,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(124,58,237,.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,#f5f3ff,#ede9fe)}
.psel-card.final .psel-num{color:var(--warn)}
/* SECTION COLORS — purple/violet/indigo spectrum */
.sec[id="sec-p1"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p2"] { --sec-acc:#8b5cf6; --sec-acc-d:#7c3aed; --sec-acc-soft:#ddd6fe; }
.sec[id="sec-p3"] { --sec-acc:#a78bfa; --sec-acc-d:#8b5cf6; --sec-acc-soft:#f3f0ff; }
.sec[id="sec-p4"] { --sec-acc:#6366f1; --sec-acc-d:#4f46e5; --sec-acc-soft:#e0e7ff; }
.sec[id="sec-p5"] { --sec-acc:#4f46e5; --sec-acc-d:#4338ca; --sec-acc-soft:#e0e7ff; }
.sec[id="sec-p6"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p7"] { --sec-acc:#8b5cf6; --sec-acc-d:#7c3aed; --sec-acc-soft:#ddd6fe; }
.sec[id="sec-p8"] { --sec-acc:#c026d3; --sec-acc-d:#a21caf; --sec-acc-soft:#fae8ff; }
.sec[id="sec-p9"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-final3"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.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}
.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(124,58,237,.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(124,58,237,.12)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}.card-icon.class{background:#3b82f6}.card-icon.home{background:#f97316}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.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}
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.btn.small{padding:5px 11px;font-size:.78rem}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.feedback{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)}
.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(139,92,246,.15);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
.tbl{width:100%;border-collapse:collapse;margin:12px 0;font-size:.88rem}
.tbl th,.tbl td{padding:7px 10px;border:1px solid var(--border);text-align:center}
.tbl th{background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));font-weight:700}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#7c3aed,#8b5cf6);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(124,58,237,.45);z-index:1002;display:none;align-items:center;gap:8px;animation:achIn .45s cubic-bezier(.34,1.56,.64,1) forwards;max-width:340px}
.ach-popup.show{display:flex}
@keyframes achIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
.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-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.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(124,58,237,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none;animation:fadeIn .18s ease}
.col-side-backdrop.show{display:block}
@media(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;animation:tipIn .15s ease}
.gloss-tip b{color:var(--sec-acc-d,var(--pri2));font-size:.92rem}
@keyframes tipIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
.search-modal.show{display:flex;animation:fadeIn .15s ease}
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
.search-results{flex:1;overflow-y:auto;padding:6px 0}
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border-left:0;border-right:0;border-top:0;width:100%;color:var(--text)}
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px;background:var(--card-soft,transparent)}
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Геометрия 8 · Глава 3</h1>
<div class="hdr-sub">Подобные треугольники</div>
</div>
<div class="hdr-side">
<a href="/textbook/geometry-8" class="hdr-btn" title="К Геометрии 8 — все главы">
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
К геометрии 8
</a>
<button id="search-btn" class="hdr-btn" title="Поиск (Ctrl+K)">
<svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg>
Поиск
</button>
<button id="sidebar-btn" class="hdr-btn" title="Шпаргалка">
<svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg>
Шпаргалка
</button>
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<span id="theme-lab">Тёмная</span>
</button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Подобие: пропорции и три признака</h2>
<p>Подобные фигуры имеют одинаковую форму, но разный размер. Ключевые инструменты: <b>теорема Фалеса</b> (обобщённая), три признака подобия треугольников, свойство <b>биссектрисы</b>. Итог — отношение площадей подобных треугольников равно квадрату коэффициента подобия.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')">
<svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg>
Начать § 1
</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" title="Опыт"></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="&#8741;"><div class="sec-header"><span class="sec-num">§ 1</span><h2 class="sec-h">Теорема Фалеса (обобщённая)</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="m:n"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Деление отрезка в отношении m : n</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="&#8764;"><div class="sec-header"><span class="sec-num">§ 3</span><h2 class="sec-h">Определение подобных треугольников</h2></div><div id="p3-body"></div></section>
<section id="sec-p4" class="sec" data-watermark="&#8741;k"><div class="sec-header"><span class="sec-num">§ 4</span><h2 class="sec-h">Свойство прямой, параллельной стороне треугольника</h2></div><div id="p4-body"></div></section>
<section id="sec-p5" class="sec" data-watermark="&#8736;&#8736;"><div class="sec-header"><span class="sec-num">§ 5</span><h2 class="sec-h">Первый признак подобия. Следствие</h2></div><div id="p5-body"></div></section>
<section id="sec-p6" class="sec" data-watermark="&#8710;"><div class="sec-header"><span class="sec-num">§ 6</span><h2 class="sec-h">Второй признак подобия</h2></div><div id="p6-body"></div></section>
<section id="sec-p7" class="sec" data-watermark="&#8710;&#8764;"><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="&#8901;"><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="k&#178;"><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-final3" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#7c3aed,#8b5cf6)">Финал главы</span><h2 class="sec-h">Итоги. Боссы главы 3</h2></div><div id="final3-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">Интерактивный учебник «Геометрия 8» · Глава 3 · Подобные треугольники · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<div id="gloss-tip" class="gloss-tip"></div>
<div id="search-modal" class="search-modal" role="dialog" aria-label="Поиск по главе">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск: признак подобия, теорема…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot"><span><kbd>&#8593;&#8595;</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE={ current:'p1', progress:{p1:0,p2:0,p3:0,p4:0,p5:0,p6:0,p7:0,p8:0,p9:0,final3:0}, achievements:new Map(), xp:0, level:1 };
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:'Начало главы 3!', ch3_done:'Подобие изучено!' };
function loadProgress(){ try{ const s=localStorage.getItem('geometry8_ch3_progress');if(s)Object.assign(STATE.progress,JSON.parse(s)); const a=localStorage.getItem('geometry8_ch3_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('geometry8_xp')||0);STATE.level=calcLevel(STATE.xp); }catch(e){} }
function saveProgress(){ try{localStorage.setItem('geometry8_ch3_progress',JSON.stringify(STATE.progress));localStorage.setItem('geometry8_ch3_achievements',JSON.stringify(Object.fromEntries(STATE.achievements)));localStorage.setItem('geometry8_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 _TB_SLUG='geometry-8-ch3';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,'geometry8-ch3-'+(src||'misc'));if(STATE.level>prev){const pop=document.getElementById('ach-popup');if(pop){document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!';pop.classList.add('show');setTimeout(()=>pop.classList.remove('show'),2600);}if(window.confetti)try{confetti();}catch(e){}}}
const TOTAL_PARAS=10;
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:'p1',num:'§ 1',name:'Теорема Фалеса (обобщённая)',sub:'Параллельные и пропорции'},
{id:'p2',num:'§ 2',name:'Деление отрезка m:n',sub:'Внутреннее и внешнее'},
{id:'p3',num:'§ 3',name:'Подобные треугольники',sub:'Определение и коэффициент'},
{id:'p4',num:'§ 4',name:'Прямая || стороне',sub:'Отсекает подобный треугольник'},
{id:'p5',num:'§ 5',name:'1-й признак подобия',sub:'ДД — два угла'},
{id:'p6',num:'§ 6',name:'2-й признак подобия',sub:'СДС — пропорц. стороны и угол'},
{id:'p7',num:'§ 7',name:'3-й признак подобия',sub:'ССС — три пропорц. стороны'},
{id:'p8',num:'§ 8',name:'Биссектриса треугольника',sub:'Делит сторону пропорционально'},
{id:'p9',num:'§ 9',name:'Отношение площадей',sub:'S₁/S₂ = k²'},
{id:'final3',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={p1:()=>buildP1(),p2:()=>buildP2(),p3:()=>buildP3(),p4:()=>buildP4(),p5:()=>buildP5(),p6:()=>buildP6stub(),p7:()=>buildP7stub(),p8:()=>buildP8stub(),p9:()=>buildP9stub(),final3:()=>buildFinal3stub()};
function ensureBuilt(id){if(BUILT.has(id))return;const fn=BUILDERS[id];if(fn){fn();BUILT.add(id);}}
function goTo(id){STATE.current=id;ensureBuilt(id);document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));const el=document.getElementById('sec-'+id);if(el)el.classList.add('active');document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active',c.dataset.id===id));buildSidebar(id);window.scrollTo({top:0,behavior:'smooth'});if((STATE.progress[id]||0)<10)bumpProgress(id,10);if(window.renderMathInElement)setTimeout(()=>renderMath(el),0);setTimeout(()=>{try{wrapGlossary(el);}catch(e){}},60);markLastPara(id);}
const SIDEBARS={
p1:{title:'Шпаргалка § 1',rows:[['Теорема Фалеса','параллельные прямые пропорционально рассекают две прямые'],['Следствие','прямая || стороне треугольника отсекает подобный треугольник']]},
p2:{title:'Шпаргалка § 2',rows:[['Внутреннее деление','точка между крайними'],['Отношение','$AM:MB = m:n$']]},
p3:{title:'Шпаргалка § 3',rows:[['Подобие','$\\triangle ABC \\sim \\triangle A\'B\'C\'$'],['Коэффициент','$k = AB/A\'B\' = BC/B\'C\'$'],['Углы','попарно равны']]},
p4:{title:'Шпаргалка § 4',rows:[['Прямая || стороне','отсекает треугольник, подобный данному'],['Пропорция','отрезки пропорциональны']]},
p5:{title:'Шпаргалка § 5',rows:[['1-й признак (ДД)','два угла одного равны двум углам другого'],['Следствие','два прямоугольных с общим острым углом подобны']]},
p6:{title:'Шпаргалка § 6',rows:[['2-й признак (СДС)','два угла по стороне пропорциональны'],['$\\dfrac{AB}{A\'B\'} = \\dfrac{AC}{A\'C\'}$, угол $A = A\'$','']]},
p7:{title:'Шпаргалка § 7',rows:[['3-й признак (ССС)','все три пары сторон пропорциональны'],['$\\dfrac{a}{a\'}=\\dfrac{b}{b\'}=\\dfrac{c}{c\'}$','']]},
p8:{title:'Шпаргалка § 8',rows:[['Биссектриса','делит противоположную сторону в отношении прилежащих сторон'],['$\\dfrac{BD}{DC}=\\dfrac{AB}{AC}$','']]},
p9:{title:'Шпаргалка § 9',rows:[['Отношение площадей','$\\dfrac{S_1}{S_2} = k^2$'],['$k$','коэффициент подобия']]},
final3:{title:'Финал главы',rows:[['9 параграфов','подобие изучено'],['3 признака','ДД, СДС, ССС']]},
};
const TIPS=[
{sec:'p1',html:'Теорема Фалеса (обобщённая): несколько параллельных прямых пропорционально пересекают любые две секущие.'},
{sec:'p2',html:'Деление в отношении $m:n$: $AM = \\dfrac{m}{m+n}\\cdot AB$.'},
{sec:'p3',html:'Коэффициент подобия $k$ — отношение <b>соответственных</b> сторон.'},
{sec:'p4',html:'Прямая, параллельная стороне треугольника, отсекает треугольник, подобный исходному.'},
{sec:'p5',html:'1-й признак: два угла равны. Это наиболее часто используемый признак!'},
{sec:'p6',html:'2-й признак (СДС): стороны пропорциональны и угол между ними равен.'},
{sec:'p7',html:'3-й признак (ССС): все три пары сторон пропорциональны.'},
{sec:'p8',html:'Биссектриса угла треугольника делит противоположную сторону в отношении смежных сторон.'},
{sec:'p9',html:'Площади подобных фигур относятся как <b>квадрат</b> коэффициента подобия: $S_1/S_2 = k^2$.'},
{sec:'final3',html:'Три признака подобия — главный инструмент геометрических доказательств.'},
];
function buildSidebar(id){const box=document.getElementById('sidebar-content');const sb=SIDEBARS[id]||SIDEBARS.p1;let html='';const xpForLv=_xpForLevel(STATE.level),xpNext=_xpForLevel(STATE.level+1);const xpInLv=STATE.xp-xpForLv,xpRange=xpNext-xpForLv,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];html+=`<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#4c1d95;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('geometry8_ch3_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('geometry8_ch3_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){elm.className='feedback '+(ok?'ok':'fail');elm.innerHTML=text||(ok?'&#10003; Верно!':'&#10007; Неверно');}
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]?' \xb7 '+title:''}</div>${num?`<div class="card-num">${num}</div>`:''}</div><div class="card-body">${body}</div></div>`;}
function secNav(prev,next){const NAMES={p1:'§1',p2:'§2',p3:'§3',p4:'§4',p5:'§5',p6:'§6',p7:'§7',p8:'§8',p9:'§9',final3:'Финал'};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;}
let _confettiCanvas=null,_confettiParticles=[],_confettiRaf=null;
function confetti(){if(!_confettiCanvas){_confettiCanvas=document.createElement('canvas');_confettiCanvas.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999';document.body.appendChild(_confettiCanvas);}const c=_confettiCanvas;c.width=window.innerWidth;c.height=window.innerHeight;const ctx=c.getContext('2d');const colors=['#7c3aed','#8b5cf6','#a78bfa','#f59e0b','#10b981'];for(let i=0;i<80;i++){_confettiParticles.push({x:window.innerWidth/2+(Math.random()-.5)*200,y:window.innerHeight/2,vx:(Math.random()-.5)*14,vy:-10-Math.random()*10,g:.4,life:100,color:colors[i%colors.length],r:4+Math.random()*4,rot:0,vRot:(Math.random()-.5)*.3});}if(_confettiRaf)cancelAnimationFrame(_confettiRaf);function frame(){ctx.clearRect(0,0,c.width,c.height);_confettiParticles=_confettiParticles.filter(p=>{p.x+=p.vx;p.y+=p.vy;p.vy+=p.g;p.life--;p.rot+=p.vRot;ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);ctx.fillStyle=p.color;ctx.fillRect(-p.r,-p.r/2,p.r*2,p.r);ctx.restore();return p.life>0&&p.y<c.height+50;});if(_confettiParticles.length>0)_confettiRaf=requestAnimationFrame(frame);else{ctx.clearRect(0,0,c.width,c.height);_confettiRaf=null;}}frame();}
const GLOSSARY=[
{term:'пропорциональность',def:'Равенство двух отношений: $a/b = c/d$. Числа $a,b,c,d$ называются пропорциональными.',sec:'p1',aliases:['пропорциональность','пропорциональны','пропорциональные','пропорциональных','пропорциональными']},
{term:'подобные треугольники',def:'Треугольники, у которых углы равны попарно и стороны пропорциональны.',sec:'p3',aliases:['подобные треугольники','подобных треугольников','подобными треугольниками','подобный треугольник','подобного треугольника']},
{term:'коэффициент подобия',def:'Отношение соответственных сторон подобных треугольников.',sec:'p3',aliases:['коэффициент подобия','коэффициента подобия','коэффициенте подобия']},
{term:'биссектриса',def:'Луч из вершины угла, делящий угол пополам.',sec:'p8',aliases:['биссектриса','биссектрисы','биссектрису','биссектрисой']},
{term:'теорема Фалеса',def:'Параллельные прямые, пересекая две секущие, отсекают пропорциональные отрезки.',sec:'p1',aliases:['теорема Фалеса','теореме Фалеса','теоремы Фалеса']},
{term:'многоугольник',def:'Замкнутая ломаная линия, образующая плоскую фигуру.',sec:'p1',aliases:['многоугольник','многоугольника','многоугольников']},
{term:'медиана',def:'Отрезок от вершины до середины противоположной стороны.',sec:'p1',aliases:['медиана','медианы','медиан','медиану']},
{term:'площадь',def:'Числовая мера размера плоской фигуры.',sec:'p9',aliases:['площадь','площади','площадью']},
{term:'параллелограмм',def:'Четырёхугольник, у которого противоположные стороны параллельны.',sec:'p1',aliases:['параллелограмм','параллелограмма','параллелограммы']},
{term:'теорема Пифагора',def:'$a^2+b^2=c^2$ в прямоугольном треугольнике.',sec:'p9',aliases:['теорема Пифагора','теоремы Пифагора','теореме Пифагора']},
{term:'касательная',def:'Прямая, касающаяся окружности в одной точке.',sec:'p1',aliases:['касательная','касательной','касательную']},
{term:'вписанный угол',def:'Угол с вершиной на окружности, стороны которого — хорды.',sec:'p1',aliases:['вписанный угол','вписанного угла']},
{term:'центральный угол',def:'Угол с вершиной в центре окружности.',sec:'p1',aliases:['центральный угол','центрального угла']},
{term:'хорда',def:'Отрезок, соединяющий две точки окружности.',sec:'p1',aliases:['хорда','хорды','хорде','хорд']},
{term:'трапеция',def:'Четырёхугольник с одной парой параллельных сторон.',sec:'p1',aliases:['трапеция','трапеции','трапецию']},
{term:'диагональ',def:'Отрезок, соединяющий несмежные вершины многоугольника.',sec:'p1',aliases:['диагональ','диагонали','диагоналей']},
{term:'гипотенуза',def:'Сторона прямоугольного треугольника напротив прямого угла.',sec:'p1',aliases:['гипотенуза','гипотенузы','гипотенузу']},
{term:'высота',def:'Перпендикуляр из вершины на противоположную сторону.',sec:'p1',aliases:['высота','высоты','высоту']},
{term:'пифагорова тройка',def:'Три натуральных числа $(a,b,c)$ с $a^2+b^2=c^2$.',sec:'p9',aliases:['пифагорова тройка','пифагоровы тройки']},
{term:'секущая',def:'Прямая, пересекающая окружность в двух точках.',sec:'p1',aliases:['секущая','секущей','секущую']},
{term:'хорда',def:'Отрезок, соединяющий две точки окружности.',sec:'p1',aliases:['хорда','хорды','хорд']},
];
function wrapGlossary(root){if(!root||root.__glossDone)return;const allAliases=[];GLOSSARY.forEach((g,i)=>g.aliases.forEach(a=>allAliases.push({a,i})));allAliases.sort((x,y)=>y.a.length-x.a.length);const re=new RegExp('(?<![\\w\\u0400-\\u04ff-])('+allAliases.map(x=>x.a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('|')+')(?![\\w\\u0400-\\u04ff-])','iu');const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,{acceptNode(node){const p=node.parentElement;if(!p)return NodeFilter.FILTER_REJECT;if(p.closest('.katex,.gloss-term,button,input,select,.wg-badge,.card-icon,.sec-num,.psel-num,.hdr,.ach-popup,script,style,.search-modal,.sidecard,.gloss-tip'))return NodeFilter.FILTER_REJECT;if(!re.test(node.nodeValue))return NodeFilter.FILTER_REJECT;return NodeFilter.FILTER_ACCEPT;}});const nodes=[];let n;while((n=walker.nextNode()))nodes.push(n);nodes.forEach(node=>{const text=node.nodeValue;const out=document.createDocumentFragment();let cursor=0;const global=new RegExp(re.source,'giu');let m;while((m=global.exec(text))!==null){if(m.index>cursor)out.appendChild(document.createTextNode(text.slice(cursor,m.index)));const found=m[0].toLowerCase();const hit=allAliases.find(x=>x.a.toLowerCase()===found);const g=hit?GLOSSARY[hit.i]:null;const sp=document.createElement('span');sp.className='gloss-term';sp.dataset.gloss=g?g.term:'';sp.textContent=m[0];out.appendChild(sp);cursor=m.index+m[0].length;}if(cursor<text.length)out.appendChild(document.createTextNode(text.slice(cursor)));node.parentNode.replaceChild(out,node);});root.__glossDone=true;}
function initGlossaryTip(){const tip=document.getElementById('gloss-tip');if(!tip)return;let lockOpen=null;function show(elm){const g=GLOSSARY.find(x=>x.term===elm.dataset.gloss);if(!g)return;tip.innerHTML='<b>'+g.term[0].toUpperCase()+g.term.slice(1)+'</b><div style="margin-top:4px">'+g.def+'</div><div style="margin-top:6px;font-size:.72rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">См. § '+g.sec.replace('p','')+'</div>';if(window.renderMathInElement)renderMath(tip);const r=elm.getBoundingClientRect();tip.classList.add('show');const tw=tip.offsetWidth,th=tip.offsetHeight;let left=r.left,top=r.bottom+8;if(left+tw>window.innerWidth-12)left=window.innerWidth-tw-12;if(top+th>window.innerHeight-12)top=r.top-th-8;tip.style.left=Math.max(8,left)+'px';tip.style.top=Math.max(8,top)+'px';}function hide(){tip.classList.remove('show');}document.addEventListener('mouseover',e=>{const elm=e.target.closest&&e.target.closest('.gloss-term');if(elm&&!lockOpen)show(elm);});document.addEventListener('mouseout',e=>{const elm=e.target.closest&&e.target.closest('.gloss-term');if(elm&&!lockOpen)hide();});document.addEventListener('click',e=>{const elm=e.target.closest&&e.target.closest('.gloss-term');if(elm){if(lockOpen===elm){lockOpen=null;hide();}else{lockOpen=elm;show(elm);}}else if(lockOpen&&!e.target.closest('.gloss-tip')){lockOpen=null;hide();}});}
const SEARCH_INDEX=(function(){const arr=[];PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));GLOSSARY.forEach(g=>arr.push({kind:'Понятие',title:g.term,desc:g.def.replace(/\$/g,''),sec:g.sec,gloss:g.term}));[['Формула','Теорема Фалеса — пропорциональность','§1','p1'],['Формула','1-й признак подобия — два угла','§5','p5'],['Формула','Биссектриса: BD/DC = AB/AC','§8','p8'],['Формула','Отношение площадей = k²','§9','p9']].forEach(([k,t,d,s])=>arr.push({kind:k,title:t,desc:d,sec:s}));return arr;})();
function initSearch(){const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');if(!modal||!inp||!out)return;let cur=0,rows=[];function score(q,it){const t=(it.title+' '+it.desc).toLowerCase();if(t.includes(q))return 100+(it.title.toLowerCase().startsWith(q)?50:0);let s=0;q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;});return s;}function rank(q){q=q.trim().toLowerCase();if(!q)return SEARCH_INDEX.slice(0,12);return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it);}function render(){cur=0;if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;}out.innerHTML=rows.map((r,i)=>`<button class="search-row${i===0?' active':''}" data-i="${i}"><div class="sr-kind">${r.kind}</div><div class="sr-title">${r.title}</div>${r.desc?`<div class="sr-desc">${r.desc.length>90?r.desc.slice(0,90)+'…':r.desc}</div>`:''}</button>`).join('');out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();}));}function pick(){const r=rows[cur];if(!r)return;close();goTo(r.sec);if(r.gloss){setTimeout(()=>{const sec=document.getElementById('sec-'+r.sec);const elm=sec&&sec.querySelector('[data-gloss="'+r.gloss+'"]');if(elm){elm.scrollIntoView({behavior:'smooth',block:'center'});elm.style.transition='background .3s';elm.style.background='var(--warn,#f59e0b)';setTimeout(()=>{elm.style.background='';},1400);}},400);}}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();initGlossaryTip();initSearch();buildParaSelector();refreshProgressUI();loadServerReadState();goTo('p1');setTimeout(()=>achievement('start','Начало главы 3!'),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);
function buildP1(){
const box=document.getElementById('p1-body');
let html='';
html+=makeCard('theory','Теорема Фалеса (классическая)','1.1',`
<p><b>Теорема Фалеса.</b> Если параллельные прямые пересекают одну из сторон угла в равных частях, то они отсекают равные части и на другой стороне угла.</p>
<p style="margin-top:8px"><b>Следствие (обобщённая теорема Фалеса).</b> Если несколько параллельных прямых пересекают две секущие прямые, то они отсекают на этих секущих <em>пропорциональные</em> отрезки:</p>
$$\\dfrac{AB}{BC} = \\dfrac{A'B'}{B'C'}$$
<p style="margin-top:8px">где $A,B,C$ — точки на первой секущей, $A',B',C'$ — точки на второй секущей, отсекаемые теми же параллельными прямыми.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 170" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- угол с вершиной O=(30,150); два луча, три параллельные прямые -->
<!-- луч 1: O→(260,60), луч 2: O→(260,150) -->
<line x1="30" y1="150" x2="260" y2="60" stroke="#7c3aed" stroke-width="2"/>
<line x1="30" y1="150" x2="260" y2="150" stroke="#7c3aed" stroke-width="2"/>
<!-- параллельные прямые (3 шт, горизонтальные) пересекают оба луча -->
<!-- луч1: при x=80→y=120.5, при x=160→y=91, при x=240→y=61.5 -->
<!-- луч2: при x=80→y=150, при x=160→y=150, при x=240→y=150 -->
<line x1="80" y1="30" x2="80" y2="160" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>
<line x1="160" y1="30" x2="160" y2="160" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>
<line x1="240" y1="30" x2="240" y2="160" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>
<!-- точки на луче 1 -->
<circle cx="80" cy="121" r="3.5" fill="#7c3aed"/>
<circle cx="160" cy="91" r="3.5" fill="#7c3aed"/>
<circle cx="240" cy="62" r="3.5" fill="#7c3aed"/>
<!-- точки на луче 2 -->
<circle cx="80" cy="150" r="3.5" fill="#7c3aed"/>
<circle cx="160" cy="150" r="3.5" fill="#7c3aed"/>
<circle cx="240" cy="150" r="3.5" fill="#7c3aed"/>
<!-- метки луч1 -->
<text x="76" y="117" text-anchor="end" font-size="10" font-weight="700" fill="#6d28d9">A</text>
<text x="156" y="87" text-anchor="end" font-size="10" font-weight="700" fill="#6d28d9">B</text>
<text x="236" y="58" text-anchor="end" font-size="10" font-weight="700" fill="#6d28d9">C</text>
<!-- метки луч2 -->
<text x="80" y="165" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">A'</text>
<text x="160" y="165" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">B'</text>
<text x="240" y="165" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">C'</text>
<text x="18" y="153" font-size="10" font-weight="700" fill="#6d28d9">O</text>
<text x="140" y="20" text-anchor="middle" font-size="10" fill="#6d28d9" font-style="italic">AB/BC = A'B'/B'C'</text>
</svg>
</div>`);
html+=makeCard('rule','Пропорциональность отрезков','1.2',`
<p>Обозначим точки пересечения параллельных прямых $\\ell_1,\\ell_2,\\ell_3$ со сторонами угла как $A,B,C$ и $A',B',C'$ соответственно. Тогда:</p>
$$\\dfrac{AB}{BC} = \\dfrac{A'B'}{B'C'}, \\quad \\dfrac{AB}{AC} = \\dfrac{A'B'}{A'C'}$$
<p style="margin-top:8px">Это выражает <b>пропорциональность отрезков</b>, отсекаемых параллельными прямыми.</p>
<p style="margin-top:8px"><b>Применение:</b> с помощью теоремы Фалеса доказывают подобие треугольников, делят отрезок в заданном отношении, строят пропорциональные отрезки.</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 130" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- треугольник ABC с точками D,E на сторонах, DE || BC -->
<polygon points="140,15 30,115 250,115" fill="rgba(124,58,237,.1)" stroke="#7c3aed" stroke-width="2"/>
<!-- DE параллельна BC, D на AB, E на AC -->
<line x1="85" y1="65" x2="195" y2="65" stroke="#a78bfa" stroke-width="2" stroke-dasharray="5,3"/>
<circle cx="85" cy="65" r="3" fill="#7c3aed"/>
<circle cx="195" cy="65" r="3" fill="#7c3aed"/>
<text x="140" y="12" text-anchor="middle" font-size="11" font-weight="700" fill="#6d28d9">A</text>
<text x="24" y="122" font-size="11" font-weight="700" fill="#6d28d9">B</text>
<text x="252" y="122" font-size="11" font-weight="700" fill="#6d28d9">C</text>
<text x="78" y="63" text-anchor="end" font-size="10" font-weight="700" fill="#8b5cf6">D</text>
<text x="201" y="63" font-size="10" font-weight="700" fill="#8b5cf6">E</text>
<text x="140" y="82" text-anchor="middle" font-size="9" fill="#6d28d9">DE ∥ BC</text>
<text x="140" y="126" text-anchor="middle" font-size="9" fill="#6d28d9" font-style="italic">AD/DB = AE/EC</text>
</svg>
</div>`);
html+=makeCard('example','Пример применения','1.3',`
<p><b>Пример.</b> Параллельные прямые отсекают на первой стороне угла отрезки $AB = 6$ и $BC = 9$. На второй стороне $A'B' = 4$. Найти $B'C'$.</p>
<p style="margin-top:8px"><b>Решение:</b> по теореме Фалеса $\\dfrac{AB}{BC}=\\dfrac{A'B'}{B'C'}$, то есть $\\dfrac{6}{9}=\\dfrac{4}{B'C'}$.</p>
<p style="margin-top:4px">$B'C' = \\dfrac{4 \\cdot 9}{6} = 6$.</p>`);
/* ИНТЕРАКТИВ 1 — SVG-угол с параллельными прямыми */
html+=`<div class="wg" id="p1-angle-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Угол с параллельными прямыми</div></div>
<div class="wg-help">Меняй количество параллельных прямых и угол наклона второй стороны. Видишь, как отношения остаются равными!</div>
<div class="sliders">
<label>Кол-во параллельных: <b id="p1-npar-val">3</b>
<input type="range" min="2" max="6" value="3" id="p1-npar-sl">
</label>
<label>Угол второй стороны: <b id="p1-angle-val">30</b>°
<input type="range" min="10" max="60" value="30" id="p1-angle-sl">
</label>
</div>
<div id="p1-angle-svg" style="display:flex;justify-content:center"></div>
<div id="p1-angle-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Пошаговое доказательство */
html+=`<div class="wg" id="p1-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство теоремы Фалеса — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» — каждый шаг показывает ключевую идею.</div>
<div id="p1-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p1-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p1-proof-next">Далее</button>
<button class="btn" id="p1-proof-reset">Сначала</button>
</div>
</div>`;
/* ИНТЕРАКТИВ 3 — Калькулятор пропорций */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор пропорций (Теорема Фалеса)</div></div>
<div class="wg-help">Дано три отрезка из пропорции $\\dfrac{a}{b}=\\dfrac{c}{x}$ — найди четвёртый.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-pa" class="tinp" placeholder="a" style="width:70px" min="0.001">
<span style="font-size:.8rem;color:var(--muted)">AB</span>
</div>
<span style="font-size:1.3rem;color:var(--muted)">/</span>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-pb" class="tinp" placeholder="b" style="width:70px" min="0.001">
<span style="font-size:.8rem;color:var(--muted)">BC</span>
</div>
<span style="font-size:1.3rem;color:var(--muted)">=</span>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-pc" class="tinp" placeholder="c" style="width:70px" min="0.001">
<span style="font-size:.8rem;color:var(--muted)">A'B'</span>
</div>
<span style="font-size:1.3rem;color:var(--muted)">/</span>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-px" class="tinp" placeholder="x" style="width:70px" disabled style="background:var(--sec-acc-soft)">
<span style="font-size:.8rem;color:var(--muted)">B'C'</span>
</div>
<button class="btn primary" id="p1-pcalc">Найти x</button>
</div>
<div id="p1-pcalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ИНТЕРАКТИВ 4 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §1 — Теорема Фалеса</div></div>
<div class="wg-help">5 задач на пропорциональность отрезков. Введи ответ и нажми «Проверить».</div>
<div class="score-display"><span>Задача <b id="p1-tr-i">1</b> / 5</span><span>Очки: <b id="p1-tr-score">0</b></span></div>
<div id="p1-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p1-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p1-tr-go">Проверить</button>
<button class="btn" id="p1-tr-start">Начать</button>
</div>
<div class="feedback" id="p1-tr-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 5 — DnD-сортер */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Определи: пропорциональные или нет?</div></div>
<div class="wg-help">Перетащи каждую карточку в нужную колонку.</div>
<div id="p1-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p1-drop-yes"><h5>Пропорциональные</h5><div class="drop-items" id="p1-drop-yes-items"></div></div>
<div class="drop-box" id="p1-drop-no"><h5>Непропорциональные</h5><div class="drop-items" id="p1-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p1-dnd-check">Проверить</button><button class="btn" id="p1-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p1-dnd-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 6 — Босс §1 */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §1</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p1-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p1-read-btn" onclick="addXp(10,'p1-read');bumpProgress('p1',40);this.textContent='Прочитано!';this.disabled=true;">
<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>
Я прочитал §1 (+10 XP)
</button>
</div>`;
html+=secNav(null,'p2');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: угол с параллелями == */
(function(){
const nSl=document.getElementById('p1-npar-sl');
const nVal=document.getElementById('p1-npar-val');
const aSl=document.getElementById('p1-angle-sl');
const aVal=document.getElementById('p1-angle-val');
const svgWrap=document.getElementById('p1-angle-svg');
const info=document.getElementById('p1-angle-info');
function draw(){
const n=+nSl.value;
const angDeg=+aSl.value;
nVal.textContent=n;
aVal.textContent=angDeg;
const W=320, H=180;
const Ox=20, Oy=H-20;
const ang=angDeg*Math.PI/180;
const L=280;
// луч 1: горизонталь вправо
// луч 2: под углом ang вверх
const pts1=[]; // точки на луче1 (горизональном)
const pts2=[]; // точки на луче2
const step=L/(n);
for(let i=1;i<=n;i++){
const x=Ox+step*i;
pts1.push({x, y:Oy});
pts2.push({x, y:Oy-step*i*Math.tan(ang)});
}
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// лучи
s+=`<line x1="${Ox}" y1="${Oy}" x2="${Ox+L+10}" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="${Ox+L+10}" y2="${Oy-(L+10)*Math.tan(ang)}" stroke="#7c3aed" stroke-width="2"/>`;
// параллельные прямые (вертикальные)
for(let i=0;i<n;i++){
const cx=pts1[i].x;
s+=`<line x1="${cx}" y1="5" x2="${cx}" y2="${H-5}" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>`;
s+=`<circle cx="${pts1[i].x}" cy="${pts1[i].y}" r="4" fill="#7c3aed"/>`;
s+=`<circle cx="${pts2[i].x}" cy="${pts2[i].y}" r="4" fill="#7c3aed"/>`;
const lbl=String.fromCharCode(65+i);
s+=`<text x="${pts1[i].x}" y="${Oy+14}" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">${lbl}</text>`;
s+=`<text x="${pts2[i].x+4}" y="${pts2[i].y-5}" font-size="10" font-weight="700" fill="#6d28d9">${lbl}'</text>`;
}
s+=`<text x="${Ox-4}" y="${Oy+4}" font-size="10" font-weight="700" fill="#6d28d9">O</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
// вычисляем отрезки
let infoHtml='';
for(let i=0;i<n-1;i++){
const seg1=Math.round((pts1[i+1].x-pts1[i].x)*10)/10;
const dy2=pts2[i+1].y-pts2[i].y, dx2=pts2[i+1].x-pts2[i].x;
const seg2=Math.round(Math.sqrt(dx2*dx2+dy2*dy2)*10)/10;
const lA=String.fromCharCode(65+i), lB=String.fromCharCode(66+i);
infoHtml+=`<span style="margin-right:16px">${lA}${lB} = <b>${seg1}</b> px; ${lA}'${lB}' = <b>${fmt(seg2)}</b> px</span>`;
}
if(n>=2){
const r1=Math.round((pts1[1].x-pts1[0].x)/(pts1[0].x-pts1[0].x+1)*1000)/1000;
const dy0=pts2[1].y-pts2[0].y, dx0=pts2[1].x-pts2[0].x;
const s1=pts1[1].x-pts1[0].x;
const s2=Math.sqrt(dx0*dx0+dy0*dy0);
infoHtml+=`<div style="margin-top:6px;color:var(--sec-acc-d,var(--pri2));font-weight:700">Отношение AB/A'B' = ${fmt(s1/s2)} ≈ постоянно для всех пар</div>`;
}
info.innerHTML=infoHtml;
addXp(1,'p1-angle');
}
nSl.addEventListener('input',draw);
aSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Даны два луча из вершины O и три параллельные прямые, пересекающие их в точках A, B, C и A\', B\', C\'.',
svg:()=>{
const W=260,H=160,Ox=20,Oy=140;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="60" stroke="#7c3aed" stroke-width="2"/>`;
[[70,140,70,65],[150,140,150,92],[230,140,230,119]].forEach(([x1,y1,x2,y2])=>{
s+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>`;
});
s+=`<text x="${Ox-4}" y="${Oy+4}" font-size="10" fill="#6d28d9" font-weight="700">O</text>`;
['A','B','C'].forEach((l,i)=>s+=`<text x="${70+i*80}" y="${Oy+14}" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">${l}</text>`);
['A\'','B\'','C\''].forEach((l,i)=>s+=`<text x="${70+i*80+4}" y="${[62,89,116][i]-4}" font-size="10" fill="#6d28d9" font-weight="700">${l}</text>`);
s+='</svg>';return s;}},
{desc:'<b>Шаг 2.</b> Через точки A, B, C проводим прямые, параллельные второму лучу. Получаем параллелограммы.',
svg:()=>{
const W=260,H=160,Ox=20,Oy=140;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="60" stroke="#7c3aed" stroke-width="2"/>`;
[[70,140,70,65],[150,140,150,92],[230,140,230,119]].forEach(([x1,y1,x2,y2])=>{
s+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>`;
});
// вспомогательные параллельные линии через A, B
s+=`<line x1="70" y1="140" x2="150" y2="92" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="3,3"/>`;
s+=`<line x1="150" y1="140" x2="230" y2="119" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="3,3"/>`;
s+=`<text x="100" y="128" font-size="9" fill="#b45309" font-weight="700">параллелограмм</text>`;
s+='</svg>';return s;}},
{desc:'<b>Шаг 3.</b> В параллелограмме противоположные стороны равны: $A_1B_1 = AB_2 = AB$, поэтому параллельная прямая откладывает равные отрезки на обоих лучах (если исходные части равны).',
svg:()=>{
const W=260,H=160;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<polygon points="70,140 150,140 150,92 70,65" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="1.5"/>`;
s+=`<text x="110" y="108" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">Параллелограмм</text>`;
s+=`<text x="110" y="120" text-anchor="middle" font-size="9" fill="#6d28d9">AB = A'B'</text>`;
s+='</svg>';return s;}},
{desc:'<b>Шаг 4.</b> Если параллельные прямые делят луч на <em>равные</em> части, то и второй луч делится на равные части. Это и есть теорема Фалеса в классической форме.',
svg:()=>{
const W=260,H=160,Ox=20,Oy=140;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="60" stroke="#7c3aed" stroke-width="2"/>`;
[[70,140,70,65],[150,140,150,92],[230,140,230,119]].forEach(([x1,y1,x2,y2])=>{
s+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#a78bfa" stroke-width="1.5"/>`;
});
// равные отрезки
s+=`<text x="110" y="${Oy+14}" text-anchor="middle" font-size="10" fill="#10b981" font-weight="700">равно</text>`;
s+=`<text x="190" y="${Oy+14}" text-anchor="middle" font-size="10" fill="#10b981" font-weight="700">равно</text>`;
s+='</svg>';return s;}},
{desc:'<b>Шаг 5.</b> Обобщённая теорема Фалеса: при <em>произвольных</em> параллельных прямых (не обязательно с равными частями) отрезки на двух лучах всё равно пропорциональны: $\\dfrac{AB}{BC}=\\dfrac{A\'B\'}{B\'C\'}$. <b>Доказано.</b>',
svg:()=>{
const W=260,H=160;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="20" y1="140" x2="250" y2="140" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="20" y1="140" x2="250" y2="50" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="80" y1="10" x2="80" y2="150" stroke="#a78bfa" stroke-width="1.5"/>`;
s+=`<line x1="170" y1="10" x2="170" y2="150" stroke="#a78bfa" stroke-width="1.5"/>`;
s+=`<text x="125" y="30" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="800">AB/BC = A'B'/B'C'</text>`;
s+=`<text x="125" y="100" text-anchor="middle" font-size="10" fill="#10b981" font-weight="700">QED</text>`;
s+='</svg>';return s;}},
];
let step=0;
const svgEl=document.getElementById('p1-proof-svg');
const descEl=document.getElementById('p1-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg();
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p1-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p1-proof-step');}
else{addXp(5,'p1-proof-done');bumpProgress('p1',10);}
});
document.getElementById('p1-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор пропорций == */
(function(){
document.getElementById('p1-pcalc').addEventListener('click',()=>{
const a=parseFloat(document.getElementById('p1-pa').value);
const b=parseFloat(document.getElementById('p1-pb').value);
const c=parseFloat(document.getElementById('p1-pc').value);
const out=document.getElementById('p1-pcalc-out');
if(!isFinite(a)||!isFinite(b)||!isFinite(c)||a<=0||b<=0||c<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи три положительных числа.</span>';return;
}
const x=b*c/a;
out.style.display='block';
out.innerHTML=`По теореме Фалеса: $x = \\dfrac{b \\cdot c}{a} = \\dfrac{${fmt(b)} \\cdot ${fmt(c)}}{${fmt(a)}} = ${fmt(x)}$`;
renderMath(out);
addXp(2,'p1-pcalc');bumpProgress('p1',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'Параллельные прямые отсекают на первом луче отрезки $AB=4$, $BC=6$. На втором луче $A\'B\'=6$. Найди $B\'C\'$.', ans:9, hint:'BC·A\'B\'/AB = 6·6/4 = 9.'},
{q:'$AB=3$, $BC=9$, $A\'B\'=5$. Найди $B\'C\'$.', ans:15, hint:'9·5/3 = 15.'},
{q:'Параллельные прямые отсекают $AB=8$, $BC=12$, $B\'C\'=9$. Найди $A\'B\'$.', ans:6, hint:'A\'B\' = AB·B\'C\'/BC = 8·9/12 = 6.'},
{q:'$AB=5$, $A\'B\'=7$, $B\'C\'=14$. Найди $BC$.', ans:10, hint:'BC = AB·B\'C\'/A\'B\' = 5·14/7 = 10.'},
{q:'Три параллельные прямые отсекают на луче отрезки $2$ и $3$. На другом луче первый отрезок равен $4$. Найди второй отрезок.', ans:6, hint:'4·3/2 = 6.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p1-tr-i').textContent=idx+1;
const t=document.getElementById('p1-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p1-tr-ans').value='';
document.getElementById('p1-tr-fb').style.display='none';
}
document.getElementById('p1-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p1-tr-score').textContent=0;show();});
document.getElementById('p1-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p1-tr-ans').value;
const fb=document.getElementById('p1-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.01){
score++;document.getElementById('p1-tr-score').textContent=score;
addXp(3,'p1-tr-'+idx);bumpProgress('p1',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p1-tr-all');bumpProgress('p1',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p1-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p1-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер == */
(function(){
const items=[
{text:'AB=3, BC=6, A\'B\'=4, B\'C\'=8',yes:true},
{text:'AB=2, BC=4, A\'B\'=3, B\'C\'=7',yes:false},
{text:'AB=5, BC=10, A\'B\'=1, B\'C\'=2',yes:true},
{text:'AB=6, BC=3, A\'B\'=8, B\'C\'=5',yes:false},
{text:'AB=4, BC=8, A\'B\'=3, B\'C\'=6',yes:true},
];
const pool=document.getElementById('p1-dnd-pool');
const yesBox=document.getElementById('p1-drop-yes-items');
const noBox=document.getElementById('p1-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p1-drop-yes'),document.getElementById('p1-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p1-drop-yes')?yesBox:box===document.getElementById('p1-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p1-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p1-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p1-dnd');bumpProgress('p1',8);}
else{feedback(fb,false,'Есть ошибки — перепроверь отношения AB/BC и A\'B\'/B\'C\'.');}
});
document.getElementById('p1-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p1-dnd-fb').style.display='none';
});
})();
/* == INIT: Босс §1 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 130" style="display:block;max-width:250px;margin:0 auto 8px;background:#f5f3ff;border:1px solid #ddd6fe;border-radius:8px"><line x1="20" y1="110" x2="220" y2="110" stroke="#7c3aed" stroke-width="2"/><line x1="20" y1="110" x2="220" y2="30" stroke="#7c3aed" stroke-width="2"/><line x1="80" y1="10" x2="80" y2="118" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/><line x1="160" y1="10" x2="160" y2="118" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/><text x="120" y="125" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">AB=10, BC=?, A\'B\'=6, B\'C\'=9</text></svg>$AB=10$, $A\'B\'=6$, $B\'C\'=9$. Найди $BC$.', ans:15, hint:'BC = AB·B\'C\'/A\'B\' = 10·9/6 = 15.'},
{q:'Параллельные прямые отсекают на двух лучах: луч 1 — отрезки $4$ и $x$, луч 2 — отрезки $6$ и $12$. Найди $x$.', ans:8, hint:'4/x = 6/12 → x = 4·12/6 = 8.'},
{q:'Прямая $DE \\parallel BC$ в треугольнике $ABC$. $AD=6$, $DB=9$, $AE=8$. Найди $EC$.', ans:12, hint:'AD/DB = AE/EC → EC = DB·AE/AD = 9·8/6 = 12.'},
{q:'$AB=15$, $BC=25$, $B\'C\'=20$. Найди $A\'B\'$.', ans:12, hint:'A\'B\' = AB·B\'C\'/BC = 15·20/25 = 12.'},
];
const bossBox=document.getElementById('p1-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p1b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p1b-a${i}').value;
const fb=document.getElementById('p1b-fb${i}');
if(Math.abs(v-${t.ans})<0.01){
feedback(fb,true,'Верно! +5 XP');
if(!window.p1BossSolved.has(${i})){ window.p1BossSolved.add(${i}); addXp(5,'p1-boss${i}'); bumpProgress('p1',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p1b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p1BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP2(){
const box=document.getElementById('p2-body');
let html='';
html+=makeCard('theory','Деление отрезка в заданном отношении','2.1',`
<p><b>Определение.</b> Говорят, что точка $C$ делит отрезок $AB$ <em>в отношении $m:n$</em> (<em>внутреннее деление</em>), если $C$ лежит между $A$ и $B$, и:</p>
$$\\dfrac{AC}{CB} = \\dfrac{m}{n}, \\quad m,n > 0$$
<p style="margin-top:8px"><b>Формула координат.</b> Если $A = (x_A, y_A)$, $B = (x_B, y_B)$, то точка деления:</p>
$$C = \\left(\\dfrac{n\\,x_A + m\\,x_B}{m+n},\\; \\dfrac{n\\,y_A + m\\,y_B}{m+n}\\right)$$
<p style="margin-top:8px"><b>Частный случай $m=n$:</b> точка $C$ — середина $AB$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 80" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- отрезок AB с точкой C -->
<line x1="20" y1="40" x2="260" y2="40" stroke="#7c3aed" stroke-width="2.5"/>
<circle cx="20" cy="40" r="4" fill="#7c3aed"/>
<circle cx="260" cy="40" r="4" fill="#7c3aed"/>
<!-- C делит в 2:3: x = 20 + 2/(2+3)*240 = 20+96 = 116 -->
<circle cx="116" cy="40" r="5" fill="#f59e0b"/>
<!-- скобки -->
<line x1="20" y1="52" x2="116" y2="52" stroke="#10b981" stroke-width="1.5"/>
<line x1="116" y1="52" x2="260" y2="52" stroke="#8b5cf6" stroke-width="1.5"/>
<text x="68" y="65" text-anchor="middle" font-size="11" fill="#10b981" font-weight="700">m = 2</text>
<text x="188" y="65" text-anchor="middle" font-size="11" fill="#8b5cf6" font-weight="700">n = 3</text>
<text x="20" y="32" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text>
<text x="116" y="32" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700">C</text>
<text x="260" y="32" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text>
</svg>
</div>`);
html+=makeCard('algo','Построение циркулем и линейкой','2.2',`
<p>Чтобы разделить отрезок $AB$ в отношении $m:n$:</p>
<ol style="margin-left:18px;margin-top:6px;line-height:2">
<li>Из точки $A$ проводим луч $AK$, не совпадающий с $AB$.</li>
<li>На луче $AK$ последовательно откладываем $m+n$ равных отрезков единичной длины: точки $P_1, P_2, \\ldots, P_{m+n}$.</li>
<li>Соединяем точку $P_{m+n}$ с точкой $B$.</li>
<li>Через $P_m$ проводим прямую, параллельную $P_{m+n}B$ — она пересекает $AB$ в нужной точке $C$.</li>
</ol>
<p style="margin-top:8px">По теореме Фалеса $\\dfrac{AC}{CB} = \\dfrac{m}{n}$. $\\square$</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 160" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- отрезок AB -->
<line x1="20" y1="60" x2="260" y2="60" stroke="#7c3aed" stroke-width="2"/>
<circle cx="20" cy="60" r="4" fill="#7c3aed"/>
<circle cx="260" cy="60" r="4" fill="#7c3aed"/>
<text x="16" y="52" font-size="10" fill="#6d28d9" font-weight="700">A</text>
<text x="256" y="52" font-size="10" fill="#6d28d9" font-weight="700">B</text>
<!-- луч AK: из A=(20,60) под углом вниз -->
<line x1="20" y1="60" x2="170" y2="140" stroke="#f59e0b" stroke-width="1.5"/>
<!-- 5 точек (m+n=2+3) на луче, шаг=30px по лучу -->
<!-- P1=(50,76), P2=(80,92), P3=(110,108), P4=(140,124), P5=(170,140) -->
<circle cx="50" cy="76" r="3" fill="#f59e0b"/>
<circle cx="80" cy="92" r="3" fill="#f59e0b"/>
<circle cx="110" cy="108" r="3" fill="#10b981"/>
<circle cx="140" cy="124" r="3" fill="#f59e0b"/>
<circle cx="170" cy="140" r="3" fill="#f59e0b"/>
<text x="50" y="72" text-anchor="middle" font-size="8" fill="#b45309">P₁</text>
<text x="80" y="88" text-anchor="middle" font-size="8" fill="#b45309">P₂</text>
<text x="110" y="104" text-anchor="middle" font-size="8" fill="#047857" font-weight="700">P_m</text>
<text x="165" y="155" text-anchor="middle" font-size="8" fill="#b45309">P₅</text>
<!-- линия P5B -->
<line x1="170" y1="140" x2="260" y2="60" stroke="#8b5cf6" stroke-width="1.2" stroke-dasharray="4,3"/>
<!-- параллельная через Pm: C на AB -->
<!-- C: x = 20 + 2/5*(260-20) = 20+96 = 116 -->
<line x1="110" y1="108" x2="116" y2="60" stroke="#10b981" stroke-width="1.8" stroke-dasharray="4,3"/>
<circle cx="116" cy="60" r="5" fill="#10b981"/>
<text x="116" y="52" text-anchor="middle" font-size="10" fill="#047857" font-weight="700">C</text>
</svg>
</div>`);
html+=makeCard('example','Пример','2.3',`
<p><b>Пример 1.</b> $A=(0,0)$, $B=(10,0)$, разделить в отношении $3:2$.</p>
<p>$C_x = \\dfrac{2\\cdot 0 + 3\\cdot 10}{3+2} = \\dfrac{30}{5} = 6$. Ответ: $C=(6,0)$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> $A=(1,2)$, $B=(7,8)$, разделить в отношении $1:2$.</p>
<p>$C_x = \\dfrac{2\\cdot 1+1\\cdot 7}{3}=3$, $C_y=\\dfrac{2\\cdot 2+1\\cdot 8}{3}=4$. Ответ: $C=(3,4)$.</p>`);
/* ИНТЕРАКТИВ 1 — SVG-построение */
html+=`<div class="wg" id="p2-build-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Построение деления отрезка m:n</div></div>
<div class="wg-help">Меняй m и n — смотри, как перемещается точка C на отрезке AB.</div>
<div class="sliders">
<label>m = <b id="p2-m-val">2</b>
<input type="range" min="1" max="6" value="2" id="p2-m-sl">
</label>
<label>n = <b id="p2-n-val">3</b>
<input type="range" min="1" max="6" value="3" id="p2-n-sl">
</label>
</div>
<div id="p2-build-svg" style="display:flex;justify-content:center"></div>
<div id="p2-build-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.93rem;line-height:1.8"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Калькулятор */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор деления отрезка</div></div>
<div class="wg-help">Введи длину отрезка и отношение m:n — получи расстояние AC от начала.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<div style="display:flex;flex-direction:column;gap:4px">
<span style="font-size:.8rem;color:var(--muted)">AB</span>
<input type="number" id="p2-lab" class="tinp" placeholder="AB" style="width:80px" min="0.001">
</div>
<div style="display:flex;flex-direction:column;gap:4px">
<span style="font-size:.8rem;color:var(--muted)">m</span>
<input type="number" id="p2-lm" class="tinp" placeholder="m" style="width:60px" min="1">
</div>
<div style="display:flex;flex-direction:column;gap:4px">
<span style="font-size:.8rem;color:var(--muted)">n</span>
<input type="number" id="p2-ln" class="tinp" placeholder="n" style="width:60px" min="1">
</div>
<button class="btn primary" id="p2-lcalc">Вычислить</button>
</div>
<div id="p2-lcalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ИНТЕРАКТИВ 3 — Пошаговое доказательство */
html+=`<div class="wg" id="p2-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Доказательство формулы — по шагам</div></div>
<div class="wg-help">4 шага — вывод формулы координат точки деления через теорему Фалеса.</div>
<div id="p2-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p2-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p2-proof-next">Далее</button>
<button class="btn" id="p2-proof-reset">Сначала</button>
</div>
</div>`;
/* ИНТЕРАКТИВ 4 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §2</div></div>
<div class="wg-help">5 задач на деление отрезка. Введи числовой ответ.</div>
<div class="score-display"><span>Задача <b id="p2-tr-i">1</b> / 5</span><span>Очки: <b id="p2-tr-score">0</b></span></div>
<div id="p2-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p2-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p2-tr-go">Проверить</button>
<button class="btn" id="p2-tr-start">Начать</button>
</div>
<div class="feedback" id="p2-tr-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 5 — Босс §2 */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §2</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p2-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p2-read-btn" onclick="addXp(10,'p2-read');bumpProgress('p2',40);this.textContent='Прочитано!';this.disabled=true;">
<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>
Я прочитал §2 (+10 XP)
</button>
</div>`;
html+=secNav('p1','p3');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: построение == */
(function(){
const mSl=document.getElementById('p2-m-sl');
const nSl=document.getElementById('p2-n-sl');
const mVal=document.getElementById('p2-m-val');
const nVal=document.getElementById('p2-n-val');
const svgWrap=document.getElementById('p2-build-svg');
const infoEl=document.getElementById('p2-build-info');
function draw(){
const m=+mSl.value, n=+nSl.value;
mVal.textContent=m; nVal.textContent=n;
const W=320, H=140;
const Ax=24, Ay=50, Bx=296, By=50;
const AB=Bx-Ax;
const Cx=Ax+m/(m+n)*AB;
const Cy=50;
// луч AK: вниз под углом 30°
const rayLen=(m+n)*22;
const rayAngle=Math.PI/6;
const Kx=Ax+rayLen*Math.cos(rayAngle);
const Ky=Ay+rayLen*Math.sin(rayAngle);
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// отрезок AB
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Bx}" y2="${By}" stroke="#7c3aed" stroke-width="2.5"/>`;
s+=`<circle cx="${Ax}" cy="${Ay}" r="4" fill="#7c3aed"/>`;
s+=`<circle cx="${Bx}" cy="${By}" r="4" fill="#7c3aed"/>`;
s+=`<text x="${Ax}" y="${Ay-8}" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text>`;
s+=`<text x="${Bx}" y="${By-8}" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text>`;
// луч AK
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Kx}" y2="${Ky}" stroke="#f59e0b" stroke-width="1.5"/>`;
// точки P1..P(m+n) на луче
for(let i=1;i<=m+n;i++){
const px=Ax+i*22*Math.cos(rayAngle);
const py=Ay+i*22*Math.sin(rayAngle);
const isM=(i===m);
s+=`<circle cx="${px}" cy="${py}" r="${isM?5:3}" fill="${isM?'#10b981':'#f59e0b'}"/>`;
s+=`<text x="${px+6}" y="${py+4}" font-size="8" fill="${isM?'#047857':'#b45309'}" font-weight="${isM?'700':'400'}">P${i}${isM?' (m)':''}</text>`;
}
// линия P(m+n)→B
const Pmn_x=Ax+(m+n)*22*Math.cos(rayAngle);
const Pmn_y=Ay+(m+n)*22*Math.sin(rayAngle);
s+=`<line x1="${Pmn_x}" y1="${Pmn_y}" x2="${Bx}" y2="${By}" stroke="#8b5cf6" stroke-width="1.3" stroke-dasharray="4,3"/>`;
// параллельная через Pm → C
const Pm_x=Ax+m*22*Math.cos(rayAngle);
const Pm_y=Ay+m*22*Math.sin(rayAngle);
s+=`<line x1="${Pm_x}" y1="${Pm_y}" x2="${Cx}" y2="${Cy}" stroke="#10b981" stroke-width="2" stroke-dasharray="4,3"/>`;
// точка C
s+=`<circle cx="${Cx}" cy="${Cy}" r="6" fill="#10b981"/>`;
s+=`<text x="${Cx}" y="${Cy-10}" text-anchor="middle" font-size="11" fill="#047857" font-weight="700">C</text>`;
// метки AC, CB
s+=`<line x1="${Ax}" y1="${Ay+14}" x2="${Cx}" y2="${Cy+14}" stroke="#10b981" stroke-width="1.5"/>`;
s+=`<line x1="${Cx}" y1="${Cy+14}" x2="${Bx}" y2="${By+14}" stroke="#8b5cf6" stroke-width="1.5"/>`;
s+=`<text x="${(Ax+Cx)/2}" y="${Cy+26}" text-anchor="middle" font-size="10" fill="#047857" font-weight="700">m=${m}</text>`;
s+=`<text x="${(Cx+Bx)/2}" y="${Cy+26}" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">n=${n}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
const AClen=fmt(m/(m+n)*100);
const CBlen=fmt(n/(m+n)*100);
infoEl.innerHTML=`Отношение $AC:CB = ${m}:${n}$. Если $AB=100$, то $AC=${AClen}$, $CB=${CBlen}$.`;
renderMath(infoEl);
addXp(1,'p2-build');
}
mSl.addEventListener('input',draw);
nSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: калькулятор == */
(function(){
document.getElementById('p2-lcalc').addEventListener('click',()=>{
const AB=parseFloat(document.getElementById('p2-lab').value);
const m=parseFloat(document.getElementById('p2-lm').value);
const n=parseFloat(document.getElementById('p2-ln').value);
const out=document.getElementById('p2-lcalc-out');
if(!isFinite(AB)||!isFinite(m)||!isFinite(n)||AB<=0||m<=0||n<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи положительные числа.</span>';return;
}
const AC=m/(m+n)*AB;
const CB=n/(m+n)*AB;
out.style.display='block';
out.innerHTML=`$AC = \\dfrac{m}{m+n}\\cdot AB = \\dfrac{${fmt(m)}}{${fmt(m+n)}}\\cdot ${fmt(AB)} = ${fmt(AC)}$<br>$CB = \\dfrac{${fmt(n)}}{${fmt(m+n)}}\\cdot ${fmt(AB)} = ${fmt(CB)}$`;
renderMath(out);
addXp(2,'p2-calc');bumpProgress('p2',5);
});
})();
/* == INIT ИНТЕРАКТИВ 3: доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Даны точки $A$ и $B$ на прямой. Хотим найти точку $C$ такую, что $AC:CB = m:n$.',
svg:`<svg viewBox="0 0 280 70" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="35" x2="260" y2="35" stroke="#7c3aed" stroke-width="2"/><circle cx="20" cy="35" r="4" fill="#7c3aed"/><circle cx="260" cy="35" r="4" fill="#7c3aed"/><text x="20" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text><text x="260" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text><text x="140" y="55" text-anchor="middle" font-size="10" fill="#6d28d9">AC : CB = m : n = ?</text></svg>`},
{desc:'<b>Шаг 2.</b> Проводим из $A$ луч $AK$ и откладываем $m+n$ равных единичных отрезков. Последняя точка — $P_{m+n}$, средняя (после $m$ шагов) — $P_m$.',
svg:`<svg viewBox="0 0 280 130" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="40" x2="260" y2="40" stroke="#7c3aed" stroke-width="2"/><line x1="20" y1="40" x2="140" y2="120" stroke="#f59e0b" stroke-width="1.5"/><circle cx="44" cy="57" r="3" fill="#f59e0b"/><circle cx="68" cy="73" r="3" fill="#f59e0b"/><circle cx="92" cy="90" r="5" fill="#10b981"/><circle cx="116" cy="106" r="3" fill="#f59e0b"/><circle cx="140" cy="120" r="3" fill="#f59e0b"/><text x="96" y="88" font-size="8" fill="#047857" font-weight="700">P_m</text><text x="142" y="118" font-size="8" fill="#b45309">P_{m+n}</text><text x="20" y="32" font-size="10" fill="#6d28d9" font-weight="700">A</text><text x="256" y="32" font-size="10" fill="#6d28d9" font-weight="700">B</text></svg>`},
{desc:'<b>Шаг 3.</b> Соединяем $P_{m+n}$ с $B$. Через $P_m$ проводим прямую, параллельную $P_{m+n}B$. По теореме Фалеса она пересекает $AB$ в точке $C$, делящей $AB$ в отношении $m:n$.',
svg:`<svg viewBox="0 0 280 130" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="40" x2="260" y2="40" stroke="#7c3aed" stroke-width="2"/><line x1="20" y1="40" x2="140" y2="120" stroke="#f59e0b" stroke-width="1.5"/><line x1="140" y1="120" x2="260" y2="40" stroke="#8b5cf6" stroke-width="1.3" stroke-dasharray="4,3"/><line x1="92" y1="90" x2="116" y2="40" stroke="#10b981" stroke-width="2" stroke-dasharray="4,3"/><circle cx="116" cy="40" r="6" fill="#10b981"/><text x="116" y="32" text-anchor="middle" font-size="11" fill="#047857" font-weight="700">C</text><circle cx="92" cy="90" r="4" fill="#10b981"/><circle cx="140" cy="120" r="4" fill="#f59e0b"/><text x="92" y="88" font-size="8" fill="#047857">P_m</text><text x="20" y="32" font-size="10" fill="#6d28d9" font-weight="700">A</text><text x="256" y="32" font-size="10" fill="#6d28d9" font-weight="700">B</text></svg>`},
{desc:'<b>Шаг 4.</b> Итог: по теореме Фалеса $\\dfrac{AC}{CB}=\\dfrac{m}{n}$. Координаты точки $C$: $C_x = \\dfrac{n\\,x_A+m\\,x_B}{m+n}$, $C_y = \\dfrac{n\\,y_A+m\\,y_B}{m+n}$. <b>Доказано.</b>',
svg:`<svg viewBox="0 0 280 70" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="35" x2="260" y2="35" stroke="#7c3aed" stroke-width="2"/><circle cx="20" cy="35" r="4" fill="#7c3aed"/><circle cx="260" cy="35" r="4" fill="#7c3aed"/><circle cx="116" cy="35" r="6" fill="#10b981"/><text x="20" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text><text x="116" y="27" text-anchor="middle" font-size="11" fill="#047857" font-weight="700">C</text><text x="260" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text><text x="140" y="58" text-anchor="middle" font-size="10" fill="#047857" font-weight="700">C = (n·A + m·B) / (m+n)</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p2-proof-svg');
const descEl=document.getElementById('p2-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg;
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p2-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p2-proof-step');}
else{addXp(5,'p2-proof-done');bumpProgress('p2',10);}
});
document.getElementById('p2-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'Отрезок $AB=20$ делится точкой $C$ в отношении $1:4$. Найди $AC$.', ans:4, hint:'AC = 1/(1+4)·20 = 4.'},
{q:'$AB=15$, $AC:CB=2:3$. Найди $CB$.', ans:9, hint:'CB = 3/(2+3)·15 = 9.'},
{q:'$A=(0,0)$, $B=(8,0)$, деление $3:1$. Найди $x$-координату точки $C$.', ans:6, hint:'C_x = (1·0+3·8)/4 = 6.'},
{q:'Отрезок $AB=30$ делится в отношении $2:1$. Найди расстояние от $C$ до $B$.', ans:10, hint:'CB = 1/(2+1)·30 = 10.'},
{q:'$A=(0,0)$, $B=(0,12)$, деление $1:2$. Найди $y$-координату точки $C$.', ans:4, hint:'C_y = (2·0+1·12)/3 = 4.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p2-tr-i').textContent=idx+1;
const t=document.getElementById('p2-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p2-tr-ans').value='';
document.getElementById('p2-tr-fb').style.display='none';
}
document.getElementById('p2-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p2-tr-score').textContent=0;show();});
document.getElementById('p2-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p2-tr-ans').value;
const fb=document.getElementById('p2-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.01){
score++;document.getElementById('p2-tr-score').textContent=score;
addXp(3,'p2-tr-'+idx);bumpProgress('p2',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p2-tr-all');bumpProgress('p2',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p2-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p2-tr-go').click();});
show();
})();
/* == INIT: Босс §2 == */
(function(){
const tasks=[
{q:'$AB=24$, деление в отношении $5:3$. Найди $AC$.', ans:15, hint:'AC = 5/8·24 = 15.'},
{q:'$A=(0,0)$, $B=(10,10)$, деление $3:2$. Найди $x_C+y_C$.', ans:12, hint:'C_x=6, C_y=6, сумма=12.'},
{q:'Точка $C$ делит $AB$ в отношении $AC:CB=4:1$. $CB=7$. Найди $AB$.', ans:35, hint:'AB = (4+1)·7 = 35.'},
{q:'$AB=100$, $C$ делит $AB$ в отношении $3:7$. Найди $BC$.', ans:70, hint:'BC = 7/10·100 = 70.'},
];
const bossBox=document.getElementById('p2-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p2b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p2b-a${i}').value;
const fb=document.getElementById('p2b-fb${i}');
if(Math.abs(v-${t.ans})<0.01){
feedback(fb,true,'Верно! +5 XP');
if(!window.p2BossSolved.has(${i})){ window.p2BossSolved.add(${i}); addXp(5,'p2-boss${i}'); bumpProgress('p2',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p2b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p2BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP3(){
const box=document.getElementById('p3-body');
let html='';
html+=makeCard('theory','Определение подобных треугольников','3.1',`
<p><b>Определение.</b> Треугольники $\\triangle ABC$ и $\\triangle A'B'C'$ называются <b>подобными</b> ($\\triangle ABC \\sim \\triangle A'B'C'$), если:</p>
<ol style="margin-left:18px;margin-top:6px;line-height:2">
<li>Соответственные углы равны: $\\angle A = \\angle A'$, $\\angle B = \\angle B'$, $\\angle C = \\angle C'$.</li>
<li>Соответственные стороны пропорциональны: $\\dfrac{a}{a'} = \\dfrac{b}{b'} = \\dfrac{c}{c'} = k$,</li>
</ol>
<p style="margin-top:6px">где $k > 0$ — <b>коэффициент подобия</b>.</p>
<p style="margin-top:8px"><b>Обозначения:</b> стороны треугольника: $a=BC$, $b=CA$, $c=AB$ и $a'=B'C'$, $b'=C'A'$, $c'=A'B'$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 150" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- большой треугольник ABC -->
<polygon points="30,130 180,130 80,30" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/>
<text x="74" y="24" font-size="11" font-weight="700" fill="#6d28d9">A</text>
<text x="22" y="143" font-size="11" font-weight="700" fill="#6d28d9">B</text>
<text x="183" y="143" font-size="11" font-weight="700" fill="#6d28d9">C</text>
<!-- малый треугольник A'B'C' (k=0.5) -->
<polygon points="200,130 270,130 237,70" fill="rgba(139,92,246,.15)" stroke="#8b5cf6" stroke-width="1.8"/>
<text x="233" y="64" font-size="10" font-weight="700" fill="#8b5cf6">A'</text>
<text x="194" y="143" font-size="10" font-weight="700" fill="#8b5cf6">B'</text>
<text x="271" y="143" font-size="10" font-weight="700" fill="#8b5cf6">C'</text>
<!-- коэффициент -->
<text x="140" y="16" text-anchor="middle" font-size="10" fill="#6d28d9">k = a/a' = b/b' = c/c'</text>
<!-- равенство углов -->
<text x="35" y="125" font-size="8" fill="#6d28d9">∠B</text>
<text x="203" y="125" font-size="8" fill="#8b5cf6">∠B'</text>
</svg>
</div>`);
html+=makeCard('rule','Коэффициент подобия и свойства','3.2',`
<p><b>Коэффициент подобия</b> $k = \\dfrac{a}{a'} = \\dfrac{b}{b'} = \\dfrac{c}{c'}$ показывает, во сколько раз стороны одного треугольника больше соответственных сторон другого.</p>
<p style="margin-top:8px"><b>Свойства подобия (отношение эквивалентности):</b></p>
<ul style="margin-left:18px;margin-top:6px;line-height:1.9">
<li><b>Рефлексивность:</b> $\\triangle ABC \\sim \\triangle ABC$ (с $k=1$).</li>
<li><b>Симметричность:</b> если $\\triangle ABC \\sim \\triangle A'B'C'$ с коэффициентом $k$, то $\\triangle A'B'C' \\sim \\triangle ABC$ с коэффициентом $\\tfrac{1}{k}$.</li>
<li><b>Транзитивность:</b> если $\\triangle ABC \\sim \\triangle A'B'C'$ и $\\triangle A'B'C' \\sim \\triangle A''B''C''$, то $\\triangle ABC \\sim \\triangle A''B''C''$.</li>
</ul>`);
html+=makeCard('example','Пример','3.3',`
<p><b>Пример.</b> $\\triangle ABC$: стороны $6, 8, 10$. $\\triangle A'B'C'$: стороны $3, 4, 5$. Подобны ли треугольники?</p>
<p style="margin-top:6px">$\\dfrac{6}{3}=2,\\; \\dfrac{8}{4}=2,\\; \\dfrac{10}{5}=2$ — отношения равны. Значит, $\\triangle ABC \\sim \\triangle A'B'C'$ с коэффициентом $k=2$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> Треугольники $5,7,9$ и $10,15,18$ — подобны ли?</p>
<p>$10/5=2$, $15/7\\approx 2.14$ — отношения неравны. <b>Не подобны.</b></p>`);
/* ИНТЕРАКТИВ 1 — SVG два треугольника с slider k */
html+=`<div class="wg" id="p3-sim-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два подобных треугольника — коэффициент k</div></div>
<div class="wg-help">Меняй коэффициент подобия $k$ — второй треугольник масштабируется. Углы всегда равны, стороны пропорциональны.</div>
<div class="sliders">
<label>Коэффициент k = <b id="p3-k-val">1.5</b>
<input type="range" min="5" max="30" value="15" id="p3-k-sl" step="1">
</label>
</div>
<div id="p3-sim-svg" style="display:flex;justify-content:center"></div>
<div id="p3-sim-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Калькулятор */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор подобия</div></div>
<div class="wg-help">Введи стороны одного треугольника и коэффициент $k$ — получи стороны подобного.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">a</span><input type="number" id="p3-ca" class="tinp" placeholder="a" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">b</span><input type="number" id="p3-cb" class="tinp" placeholder="b" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">c</span><input type="number" id="p3-cc" class="tinp" placeholder="c" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">k</span><input type="number" id="p3-ck" class="tinp" placeholder="k" style="width:100%" min="0.01"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p3-ccalc" style="width:100%">Вычислить</button></div>
</div>
<div id="p3-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ИНТЕРАКТИВ 3 — DnD-сортер подобные / неподобные */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Подобные или нет? — Сортировка</div></div>
<div class="wg-help">Перетащи каждую пару треугольников в нужную колонку.</div>
<div id="p3-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p3-drop-yes"><h5>Подобные</h5><div class="drop-items" id="p3-drop-yes-items"></div></div>
<div class="drop-box" id="p3-drop-no"><h5>Не подобные</h5><div class="drop-items" id="p3-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p3-dnd-check">Проверить</button><button class="btn" id="p3-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p3-dnd-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 4 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §3 — Подобные треугольники</div></div>
<div class="wg-help">5 задач на коэффициент подобия и неизвестные стороны.</div>
<div class="score-display"><span>Задача <b id="p3-tr-i">1</b> / 5</span><span>Очки: <b id="p3-tr-score">0</b></span></div>
<div id="p3-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p3-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p3-tr-go">Проверить</button>
<button class="btn" id="p3-tr-start">Начать</button>
</div>
<div class="feedback" id="p3-tr-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 5 — Мини-квиз */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Мини-квиз: теория подобия</div></div>
<div class="wg-help">4 вопроса на проверку понимания теории.</div>
<div id="p3-quiz-body"></div>
<div class="actions"><button class="btn primary" id="p3-quiz-check">Проверить квиз</button></div>
<div class="feedback" id="p3-quiz-fb" style="display:none;margin-top:8px"></div>
</div>`;
/* ИНТЕРАКТИВ 6 — Босс §3 */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §3</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p3-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p3-read-btn" onclick="addXp(10,'p3-read');bumpProgress('p3',40);this.textContent='Прочитано!';this.disabled=true;">
<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>
Я прочитал §3 (+10 XP)
</button>
</div>`;
html+=secNav('p2','p4');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: два треугольника == */
(function(){
const kSl=document.getElementById('p3-k-sl');
const kVal=document.getElementById('p3-k-val');
const svgWrap=document.getElementById('p3-sim-svg');
const infoEl=document.getElementById('p3-sim-info');
// базовый треугольник: A=(80,20), B=(20,120), C=(160,120)
const Ax=80,Ay=20,Bx=20,By=120,Cx=160,Cy=120;
const a=Math.hypot(Cx-Bx,Cy-By); // BC
const b=Math.hypot(Ax-Cx,Ay-Cy); // CA
const c=Math.hypot(Bx-Ax,By-Ay); // AB
function draw(){
const k=+kSl.value/10;
kVal.textContent=k.toFixed(1);
// второй треугольник масштабируем от центра C' = (240, 120)
const ox=240, oy=120;
const Ax2=ox+(Ax-Cx)/k, Ay2=oy+(Ay-Cy)/k;
const Bx2=ox+(Bx-Cx)/k, By2=oy+(By-Cy)/k;
const Cx2=ox, Cy2=oy;
const W=380, H=160;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// большой треугольник
s+=`<polygon points="${Ax},${Ay} ${Bx},${By} ${Cx},${Cy}" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<text x="${Ax}" y="${Ay-6}" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text>`;
s+=`<text x="${Bx-10}" y="${By+4}" font-size="11" fill="#6d28d9" font-weight="700">B</text>`;
s+=`<text x="${Cx+4}" y="${Cy+4}" font-size="11" fill="#6d28d9" font-weight="700">C</text>`;
// подписи сторон
s+=`<text x="${(Bx+Cx)/2}" y="${By+14}" text-anchor="middle" font-size="9" fill="#6d28d9">a=${fmt(a/10)}</text>`;
s+=`<text x="${(Ax+Bx)/2-10}" y="${(Ay+By)/2}" text-anchor="end" font-size="9" fill="#6d28d9">c=${fmt(c/10)}</text>`;
s+=`<text x="${(Ax+Cx)/2+4}" y="${(Ay+Cy)/2}" font-size="9" fill="#6d28d9">b=${fmt(b/10)}</text>`;
// второй треугольник
s+=`<polygon points="${Ax2},${Ay2} ${Bx2},${By2} ${Cx2},${Cy2}" fill="rgba(139,92,246,.15)" stroke="#8b5cf6" stroke-width="1.8"/>`;
s+=`<text x="${Ax2}" y="${Ay2-6}" text-anchor="middle" font-size="10" fill="#8b5cf6" font-weight="700">A'</text>`;
s+=`<text x="${Bx2-12}" y="${By2+4}" font-size="10" fill="#8b5cf6" font-weight="700">B'</text>`;
s+=`<text x="${Cx2+4}" y="${Cy2+4}" font-size="10" fill="#8b5cf6" font-weight="700">C'</text>`;
// подписи сторон 2
const a2=a/k,b2=b/k,c2=c/k;
s+=`<text x="${(Bx2+Cx2)/2}" y="${By2+14}" text-anchor="middle" font-size="9" fill="#8b5cf6">a'=${fmt(a2/10)}</text>`;
// k-label
s+=`<text x="190" y="15" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="800">k = ${k.toFixed(1)}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
infoEl.innerHTML=`$k=${k.toFixed(1)}$: $a/a'=${fmt(a/a*k/k)} \\to a'=${fmt(a/10/k)}$, $b'=${fmt(b/10/k)}$, $c'=${fmt(c/10/k)}$. Углы всегда равны: $\\angle A=\\angle A', \\angle B=\\angle B', \\angle C=\\angle C'$.`;
renderMath(infoEl);
addXp(1,'p3-sim');
}
kSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: калькулятор == */
(function(){
document.getElementById('p3-ccalc').addEventListener('click',()=>{
const a=parseFloat(document.getElementById('p3-ca').value);
const b=parseFloat(document.getElementById('p3-cb').value);
const c=parseFloat(document.getElementById('p3-cc').value);
const k=parseFloat(document.getElementById('p3-ck').value);
const out=document.getElementById('p3-ccalc-out');
if([a,b,c,k].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи положительные числа для a, b, c, k.</span>';return;
}
out.style.display='block';
out.innerHTML=`Стороны подобного треугольника при $k=${fmt(k)}$:<br>$a'=a/k=${fmt(a/k)}$, $b'=b/k=${fmt(b/k)}$, $c'=c/k=${fmt(c/k)}$`;
renderMath(out);
addXp(2,'p3-calc');bumpProgress('p3',5);
});
})();
/* == INIT ИНТЕРАКТИВ 3: DnD == */
(function(){
const items=[
{text:'(3,4,5) и (6,8,10)',yes:true},
{text:'(5,7,9) и (10,15,18)',yes:false},
{text:'(2,3,4) и (4,6,8)',yes:true},
{text:'(1,2,3) и (2,4,7)',yes:false},
{text:'(6,8,10) и (3,4,5)',yes:true},
];
const pool=document.getElementById('p3-dnd-pool');
const yesBox=document.getElementById('p3-drop-yes-items');
const noBox=document.getElementById('p3-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p3-drop-yes'),document.getElementById('p3-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p3-drop-yes')?yesBox:box===document.getElementById('p3-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p3-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p3-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p3-dnd');bumpProgress('p3',8);}
else{feedback(fb,false,'Есть ошибки — проверь пропорциональность сторон.');}
});
document.getElementById('p3-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p3-dnd-fb').style.display='none';
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'$\\triangle ABC$ со сторонами $6,8,10$ подобен $\\triangle A\'B\'C\'$ со сторонами $3,4,5$. Чему равен коэффициент подобия?', ans:2, hint:'k = 6/3 = 8/4 = 10/5 = 2.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, коэффициент $k=3$. Сторона $a=15$. Найди $a\'$.', ans:5, hint:'a\' = a/k = 15/3 = 5.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=2$. Сторона $b\'=7$. Найди $b$.', ans:14, hint:'b = k·b\' = 2·7 = 14.'},
{q:'Стороны треугольников $4,6,8$ и $6,9,12$. Найди коэффициент подобия.', ans:1.5, hint:'k = 6/4 = 9/6 = 12/8 = 1.5.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $a=20$, $a\'=4$. Чему равен $k$?', ans:5, hint:'k = a/a\' = 20/4 = 5.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p3-tr-i').textContent=idx+1;
const t=document.getElementById('p3-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p3-tr-ans').value='';
document.getElementById('p3-tr-fb').style.display='none';
}
document.getElementById('p3-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p3-tr-score').textContent=0;show();});
document.getElementById('p3-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p3-tr-ans').value;
const fb=document.getElementById('p3-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.05){
score++;document.getElementById('p3-tr-score').textContent=score;
addXp(3,'p3-tr-'+idx);bumpProgress('p3',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p3-tr-all');bumpProgress('p3',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p3-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p3-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: мини-квиз == */
(function(){
const qs=[
{q:'Коэффициент подобия равен 1. Что это означает?', opts:['Треугольники конгруэнтны','Треугольники не подобны','Треугольники равновелики по площади','Треугольники прямоугольные'], ans:0},
{q:'Если $\\triangle ABC \\sim \\triangle A\'B\'C\'$ с $k=3$, то $\\triangle A\'B\'C\' \\sim \\triangle ABC$ с коэффициентом:', opts:['3','1/3','9','1/9'], ans:1},
{q:'Что является достаточным условием подобия треугольников?', opts:['Равенство двух углов','Равенство одной стороны','Равенство периметров','Равенство площадей'], ans:0},
{q:'Подобие — это:', opts:['Отношение эквивалентности','Только рефлексивное отношение','Только транзитивное отношение','Отношение порядка'], ans:0},
];
const body=document.getElementById('p3-quiz-body');
body.innerHTML=qs.map((q,qi)=>`
<div style="margin-bottom:14px;padding:12px;background:var(--card);border-radius:10px;border:1px solid var(--border)">
<div style="font-weight:700;margin-bottom:8px;font-size:.95rem">${qi+1}. ${q.q}</div>
${q.opts.map((o,oi)=>`<label style="display:flex;align-items:center;gap:8px;margin-bottom:6px;cursor:pointer;font-size:.92rem"><input type="radio" name="p3q${qi}" value="${oi}" style="accent-color:var(--sec-acc,var(--pri))"> ${o}</label>`).join('')}
</div>`).join('');
document.getElementById('p3-quiz-check').addEventListener('click',()=>{
const fb=document.getElementById('p3-quiz-fb');
let correct=0;
qs.forEach((q,qi)=>{
const sel=document.querySelector(`input[name="p3q${qi}"]:checked`);
if(sel&&+sel.value===q.ans)correct++;
});
if(correct===qs.length){
feedback(fb,true,`Все ${qs.length} верно! +10 XP`);
addXp(10,'p3-quiz');bumpProgress('p3',10);confetti();
} else {
feedback(fb,false,`Верно ${correct} из ${qs.length}. Перечитай карточки теории.`);
}
});
})();
/* == INIT: Босс §3 == */
(function(){
const tasks=[
{q:'$\\triangle ABC$ со сторонами $9,12,15$. Коэффициент подобия с $\\triangle A\'B\'C\'$ равен $3$. Найди наименьшую сторону $\\triangle A\'B\'C\'$.', ans:3, hint:'a\' = 9/3 = 3.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$. Стороны $\\triangle A\'B\'C\'$: $5,7,x$, стороны $\\triangle ABC$: $10,14,16$. Найди $x$.', ans:8, hint:'k=2, x = 16/2 = 8.'},
{q:'Периметр $\\triangle ABC = 36$, $k=3$. Найди периметр подобного треугольника $\\triangle A\'B\'C\'$.', ans:12, hint:'P\' = P/k = 36/3 = 12.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=4$. Сторона $c=20$. Найди $c\'$.', ans:5, hint:'c\' = c/k = 20/4 = 5.'},
];
const bossBox=document.getElementById('p3-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p3b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p3b-a${i}').value;
const fb=document.getElementById('p3b-fb${i}');
if(Math.abs(v-${t.ans})<0.05){
feedback(fb,true,'Верно! +5 XP');
if(!window.p3BossSolved.has(${i})){ window.p3BossSolved.add(${i}); addXp(5,'p3-boss${i}'); bumpProgress('p3',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p3b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p3BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP4(){
const box=document.getElementById('p4-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Теорема о прямой, параллельной стороне треугольника','4.1',`
<p><b>Теорема.</b> Прямая, параллельная одной из сторон треугольника и пересекающая две другие стороны, отсекает треугольник, подобный исходному.</p>
<p style="margin-top:8px"><b>Формально:</b> в $\\triangle ABC$ прямая $MN \\parallel BC$ пересекает $AB$ в точке $M$ и $AC$ в точке $N$. Тогда $\\triangle AMN \\sim \\triangle ABC$ с коэффициентом подобия:</p>
$$k = \\dfrac{AM}{AB} = \\dfrac{AN}{AC} = \\dfrac{MN}{BC}$$
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 175" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- triangle ABC -->
<polygon points="140,15 30,155 250,155" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="2"/>
<!-- MN line at t=0.45 -->
<!-- M on AB: M = A + 0.45*(B-A) = (140+0.45*(30-140), 15+0.45*(155-15)) = (89.5, 78) -->
<!-- N on AC: N = A + 0.45*(C-A) = (140+0.45*(250-140), 15+0.45*(155-15)) = (189.5, 78) -->
<polygon points="140,15 89.5,78 189.5,78" fill="rgba(99,102,241,.22)" stroke="#4f46e5" stroke-width="2"/>
<line x1="72" y1="78" x2="210" y2="78" stroke="#4f46e5" stroke-width="1.8" stroke-dasharray="5,3"/>
<!-- labels -->
<text x="140" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="22" y="165" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="253" y="165" font-size="11" font-weight="700" fill="#4338ca">C</text>
<text x="82" y="76" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text>
<text x="196" y="76" font-size="10" font-weight="700" fill="#4f46e5">N</text>
<text x="140" y="95" text-anchor="middle" font-size="9" fill="#4f46e5">MN ∥ BC</text>
<text x="140" y="170" text-anchor="middle" font-size="9" fill="#4338ca" font-style="italic">△AMN △ABC</text>
<!-- k label -->
<text x="265" y="30" font-size="10" fill="#4f46e5" font-weight="700">k=AM/AB</text>
</svg>
</div>`);
html+=makeCard('rule','Следствие — теорема Фалеса для треугольника','4.2',`
<p><b>Следствие.</b> Если $MN \\parallel BC$ в $\\triangle ABC$, то:</p>
$$\\dfrac{AM}{MB} = \\dfrac{AN}{NC}$$
<p style="margin-top:8px">То есть прямая, параллельная стороне треугольника, делит две другие стороны пропорционально.</p>
<p style="margin-top:8px">Это прямое следствие обобщённой теоремы Фалеса: прямые $BC$, $MN$ и $A$ порождают пропорциональное деление сторон $AB$ и $AC$.</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 155" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<polygon points="140,12 30,140 250,140" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/>
<!-- MN at t=0.5 -->
<line x1="85" y1="76" x2="195" y2="76" stroke="#4f46e5" stroke-width="2"/>
<circle cx="85" cy="76" r="3.5" fill="#4f46e5"/>
<circle cx="195" cy="76" r="3.5" fill="#4f46e5"/>
<text x="140" y="8" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="22" y="150" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="253" y="150" font-size="11" font-weight="700" fill="#4338ca">C</text>
<text x="78" y="74" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text>
<text x="200" y="74" font-size="10" font-weight="700" fill="#4f46e5">N</text>
<!-- AM, MB labels -->
<text x="62" y="44" text-anchor="middle" font-size="9" fill="#4f46e5">AM</text>
<text x="56" y="112" text-anchor="middle" font-size="9" fill="#6366f1">MB</text>
<!-- AN, NC labels -->
<text x="221" y="44" text-anchor="middle" font-size="9" fill="#4f46e5">AN</text>
<text x="228" y="112" text-anchor="middle" font-size="9" fill="#6366f1">NC</text>
<text x="140" y="148" text-anchor="middle" font-size="9" fill="#4338ca" font-style="italic">AM/MB = AN/NC</text>
</svg>
</div>`);
html+=makeCard('example','Пример вычисления','4.3',`
<p><b>Пример.</b> В $\\triangle ABC$ прямая $MN \\parallel BC$. $AM = 6$, $AB = 10$, $BC = 15$. Найти $MN$.</p>
<p style="margin-top:8px"><b>Решение:</b> коэффициент подобия $k = AM/AB = 6/10 = 0{,}6$.</p>
<p style="margin-top:4px">$MN = k \\cdot BC = 0{,}6 \\cdot 15 = 9$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> $AM = 4$, $MB = 6$. Найти $AN/NC$.</p>
<p style="margin-top:4px">По следствию: $AN/NC = AM/MB = 4/6 = 2/3$.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG треугольник со slider ---- */
html+=`<div class="wg" id="p4-par-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Параллельная прямая отсекает подобный треугольник</div></div>
<div class="wg-help">Перемещай прямую $MN \\parallel BC$ по высоте треугольника. Коэффициент подобия $k$ меняется — $\\triangle AMN \\sim \\triangle ABC$.</div>
<div class="sliders">
<label>Положение $MN$ (t = AM/AB): <b id="p4-t-val">0.50</b>
<input type="range" min="10" max="90" value="50" id="p4-t-sl" step="1">
</label>
</div>
<div id="p4-par-svg" style="display:flex;justify-content:center"></div>
<div id="p4-par-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p4-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» — каждый шаг раскрывает ключевую идею доказательства.</div>
<div id="p4-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p4-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p4-proof-next">Далее</button>
<button class="btn" id="p4-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Калькулятор ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор: параллельная сторона</div></div>
<div class="wg-help">Введи $AM$, $AB$, $BC$ → найди $MN = BC \\cdot AM/AB$. Или задай $MN$, $BC$ → найди $AM/AB$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">AM</span><input type="number" id="p4-cAM" class="tinp" placeholder="AM" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">AB</span><input type="number" id="p4-cAB" class="tinp" placeholder="AB" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">BC</span><input type="number" id="p4-cBC" class="tinp" placeholder="BC" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p4-ccalc" style="width:100%">Вычислить MN</button></div>
</div>
<div id="p4-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §4</div></div>
<div class="wg-help">5 задач на нахождение MN, AN, AM и отношений.</div>
<div class="score-display"><span>Задача <b id="p4-tr-i">1</b> / 5</span><span>Очки: <b id="p4-tr-score">0</b></span></div>
<div id="p4-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p4-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p4-tr-go">Проверить</button>
<button class="btn" id="p4-tr-start">Начать</button>
</div>
<div class="feedback" id="p4-tr-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 5: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Соотнеси параллельные прямые и пропорции</div></div>
<div class="wg-help">Перетащи каждую карточку: «Пропорция верна» или «Пропорция неверна».</div>
<div id="p4-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p4-drop-yes"><h5>Пропорция верна</h5><div class="drop-items" id="p4-drop-yes-items"></div></div>
<div class="drop-box" id="p4-drop-no"><h5>Пропорция неверна</h5><div class="drop-items" id="p4-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p4-dnd-check">Проверить</button><button class="btn" id="p4-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p4-dnd-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 6: Босс §4 ---- */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §4</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p4-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p4-read-btn" onclick="addXp(10,'p4-read');bumpProgress('p4',40);this.textContent='Прочитано!';this.disabled=true;">
<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>
Я прочитал §4 (+10 XP)
</button>
</div>`;
html+=secNav('p3','p5');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: slider параллельной прямой == */
(function(){
const tSl=document.getElementById('p4-t-sl');
const tVal=document.getElementById('p4-t-val');
const svgWrap=document.getElementById('p4-par-svg');
const infoEl=document.getElementById('p4-par-info');
// base triangle: A=(140,15), B=(30,155), C=(250,155)
const Ax=140,Ay=15,Bx=30,By=155,Cx=250,Cy=155;
const BC=Math.hypot(Cx-Bx,Cy-By);
const AB=Math.hypot(Bx-Ax,By-Ay);
const AC=Math.hypot(Cx-Ax,Cy-Ay);
function draw(){
const t=+tSl.value/100;
tVal.textContent=t.toFixed(2);
const Mx=Ax+t*(Bx-Ax), My=Ay+t*(By-Ay);
const Nx=Ax+t*(Cx-Ax), Ny=Ay+t*(Cy-Ay);
const MN=Math.hypot(Nx-Mx,Ny-My);
const AM=Math.hypot(Mx-Ax,My-Ay);
const AN=Math.hypot(Nx-Ax,Ny-Ay);
const W=300, H=180;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// full triangle light
s+=`<polygon points="${Ax},${Ay} ${Bx},${By} ${Cx},${Cy}" fill="rgba(99,102,241,.07)" stroke="#6366f1" stroke-width="1.5"/>`;
// inner triangle AMN highlighted
s+=`<polygon points="${Ax},${Ay} ${Mx},${My} ${Nx},${Ny}" fill="rgba(79,70,229,.22)" stroke="#4f46e5" stroke-width="2"/>`;
// MN line
s+=`<line x1="${Mx}" y1="${My}" x2="${Nx}" y2="${Ny}" stroke="#4f46e5" stroke-width="2"/>`;
// points
s+=`<circle cx="${Mx}" cy="${My}" r="4" fill="#4f46e5"/>`;
s+=`<circle cx="${Nx}" cy="${Ny}" r="4" fill="#4f46e5"/>`;
// labels
s+=`<text x="${Ax}" y="${Ay-5}" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>`;
s+=`<text x="${Bx-8}" y="${By+2}" font-size="11" font-weight="700" fill="#4338ca">B</text>`;
s+=`<text x="${Cx+4}" y="${Cy+2}" font-size="11" font-weight="700" fill="#4338ca">C</text>`;
s+=`<text x="${Mx-8}" y="${My}" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text>`;
s+=`<text x="${Nx+4}" y="${Ny}" font-size="10" font-weight="700" fill="#4f46e5">N</text>`;
// k badge
s+=`<text x="${W/2}" y="14" text-anchor="middle" font-size="11" font-weight="800" fill="#4338ca">k = ${t.toFixed(2)}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
infoEl.innerHTML=`$k = AM/AB = ${t.toFixed(3)}$. $MN = k \\cdot BC = ${t.toFixed(3)} \\cdot ${fmt(BC/10)} = ${fmt(MN/10)}$. <br> $AM/MB = ${fmt(t/(1-t+1e-9))}$. По следствию: $AN/NC = AM/MB = ${fmt(t/(1-t+1e-9))}$.`;
renderMath(infoEl);
addXp(1,'p4-par');
}
tSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Дан $\\triangle ABC$. Прямая $MN \\parallel BC$ пересекает $AB$ в $M$ и $AC$ в $N$. Требуется доказать $\\triangle AMN \\sim \\triangle ABC$.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/><line x1="78" y1="81" x2="182" y2="81" stroke="#4f46e5" stroke-width="2" stroke-dasharray="5,3"/><circle cx="78" cy="81" r="3.5" fill="#4f46e5"/><circle cx="182" cy="81" r="3.5" fill="#4f46e5"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><text x="130" y="97" text-anchor="middle" font-size="9" fill="#4f46e5">MN ∥ BC</text></svg>`},
{desc:'<b>Шаг 2.</b> $\\angle AMN = \\angle ABC$, так как $MN \\parallel BC$ и $AB$ — секущая: это соответственные углы при параллельных прямых и секущей.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/><line x1="78" y1="81" x2="182" y2="81" stroke="#4f46e5" stroke-width="2"/><circle cx="78" cy="81" r="3.5" fill="#4f46e5"/><circle cx="182" cy="81" r="3.5" fill="#4f46e5"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><path d="M78,81 Q88,88 83,95" fill="none" stroke="#f59e0b" stroke-width="2"/><path d="M25,148 Q38,138 33,128" fill="none" stroke="#f59e0b" stroke-width="2"/><text x="96" y="100" font-size="9" fill="#b45309" font-weight="700">∠AMN = ∠ABC</text></svg>`},
{desc:'<b>Шаг 3.</b> Аналогично $\\angle ANM = \\angle ACB$: $MN \\parallel BC$ и $AC$ — секущая, получаем соответственные углы.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/><line x1="78" y1="81" x2="182" y2="81" stroke="#4f46e5" stroke-width="2"/><circle cx="78" cy="81" r="3.5" fill="#4f46e5"/><circle cx="182" cy="81" r="3.5" fill="#4f46e5"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><path d="M182,81 Q172,88 177,95" fill="none" stroke="#10b981" stroke-width="2"/><path d="M235,148 Q222,138 227,128" fill="none" stroke="#10b981" stroke-width="2"/><text x="130" y="100" text-anchor="middle" font-size="9" fill="#047857" font-weight="700">∠ANM = ∠ACB</text></svg>`},
{desc:'<b>Шаг 4.</b> Угол $\\angle A$ общий у $\\triangle AMN$ и $\\triangle ABC$. Два угла $\\triangle AMN$ равны соответствующим двум углам $\\triangle ABC$ → по признаку ДД (два угла) $\\triangle AMN \\sim \\triangle ABC$.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="1.5"/><polygon points="130,14 78,81 182,81" fill="rgba(79,70,229,.22)" stroke="#4f46e5" stroke-width="2"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><text x="130" y="95" text-anchor="middle" font-size="9" fill="#4f46e5">△AMN △ABC</text></svg>`},
{desc:'<b>Шаг 5.</b> Из подобия следует пропорциональность сторон: $AM/AB = AN/AC = MN/BC = k$. Следствие: $AM/MB = AN/NC$ — прямое следствие теоремы Фалеса. <b>Доказано.</b>',
svg:`<svg viewBox="0 0 260 80" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="130" y="24" text-anchor="middle" font-size="11" font-weight="800" fill="#4338ca">AM/AB = AN/AC = MN/BC = k</text><text x="130" y="46" text-anchor="middle" font-size="10" fill="#4f46e5" font-weight="700">AM/MB = AN/NC</text><text x="130" y="68" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p4-proof-svg');
const descEl=document.getElementById('p4-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg;
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p4-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p4-proof-step');}
else{addXp(5,'p4-proof-done');bumpProgress('p4',10);}
});
document.getElementById('p4-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор == */
(function(){
document.getElementById('p4-ccalc').addEventListener('click',()=>{
const AM=parseFloat(document.getElementById('p4-cAM').value);
const AB=parseFloat(document.getElementById('p4-cAB').value);
const BC=parseFloat(document.getElementById('p4-cBC').value);
const out=document.getElementById('p4-ccalc-out');
if(!isFinite(AM)||!isFinite(AB)||!isFinite(BC)||AM<=0||AB<=0||BC<=0||AM>AB){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи положительные числа; AM должно быть ≤ AB.</span>';return;
}
const k=AM/AB;
const MN=k*BC;
const AN_over_NC=AM/(AB-AM);
out.style.display='block';
out.innerHTML=`$k = AM/AB = ${fmt(AM)}/${fmt(AB)} = ${fmt(k)}$<br>$MN = k \\cdot BC = ${fmt(k)} \\cdot ${fmt(BC)} = ${fmt(MN)}$<br>Следствие: $AM/MB = AN/NC = ${fmt(AM)}/${fmt(AB-AM)} = ${fmt(AN_over_NC)}$`;
renderMath(out);
addXp(2,'p4-calc');bumpProgress('p4',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'В $\\triangle ABC$ прямая $MN \\parallel BC$, $AM=4$, $AB=10$, $BC=20$. Найди $MN$.',ans:8,hint:'k=4/10=0.4; MN=0.4·20=8.'},
{q:'В $\\triangle ABC$ прямая $MN \\parallel BC$, $AM=6$, $AB=9$, $BC=12$. Найди $MN$.',ans:8,hint:'k=6/9=2/3; MN=2/3·12=8.'},
{q:'$MN \\parallel BC$, $AM=5$, $MB=10$. Найди $AN/NC$.',ans:0.5,hint:'AN/NC = AM/MB = 5/10 = 0.5.'},
{q:'$MN \\parallel BC$, $k=0.4$, $BC=30$. Найди $MN$.',ans:12,hint:'MN=k·BC=0.4·30=12.'},
{q:'$MN \\parallel BC$, $AM=3$, $MB=6$. Чему равно $AN/AC$?',ans:0.333,hint:'AN/AC = AM/AB = 3/(3+6) = 1/3 ≈ 0.333.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p4-tr-i').textContent=idx+1;
const t=document.getElementById('p4-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p4-tr-ans').value='';
document.getElementById('p4-tr-fb').style.display='none';
}
document.getElementById('p4-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p4-tr-score').textContent=0;show();});
document.getElementById('p4-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p4-tr-ans').value;
const fb=document.getElementById('p4-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.01){
score++;document.getElementById('p4-tr-score').textContent=score;
addXp(3,'p4-tr-'+idx);bumpProgress('p4',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p4-tr-all');bumpProgress('p4',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p4-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p4-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер == */
(function(){
const items=[
{text:'MN∥BC, AM=3, AB=6, AN=4, AC=8 → AM/MB=AN/NC',yes:true},
{text:'MN∥BC, AM=4, MB=6, AN=3, NC=5 → AM/MB≠AN/NC',yes:false},
{text:'MN∥BC, AM/AB=0.5, MN/BC=0.5',yes:true},
{text:'MN∥BC, AM=2, MB=4, AN=3, NC=6 → AM/MB=AN/NC',yes:true},
{text:'MN∥BC, k=1/3, MN=BC/2',yes:false},
];
const pool=document.getElementById('p4-dnd-pool');
const yesBox=document.getElementById('p4-drop-yes-items');
const noBox=document.getElementById('p4-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p4-drop-yes'),document.getElementById('p4-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p4-drop-yes')?yesBox:box===document.getElementById('p4-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p4-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p4-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p4-dnd');bumpProgress('p4',8);}
else{feedback(fb,false,'Есть ошибки. Проверь: MN∥BC → AM/MB=AN/NC и MN/BC=AM/AB.');}
});
document.getElementById('p4-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p4-dnd-fb').style.display='none';
});
})();
/* == INIT: Босс §4 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 140" style="display:block;max-width:240px;margin:0 auto 8px;background:#e0e7ff;border:1px solid #a5b4fc;border-radius:8px"><polygon points="120,12 20,128 220,128" fill="rgba(99,102,241,.12)" stroke="#6366f1" stroke-width="2"/><line x1="70" y1="70" x2="170" y2="70" stroke="#4f46e5" stroke-width="2"/><text x="120" y="8" text-anchor="middle" font-size="10" fill="#4338ca" font-weight="700">A</text><text x="12" y="136" font-size="10" fill="#4338ca" font-weight="700">B</text><text x="222" y="136" font-size="10" fill="#4338ca" font-weight="700">C</text><text x="62" y="68" text-anchor="end" font-size="10" fill="#4f46e5" font-weight="700">M</text><text x="174" y="68" font-size="10" fill="#4f46e5" font-weight="700">N</text><text x="120" y="90" text-anchor="middle" font-size="9" fill="#4f46e5">AM=5, AB=8, BC=24</text></svg>$MN \\parallel BC$. $AM=5$, $AB=8$, $BC=24$. Найди $MN$.',ans:15,hint:'k=5/8; MN=5/8·24=15.'},
{q:'$MN \\parallel BC$, $AM=6$, $MB=9$. Найди $AN/NC$.',ans:0.667,hint:'AN/NC = AM/MB = 6/9 = 2/3 ≈ 0.667.'},
{q:'$MN \\parallel BC$, $MN=8$, $BC=12$. Найди $AM/AB$.',ans:0.667,hint:'AM/AB = MN/BC = 8/12 = 2/3 ≈ 0.667.'},
{q:'В $\\triangle ABC$ прямая $MN \\parallel BC$, $AM=3$, $AB=9$, $BC=18$. Найди $MN$.',ans:6,hint:'k=3/9=1/3; MN=1/3·18=6.'},
];
const bossBox=document.getElementById('p4-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p4b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p4b-a${i}').value;
const fb=document.getElementById('p4b-fb${i}');
if(Math.abs(v-${t.ans})<0.01){
feedback(fb,true,'Верно! +5 XP');
if(!window.p4BossSolved.has(${i})){ window.p4BossSolved.add(${i}); addXp(5,'p4-boss${i}'); bumpProgress('p4',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p4b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p4BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP5(){
const box=document.getElementById('p5-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Первый признак подобия треугольников (ДД — два угла)','5.1',`
<p><b>Теорема (1-й признак подобия).</b> Если два угла одного треугольника соответственно равны двум углам другого треугольника, то такие треугольники подобны.</p>
<p style="margin-top:8px">Если $\\angle A = \\angle A'$ и $\\angle B = \\angle B'$, то $\\triangle ABC \\sim \\triangle A'B'C'$.</p>
<p style="margin-top:8px"><b>Почему достаточно двух углов?</b> Сумма углов треугольника равна $180°$. Если два угла равны, третий автоматически тоже равен: $\\angle C = 180° - \\angle A - \\angle B = 180° - \\angle A' - \\angle B' = \\angle C'$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 155" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- большой треугольник ABC -->
<polygon points="50,140 180,140 100,30" fill="rgba(79,70,229,.11)" stroke="#4f46e5" stroke-width="2"/>
<text x="96" y="25" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="40" y="150" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="183" y="150" font-size="11" font-weight="700" fill="#4338ca">C</text>
<!-- угловые метки -->
<path d="M100,30 Q112,40 105,50" fill="none" stroke="#f59e0b" stroke-width="2"/>
<path d="M50,140 Q62,130 70,136" fill="none" stroke="#10b981" stroke-width="2"/>
<!-- маленький треугольник A'B'C' -->
<polygon points="200,140 264,140 230,76" fill="rgba(99,102,241,.15)" stroke="#6366f1" stroke-width="1.8"/>
<text x="227" y="70" font-size="10" font-weight="700" fill="#4f46e5">A'</text>
<text x="193" y="150" font-size="10" font-weight="700" fill="#4f46e5">B'</text>
<text x="266" y="150" font-size="10" font-weight="700" fill="#4f46e5">C'</text>
<!-- угловые метки -->
<path d="M230,76 Q237,84 232,91" fill="none" stroke="#f59e0b" stroke-width="1.5"/>
<path d="M200,140 Q210,132 215,136" fill="none" stroke="#10b981" stroke-width="1.5"/>
<text x="140" y="15" text-anchor="middle" font-size="10" fill="#4338ca">∠A=∠A', ∠B=∠B' → △ABC∼△A'B'C'</text>
</svg>
</div>`);
html+=makeCard('rule','Следствие: прямоугольные треугольники','5.2',`
<p><b>Следствие.</b> Два прямоугольных треугольника подобны, если у них равны острые углы (достаточно одной пары).</p>
<p style="margin-top:8px">Если $\\angle C = \\angle C' = 90°$ и $\\angle A = \\angle A'$, то $\\triangle ABC \\sim \\triangle A'B'C'$.</p>
<p style="margin-top:8px">Это потому, что прямой угол — один из двух равных углов, а второй угол задан условием.</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 130" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- прямоугольный треугольник 1: A=(40,20), B=(40,110), C=(160,110) -->
<polygon points="40,20 40,110 160,110" fill="rgba(79,70,229,.10)" stroke="#4f46e5" stroke-width="2"/>
<!-- marker угла B (прямой) — inside polygon -->
<polyline points="40,100 50,100 50,110" fill="none" stroke="#4f46e5" stroke-width="1.5"/>
<text x="34" y="16" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="28" y="120" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="163" y="120" font-size="11" font-weight="700" fill="#4338ca">C</text>
<text x="36" y="116" font-size="8" fill="#4f46e5">90°</text>
<!-- прямоугольный треугольник 2: A'=(185,50), B'=(185,110), C'=(250,110) -->
<polygon points="185,50 185,110 250,110" fill="rgba(99,102,241,.15)" stroke="#6366f1" stroke-width="1.8"/>
<!-- marker прямого угла B' -->
<polyline points="185,100 195,100 195,110" fill="none" stroke="#6366f1" stroke-width="1.5"/>
<text x="178" y="46" font-size="10" font-weight="700" fill="#4f46e5">A'</text>
<text x="172" y="120" font-size="10" font-weight="700" fill="#4f46e5">B'</text>
<text x="253" y="120" font-size="10" font-weight="700" fill="#4f46e5">C'</text>
<text x="140" y="14" text-anchor="middle" font-size="9" fill="#4338ca">∠B=∠B'=90°, ∠A=∠A' → подобны</text>
</svg>
</div>`);
html+=makeCard('example','Пример применения признака ДД','5.3',`
<p><b>Пример 1.</b> В $\\triangle ABC$: $\\angle A = 50°$, $\\angle B = 70°$. В $\\triangle A'B'C'$: $\\angle A' = 50°$, $\\angle C' = 60°$. Подобны ли треугольники?</p>
<p style="margin-top:6px">$\\angle C = 180° - 50° - 70° = 60°$. $\\angle B' = 180° - 50° - 60° = 70°$. Углы: $50°, 70°, 60°$ в обоих. По признаку ДД: $\\triangle ABC \\sim \\triangle A'C'B'$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> Два прямоугольных треугольника, у одного острый угол $35°$, у другого $35°$. Подобны ли?</p>
<p style="margin-top:4px">Да. Оба имеют $90°$ и $35°$ — два совпадающих угла.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG два треугольника, слайдер углов ---- */
html+=`<div class="wg" id="p5-ang-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два угла задают форму — первый признак</div></div>
<div class="wg-help">Задай углы $\\alpha$ и $\\beta$. Оба треугольника строятся с этими углами — они подобны. Коэффициент $k$ определяется масштабом.</div>
<div class="sliders">
<label>Угол $\\alpha$: <b id="p5-a-val">50</b>°
<input type="range" min="20" max="130" value="50" id="p5-a-sl" step="1">
</label>
<label>Угол $\\beta$: <b id="p5-b-val">60</b>°
<input type="range" min="20" max="130" value="60" id="p5-b-sl" step="1">
</label>
<label>Масштаб $k$: <b id="p5-k-val">1.8</b>
<input type="range" min="12" max="30" value="18" id="p5-k-sl" step="1">
</label>
</div>
<div id="p5-ang-svg" style="display:flex;justify-content:center"></div>
<div id="p5-ang-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p5-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство 1-го признака — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» — шаг за шагом увидишь логику доказательства.</div>
<div id="p5-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p5-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p5-proof-next">Далее</button>
<button class="btn" id="p5-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Тренажёр §5 — Признак ДД</div></div>
<div class="wg-help">5 задач на подобие по углам и нахождение сторон.</div>
<div class="score-display"><span>Задача <b id="p5-tr-i">1</b> / 5</span><span>Очки: <b id="p5-tr-score">0</b></span></div>
<div id="p5-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p5-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p5-tr-go">Проверить</button>
<button class="btn" id="p5-tr-start">Начать</button>
</div>
<div class="feedback" id="p5-tr-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Подобные пары или нет? — Сортировка по углам</div></div>
<div class="wg-help">Перетащи каждую пару треугольников в нужную колонку.</div>
<div id="p5-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p5-drop-yes"><h5>Подобны (ДД)</h5><div class="drop-items" id="p5-drop-yes-items"></div></div>
<div class="drop-box" id="p5-drop-no"><h5>Не подобны</h5><div class="drop-items" id="p5-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p5-dnd-check">Проверить</button><button class="btn" id="p5-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p5-dnd-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 5: Калькулятор ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Калькулятор: найти сторону через подобие ДД</div></div>
<div class="wg-help">Введи два угла обоих треугольников и одну сторону первого — получи соответствующую сторону второго.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠A₁ (°)</span><input type="number" id="p5-cA1" class="tinp" placeholder="напр. 50" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠B₁ (°)</span><input type="number" id="p5-cB1" class="tinp" placeholder="напр. 70" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠A₂ (°)</span><input type="number" id="p5-cA2" class="tinp" placeholder="напр. 50" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠B₂ (°)</span><input type="number" id="p5-cB2" class="tinp" placeholder="напр. 70" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">сторона a₁</span><input type="number" id="p5-ca1" class="tinp" placeholder="напр. 12" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p5-ccalc" style="width:100%">Найти a₂</button></div>
</div>
<div id="p5-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 6: Босс §5 ---- */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §5</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p5-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p5-read-btn" onclick="addXp(10,'p5-read');bumpProgress('p5',40);this.textContent='Прочитано!';this.disabled=true;">
<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>
Я прочитал §5 (+10 XP)
</button>
</div>`;
html+=secNav('p4','p6');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: слайдер углов == */
(function(){
const aSl=document.getElementById('p5-a-sl');
const bSl=document.getElementById('p5-b-sl');
const kSl=document.getElementById('p5-k-sl');
const aVal=document.getElementById('p5-a-val');
const bVal=document.getElementById('p5-b-val');
const kVal=document.getElementById('p5-k-val');
const svgWrap=document.getElementById('p5-ang-svg');
const infoEl=document.getElementById('p5-ang-info');
function buildTriFromAngles(alpha,beta,baseLen,ox,oy){
// alpha at A (left), beta at B (right)
// base AB horizontal, C above
const gamma=Math.PI-alpha-beta;
if(gamma<=0.05) return null;
// by sine rule: a/sin(alpha) = base/sin(gamma)
const sinG=Math.sin(gamma);
const AB=baseLen;
const BC=AB*Math.sin(alpha)/sinG;
const AC=AB*Math.sin(beta)/sinG;
// place A at (ox,oy), B at (ox+AB,oy)
const Ax=ox, Ay=oy;
const Bx=ox+AB, By=oy;
// C: from A at angle alpha above AB
const Cx=Ax+AC*Math.cos(alpha);
const Cy=Ay-AC*Math.sin(alpha);
return {Ax,Ay,Bx,By,Cx,Cy,AB,BC,AC};
}
function draw(){
const alpha=+aSl.value*Math.PI/180;
const beta=+bSl.value*Math.PI/180;
const k=+kSl.value/10;
aVal.textContent=+aSl.value;
bVal.textContent=+bSl.value;
kVal.textContent=k.toFixed(1);
const gamma=(Math.PI-alpha-beta)*180/Math.PI;
if(gamma<=3){
svgWrap.innerHTML='<div style="color:var(--bad);padding:10px">Сумма углов превышает 180°. Уменьши углы.</div>';
infoEl.innerHTML='Сумма углов не должна превышать 180°.';
return;
}
const W=360, H=170;
const t1=buildTriFromAngles(alpha,beta,90,20,145);
if(!t1){svgWrap.innerHTML='';return;}
const t2=buildTriFromAngles(alpha,beta,90/k,240,145);
if(!t2){svgWrap.innerHTML='';return;}
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// triangle 1
s+=`<polygon points="${t1.Ax},${t1.Ay} ${t1.Bx},${t1.By} ${t1.Cx},${t1.Cy}" fill="rgba(79,70,229,.12)" stroke="#4f46e5" stroke-width="2"/>`;
s+=`<text x="${t1.Cx}" y="${t1.Cy-5}" text-anchor="middle" font-size="10" font-weight="700" fill="#4338ca">A</text>`;
s+=`<text x="${t1.Ax-8}" y="${t1.Ay+4}" font-size="10" font-weight="700" fill="#4338ca">B</text>`;
s+=`<text x="${t1.Bx+4}" y="${t1.By+4}" font-size="10" font-weight="700" fill="#4338ca">C</text>`;
// angle markers t1
s+=`<path d="M${t1.Cx},${t1.Cy} Q${t1.Cx+(t1.Ax-t1.Cx)*0.18+3},${t1.Cy+(t1.Ay-t1.Cy)*0.18} ${t1.Cx+(t1.Ax-t1.Cx)*0.22},${t1.Cy+(t1.Ay-t1.Cy)*0.22}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
s+=`<path d="M${t1.Ax},${t1.Ay} Q${t1.Ax+(t1.Cx-t1.Ax)*0.12+(t1.Bx-t1.Ax)*0.08},${t1.Ay+(t1.Cy-t1.Ay)*0.12} ${t1.Ax+(t1.Bx-t1.Ax)*0.15},${t1.Ay}" fill="none" stroke="#10b981" stroke-width="1.5"/>`;
// triangle 2 (smaller)
s+=`<polygon points="${t2.Ax},${t2.Ay} ${t2.Bx},${t2.By} ${t2.Cx},${t2.Cy}" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/>`;
s+=`<text x="${t2.Cx}" y="${t2.Cy-5}" text-anchor="middle" font-size="10" font-weight="700" fill="#4f46e5">A'</text>`;
s+=`<text x="${t2.Ax-10}" y="${t2.Ay+4}" font-size="10" font-weight="700" fill="#4f46e5">B'</text>`;
s+=`<text x="${t2.Bx+4}" y="${t2.By+4}" font-size="10" font-weight="700" fill="#4f46e5">C'</text>`;
// angle markers t2
s+=`<path d="M${t2.Cx},${t2.Cy} Q${t2.Cx+(t2.Ax-t2.Cx)*0.22+2},${t2.Cy+(t2.Ay-t2.Cy)*0.22} ${t2.Cx+(t2.Ax-t2.Cx)*0.28},${t2.Cy+(t2.Ay-t2.Cy)*0.28}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
s+=`<path d="M${t2.Ax},${t2.Ay} Q${t2.Ax+(t2.Cx-t2.Ax)*0.14+(t2.Bx-t2.Ax)*0.10},${t2.Ay+(t2.Cy-t2.Ay)*0.14} ${t2.Ax+(t2.Bx-t2.Ax)*0.18},${t2.Ay}" fill="none" stroke="#10b981" stroke-width="1.5"/>`;
// label
s+=`<text x="${W/2}" y="12" text-anchor="middle" font-size="10" font-weight="800" fill="#4338ca">k = ${k.toFixed(1)}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
infoEl.innerHTML=`$\\alpha=${+aSl.value}°$, $\\beta=${+bSl.value}°$, $\\gamma=${fmt(gamma)}°$. Оба треугольника имеют одинаковые углы → по признаку ДД они подобны. Коэффициент подобия $k=${k.toFixed(1)}$.`;
renderMath(infoEl);
addXp(1,'p5-ang');
}
aSl.addEventListener('input',draw);
bSl.addEventListener('input',draw);
kSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Даны $\\triangle ABC$ и $\\triangle A\'B\'C\'$ с $\\angle A = \\angle A\'$ и $\\angle B = \\angle B\'$. Требуется доказать $\\triangle ABC \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 280 145" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="50,130 170,130 100,30" fill="rgba(79,70,229,.10)" stroke="#4f46e5" stroke-width="2"/><polygon points="185,130 255,130 220,68" fill="rgba(99,102,241,.15)" stroke="#6366f1" stroke-width="1.8"/><text x="97" y="25" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="40" y="140" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="172" y="140" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="218" y="63" font-size="10" font-weight="700" fill="#4f46e5">A'</text><text x="177" y="140" font-size="10" font-weight="700" fill="#4f46e5">B'</text><text x="257" y="140" font-size="10" font-weight="700" fill="#4f46e5">C'</text><text x="140" y="14" text-anchor="middle" font-size="9" fill="#4338ca">∠A=∠A', ∠B=∠B' — условие</text></svg>`},
{desc:'<b>Шаг 2.</b> Из суммы углов треугольника: $\\angle C = 180° - \\angle A - \\angle B$ и $\\angle C\' = 180° - \\angle A\' - \\angle B\'$. Так как $\\angle A=\\angle A\'$ и $\\angle B=\\angle B\'$, получаем $\\angle C = \\angle C\'$.',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="700">∠A+∠B+∠C = 180°</text><text x="140" y="46" text-anchor="middle" font-size="11" fill="#4f46e5" font-weight="700">∠A'+∠B'+∠C' = 180°</text><text x="140" y="72" text-anchor="middle" font-size="11" fill="#10b981" font-weight="800">∠C = 180°−∠A−∠B = 180°−∠A'−∠B' = ∠C'</text></svg>`},
{desc:'<b>Шаг 3.</b> На луче $A\'B\'$ откладываем отрезок $A\'M = AB$. Через $M$ проводим прямую, параллельную $B\'C\'$ — она встречает $A\'C\'$ в точке $N$. По теореме §4: $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 280 145" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="185,130 255,130 220,68" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="1.5"/><polygon points="185,130 230,130 207,99" fill="rgba(79,70,229,.22)" stroke="#4f46e5" stroke-width="2"/><line x1="207" y1="99" x2="230" y2="130" stroke="#4f46e5" stroke-width="1.5" stroke-dasharray="4,3"/><text x="218" y="63" font-size="10" font-weight="700" fill="#4f46e5">A'</text><text x="177" y="140" font-size="10" font-weight="700" fill="#4338ca">B'</text><text x="257" y="140" font-size="10" font-weight="700" fill="#4338ca">C'</text><text x="228" y="138" font-size="9" font-weight="700" fill="#4f46e5">M</text><text x="209" y="97" font-size="9" font-weight="700" fill="#4f46e5">N</text><text x="50" y="80" font-size="9" fill="#4338ca">△A'MN∼△A'B'C'</text></svg>`},
{desc:'<b>Шаг 4.</b> Поскольку $A\'M = AB$ и углы треугольников $\\triangle A\'MN$ и $\\triangle ABC$ попарно равны (все три угла), а $\\angle A\' = \\angle A$ — вершины совпадают, то $\\triangle A\'MN \\cong \\triangle ABC$ (по условию и построению).',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="700">A'M = AB, ∠A'=∠A, ∠B'=∠B</text><text x="140" y="46" text-anchor="middle" font-size="11" fill="#4f46e5" font-weight="700">→ △A'MN ≅ △ABC (признак у-с-у)</text><text x="140" y="70" text-anchor="middle" font-size="10" fill="#6366f1">Следовательно MN = BC, A'N = AC</text></svg>`},
{desc:'<b>Шаг 5.</b> Итог: $\\triangle A\'MN \\cong \\triangle ABC$ и $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$ — отсюда $\\triangle ABC \\sim \\triangle A\'B\'C\'$ (транзитивность подобия). <b>Первый признак подобия доказан.</b>',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="12" fill="#4338ca" font-weight="800">∠A=∠A', ∠B=∠B' → △ABC △A'B'C'</text><text x="140" y="50" text-anchor="middle" font-size="10" fill="#4f46e5">Признак ДД — первый признак подобия</text><text x="140" y="70" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p5-proof-svg');
const descEl=document.getElementById('p5-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg;
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p5-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p5-proof-step');}
else{addXp(5,'p5-proof-done');bumpProgress('p5',10);}
});
document.getElementById('p5-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: тренажёр == */
(function(){
const tasks=[
{q:'В $\\triangle ABC$: $\\angle A=40°$, $\\angle B=70°$. В $\\triangle A\'B\'C\'$: $\\angle A\'=40°$, $\\angle B\'=70°$. Чему равен $\\angle C\'$?',ans:70,hint:'∠C\'=1804070=70°.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по признаку ДД. $AB=12$, $A\'B\'=4$. Найди коэффициент подобия $k$.',ans:3,hint:'k=AB/A\'B\'=12/4=3.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=2.5$. Сторона $a\'=6$. Найди $a$.',ans:15,hint:'a=k·a\'=2.5·6=15.'},
{q:'Два прямоугольных треугольника. В первом острый угол $37°$, во втором $37°$. Сторона при прямом угле в первом — $10$, во втором — $6$. Найди коэффициент подобия.',ans:1.667,hint:'k=10/6≈1.667.'},
{q:'$\\angle A=\\angle A\'=55°$, $\\angle B=\\angle B\'=65°$. Сторона $c=18$ в $\\triangle ABC$, $c\'=9$ в $\\triangle A\'B\'C\'$. Найди $k$.',ans:2,hint:'k=c/c\'=18/9=2.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p5-tr-i').textContent=idx+1;
const t=document.getElementById('p5-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p5-tr-ans').value='';
document.getElementById('p5-tr-fb').style.display='none';
}
document.getElementById('p5-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p5-tr-score').textContent=0;show();});
document.getElementById('p5-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p5-tr-ans').value;
const fb=document.getElementById('p5-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.02){
score++;document.getElementById('p5-tr-score').textContent=score;
addXp(3,'p5-tr-'+idx);bumpProgress('p5',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p5-tr-all');bumpProgress('p5',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p5-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p5-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 4: DnD-сортер == */
(function(){
const items=[
{text:'(50°,70°,60°) и (50°,70°,60°)',yes:true},
{text:'(40°,60°,80°) и (40°,50°,90°)',yes:false},
{text:'(90°,35°,55°) и (90°,35°,55°)',yes:true},
{text:'(30°,60°,90°) и (45°,45°,90°)',yes:false},
{text:'(80°,60°,40°) и (80°,40°,60°)',yes:true},
];
const pool=document.getElementById('p5-dnd-pool');
const yesBox=document.getElementById('p5-drop-yes-items');
const noBox=document.getElementById('p5-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p5-drop-yes'),document.getElementById('p5-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p5-drop-yes')?yesBox:box===document.getElementById('p5-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p5-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p5-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p5-dnd');bumpProgress('p5',8);}
else{feedback(fb,false,'Есть ошибки. Два треугольника подобны, если у них есть хотя бы две пары равных углов.');}
});
document.getElementById('p5-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p5-dnd-fb').style.display='none';
});
})();
/* == INIT ИНТЕРАКТИВ 5: калькулятор == */
(function(){
document.getElementById('p5-ccalc').addEventListener('click',()=>{
const A1=parseFloat(document.getElementById('p5-cA1').value);
const B1=parseFloat(document.getElementById('p5-cB1').value);
const A2=parseFloat(document.getElementById('p5-cA2').value);
const B2=parseFloat(document.getElementById('p5-cB2').value);
const a1=parseFloat(document.getElementById('p5-ca1').value);
const out=document.getElementById('p5-ccalc-out');
if([A1,B1,A2,B2,a1].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи все поля.</span>';return;
}
const C1=180-A1-B1, C2=180-A2-B2;
if(C1<=0||C2<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Сумма углов превышает 180°.</span>';return;
}
// check similarity: sort angles and compare
const ang1=[A1,B1,C1].sort((a,b)=>a-b);
const ang2=[A2,B2,C2].sort((a,b)=>a-b);
const similar=ang1.every((v,i)=>Math.abs(v-ang2[i])<0.5);
if(!similar){
out.style.display='block';out.innerHTML=`$\\angle C_1=${fmt(C1)}°$, $\\angle C_2=${fmt(C2)}°$. Наборы углов различаются — треугольники <b>не подобны</b>. Признак ДД не выполнен.`;
renderMath(out);return;
}
// find matching side using sine rule: a/sin(A)=b/sin(B)
// k = a1 / (2R sin(A)) ... use ratio of sides
// match angles: find which angle in t2 corresponds to A1
// simplification: by ratio of corresponding sides via sine rule
// a1/sin(A1) = a2/sin(A2) → a2 = a1*sin(A2*pi/180)/sin(A1*pi/180)
const a2=a1*Math.sin(A2*Math.PI/180)/Math.sin(A1*Math.PI/180);
const k=a1/a2;
out.style.display='block';
out.innerHTML=`Треугольники подобны по признаку ДД.<br>$\\angle C_1 = ${fmt(C1)}°$, $\\angle C_2 = ${fmt(C2)}°$.<br>По теореме синусов: $a_2 = a_1 \\cdot \\dfrac{\\sin \\angle A_2}{\\sin \\angle A_1} = ${fmt(a1)} \\cdot \\dfrac{${fmt(Math.sin(A2*Math.PI/180).toFixed(4))}}{${fmt(Math.sin(A1*Math.PI/180).toFixed(4))}} = ${fmt(a2)}$.<br>Коэффициент подобия $k = a_1/a_2 = ${fmt(k)}$.`;
renderMath(out);
addXp(3,'p5-calc');bumpProgress('p5',5);
});
})();
/* == INIT: Босс §5 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 140" style="display:block;max-width:240px;margin:0 auto 8px;background:#e0e7ff;border:1px solid #a5b4fc;border-radius:8px"><polygon points="50,125 165,125 100,30" fill="rgba(79,70,229,.12)" stroke="#4f46e5" stroke-width="2"/><polygon points="175,125 230,125 200,78" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/><text x="97" y="26" font-size="10" fill="#4338ca" font-weight="700">A</text><text x="40" y="134" font-size="10" fill="#4338ca" font-weight="700">B</text><text x="167" y="134" font-size="10" fill="#4338ca" font-weight="700">C</text><text x="197" y="74" font-size="9" fill="#4f46e5" font-weight="700">A\'</text><text x="168" y="134" font-size="9" fill="#4f46e5" font-weight="700">B\'</text><text x="232" y="134" font-size="9" fill="#4f46e5" font-weight="700">C\'</text><text x="120" y="12" text-anchor="middle" font-size="8" fill="#4338ca">∠A=∠A\'=60°, ∠B=∠B\'=80°</text></svg>$\\angle A=\\angle A\'=60°$, $\\angle B=\\angle B\'=80°$. $AB=15$, $A\'B\'=5$. Найди $k$.',ans:3,hint:'k=AB/A\'B\'=15/5=3.'},
{q:'Два прямоугольных треугольника. Острый угол одного $42°$, другого $42°$. Подобны ли? Гипотенуза первого $13$, второго $6.5$. Найди $k$.',ans:2,hint:'Подобны (ДД). k=13/6.5=2.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по ДД. $k=4$. Периметр $\\triangle A\'B\'C\'=18$. Найди периметр $\\triangle ABC$.',ans:72,hint:'P=k·P\'=4·18=72.'},
{q:'В $\\triangle ABC$ и $\\triangle DEF$: $\\angle A=\\angle D=55°$, $\\angle B=\\angle E=75°$. $BC=20$, $EF=8$. Найди $k=BC/EF$.',ans:2.5,hint:'k=20/8=2.5.'},
];
const bossBox=document.getElementById('p5-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p5b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p5b-a${i}').value;
const fb=document.getElementById('p5b-fb${i}');
if(Math.abs(v-${t.ans})<0.02){
feedback(fb,true,'Верно! +5 XP');
if(!window.p5BossSolved.has(${i})){ window.p5BossSolved.add(${i}); addXp(5,'p5-boss${i}'); bumpProgress('p5',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p5b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p5BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP6stub(){ document.getElementById('p6-body').innerHTML='<div class="card"><div class="card-body"><p><b>§6 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p5','p7'); }
function buildP7stub(){ document.getElementById('p7-body').innerHTML='<div class="card"><div class="card-body"><p><b>§7 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p6','p8'); }
function buildP8stub(){ document.getElementById('p8-body').innerHTML='<div class="card"><div class="card-body"><p><b>§8 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p7','p9'); }
function buildP9stub(){ document.getElementById('p9-body').innerHTML='<div class="card"><div class="card-body"><p><b>§9 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p8','final3'); }
function buildFinal3stub(){ document.getElementById('final3-body').innerHTML='<div class="card"><div class="card-body"><p><b>Финал главы 3 — Волна 1</b>: боссы и итоги появятся в следующем обновлении.</p></div></div>'+secNav('p9',null); }
</script>
</body>
</html>