Files
Learn_System/frontend/textbooks/geometry_8_ch1.html
T
Maxim Dolgolyov e1c05da294 fix(geom8 ch1): пропущен закрывающий $ в SIDEBARS p1 'Число диагоналей'
В SIDEBARS p1 (line 516, шпаргалка боковой панели) у формулы числа
диагоналей \$\dfrac{n(n-3)}{2} не было закрывающего \$.
KaTeX видел незакрытый блок $...$ — отображал как сырой текст:
'Число диагоналей — $\dfrac{n(n-3)}{2}'.

Исправлено: добавлен закрывающий $.

Полный аудит KaTeX по всем 4 главам Геометрии 8 — это была
единственная найденная ошибка. Остальные $...$ блоки чисты.

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

7296 lines
587 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 · Глава 1 · Многоугольники</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#d97706; --pri2:#b45309; --pri-soft:#fef3c7;
--acc:#f59e0b; --acc2:#d97706; --acc-soft:#fef9c3;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#0a0a0e; --card:#13120a; --card-soft:#18160a; --text:#fef9e7; --ink:#fef9e7; --muted:#a39070; --border:#2a2512}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
/* HEADER */
.hdr{position:relative;background:linear-gradient(110deg,#92400e 0%,#d97706 55%,#fbbf24 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(251,191,36,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 1';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,235,180,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
/* MAIN GRID */
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
/* HERO */
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero::before{content:'&#9632;';position:absolute;right:-10px;top:-20px;font-size:clamp(2rem,8vw,5.5rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none}
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(217,119,6,.32)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(217,119,6,.18);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(217,119,6,.22);font-family:'Unbounded',sans-serif}
/* PARA SELECTOR */
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(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(217,119,6,.10);border-radius:3px;overflow:hidden}
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
.psel-card.final{background:linear-gradient(135deg,#fff5e1,#fef3c7)}
.psel-card.final .psel-num{color:var(--warn)}
/* SECTION COLORS — amber/orange/warm palette */
.sec[id="sec-p1"] { --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p2"] { --sec-acc:#f59e0b; --sec-acc-d:#d97706; --sec-acc-soft:#fef9c3; }
.sec[id="sec-p3"] { --sec-acc:#f97316; --sec-acc-d:#ea580c; --sec-acc-soft:#ffedd5; }
.sec[id="sec-p4"] { --sec-acc:#dc2626; --sec-acc-d:#b91c1c; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p5"] { --sec-acc:#e11d48; --sec-acc-d:#be123c; --sec-acc-soft:#ffe4e6; }
.sec[id="sec-p6"] { --sec-acc:#c026d3; --sec-acc-d:#a21caf; --sec-acc-soft:#fae8ff; }
.sec[id="sec-p7"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p8"] { --sec-acc:#2563eb; --sec-acc-d:#1d4ed8; --sec-acc-soft:#dbeafe; }
.sec[id="sec-p9"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p10"] { --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p11"] { --sec-acc:#16a34a; --sec-acc-d:#15803d; --sec-acc-soft:#dcfce7; }
.sec[id="sec-p12"] { --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p13"] { --sec-acc:#f59e0b; --sec-acc-d:#d97706; --sec-acc-soft:#fef9c3; }
.sec[id="sec-p14"] { --sec-acc:#f97316; --sec-acc-d:#ea580c; --sec-acc-soft:#ffedd5; }
.sec[id="sec-p15"] { --sec-acc:#dc2626; --sec-acc-d:#b91c1c; --sec-acc-soft:#fee2e2; }
.sec[id="sec-p16"] { --sec-acc:#e11d48; --sec-acc-d:#be123c; --sec-acc-soft:#ffe4e6; }
.sec[id="sec-final1"] { --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.7rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
/* CARDS */
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(217,119,6,.06);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(217,119,6,.12)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}.card-icon.class{background:#3b82f6}.card-icon.home{background:#f97316}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.card-body p:last-child{margin-bottom:0}
/* WIDGET */
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
/* BUTTONS */
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.btn.small{padding:5px 11px;font-size:.78rem}
/* INPUTS */
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
/* FEEDBACK */
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
/* SIDEBAR */
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
.sidecard-row:last-child{margin-bottom:0}
@media(max-width:980px){.col-side{position:static;max-height:none}}
/* XP card */
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
.xp-bar{height:9px;background:rgba(245,158,11,.15);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
/* SPOILER */
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
/* TABLES */
.tbl{width:100%;border-collapse:collapse;margin:12px 0;font-size:.88rem}
.tbl th,.tbl td{padding:7px 10px;border:1px solid var(--border);text-align:center}
.tbl th{background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));font-weight:700}
/* ACH popup */
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#d97706,#f59e0b);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(217,119,6,.45);z-index:1002;display:none;align-items:center;gap:8px;animation:achIn .45s cubic-bezier(.34,1.56,.64,1) forwards;max-width:340px}
.ach-popup.show{display:flex}
@keyframes achIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
/* DRAG & DROP */
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
/* SIDEBAR DRAWER for narrow viewports */
.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}
}
/* GLOSSARY tooltip */
.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 */
.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 · Глава 1</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>
<span>Поиск</span>
</button>
<button id="sidebar-btn" class="hdr-btn" title="Шпаргалка">
<svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg>
Шпаргалка
</button>
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<span id="theme-lab">Тёмная</span>
</button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Многоугольники: углы, стороны, свойства</h2>
<p>В этой главе мы изучаем <b>выпуклые многоугольники</b> и их диагонали, доказываем формулу суммы углов, исследуем параллелограммы, ромбы, прямоугольники и квадраты. Кульминация — <b>теорема Фалеса</b> и <b>средние линии</b>.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')">
<svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg>
Начать § 1
</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" title="Опыт"></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="&#9653;"><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="&#8721;&#8736;"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Сумма углов выпуклого многоугольника</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="360"><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="&#9649;"><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="&#8741;&#8741;"><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="&#8660;"><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="&#9645;"><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="90&#176;"><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="&#9671;"><div class="sec-header"><span class="sec-num">§ 9</span><h2 class="sec-h">Ромб. Свойство диагоналей ромба. Признаки ромба</h2></div><div id="p9-body"></div></section>
<section id="sec-p10" class="sec" data-watermark="&#9632;"><div class="sec-header"><span class="sec-num">§ 10</span><h2 class="sec-h">Квадрат. Свойства квадрата</h2></div><div id="p10-body"></div></section>
<section id="sec-p11" class="sec" data-watermark="&#8942;"><div class="sec-header"><span class="sec-num">§ 11</span><h2 class="sec-h">Теорема Фалеса. Деление отрезка на равные части</h2></div><div id="p11-body"></div></section>
<section id="sec-p12" class="sec" data-watermark="m"><div class="sec-header"><span class="sec-num">§ 12</span><h2 class="sec-h">Свойство медиан треугольника</h2></div><div id="p12-body"></div></section>
<section id="sec-p13" class="sec" data-watermark="&#8869;"><div class="sec-header"><span class="sec-num">§ 13</span><h2 class="sec-h">Свойство средней линии треугольника</h2></div><div id="p13-body"></div></section>
<section id="sec-p14" class="sec" data-watermark="&#8767;"><div class="sec-header"><span class="sec-num">§ 14</span><h2 class="sec-h">Трапеция. Свойство средней линии</h2></div><div id="p14-body"></div></section>
<section id="sec-p15" class="sec" data-watermark="&#8736;&#8736;"><div class="sec-header"><span class="sec-num">§ 15</span><h2 class="sec-h">Свойство углов и диагоналей равнобедренной трапеции</h2></div><div id="p15-body"></div></section>
<section id="sec-p16" class="sec" data-watermark="&#8801;"><div class="sec-header"><span class="sec-num">§ 16</span><h2 class="sec-h">Признаки равнобедренной трапеции</h2></div><div id="p16-body"></div></section>
<section id="sec-final1" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#d97706,#f59e0b)">Финал главы</span><h2 class="sec-h">Итоги. Боссы главы 1</h2></div><div id="final1-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Геометрия 8» · Глава 1 · Многоугольники · LearnSpace</footer>
<div id="ach-popup" class="ach-popup">
<svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg>
<span id="ach-text">Достижение!</span>
</div>
<div id="gloss-tip" class="gloss-tip"></div>
<div id="search-modal" class="search-modal" role="dialog" aria-label="Поиск по главе">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск: понятие, теорема, параграф…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot">
<span><kbd>&#8593;&#8595;</kbd> навигация</span>
<span><kbd>Enter</kbd> открыть</span>
<span><kbd>Esc</kbd> закрыть</span>
</div>
</div>
</div>
<script>
'use strict';
/* STATE */
const STATE = {
current: 'p1',
progress: { p1:0,p2:0,p3:0,p4:0,p5:0,p6:0,p7:0,p8:0,p9:0,p10:0,p11:0,p12:0,p13:0,p14:0,p15:0,p16:0,final1: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: 'Начало главы 1!',
ch1_done: 'Многоугольники изучены!',
};
function loadProgress(){
try{
const s = localStorage.getItem('geometry8_ch1_progress');
if(s) Object.assign(STATE.progress, JSON.parse(s));
const a = localStorage.getItem('geometry8_ch1_achievements');
if(a){
const p = JSON.parse(a);
if(Array.isArray(p)) p.forEach(id => STATE.achievements.set(id, ACH_LABELS[id] || id));
else if(p && typeof p === 'object'){
for(const [id, t] of Object.entries(p)) STATE.achievements.set(id, (t && t !== id) ? t : (ACH_LABELS[id] || id));
}
}
STATE.xp = +(localStorage.getItem('geometry8_xp') || 0);
STATE.level = calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('geometry8_ch1_progress', JSON.stringify(STATE.progress));
localStorage.setItem('geometry8_ch1_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);
}
/* Server sync */
const _TB_SLUG = 'geometry-8-ch1';
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-ch1-' + (src||'misc'));
if(STATE.level > prev){
const pop = document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent = 'Уровень ' + STATE.level + '!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'), 2600); }
if(window.confetti) try{ confetti(); }catch(e){}
}
}
const TOTAL_PARAS = 17; /* 16 параграфов + финал */
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);
}
/* PARAGRAPH LIST */
const PARAS = [
{ id:'p1', num:'§ 1', name:'Выпуклые многоугольники', sub:'Диагональ и периметр' },
{ id:'p2', num:'§ 2', name:'Сумма углов', sub:'Формула (n-2)·180°' },
{ id:'p3', num:'§ 3', name:'Сумма внешних углов', sub:'Всегда 360°' },
{ id:'p4', num:'§ 4', name:'Параллелограмм', sub:'Определение и виды' },
{ id:'p5', num:'§ 5', name:'Свойства параллелограмма', sub:'Стороны и диагонали' },
{ id:'p6', num:'§ 6', name:'Признаки параллелограмма', sub:'3 признака' },
{ id:'p7', num:'§ 7', name:'Прямоугольник', sub:'Свойство диагоналей' },
{ id:'p8', num:'§ 8', name:'Признак прямоугольника', sub:'Равные диагонали' },
{ id:'p9', num:'§ 9', name:'Ромб', sub:'Диагонали ромба' },
{ id:'p10', num:'§ 10', name:'Квадрат', sub:'Свойства квадрата' },
{ id:'p11', num:'§ 11', name:'Теорема Фалеса', sub:'Деление отрезка' },
{ id:'p12', num:'§ 12', name:'Медианы треугольника', sub:'Точка пересечения' },
{ id:'p13', num:'§ 13', name:'Средняя линия треугольника', sub:'Параллельность и длина' },
{ id:'p14', num:'§ 14', name:'Трапеция', sub:'Средняя линия трапеции' },
{ id:'p15', num:'§ 15', name:'Равнобедренная трапеция', sub:'Углы и диагонали' },
{ id:'p16', num:'§ 16', name:'Признаки равнобедр. трапеции', sub:'Два признака' },
{ id:'final1', 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:()=>buildP6(), p7:()=>buildP7(), p8:()=>buildP8(),
p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildP11(), p12:()=>buildP12(),
p13:()=>buildP13(), p14:()=>buildP14(), p15:()=>buildP15(), p16:()=>buildP16(),
final1:()=>buildFinal1(),
};
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn = BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current = id; ensureBuilt(id);
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
const el = document.getElementById('sec-' + id); if(el) el.classList.add('active');
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id === id));
buildSidebar(id);
window.scrollTo({top:0, behavior:'smooth'});
if((STATE.progress[id]||0) < 10) bumpProgress(id, 10);
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
setTimeout(()=>{ try{ wrapGlossary(el); }catch(e){} }, 60);
markLastPara(id);
}
/* SIDEBAR DATA */
const SIDEBARS = {
p1: { title:'Шпаргалка § 1', rows:[['Многоугольник','замкнутая ломаная из $n$ звеньев'],['Диагональ','отрезок, соединяющий несмежные вершины'],['Число диагоналей','$\\dfrac{n(n-3)}{2}$'],['Периметр','сумма всех сторон']] },
p2: { title:'Шпаргалка § 2', rows:[['Сумма углов','$(n-2)\\cdot 180^{\\circ}$'],['Треугольник','$180^{\\circ}$'],['Четырёхугольник','$360^{\\circ}$'],['Пятиугольник','$540^{\\circ}$']] },
p3: { title:'Шпаргалка § 3', rows:[['Сумма внешних углов','всегда $360^{\\circ}$'],['Независимо от $n$','для любого выпуклого многоугольника']] },
p4: { title:'Шпаргалка § 4', rows:[['Параллелограмм','четырёхугольник с двумя парами параллельных сторон'],['Обозначение','$\\Box ABCD$']] },
p5: { title:'Шпаргалка § 5', rows:[['Противоположные стороны','равны'],['Противоположные углы','равны'],['Диагонали','делятся точкой пересечения пополам']] },
p6: { title:'Шпаргалка § 6', rows:[['Признак 1','две пары равных противоположных сторон'],['Признак 2','одна пара параллельных и равных сторон'],['Признак 3','диагонали делятся пополам']] },
p7: { title:'Шпаргалка § 7', rows:[['Прямоугольник','параллелограмм с прямым углом'],['Диагонали','равны между собой']] },
p8: { title:'Шпаргалка § 8', rows:[['Признак прямоугольника','параллелограмм с равными диагоналями']] },
p9: { title:'Шпаргалка § 9', rows:[['Ромб','параллелограмм с равными сторонами'],['Диагонали ромба','взаимно перпендикулярны и делят углы пополам']] },
p10: { title:'Шпаргалка § 10', rows:[['Квадрат','прямоугольник и ромб одновременно'],['Диагональ квадрата','$d = a\\sqrt{2}$']] },
p11: { title:'Шпаргалка § 11', rows:[['Теорема Фалеса','параллельные прямые отсекают равные отрезки'],['Применение','деление отрезка на $n$ равных частей']] },
p12: { title:'Шпаргалка § 12', rows:[['Медиана','отрезок от вершины до середины стороны'],['Точка пересечения','делит каждую медиану в отношении $2:1$']] },
p13: { title:'Шпаргалка § 13', rows:[['Средняя линия','соединяет середины двух сторон'],['Длина','равна половине основания'],['Параллельна','основанию треугольника']] },
p14: { title:'Шпаргалка § 14', rows:[['Трапеция','один угол 180°? нет — четырёхугольник с одной парой параллельных сторон'],['Средняя линия','параллельна основаниям, равна их полусумме: $m=\\dfrac{a+b}{2}$']] },
p15: { title:'Шпаргалка § 15', rows:[['Равнобедренная','углы при основании равны'],['Диагонали','равны']] },
p16: { title:'Шпаргалка § 16', rows:[['Признак 1','углы при одном основании равны'],['Признак 2','диагонали равны']] },
final1: { title:'Финал главы', rows:[['16 параграфов','многоугольники изучены'],['Боссы','по одному на каждую тему']] },
};
const TIPS = [
{ sec:'p1', html:'Число диагоналей $n$-угольника: $\\dfrac{n(n-3)}{2}$. Проверь: у четырёхугольника $\\dfrac{4\\cdot1}{2}=2$.' },
{ sec:'p2', html:'Сумма углов $= (n-2)\\cdot180°$. Разбиение на треугольники даёт доказательство.' },
{ sec:'p3', html:'Сумма внешних углов <b>любого</b> выпуклого многоугольника равна $360°$.' },
{ sec:'p4', html:'Параллелограмм — это обобщение: прямоугольник, ромб, квадрат — его частные случаи.' },
{ sec:'p5', html:'Диагонали параллелограмма <b>делятся пополам</b>, а не равны (это у прямоугольника).' },
{ sec:'p6', html:'Три признака: равные противоположные стороны; одна пара параллельных и равных; диагонали пополам.' },
{ sec:'p7', html:'Прямоугольник: диагонали равны. Ромб: диагонали перпендикулярны. Квадрат: оба свойства.' },
{ sec:'p8', html:'Если у параллелограмма диагонали равны — это прямоугольник.' },
{ sec:'p9', html:'Диагонали ромба взаимно перпендикулярны и являются биссектрисами углов.' },
{ sec:'p10', html:'Квадрат совмещает все свойства ромба и прямоугольника.' },
{ sec:'p11', html:'Теорема Фалеса: если параллельные прямые пересекают две прямые, они отсекают пропорциональные отрезки.' },
{ sec:'p12', html:'Медианы пересекаются в точке, делящей каждую в отношении $2:1$ от вершины.' },
{ sec:'p13', html:'Средняя линия треугольника $\\parallel$ основанию и равна его <b>половине</b>.' },
{ sec:'p14', html:'Средняя линия трапеции $= \\dfrac{a+b}{2}$, параллельна основаниям.' },
{ sec:'p15', html:'В равнобедренной трапеции углы при каждом основании равны, диагонали равны.' },
{ sec:'p16', html:'Два признака равнобедренной трапеции: равные углы при основании или равные диагонали.' },
{ sec:'final1', html:'Повторите все формулы: сумма углов, диагонали параллелограмма, средние линии.' },
];
function buildSidebar(id){
const box = document.getElementById('sidebar-content');
const sb = SIDEBARS[id] || SIDEBARS.p1;
let html = '';
const xpForLv = _xpForLevel(STATE.level), xpNext = _xpForLevel(STATE.level + 1);
const xpInLv = STATE.xp - xpForLv, xpRange = xpNext - xpForLv;
const xpPct = xpRange > 0 ? Math.round(xpInLv / xpRange * 100) : 100;
html += `<div class="xp-card"><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:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">${tip.html}</div></div>`;
if(STATE.achievements.size > 0){
html += `<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">${STATE.achievements.size}</span></h4>`;
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html += `<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ ${text}</div>`; });
html += '</div>';
}
box.innerHTML = html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
/* THEME */
function initTheme(){
const t = localStorage.getItem('geometry8_ch1_theme') || 'light';
if(t === 'dark') document.documentElement.classList.add('dark');
document.getElementById('theme-lab').textContent = t === 'dark' ? 'Светлая' : 'Тёмная';
document.getElementById('theme-btn').addEventListener('click', ()=>{
document.documentElement.classList.toggle('dark');
const dark = document.documentElement.classList.contains('dark');
localStorage.setItem('geometry8_ch1_theme', dark ? 'dark' : 'light');
document.getElementById('theme-lab').textContent = dark ? 'Светлая' : 'Тёмная';
});
}
/* HELPERS */
function renderMath(root){
if(window.renderMathInElement){
try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){}
}
}
function feedback(elm, ok, text){ if(!elm)return; elm.className = 'feedback ' + (ok ? 'ok' : 'fail'); elm.innerHTML = text || (ok ? '&#10003; Верно!' : '&#10007; Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n - Math.round(n)) < 1e-9 ? String(Math.round(n)) : (+n.toFixed(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',p10:'§10',p11:'§11',p12:'§12',p13:'§13',p14:'§14',p15:'§15',p16:'§16',final1:'Финал'};
let h = '<div class="sec-nav">';
h += prev ? `<button class="btn" onclick="goTo('${prev}')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> ${NAMES[prev]}</button>` : '<span></span>';
h += next ? `<button class="btn primary" onclick="goTo('${next}')">${NAMES[next]} <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>` : '<span></span>';
h += '</div>'; return h;
}
/* CONFETTI */
let _confettiCanvas = null, _confettiParticles = [], _confettiRaf = null;
function confetti(){
if(!_confettiCanvas){ _confettiCanvas = document.createElement('canvas'); _confettiCanvas.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999'; document.body.appendChild(_confettiCanvas); }
const c = _confettiCanvas; c.width = window.innerWidth; c.height = window.innerHeight;
const ctx = c.getContext('2d');
const colors = ['#d97706','#f59e0b','#fbbf24','#10b981','#0891b2'];
for(let i = 0; i < 80; i++){ _confettiParticles.push({ x:window.innerWidth/2+(Math.random()-.5)*200, y:window.innerHeight/2, vx:(Math.random()-.5)*14, vy:-10-Math.random()*10, g:.4, life:100, color:colors[i%colors.length], r:4+Math.random()*4, rot:0, vRot:(Math.random()-.5)*.3 }); }
if(_confettiRaf) cancelAnimationFrame(_confettiRaf);
function frame(){ ctx.clearRect(0,0,c.width,c.height); _confettiParticles=_confettiParticles.filter(p=>{ p.x+=p.vx;p.y+=p.vy;p.vy+=p.g;p.life--;p.rot+=p.vRot;ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);ctx.fillStyle=p.color;ctx.fillRect(-p.r,-p.r/2,p.r*2,p.r);ctx.restore();return p.life>0&&p.y<c.height+50; }); if(_confettiParticles.length>0) _confettiRaf=requestAnimationFrame(frame); else{ ctx.clearRect(0,0,c.width,c.height);_confettiRaf=null; } }
frame();
}
/* DnD sorter */
function setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
const DND_HINT_HTML = '<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> Перетащивайте или нажмите карточку, затем — на нужный ящик.</div>';
/* GLOSSARY */
const GLOSSARY = [
{ term:'многоугольник', def:'Замкнутая ломаная линия, образующая плоскую фигуру.', sec:'p1', aliases:['многоугольник','многоугольника','многоугольнике','многоугольников','многоугольники','многоугольникам'] },
{ term:'выпуклый многоугольник', def:'Многоугольник, у которого все диагонали лежат внутри фигуры.', sec:'p1', aliases:['выпуклый многоугольник','выпуклого многоугольника','выпуклые многоугольники','выпуклых многоугольников'] },
{ term:'диагональ', def:'Отрезок, соединяющий две несмежные (нессоседние) вершины многоугольника.', sec:'p1', aliases:['диагональ','диагонали','диагоналей','диагональю','диагоналями'] },
{ term:'периметр', def:'Сумма длин всех сторон многоугольника.', sec:'p1', aliases:['периметр','периметра','периметром'] },
{ term:'параллелограмм', def:'Четырёхугольник, у которого противоположные стороны попарно параллельны.', sec:'p4', aliases:['параллелограмм','параллелограмма','параллелограмме','параллелограммов','параллелограммы'] },
{ term:'прямоугольник', def:'Параллелограмм, у которого все углы прямые.', sec:'p7', aliases:['прямоугольник','прямоугольника','прямоугольнике','прямоугольников','прямоугольники'] },
{ term:'ромб', def:'Параллелограмм, у которого все стороны равны.', sec:'p9', aliases:['ромб','ромба','ромбе','ромбов','ромбы'] },
{ term:'квадрат', def:'Прямоугольник, у которого все стороны равны (одновременно ромб и прямоугольник).', sec:'p10', aliases:['квадрат','квадрата','квадрате','квадратов','квадраты'] },
{ term:'трапеция', def:'Четырёхугольник, у которого ровно одна пара параллельных сторон (основания).', sec:'p14', aliases:['трапеция','трапеции','трапецию','трапеций','трапеций'] },
{ term:'равнобедренная трапеция', def:'Трапеция, у которой боковые стороны равны.', sec:'p15', aliases:['равнобедренная трапеция','равнобедренной трапеции','равнобедренную трапецию'] },
{ term:'средняя линия треугольника', def:'Отрезок, соединяющий середины двух сторон треугольника. Параллелен третьей стороне и равен её половине.', sec:'p13', aliases:['средняя линия треугольника','средней линии треугольника','средние линии треугольника'] },
{ term:'средняя линия трапеции', def:'Отрезок, соединяющий середины боковых сторон трапеции. Равен полусумме оснований.', sec:'p14', aliases:['средняя линия трапеции','средней линии трапеции'] },
{ term:'медиана', def:'Отрезок, проведённый из вершины треугольника к середине противоположной стороны.', sec:'p12', aliases:['медиана','медианы','медиан','медианам','медиану'] },
{ term:'центроид', def:'Точка пересечения медиан треугольника (центр тяжести). Делит каждую медиану в отношении 2:1 от вершины.', sec:'p12', aliases:['центроид','центроида','центроиде'] },
{ term:'основания трапеции', def:'Параллельные стороны трапеции, обозначаемые обычно a (большее) и b (меньшее основание).', sec:'p14', aliases:['основания трапеции','основаниям трапеции','основание трапеции','основания'] },
{ term:'теорема Фалеса', def:'Если параллельные прямые пересекают две прямые, они отсекают на них пропорциональные отрезки.', sec:'p11', aliases:['теорема Фалеса','теореме Фалеса','теоремы Фалеса'] },
{ term:'подобные треугольники', def:'Треугольники, у которых углы равны попарно и стороны пропорциональны.', sec:'p11', aliases:['подобные треугольники','подобных треугольников','подобными треугольниками'] },
{ term:'касательная', def:'Прямая, имеющая одну общую точку с окружностью и перпендикулярная радиусу в точке касания.', sec:'p11', aliases:['касательная','касательной','касательную','касательных'] },
{ term:'центральный угол', def:'Угол с вершиной в центре окружности, стороны которого — радиусы.', sec:'p11', aliases:['центральный угол','центрального угла','центральному углу'] },
{ term:'вписанный угол', def:'Угол с вершиной на окружности, стороны которого — хорды.', sec:'p11', aliases:['вписанный угол','вписанного угла','вписанном угле'] },
{ term:'теорема Пифагора', def:'В прямоугольном треугольнике: $a^2 + b^2 = c^2$, где $c$ — гипотенуза.', sec:'p11', aliases:['теорема Пифагора','теореме Пифагора','теоремы Пифагора'] },
{ term:'площадь', def:'Числовая характеристика плоской фигуры, выражающая её размер.', sec:'p11', 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();} });
}
/* SEARCH */
const SEARCH_INDEX = (function(){
const arr = [];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
GLOSSARY.forEach(g=>arr.push({kind:'Понятие',title:g.term,desc:g.def.replace(/\$/g,''),sec:g.sec,gloss:g.term}));
[
['Формула','Число диагоналей n-угольника: n(n-3)/2','§1','p1'],
['Формула','Сумма углов: (n-2)·180°','§2','p2'],
['Формула','Сумма внешних углов: 360°','§3','p3'],
['Формула','Средняя линия треугольника = половина основания','§13','p13'],
['Формула','Средняя линия трапеции = (a+b)/2','§14','p14'],
].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(); } });
}
/* SIDEBAR TOGGLE */
function initSidebarToggle(){
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
if(!side||!btn) return;
function open(){ side.classList.add('open');back.classList.add('show'); }
function close(){ side.classList.remove('open');back.classList.remove('show'); }
btn.addEventListener('click',()=>{ if(side.classList.contains('open'))close();else open(); });
back.addEventListener('click',close);
document.addEventListener('keydown',e=>{ if(e.key==='Escape')close(); });
}
/* INIT */
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initGlossaryTip(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo('p1');
setTimeout(()=>achievement('start','Начало главы 1!'), 600);
if(window.LS && window.LS.xp){
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
}
}
document.addEventListener('DOMContentLoaded', init);
/* ============================================================
§ 1 — ВЫПУКЛЫЕ МНОГОУГОЛЬНИКИ. ДИАГОНАЛЬ. ПЕРИМЕТР
============================================================ */
function buildP1(){
const box = document.getElementById('p1-body');
let html = '';
html += makeCard('theory','Многоугольник — определение','1.1',`
<p><b>Многоугольник</b> — это замкнутая ломаная без самопересечений. Она делит плоскость на внутреннюю часть (многоугольник) и внешнюю.</p>
<p>Элементы: <b>вершины</b> (точки излома), <b>стороны</b> (звенья ломаной), <b>углы</b> (при каждой вершине).</p>
<p>Названия: <b>3</b> стороны — треугольник, <b>4</b> — четырёхугольник, <b>5</b> — пятиугольник, <b>6</b> — шестиугольник, …, <b>n</b> — n-угольник.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 210" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Pentagon ABCDE: cx=150,cy=103,R=80; angles -90,-18,54,126,198 deg -->
<!-- A=(150,23) B=(226,78) C=(197,168) D=(103,168) E=(74,78) -->
<polygon points="150,23 226,78 197,168 103,168 74,78" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- diagonal AC -->
<line x1="150" y1="23" x2="197" y2="168" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="6 3"/>
<!-- side midpoint labels (outside polygon, offset by ~10px) -->
<text x="197" y="46" text-anchor="middle" font-size="12" fill="#64748b" font-family="JetBrains Mono,monospace" font-weight="700">a</text>
<text x="225" y="130" text-anchor="middle" font-size="12" fill="#64748b" font-family="JetBrains Mono,monospace" font-weight="700">b</text>
<text x="158" y="185" text-anchor="middle" font-size="12" fill="#64748b" font-family="JetBrains Mono,monospace" font-weight="700">c</text>
<text x="83" y="178" text-anchor="middle" font-size="12" fill="#64748b" font-family="JetBrains Mono,monospace" font-weight="700">d</text>
<text x="98" y="46" text-anchor="middle" font-size="12" fill="#64748b" font-family="JetBrains Mono,monospace" font-weight="700">e</text>
<!-- diagonal label at midpoint -->
<text x="187" y="108" text-anchor="middle" font-size="11" fill="#8b5cf6" font-family="Unbounded,sans-serif" font-weight="800">AC</text>
<!-- vertex dots -->
<circle cx="150" cy="23" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="226" cy="78" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="197" cy="168" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="103" cy="168" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="74" cy="78" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels offset away from centroid (150,103) -->
<text x="150" y="12" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="240" y="80" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="210" y="184" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="90" y="184" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<text x="58" y="80" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">E</text>
<!-- caption -->
<text x="150" y="202" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">Вершины, стороны, диагональ AC</text>
</svg></div>`);
html += makeCard('rule','Выпуклый многоугольник','1.2',`
<p><b>Выпуклый многоугольник</b> — многоугольник, у которого каждая сторона (её прямая) не разделяет оставшиеся вершины на две части: все они лежат по одну сторону от этой прямой.</p>
<p>Эквивалентно: все диагонали лежат <b>внутри</b> фигуры.</p>
<p>Невыпуклый (вогнутый) — если хотя бы одна диагональ выходит наружу.</p>
<div style="display:flex;justify-content:center;gap:20px;margin-top:12px;flex-wrap:wrap;align-items:flex-start">
<div style="text-align:center">
<svg viewBox="0 0 140 135" style="width:130px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<!-- Convex pentagon: cx=70,cy=62,R=52; angles -90,-18,54,126,198 -->
<!-- A=(70,10) B=(119,46) C=(112,112) D=(28,112) E=(21,46) -->
<polygon points="70,10 119,46 112,112 28,112 21,46" fill="rgba(16,185,129,.12)" stroke="#10b981" stroke-width="2"/>
<!-- two diagonals from A stay inside -->
<line x1="70" y1="10" x2="112" y2="112" stroke="#10b981" stroke-width="1.2" stroke-dasharray="4 2" opacity=".8"/>
<line x1="70" y1="10" x2="28" y2="112" stroke="#10b981" stroke-width="1.2" stroke-dasharray="4 2" opacity=".8"/>
<circle cx="70" cy="10" r="2.5" fill="#10b981"/>
<circle cx="119" cy="46" r="2.5" fill="#10b981"/>
<circle cx="112" cy="112" r="2.5" fill="#10b981"/>
<circle cx="28" cy="112" r="2.5" fill="#10b981"/>
<circle cx="21" cy="46" r="2.5" fill="#10b981"/>
<text x="70" y="128" text-anchor="middle" font-size="10" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">Выпуклый</text>
</svg>
<div style="font-size:.75rem;color:#047857;margin-top:3px;font-weight:600">Все диагонали<br>внутри</div>
</div>
<div style="text-align:center">
<svg viewBox="0 0 140 135" style="width:130px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<!-- Non-convex hexagon: 5 normal vertices, one "dented" vertex C pulled inward -->
<!-- A=(70,10) B=(120,50) C=(90,62) [dented] D=(115,110) E=(25,110) F=(20,50) -->
<polygon points="70,10 120,50 90,62 115,110 25,110 20,50" fill="rgba(245,158,11,.12)" stroke="#f59e0b" stroke-width="2"/>
<!-- diagonal from A to D crosses exterior due to dent -->
<line x1="70" y1="10" x2="115" y2="110" stroke="#ef4444" stroke-width="1.5" stroke-dasharray="4 2" opacity=".9"/>
<!-- dented vertex highlighted -->
<circle cx="90" cy="62" r="3.5" fill="#ef4444" stroke="#fff" stroke-width="1.5"/>
<circle cx="70" cy="10" r="2.5" fill="#f59e0b"/>
<circle cx="120" cy="50" r="2.5" fill="#f59e0b"/>
<circle cx="115" cy="110" r="2.5" fill="#f59e0b"/>
<circle cx="25" cy="110" r="2.5" fill="#f59e0b"/>
<circle cx="20" cy="50" r="2.5" fill="#f59e0b"/>
<text x="70" y="128" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">Невыпуклый</text>
</svg>
<div style="font-size:.75rem;color:#b45309;margin-top:3px;font-weight:600">Есть «вогнутая»<br>вершина</div>
</div>
</div>`);
html += makeCard('rule','Диагональ. Число диагоналей','1.3',`
<p><b>Диагональ</b> — отрезок, соединяющий две <em>несмежные</em> (несоседние) вершины многоугольника.</p>
<p>В n-угольнике число диагоналей:</p>
\\[D = \\dfrac{n(n-3)}{2}\\]
<p style="font-size:.88rem;color:var(--muted)">Объяснение: из каждой из $n$ вершин можно провести $(n-3)$ диагонали (не к самой себе и не к двум соседним). Делим на 2, т.к. каждая диагональ считается дважды.</p>
<table class="tbl" style="margin-top:10px">
<thead><tr><th>$n$</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>10</th></tr></thead>
<tbody><tr><th>Диагоналей</th><td>0</td><td>2</td><td>5</td><td>9</td><td>14</td><td>20</td><td>35</td></tr></tbody>
</table>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 210" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Regular hexagon: cx=150,cy=102,R=82; angles -90,-30,30,90,150,210 deg -->
<!-- A=(150,20) B=(221,61) C=(221,143) D=(150,184) E=(79,143) F=(79,61) -->
<polygon points="150,20 221,61 221,143 150,184 79,143 79,61" fill="rgba(217,119,6,.09)" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- 9 diagonals: skip sides (consecutive). Pairs: (A,C),(A,D),(A,E),(B,D),(B,E),(B,F),(C,E),(C,F),(D,F) -->
<line x1="150" y1="20" x2="221" y2="143" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="150" y1="20" x2="150" y2="184" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="150" y1="20" x2="79" y2="143" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="221" y1="61" x2="150" y2="184" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="221" y1="61" x2="79" y2="143" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="221" y1="61" x2="79" y2="61" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="221" y1="143" x2="79" y2="61" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="221" y1="143" x2="79" y2="143" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<line x1="150" y1="184" x2="79" y2="61" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3" opacity=".75"/>
<!-- vertex dots -->
<circle cx="150" cy="20" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="221" cy="61" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="221" cy="143" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="150" cy="184" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="79" cy="143" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="79" cy="61" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels offset away from centroid (150,102) -->
<text x="150" y="9" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="236" y="62" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="236" y="146" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="150" y="200" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<text x="64" y="146" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">E</text>
<text x="64" y="62" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">F</text>
<text x="150" y="208" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">n=6: D = 6·3/2 = 9 диагоналей</text>
</svg></div>`);
html += makeCard('rule','Периметр','1.4',`
<p><b>Периметр</b> многоугольника — сумма длин всех его сторон:</p>
\\[P = a_1 + a_2 + \\cdots + a_n\\]
<p>Для правильного n-угольника со стороной $a$: $P = n \\cdot a$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 200" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Quadrilateral ABCD: A=(44,158) B=(78,38) C=(222,30) D=(256,148) -->
<!-- Nice irregular quad, all sides clearly different -->
<polygon points="44,158 78,38 222,30 256,148" fill="rgba(8,145,178,.10)" stroke="#0891b2" stroke-width="2.2" stroke-linejoin="round"/>
<!-- side midpoints for labels, offset outward from centroid (150,94) -->
<!-- AB mid=(61,98) outward → label at (46,98) -->
<text x="43" y="100" text-anchor="middle" font-size="13" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<!-- BC mid=(150,34) outward → label at (150,18) -->
<text x="150" y="18" text-anchor="middle" font-size="13" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">b</text>
<!-- CD mid=(239,89) outward → label at (258,89) -->
<text x="260" y="92" text-anchor="middle" font-size="13" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">c</text>
<!-- DA mid=(150,153) outward → label at (150,172) -->
<text x="150" y="177" text-anchor="middle" font-size="13" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">d</text>
<!-- vertices -->
<circle cx="44" cy="158" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="78" cy="38" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="222" cy="30" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="256" cy="148" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="28" y="164" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">A</text>
<text x="78" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">B</text>
<text x="222" y="18" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">C</text>
<text x="270" y="153" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">D</text>
<!-- formula -->
<text x="150" y="194" text-anchor="middle" font-size="11" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">P = a + b + c + d</text>
</svg></div>`);
html += makeCard('example','Пример','1.5',`
<p><b>Сколько диагоналей у восьмиугольника?</b></p>
<p>$n = 8$: $D = \\dfrac{8 \\cdot (8-3)}{2} = \\dfrac{8 \\cdot 5}{2} = \\dfrac{40}{2} = 20$. Ответ: 20 диагоналей.</p>
<p style="margin-top:8px"><b>Периметр четырёхугольника</b> со сторонами 5, 7, 4, 6 равен $5+7+4+6=22$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 220" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Regular octagon: cx=150,cy=106,R=88; angles -90,-45,0,45,90,135,180,225 deg -->
<!-- A=(150,18) B=(212,50) C=(238,106) D=(212,162) E=(150,194) F=(88,162) G=(62,106) H=(88,50) -->
<polygon points="150,18 212,50 238,106 212,162 150,194 88,162 62,106 88,50" fill="rgba(139,92,246,.09)" stroke="#8b5cf6" stroke-width="2.2" stroke-linejoin="round"/>
<!-- All 20 diagonals: skip pairs (i,i+1) mod 8 -->
<!-- From A(0): to C,D,E,F,G = 5 diagonals -->
<line x1="150" y1="18" x2="238" y2="106" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="150" y1="18" x2="212" y2="162" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="150" y1="18" x2="150" y2="194" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2" opacity=".7"/>
<line x1="150" y1="18" x2="88" y2="162" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="150" y1="18" x2="62" y2="106" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<!-- From B(1): to D,E,F,G = 4 diagonals -->
<line x1="212" y1="50" x2="212" y2="162" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="212" y1="50" x2="150" y2="194" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="212" y1="50" x2="88" y2="162" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2" opacity=".7"/>
<line x1="212" y1="50" x2="62" y2="106" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<!-- From C(2): to E,F,G = 3 diagonals -->
<line x1="238" y1="106" x2="150" y2="194" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="238" y1="106" x2="88" y2="162" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="238" y1="106" x2="62" y2="106" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2" opacity=".7"/>
<!-- From D(3): to F,G = 2 diagonals -->
<line x1="212" y1="162" x2="88" y2="162" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<line x1="212" y1="162" x2="62" y2="106" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<!-- From E(4): to G = 1 diagonal -->
<line x1="150" y1="194" x2="62" y2="106" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<!-- F(5),G(6),H(7): all covered above -->
<!-- From F(5): to H = 1 diagonal (skip G=adjacent) -->
<line x1="88" y1="162" x2="88" y2="50" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 2" opacity=".55"/>
<!-- vertex dots -->
<circle cx="150" cy="18" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="212" cy="50" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="238" cy="106" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="212" cy="162" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="150" cy="194" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="88" cy="162" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="62" cy="106" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="88" cy="50" r="3" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<text x="150" y="212" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">n=8: D = 8·5/2 = 20 диагоналей</text>
</svg></div>`);
/* --- INTERACTIVE 1: SVG-конструктор многоугольника --- */
html += `<div class="wg" id="p1-polycon">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор многоугольника</div></div>
<div class="wg-help">Тащи вершины мышью (или пальцем). Меняй количество вершин слайдером. Смотри: периметр, диагонали, выпуклость.</div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:10px">
<label style="font-size:.92rem;color:var(--muted)">Вершин: <b id="p1-poly-n-val">5</b>
<input type="range" min="3" max="10" value="5" id="p1-poly-n-sl" style="vertical-align:middle;width:120px;accent-color:var(--sec-acc,var(--pri))">
</label>
<button class="btn small" id="p1-poly-reset">Сбросить</button>
</div>
<div id="p1-poly-svg-wrap" style="display:flex;justify-content:center"></div>
<div id="p1-poly-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 2: Калькулятор диагоналей --- */
html += `<div class="wg" id="p1-diagcalc">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор диагоналей</div></div>
<div class="wg-help">Выбери n — увидишь пошаговое вычисление формулы $D = \\dfrac{n(n-3)}{2}$.</div>
<div class="sliders">
<label>$n$ = <b id="p1-dc-nval">7</b><input type="range" min="3" max="20" value="7" id="p1-dc-n" style="accent-color:var(--sec-acc,var(--pri))"></label>
</div>
<div id="p1-dc-out" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.8"></div>
</div>`;
/* --- INTERACTIVE 3: DnD — выпуклый/невыпуклый/не многоугольник --- */
html += `<div class="wg" id="p1-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Выпуклый / Невыпуклый / Не многоугольник</div></div>
<div class="wg-help">Разложи фигуры по категориям. Нажми карточку, затем — на нужный ящик. Или перетащи.</div>
<div id="p1-dnd-hint" class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> Перетащивайте или нажмите карточку, затем — на нужный ящик.</div>
<div id="p1-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="conv">Выпуклый</h5><div class="drop-items" data-cat="conv"></div></div>
<div class="drop-box"><h5 data-cat="conc">Невыпуклый</h5><div class="drop-items" data-cat="conc"></div></div>
<div class="drop-box"><h5 data-cat="not">Не многоугольник</h5><div class="drop-items" data-cat="not"></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>`;
/* --- INTERACTIVE 4: Тренажёр периметра --- */
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 class="score-display"><span>Задача <b id="p1-pt-i">1</b> / 5</span><span>Очки: <b id="p1-pt-score">0</b></span></div>
<div id="p1-pt-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p1-pt-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p1-pt-go">Проверить</button>
<button class="btn" id="p1-pt-start">Начать</button>
</div>
<div class="feedback" id="p1-pt-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 5: Босс §1 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<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 задачи — получишь XP. Каждая правильно решённая задача даёт +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);document.getElementById('p1-read-btn').textContent='Прочитано!';document.getElementById('p1-read-btn').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: SVG-конструктор многоугольника == */
(function(){
const W = 360, H = 320, cx = W/2, cy = H/2, R = 120;
let n = 5;
let verts = [];
function defaultVerts(nv){
const v = [];
for(let i=0;i<nv;i++){
const a = -Math.PI/2 + 2*Math.PI*i/nv;
v.push({x: cx + R*Math.cos(a), y: cy + R*Math.sin(a)});
}
return v;
}
function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); }
function cross2d(o,a,b){ return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x); }
function segIntersect(p,r,q,s){
const rxs = r.x*s.y-r.y*s.x;
if(Math.abs(rxs)<1e-10) return false;
const qp = {x:q.x-p.x,y:q.y-p.y};
const t=(qp.x*s.y-qp.y*s.x)/rxs;
const u=(qp.x*r.y-qp.y*r.x)/rxs;
return t>1e-6&&t<1-1e-6&&u>1e-6&&u<1-1e-6;
}
function hasSelfIntersection(vs){
const n=vs.length;
for(let i=0;i<n;i++){
const p=vs[i],r={x:vs[(i+1)%n].x-p.x,y:vs[(i+1)%n].y-p.y};
for(let j=i+2;j<n;j++){
if(i===0&&j===n-1) continue;
const q=vs[j],s={x:vs[(j+1)%n].x-q.x,y:vs[(j+1)%n].y-q.y};
if(segIntersect(p,r,q,s)) return true;
}
}
return false;
}
function isConvex(vs){
const n=vs.length; let sign=0;
for(let i=0;i<n;i++){
const c=cross2d(vs[i],vs[(i+1)%n],vs[(i+2)%n]);
if(Math.abs(c)<1e-9) continue;
const s=c>0?1:-1;
if(!sign) sign=s;
else if(sign!==s) return false;
}
return true;
}
function interiorAngle(vs,i){
const n=vs.length;
const prev=vs[(i-1+n)%n],cur=vs[i],next=vs[(i+1)%n];
const ax=prev.x-cur.x,ay=prev.y-cur.y;
const bx=next.x-cur.x,by=next.y-cur.y;
const dot=ax*bx+ay*by,cross=ax*by-ay*bx;
let a=Math.atan2(Math.abs(cross),dot)*180/Math.PI;
if(cross<0) a=360-a;
return a;
}
function redraw(){
const svg=document.getElementById('p1-poly-svg');
if(!svg) return;
const nv=verts.length;
const selfX=hasSelfIntersection(verts);
const conv=!selfX&&isConvex(verts);
const pts=verts.map(v=>v.x+','+v.y).join(' ');
const col=selfX?'#ef4444':(conv?'#10b981':'#f59e0b');
const fillCol=selfX?'rgba(239,68,68,.12)':(conv?'rgba(16,185,129,.12)':'rgba(245,158,11,.12)');
let s='';
s+='<polygon points="'+pts+'" fill="'+fillCol+'" stroke="'+col+'" stroke-width="2.5" stroke-linejoin="round"/>';
// диагонали
for(let i=0;i<nv;i++) for(let j=i+2;j<nv;j++){
if(i===0&&j===nv-1) continue;
s+='<line x1="'+verts[i].x+'" y1="'+verts[i].y+'" x2="'+verts[j].x+'" y2="'+verts[j].y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 3" opacity=".7"/>';
}
// метки сторон
for(let i=0;i<nv;i++){
const a=verts[i],b=verts[(i+1)%nv];
const mx=(a.x+b.x)/2,my=(a.y+b.y)/2;
const d=dist(a,b);
s+='<text x="'+mx+'" y="'+my+'" text-anchor="middle" dominant-baseline="middle" font-size="10" fill="#64748b" font-family="JetBrains Mono,monospace" dy="-7">'+d.toFixed(1)+'</text>';
}
// вершины
for(let i=0;i<nv;i++){
const v=verts[i];
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="10" fill="'+col+'" opacity=".22" class="p1-poly-vh" data-i="'+i+'"/>';
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="6" fill="'+col+'" stroke="#fff" stroke-width="2" class="p1-poly-vh" data-i="'+i+'"/>';
const letter=String.fromCharCode(65+i);
const lx=v.x+(v.x-cx)*0.22+2,ly=v.y+(v.y-cy)*0.22+2;
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="'+col+'" font-family="Unbounded,sans-serif">'+letter+'</text>';
}
svg.innerHTML=s;
// drag handlers
svg.querySelectorAll('.p1-poly-vh').forEach(el=>{
el.style.cursor='grab';
el.addEventListener('pointerdown',ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const idx=+el.dataset.i;
el.style.cursor='grabbing';
function onMove(e){
e.preventDefault();
const rect=svg.getBoundingClientRect();
const sx=svg.viewBox.baseVal.width/rect.width;
const sy=svg.viewBox.baseVal.height/rect.height;
const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx));
const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy));
verts[idx]={x:nx,y:ny};
redraw(); updateInfo();
}
function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);el.style.cursor='grab'; }
window.addEventListener('pointermove',onMove,{passive:false});
window.addEventListener('pointerup',onUp);
window.addEventListener('pointercancel',onUp);
});
});
const statusEl=document.getElementById('p1-poly-status');
if(statusEl){
statusEl.textContent=selfX?'Самопересечение!':(conv?'Выпуклый':'Невыпуклый');
statusEl.style.color=col;
}
}
function updateInfo(){
const nv=verts.length;
const perimeter=verts.reduce((s,v,i)=>s+dist(v,verts[(i+1)%nv]),0);
const diags=nv*(nv-3)/2;
const selfX=hasSelfIntersection(verts);
const conv=!selfX&&isConvex(verts);
const angStr=verts.map((v,i)=>interiorAngle(verts,i).toFixed(1)+'°').join(', ');
const angSum=verts.reduce((s,v,i)=>s+interiorAngle(verts,i),0);
const col=selfX?'#ef4444':(conv?'#10b981':'#f59e0b');
document.getElementById('p1-poly-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Вид</div><b style="color:${col}">${selfX?'Самопересечение!':(conv?'Выпуклый':'Невыпуклый')}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Периметр</div><b>${perimeter.toFixed(2)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Диагоналей</div><b>${Math.max(0,diags)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Сумма углов</div><b>${angSum.toFixed(1)}°</b></div>`;
}
function build(){
verts=defaultVerts(n);
const wrap=document.getElementById('p1-poly-svg-wrap');
wrap.innerHTML='<svg id="p1-poly-svg" viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:380px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none"><text id="p1-poly-status" x="'+(W/2)+'" y="18" text-anchor="middle" font-size="13" font-weight="700" font-family="Unbounded,sans-serif"></text></svg>';
redraw(); updateInfo();
}
build();
document.getElementById('p1-poly-n-sl').addEventListener('input',function(){
n=+this.value; document.getElementById('p1-poly-n-val').textContent=n; build();
});
document.getElementById('p1-poly-reset').addEventListener('click',()=>build());
})();
/* == INIT: Калькулятор диагоналей == */
(function(){
const sl=document.getElementById('p1-dc-n'),val=document.getElementById('p1-dc-nval'),out=document.getElementById('p1-dc-out');
function update(){
const n=+sl.value; val.textContent=n;
const d=n*(n-3)/2;
out.innerHTML=`
<div style="margin-bottom:6px">$n = ${n}$, формула: $D = \\dfrac{n(n-3)}{2}$</div>
<div style="margin-bottom:6px">Подставляем: $D = \\dfrac{${n} \\cdot (${n} - 3)}{2} = \\dfrac{${n} \\cdot ${n-3}}{2} = \\dfrac{${n*(n-3)}}{2}$</div>
<div style="font-size:1.15rem;font-weight:800;color:var(--sec-acc-d,var(--pri2))">Ответ: $D = ${d}$ диагоналей</div>`;
renderMath(out);
}
sl.addEventListener('input',update); update();
})();
/* == INIT: DnD сортировка == */
(function(){
const items=[
{id:'tri', html:'<svg style="width:36px;height:30px" viewBox="0 0 36 30"><polygon points="18,2 34,28 2,28" fill="rgba(16,185,129,.2)" stroke="#10b981" stroke-width="1.8"/></svg><span style="font-size:.8rem">Треугольник</span>', ans:'conv'},
{id:'square',html:'<svg style="width:30px;height:30px" viewBox="0 0 30 30"><rect x="2" y="2" width="26" height="26" fill="rgba(16,185,129,.2)" stroke="#10b981" stroke-width="1.8"/></svg><span style="font-size:.8rem">Квадрат</span>', ans:'conv'},
{id:'hex', html:'<svg style="width:36px;height:32px" viewBox="0 0 36 32"><polygon points="18,2 34,10 34,22 18,30 2,22 2,10" fill="rgba(16,185,129,.2)" stroke="#10b981" stroke-width="1.8"/></svg><span style="font-size:.8rem">Правильный шестиугольник</span>', ans:'conv'},
{id:'star', html:'<svg style="width:36px;height:36px" viewBox="0 0 36 36"><polygon points="18,2 21,13 33,13 23,20 27,32 18,25 9,32 13,20 3,13 15,13" fill="rgba(245,158,11,.2)" stroke="#f59e0b" stroke-width="1.8"/></svg><span style="font-size:.8rem">Звезда</span>', ans:'conc'},
{id:'arrow', html:'<svg style="width:36px;height:30px" viewBox="0 0 36 30"><polygon points="2,10 20,10 20,2 34,15 20,28 20,20 2,20" fill="rgba(245,158,11,.2)" stroke="#f59e0b" stroke-width="1.8"/></svg><span style="font-size:.8rem">Стрелка</span>', ans:'conc'},
{id:'circle',html:'<svg style="width:30px;height:30px" viewBox="0 0 30 30"><circle cx="15" cy="15" r="13" fill="rgba(148,163,184,.2)" stroke="#94a3b8" stroke-width="1.8"/></svg><span style="font-size:.8rem">Круг</span>', ans:'not'},
{id:'open', html:'<svg style="width:36px;height:30px" viewBox="0 0 36 30"><polyline points="2,28 10,2 18,28 26,2 34,28" fill="none" stroke="#94a3b8" stroke-width="1.8"/></svg><span style="font-size:.8rem">Незамкнутая ломаная</span>', ans:'not'},
];
const sorter=setupSorter({poolId:'p1-dnd-pool',scopeSelector:'#p1-dnd-wrap',items,cats:['conv','conc','not']});
document.getElementById('p1-dnd-reset').addEventListener('click',()=>{ sorter.reset(); document.getElementById('p1-dnd-fb').style.display='none'; });
document.getElementById('p1-dnd-check').addEventListener('click',()=>{
let ok=0,total=items.length;
items.forEach(it=>{ if(sorter.placed[it.id]===it.ans) ok++; });
const fb=document.getElementById('p1-dnd-fb');
if(ok===total){ feedback(fb,true,'Все '+total+' фигур разложены верно! +5 XP'); addXp(5,'p1-dnd'); bumpProgress('p1',15); }
else feedback(fb,false,'Верно: '+ok+' из '+total+'. Попробуй ещё раз.');
});
})();
/* == INIT: Тренажёр периметра == */
(function(){
const tasks=[
{sides:[5,7,4,6], ans:22, text:'Четырёхугольник со сторонами 5, 7, 4, 6.'},
{sides:[3,4,5], ans:12, text:'Треугольник со сторонами 3, 4, 5.'},
{sides:[8,8,8,8,8], ans:40, text:'Правильный пятиугольник со стороной 8.'},
{sides:[6,9,6,9], ans:30, text:'Прямоугольник со сторонами 6 и 9.'},
{sides:[7,5,8,4,6], ans:30, text:'Пятиугольник со сторонами 7, 5, 8, 4, 6.'},
];
let idx=0,score=0;
function show(){
const t=tasks[idx];
document.getElementById('p1-pt-i').textContent=idx+1;
document.getElementById('p1-pt-task').innerHTML='<b>'+t.text+'</b><br>Найди периметр.';
document.getElementById('p1-pt-ans').value='';
document.getElementById('p1-pt-fb').style.display='none';
}
document.getElementById('p1-pt-start').addEventListener('click',()=>{ idx=0;score=0;document.getElementById('p1-pt-score').textContent=0;show(); });
document.getElementById('p1-pt-go').addEventListener('click',()=>{
if(idx>=tasks.length) return;
const ans=+document.getElementById('p1-pt-ans').value;
const fb=document.getElementById('p1-pt-fb');
if(ans===tasks[idx].ans){
score++; document.getElementById('p1-pt-score').textContent=score;
addXp(3,'p1-perim'); bumpProgress('p1',5);
if(idx<tasks.length-1){ feedback(fb,true,'Верно! +3 XP'); idx++; setTimeout(()=>show(),900); }
else{ feedback(fb,true,'Все задачи решены! +5 XP'); addXp(5,'p1-perim-all'); bumpProgress('p1',10); }
} else {
feedback(fb,false,'Неверно. Ответ: '+tasks[idx].ans+'. Периметр = сумма всех сторон.');
}
});
document.getElementById('p1-pt-ans').addEventListener('keydown',e=>{ if(e.key==='Enter') document.getElementById('p1-pt-go').click(); });
show();
})();
/* == INIT: Босс §1 == */
(function(){
const tasks=[
{ q:'Сколько диагоналей у <b>7-угольника</b>?', ans:14, hint:'Формула: n(n-3)/2, n=7.' },
{ q:'Найди периметр прямоугольника со сторонами <b>AB=12</b> и <b>BC=7</b>.', ans:38, hint:'P = 2(12+7) = 38.' },
{ q:'В многоугольнике <b>20 диагоналей</b>. Сколько в нём вершин? (Число сторон)', ans:8, hint:'n(n-3)/2=20 → n=8.' },
{ q:'Периметр правильного шестиугольника равен <b>54</b>. Чему равна одна сторона?', ans:9, hint:'P = 6a → a = 54/6 = 9.' },
];
const box=document.getElementById('p1-boss-tasks');
let solved=new Set();
box.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px" id="p1-boss-t${i}">
<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="p1-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p1-boss-a${i}').value;
const fb=document.getElementById('p1-boss-fb${i}');
if(v===${t.ans}){
feedback(fb,true,'Верно! +5 XP');
if(!p1BossSolved.has(${i})){ p1BossSolved.add(${i}); addXp(5,'p1-boss${i}'); bumpProgress('p1',10); }
} else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p1-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p1BossSolved=new Set();
})();
}
/* ============================================================
§ 2 — СУММА УГЛОВ ВЫПУКЛОГО МНОГОУГОЛЬНИКА
============================================================ */
function buildP2(){
const box = document.getElementById('p2-body');
let html = '';
html += makeCard('theory','Теорема о сумме углов','2.1',`
<p><b>Теорема.</b> Сумма внутренних углов выпуклого $n$-угольника равна:</p>
\\[(n-2)\\cdot 180^{\\circ}\\]
<p><b>Доказательство.</b> Из любой вершины $A_1$ проводим диагонали ко всем несмежным вершинам. Получаем $(n-2)$ треугольника. Сумма углов каждого треугольника равна $180°$, и все эти углы вместе составляют углы многоугольника. Итого: $(n-2) \\cdot 180°$.</p>
<table class="tbl" style="margin-top:10px">
<thead><tr><th>$n$</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>10</th></tr></thead>
<tbody><tr><th>Сумма углов</th><td>$180°$</td><td>$360°$</td><td>$540°$</td><td>$720°$</td><td>$900°$</td><td>$1080°$</td><td>$1440°$</td></tr></tbody>
</table>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 210" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Pentagon: cx=150,cy=103,R=80; A1 at top -->
<!-- A1=(150,23) A2=(226,78) A3=(197,168) A4=(103,168) A5=(74,78) -->
<!-- Triangle fills first (behind borders) -->
<polygon points="150,23 226,78 197,168" fill="rgba(16,185,129,.18)" stroke="none"/>
<polygon points="150,23 197,168 103,168" fill="rgba(8,145,178,.18)" stroke="none"/>
<polygon points="150,23 103,168 74,78" fill="rgba(217,119,6,.18)" stroke="none"/>
<!-- Outer pentagon border -->
<polygon points="150,23 226,78 197,168 103,168 74,78" fill="none" stroke="#10b981" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Triangulation diagonals from A1 -->
<line x1="150" y1="23" x2="197" y2="168" stroke="#d97706" stroke-width="1.8" stroke-dasharray="6 3"/>
<line x1="150" y1="23" x2="103" y2="168" stroke="#d97706" stroke-width="1.8" stroke-dasharray="6 3"/>
<!-- Triangle labels at centroids -->
<text x="191" y="100" text-anchor="middle" font-size="12" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">T1</text>
<text x="150" y="135" text-anchor="middle" font-size="12" fill="#0e7490" font-weight="700" font-family="Unbounded,sans-serif">T2</text>
<text x="107" y="100" text-anchor="middle" font-size="12" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">T3</text>
<!-- vertex dots -->
<circle cx="150" cy="23" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="226" cy="78" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="197" cy="168" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="103" cy="168" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="74" cy="78" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="150" y="11" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A₁</text>
<text x="242" y="80" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A₂</text>
<text x="211" y="184" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A₃</text>
<text x="89" y="184" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A₄</text>
<text x="57" y="80" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A₅</text>
<text x="150" y="202" text-anchor="middle" font-size="10" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">n=5: 3 треугольника, сумма = 540°</text>
</svg></div>`);
html += makeCard('rule','Правильный многоугольник','2.2',`
<p><b>Правильный n-угольник</b> — все стороны равны и все углы равны.</p>
<p>Каждый внутренний угол правильного $n$-угольника:</p>
\\[\\alpha = \\dfrac{(n-2)\\cdot 180^{\\circ}}{n}\\]
<table class="tbl" style="margin-top:8px">
<thead><tr><th>Фигура</th><th>$n$</th><th>Угол $\\alpha$</th></tr></thead>
<tbody>
<tr><td>Треугольник</td><td>3</td><td>$60°$</td></tr>
<tr><td>Квадрат</td><td>4</td><td>$90°$</td></tr>
<tr><td>Пятиугольник</td><td>5</td><td>$108°$</td></tr>
<tr><td>Шестиугольник</td><td>6</td><td>$120°$</td></tr>
<tr><td>Восьмиугольник</td><td>8</td><td>$135°$</td></tr>
<tr><td>Двенадцатиугольник</td><td>12</td><td>$150°$</td></tr>
</tbody>
</table>
<div style="display:flex;justify-content:center;gap:10px;margin-top:12px;flex-wrap:wrap">
<!-- Equilateral triangle: cx=45,cy=44,R=33; angles -90,30,150 -->
<!-- A=(45,11) B=(74,55) C=(16,55) -->
<div style="text-align:center"><svg viewBox="0 0 90 78" style="width:82px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="45,11 74,55 16,55" fill="rgba(16,185,129,.13)" stroke="#10b981" stroke-width="2"/>
<!-- angle arc at A=(45,11) -->
<path d="M51,24 A14,14 0 0,0 39,24" stroke="#10b981" stroke-width="1.8" fill="rgba(16,185,129,.22)"/>
<text x="45" y="72" text-anchor="middle" font-size="9" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">60°</text>
</svg></div>
<!-- Square: cx=45,cy=45,R=30; right angle markers at all corners -->
<div style="text-align:center"><svg viewBox="0 0 90 78" style="width:82px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<rect x="16" y="16" width="58" height="52" fill="rgba(8,145,178,.13)" stroke="#0891b2" stroke-width="2"/>
<!-- right angle L-marks inside at corner (16,68) -->
<polyline points="25,68 25,59 16,59" fill="none" stroke="#0891b2" stroke-width="1.6"/>
<text x="45" y="72" text-anchor="middle" font-size="9" fill="#0e7490" font-weight="700" font-family="Unbounded,sans-serif">90°</text>
</svg></div>
<!-- Regular pentagon: cx=45,cy=44,R=33; angles -90,-18,54,126,198 -->
<!-- A=(45,11) B=(76,34) C=(65,68) D=(25,68) E=(14,34) -->
<div style="text-align:center"><svg viewBox="0 0 90 78" style="width:82px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="45,11 76,34 65,68 25,68 14,34" fill="rgba(217,119,6,.13)" stroke="#d97706" stroke-width="2"/>
<!-- angle arc at A=(45,11) -->
<path d="M55,26 A14,14 0 0,0 35,26" stroke="#d97706" stroke-width="1.8" fill="rgba(217,119,6,.22)"/>
<text x="45" y="72" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">108°</text>
</svg></div>
<!-- Regular hexagon: cx=45,cy=44,R=30; angles -90,-30,30,90,150,210 -->
<!-- A=(45,14) B=(71,29) C=(71,59) D=(45,74) E=(19,59) F=(19,29) -->
<div style="text-align:center"><svg viewBox="0 0 90 78" style="width:82px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="45,14 71,29 71,59 45,74 19,59 19,29" fill="rgba(139,92,246,.13)" stroke="#8b5cf6" stroke-width="2"/>
<!-- angle arc at A=(45,14) — interior angle 120° -->
<path d="M56,27 A14,14 0 0,0 34,27" stroke="#8b5cf6" stroke-width="1.8" fill="rgba(139,92,246,.22)"/>
<text x="45" y="72" text-anchor="middle" font-size="9" fill="#6d28d9" font-weight="700" font-family="Unbounded,sans-serif">120°</text>
</svg></div>
</div>`);
html += makeCard('example','Примеры','2.3',`
<p><b>Один угол девятиугольника.</b> $n=9$: сумма $= (9-2)\\cdot 180°=7\\cdot 180°=1260°$. Один угол правильного 9-угольника: $1260°/9=140°$.</p>
<p style="margin-top:6px"><b>Найти n по сумме углов.</b> Сумма $=1440°$: $(n-2)\\cdot 180°=1440° \\Rightarrow n-2=8 \\Rightarrow n=10$. Ответ: десятиугольник.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 220" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Regular nonagon (9-gon): cx=150,cy=108,R=88 -->
<!-- angles: -90, -90+40, -90+80, ..., step=360/9=40 deg -->
<!-- A0=(150,20), A1=(207,36), A2=(248,81), A3=(256,132), A4=(225,177), A5=(161,196), A6=(97,196), A7=(33,177), A8=(2,132) — wait, let me recalculate -->
<!-- cx=150, cy=108, R=88: -->
<!-- A0: angle=-90°: (150+88*cos(-90°), 108+88*sin(-90°)) = (150,20) -->
<!-- A1: angle=-50°: (150+88*cos(-50°), 108+88*sin(-50°)) = (150+56.6,108-67.4) = (207,41) -->
<!-- A2: angle=-10°: (150+88*cos(-10°), 108+88*sin(-10°)) = (150+86.6,108-15.3) = (237,93) — -->
<!-- Let me use accurate values: -->
<!-- n=9, step=40deg: angles at -90, -50, -10, 30, 70, 110, 150, 190, 230 deg -->
<!-- A=(150,20) B=(207,41) C=(237,93) D=(233,153) E=(183,193) F=(117,193) G=(67,153) H=(63,93) I=(93,41) -->
<polygon points="150,20 207,41 237,93 233,153 183,193 117,193 67,153 63,93 93,41" fill="rgba(217,119,6,.09)" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Highlight vertex A and the two adjacent edges going to B and I -->
<!-- angle arc at A=(150,20): neighbors B=(207,41) and I=(93,41) -->
<!-- directions from A to B: (57,21)/|..|=59.8 → unit=(0.953,0.351); to I: (-57,21)/59.8 → (-0.953,0.351) -->
<!-- arc start: A + 24*(0.953,0.351) = (173,28) ; arc end: A + 24*(-0.953,0.351) = (127,28) -->
<!-- The interior angle is 140°, so the arc sweeps 140°. Since directions are symmetric, use path -->
<path d="M173,28 A24,24 0 0,0 127,28" stroke="#d97706" stroke-width="2.2" fill="rgba(217,119,6,.28)"/>
<text x="150" y="56" text-anchor="middle" font-size="13" fill="#b45309" font-weight="800" font-family="Unbounded,sans-serif">140°</text>
<!-- highlight vertex A -->
<circle cx="150" cy="20" r="4" fill="#d97706" stroke="#fff" stroke-width="2"/>
<!-- label A -->
<text x="150" y="9" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<!-- remaining vertex dots (smaller) -->
<circle cx="207" cy="41" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="237" cy="93" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="233" cy="153" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="183" cy="193" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="117" cy="193" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="67" cy="153" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="63" cy="93" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="93" cy="41" r="3" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<!-- caption -->
<text x="150" y="213" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">n=9: (9-2)·180°/9 = 140°</text>
</svg></div>`);
/* --- INTERACTIVE 1: Анимация триангуляции --- */
html += `<div class="wg" id="p2-triang-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Триангуляция и сумма углов</div></div>
<div class="wg-help">Выбери n-угольник и нажми «Триангулировать» — увидишь разбиение на треугольники и подсчёт суммы углов.</div>
<div class="sliders">
<label>$n$ = <b id="p2-tr-nval">5</b><input type="range" min="3" max="12" value="5" id="p2-tr-n" style="accent-color:var(--sec-acc,var(--pri))"></label>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px">
<button class="btn primary" id="p2-tr-go">Триангулировать</button>
<button class="btn" id="p2-tr-reset">Сбросить</button>
</div>
<div id="p2-tr-svg-wrap" style="display:flex;justify-content:center"></div>
<div id="p2-tr-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-top:10px;font-size:.95rem;display:none"></div>
</div>`;
/* --- INTERACTIVE 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">Задай $n$ — мгновенно увидишь сумму всех углов и величину одного угла правильного $n$-угольника.</div>
<div class="sliders">
<label>$n$ = <b id="p2-cl-nval">6</b><input type="range" min="3" max="20" value="6" id="p2-cl-n" style="accent-color:var(--sec-acc,var(--pri))"></label>
</div>
<div id="p2-cl-out" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.9"></div>
</div>`;
/* --- INTERACTIVE 3: Обратная задача --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Обратная задача: по сумме углов найти n</div></div>
<div class="wg-help">Введи сумму внутренних углов (кратную 180°, больше 180°) — система найдёт n или скажет, что такого многоугольника нет.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" id="p2-rev-inp" class="tinp" placeholder="Сумма углов в °" style="width:160px" value="1440">
<button class="btn primary" id="p2-rev-go">Найти n</button>
</div>
<div id="p2-rev-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-top:10px;display:none"></div>
</div>`;
/* --- INTERACTIVE 4: DnD правильные многоугольники ↔ углы --- */
html += `<div class="wg" id="p2-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Правильные многоугольники и их углы</div></div>
<div class="wg-help">Разложи названия правильных многоугольников по значению угла.</div>
<div id="p2-dnd-hint" class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> Перетащивайте или нажмите карточку, затем — на нужный ящик.</div>
<div id="p2-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5>$60°$</h5><div class="drop-items" data-cat="60"></div></div>
<div class="drop-box"><h5>$90°$</h5><div class="drop-items" data-cat="90"></div></div>
<div class="drop-box"><h5>$108°$</h5><div class="drop-items" data-cat="108"></div></div>
<div class="drop-box"><h5>$120°$</h5><div class="drop-items" data-cat="120"></div></div>
<div class="drop-box"><h5>$135°$</h5><div class="drop-items" data-cat="135"></div></div>
<div class="drop-box"><h5>$140°$</h5><div class="drop-items" data-cat="140"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p2-dnd-check">Проверить</button><button class="btn" id="p2-dnd-reset">Сначала</button></div>
<div class="feedback" id="p2-dnd-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 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">Итоговые задачи по §2</div></div>
<div class="wg-help">Реши задачи — каждая верная даёт +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);document.getElementById('p2-read-btn').textContent='Прочитано!';document.getElementById('p2-read-btn').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: Триангуляция == */
(function(){
const W=360, H=300, cx=W/2, cy=H/2, R=110;
let n=5, shown=false;
const colors=['#d97706','#10b981','#0891b2','#8b5cf6','#ef4444','#f97316','#14b8a6','#a855f7','#e11d48','#6366f1'];
function pts(nv,r,ox,oy){ const a=[]; for(let i=0;i<nv;i++){const ang=-Math.PI/2+2*Math.PI*i/nv;a.push({x:ox+r*Math.cos(ang),y:oy+r*Math.sin(ang)});}return a; }
function draw(triangulate){
const vs=pts(n,R,cx,cy);
const wrap=document.getElementById('p2-tr-svg-wrap');
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:380px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
if(triangulate){
for(let i=1;i<n-1;i++){
const col=colors[i%colors.length]+'33';
const sc=colors[i%colors.length];
s+='<polygon points="'+vs[0].x+','+vs[0].y+' '+vs[i].x+','+vs[i].y+' '+vs[i+1].x+','+vs[i+1].y+'" fill="'+col+'" stroke="'+sc+'" stroke-width="1.5" stroke-dasharray="5 3"/>';
}
const out=document.getElementById('p2-tr-out');
out.style.display='block';
out.innerHTML='$n='+n+'$, треугольников: $'+(n-2)+'$.<br>Сумма углов $= ('+n+'-2)\\cdot 180° = '+(n-2)+'\\cdot 180° = '+((n-2)*180)+'°$';
renderMath(out);
}
const ptstr=vs.map(v=>v.x+','+v.y).join(' ');
s+='<polygon points="'+ptstr+'" fill="'+(!triangulate?'rgba(217,119,6,.1)':'none')+'" stroke="#d97706" stroke-width="2.5" stroke-linejoin="round"/>';
vs.forEach((v,i)=>{
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#d97706" stroke="#fff" stroke-width="2"/>';
const lx=v.x+(v.x-cx)*0.22,ly=v.y+(v.y-cy)*0.22;
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">'+ String.fromCharCode(65+i)+'</text>';
});
if(triangulate){
for(let i=1;i<n-1;i++){
s+='<line x1="'+vs[0].x+'" y1="'+vs[0].y+'" x2="'+vs[i+1].x+'" y2="'+vs[i+1].y+'" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 3"/>';
}
for(let i=1;i<n-1;i++){
const tx=(vs[0].x+vs[i].x+vs[i+1].x)/3,ty=(vs[0].y+vs[i].y+vs[i+1].y)/3;
s+='<text x="'+tx+'" y="'+ty+'" text-anchor="middle" dominant-baseline="middle" font-size="11" font-weight="700" fill="'+colors[i%colors.length]+'" font-family="Unbounded,sans-serif">T'+(i)+'</text>';
}
}
s+='</svg>';
wrap.innerHTML=s;
}
const nlSl=document.getElementById('p2-tr-n');
const nlVal=document.getElementById('p2-tr-nval');
nlSl.addEventListener('input',function(){ n=+this.value; nlVal.textContent=n; shown=false; document.getElementById('p2-tr-out').style.display='none'; draw(false); });
document.getElementById('p2-tr-go').addEventListener('click',()=>{ shown=true; draw(true); addXp(2,'p2-triang'); bumpProgress('p2',10); });
document.getElementById('p2-tr-reset').addEventListener('click',()=>{ shown=false; document.getElementById('p2-tr-out').style.display='none'; draw(false); });
draw(false);
})();
/* == INIT: Калькулятор == */
(function(){
const sl=document.getElementById('p2-cl-n'),val=document.getElementById('p2-cl-nval'),out=document.getElementById('p2-cl-out');
function update(){
const n=+sl.value; val.textContent=n;
const sum=(n-2)*180;
const ang=sum/n;
out.innerHTML='$n='+n+'$<br>Сумма всех углов: $('+n+'-2)\\cdot 180° = '+sum+'°$<br>Один угол правильного $'+n+'$-угольника: $\\dfrac{'+sum+'°}{'+n+'} = '+ang.toFixed(2).replace(/\.00$/,'')+'°$';
renderMath(out);
}
sl.addEventListener('input',update); update();
})();
/* == INIT: Обратная задача == */
(function(){
document.getElementById('p2-rev-go').addEventListener('click',()=>{
const s=+document.getElementById('p2-rev-inp').value;
const out=document.getElementById('p2-rev-out');
out.style.display='block';
if(s<=0||isNaN(s)){ out.innerHTML='<span style="color:var(--bad)">Введи положительное число.</span>'; return; }
if(s%180!==0){ out.innerHTML='<span style="color:var(--bad)">Сумма углов должна делиться на 180°.</span>'; return; }
const n=(s/180)+2;
if(!Number.isInteger(n)||n<3){ out.innerHTML='<span style="color:var(--bad)">Нет такого многоугольника (n должен быть целым ≥ 3).</span>'; return; }
out.innerHTML='$(n-2)\\cdot 180°='+s+'° \\Rightarrow n-2='+s/180+' \\Rightarrow n='+n+'$<br><b style="color:var(--sec-acc-d,var(--pri2))">Ответ: '+n+'-угольник</b>';
renderMath(out);
addXp(3,'p2-rev');
});
document.getElementById('p2-rev-inp').addEventListener('keydown',e=>{ if(e.key==='Enter') document.getElementById('p2-rev-go').click(); });
})();
/* == INIT: DnD правильные ↔ углы == */
(function(){
const items=[
{id:'tri3', html:'Треугольник', ans:'60'},
{id:'sq4', html:'Квадрат', ans:'90'},
{id:'pent5', html:'Пятиугольник', ans:'108'},
{id:'hex6', html:'Шестиугольник', ans:'120'},
{id:'oct8', html:'Восьмиугольник', ans:'135'},
{id:'n9', html:'Девятиугольник', ans:'140'},
];
const sorter=setupSorter({poolId:'p2-dnd-pool',scopeSelector:'#p2-dnd-wrap',items,cats:['60','90','108','120','135','140']});
document.getElementById('p2-dnd-reset').addEventListener('click',()=>{ sorter.reset(); document.getElementById('p2-dnd-fb').style.display='none'; });
document.getElementById('p2-dnd-check').addEventListener('click',()=>{
let ok=0;
items.forEach(it=>{ if(sorter.placed[it.id]===it.ans) ok++; });
const fb=document.getElementById('p2-dnd-fb');
if(ok===items.length){ feedback(fb,true,'Все фигуры сопоставлены верно! +5 XP'); addXp(5,'p2-dnd'); bumpProgress('p2',15); }
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Используй формулу α=(n-2)·180°/n.');
});
})();
/* == INIT: Босс §2 == */
(function(){
const tasks=[
{ q:'Найди сумму углов <b>выпуклого десятиугольника</b>.', ans:1440, hint:'(10-2)·180=1440' },
{ q:'Один угол правильного <b>восьмиугольника</b> равен … °', ans:135, hint:'(8-2)·180/8=135' },
{ q:'Сумма углов многоугольника равна <b>900°</b>. Сколько сторон?', ans:7, hint:'(n-2)·180=900→n=7' },
{ q:'Сумма углов правильного многоугольника = <b>2520°</b>. Найди один угол.', ans:157.5, hint:'(n-2)·180=2520 → n=16; угол=2520/16=157.5' },
];
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="p2-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p2-boss-a${i}').value;
const fb=document.getElementById('p2-boss-fb${i}');
if(v===${t.ans}||Math.abs(v-${t.ans})<0.5){
feedback(fb,true,'Верно! +5 XP');
if(!p2BossSolved.has(${i})){ p2BossSolved.add(${i}); addXp(5,'p2-boss${i}'); bumpProgress('p2',10); }
} else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p2-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p2BossSolved=new Set();
})();
}
/* ============================================================
§ 3 — СУММА ВНЕШНИХ УГЛОВ ВЫПУКЛОГО МНОГОУГОЛЬНИКА
============================================================ */
function buildP3(){
const box = document.getElementById('p3-body');
let html = '';
html += makeCard('theory','Внешний угол. Теорема','3.1',`
<p><b>Внешний угол</b> выпуклого многоугольника при вершине — угол между стороной и продолжением соседней стороны. Внешний угол при вершине $A_i$:</p>
\\[\\beta_i = 180^{\\circ} - \\alpha_i\\]
<p>где $\\alpha_i$ — соответствующий внутренний угол.</p>
<p><b>Теорема.</b> Сумма всех внешних углов выпуклого многоугольника (по одному у каждой вершины) равна $360°$ — при любом $n$.</p>
<p><b>Объяснение:</b> если обойти многоугольник по периметру, на каждой вершине повернёшься на внешний угол. За полный обход — ровно один полный оборот $= 360°$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 195" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle ABC: A=(44,162) B=(210,162) C=(130,42) -->
<!-- Extension of AB beyond B to B'=(272,162) -->
<!-- Fill triangle -->
<polygon points="44,162 210,162 130,42" fill="rgba(16,185,129,.10)" stroke="none"/>
<!-- Extension line (dashed) -->
<line x1="210" y1="162" x2="275" y2="162" stroke="#94a3b8" stroke-width="1.8" stroke-dasharray="6 3"/>
<!-- Triangle outline -->
<polygon points="44,162 210,162 130,42" fill="none" stroke="#10b981" stroke-width="2.2"/>
<!-- Internal angle arc at B: arc from toward A (left) to toward C (up-left) -->
<!-- Direction B→A: (-166,0)/166=(-1,0); Direction B→C: (-80,-120)/√(6400+14400)=(-80,-120)/144.2=(-0.555,-0.832) -->
<!-- Arc from (-1,0) to (-0.555,-0.832) — counterclockwise (sweep=0) -->
<!-- Start: B+22*(-1,0)=(188,162); End: B+22*(-0.555,-0.832)=(198,144) -->
<path d="M188,162 A22,22 0 0,0 198,143" stroke="#10b981" stroke-width="2" fill="rgba(16,185,129,.22)"/>
<text x="183" y="154" text-anchor="middle" font-size="12" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">α</text>
<!-- External angle arc at B: from toward C to toward B' (extension) -->
<!-- Direction B→C: (-0.555,-0.832); Direction B→B': (1,0) -->
<!-- Start: B+26*(-0.555,-0.832)=(196,140); End: B+26*(1,0)=(236,162) -->
<path d="M196,140 A26,26 0 0,1 236,162" stroke="#d97706" stroke-width="2.5" fill="rgba(217,119,6,.25)"/>
<text x="242" y="153" text-anchor="middle" font-size="13" fill="#b45309" font-weight="800" font-family="Unbounded,sans-serif">β</text>
<!-- vertex dots -->
<circle cx="44" cy="162" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="210" cy="162" r="4" fill="#d97706" stroke="#fff" stroke-width="2"/>
<circle cx="130" cy="42" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="32" y="177" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="210" y="180" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="130" y="30" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">C</text>
<!-- B' label -->
<text x="276" y="177" text-anchor="middle" font-size="12" font-weight="700" fill="#94a3b8" font-family="Unbounded,sans-serif">B'</text>
<!-- formula caption -->
<text x="150" y="190" text-anchor="middle" font-size="10" fill="#64748b" font-weight="700" font-family="JetBrains Mono,monospace">β = 180° α, сумма β = 360°</text>
</svg></div>`);
html += makeCard('rule','Правильный n-угольник','3.2',`
<p>Внешний угол правильного $n$-угольника:</p>
\\[\\beta = \\dfrac{360^{\\circ}}{n}\\]
<p>Внутренний угол: $\\alpha = 180° - \\dfrac{360°}{n} = \\dfrac{(n-2)\\cdot 180°}{n}$.</p>
<p>Отсюда: зная внешний угол $\\beta$, можно найти $n = \\dfrac{360°}{\\beta}$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 210" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Regular hexagon: cx=150,cy=104,R=82; angles -90,-30,30,90,150,210 deg -->
<!-- A=(150,22) B=(221,63) C=(221,145) D=(150,186) E=(79,145) F=(79,63) -->
<polygon points="150,22 221,63 221,145 150,186 79,145 79,63" fill="rgba(8,145,178,.10)" stroke="#0891b2" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Highlight vertex B=(221,63). Neighbors: A=(150,22) and C=(221,145) -->
<!-- Direction B→A: (-71,-41)/|..| = (-71,-41)/82 = (-0.866,-0.5) [that's 210° direction] -->
<!-- Direction B→C: (0,82)/82 = (0,1) [pointing down] -->
<!-- Extension of AB beyond B: direction B→A reversed = (0.866,0.5); B'=B+50*(0.866,0.5)=(264,88) -->
<line x1="150" y1="22" x2="221" y2="63" stroke="#0891b2" stroke-width="2.2"/>
<line x1="221" y1="63" x2="264" y2="88" stroke="#94a3b8" stroke-width="1.8" stroke-dasharray="6 3"/>
<!-- External angle arc at B: from direction B→B'=(0.866,0.5) to direction B→C=(0,1) -->
<!-- Start: B+28*(0.866,0.5)=(245,77); End: B+28*(0,1)=(221,91) -->
<path d="M245,77 A28,28 0 0,1 221,91" stroke="#d97706" stroke-width="2.5" fill="rgba(217,119,6,.25)"/>
<text x="252" y="82" text-anchor="middle" font-size="14" fill="#b45309" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Internal angle arc at B (120°): from B→A to B→C but inside -->
<!-- Start: B+20*(-0.866,-0.5)=(204,53); End: B+20*(0,1)=(221,83) -->
<path d="M204,53 A20,20 0 0,0 221,83" stroke="#0891b2" stroke-width="1.8" fill="rgba(8,145,178,.18)"/>
<text x="204" y="74" text-anchor="middle" font-size="11" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">120°</text>
<!-- vertex dots -->
<circle cx="150" cy="22" r="3" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="221" cy="63" r="4" fill="#d97706" stroke="#fff" stroke-width="2"/>
<circle cx="221" cy="145" r="3" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="150" cy="186" r="3" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="79" cy="145" r="3" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="79" cy="63" r="3" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="150" y="10" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">A</text>
<text x="235" y="60" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="235" y="149" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">C</text>
<text x="150" y="202" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">D</text>
<text x="65" y="149" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">E</text>
<text x="65" y="60" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">F</text>
<!-- caption -->
<text x="150" y="208" text-anchor="middle" font-size="10" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">n=6: β=360°/6=60°</text>
</svg></div>`);
html += makeCard('example','Примеры','3.3',`
<p><b>Внешний угол правильного шестиугольника:</b> $\\beta = 360°/6 = 60°$, внутренний $= 180°-60°=120°$.</p>
<p style="margin-top:6px"><b>Внешний угол правильного многоугольника = 24°. Найти n:</b> $n = 360°/24° = 15$. Ответ: 15-угольник.</p>
<p style="margin-top:6px"><b>Сумма внешних углов правильного 100-угольника:</b> всегда $360°$, независимо от $n$!</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 200" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Title -->
<text x="150" y="16" text-anchor="middle" font-size="11" fill="#64748b" font-weight="700" font-family="Unbounded,sans-serif">Внешние углы → 360°</text>
<!-- Pie chart: cx=150,cy=106,R=72 -->
<!-- 6 sectors of exactly 60° each, starting from -90° -->
<!-- Sector 0: -90° to -30° → start=(150,34), end=(213,70) -->
<path d="M150,106 L150,34 A72,72 0 0,1 213,70 Z" fill="rgba(217,119,6,.40)" stroke="#d97706" stroke-width="1.5"/>
<!-- Sector 1: -30° to 30° → start=(213,70), end=(213,142) -->
<path d="M150,106 L213,70 A72,72 0 0,1 213,142 Z" fill="rgba(16,185,129,.40)" stroke="#10b981" stroke-width="1.5"/>
<!-- Sector 2: 30° to 90° → start=(213,142), end=(150,178) -->
<path d="M150,106 L213,142 A72,72 0 0,1 150,178 Z" fill="rgba(8,145,178,.40)" stroke="#0891b2" stroke-width="1.5"/>
<!-- Sector 3: 90° to 150° → start=(150,178), end=(87,142) -->
<path d="M150,106 L150,178 A72,72 0 0,1 87,142 Z" fill="rgba(139,92,246,.40)" stroke="#8b5cf6" stroke-width="1.5"/>
<!-- Sector 4: 150° to 210° → start=(87,142), end=(87,70) -->
<path d="M150,106 L87,142 A72,72 0 0,1 87,70 Z" fill="rgba(239,68,68,.35)" stroke="#ef4444" stroke-width="1.5"/>
<!-- Sector 5: 210° to 270° → start=(87,70), end=(150,34) -->
<path d="M150,106 L87,70 A72,72 0 0,1 150,34 Z" fill="rgba(249,115,22,.35)" stroke="#f97316" stroke-width="1.5"/>
<!-- Sector labels at midpoints (R=46 from center) -->
<!-- Sector 0 mid=-60°: (150+46*cos(-60°),106+46*sin(-60°))=(173,66) -->
<text x="173" y="68" text-anchor="middle" font-size="12" fill="#b45309" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Sector 1 mid=0°: (196,106) -->
<text x="197" y="110" text-anchor="middle" font-size="12" fill="#047857" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Sector 2 mid=60°: (173,146) -->
<text x="173" y="148" text-anchor="middle" font-size="12" fill="#0e7490" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Sector 3 mid=120°: (127,146) -->
<text x="127" y="148" text-anchor="middle" font-size="12" fill="#6d28d9" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Sector 4 mid=180°: (104,106) -->
<text x="100" y="110" text-anchor="middle" font-size="12" fill="#dc2626" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Sector 5 mid=240°: (127,66) -->
<text x="127" y="68" text-anchor="middle" font-size="12" fill="#c2410c" font-weight="800" font-family="Unbounded,sans-serif">60°</text>
<!-- Center dot -->
<circle cx="150" cy="106" r="4" fill="#0f172a"/>
<!-- Caption -->
<text x="150" y="196" text-anchor="middle" font-size="11" fill="#64748b" font-weight="700" font-family="JetBrains Mono,monospace">6 × 60° = 360°</text>
</svg></div>`);
/* --- INTERACTIVE 1: SVG внешние углы + анимация "свернуть в точку" --- */
html += `<div class="wg" id="p3-ext-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Внешние углы: визуализация</div></div>
<div class="wg-help">Выбери $n$. Посмотри на внешние углы (жёлтые дуги). Нажми «Свернуть» — углы образуют полный оборот $360°$.</div>
<div class="sliders">
<label>$n$ = <b id="p3-ex-nval">6</b><input type="range" min="3" max="10" value="6" id="p3-ex-n" style="accent-color:var(--sec-acc,var(--pri))"></label>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px">
<button class="btn primary" id="p3-ex-fold">Свернуть в точку</button>
<button class="btn" id="p3-ex-reset">Сбросить</button>
</div>
<div id="p3-ex-svg-wrap" style="display:flex;justify-content:center"></div>
<div id="p3-ex-info" style="padding:10px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-top:10px;text-align:center;font-size:.95rem"></div>
</div>`;
/* --- INTERACTIVE 2: Калькулятор правильного n-угольника --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор правильного многоугольника</div></div>
<div class="wg-help">Задай $n$ — получишь и внешний, и внутренний угол.</div>
<div class="sliders">
<label>$n$ = <b id="p3-cl-nval">6</b><input type="range" min="3" max="20" value="6" id="p3-cl-n" style="accent-color:var(--sec-acc,var(--pri))"></label>
</div>
<div id="p3-cl-out" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;line-height:1.9"></div>
</div>`;
/* --- INTERACTIVE 3: Тренажёр — найти n по внешнему углу --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Тренажёр: найди n по внешнему углу</div></div>
<div class="wg-help">Дан один внешний угол правильного n-угольника. Найди n.</div>
<div class="score-display"><span>Задача <b id="p3-tr-i">1</b> / 4</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.05rem;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="n = ?" style="width:100px">
<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>`;
/* --- INTERACTIVE 4: Mini-quiz --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Мини-квиз по §3</div></div>
<div id="p3-quiz-container"></div>
</div>`;
/* --- INTERACTIVE 5: Босс §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">Итоговые задачи §3</div></div>
<div class="wg-help">Реши задачи — +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);document.getElementById('p3-read-btn').textContent='Прочитано!';document.getElementById('p3-read-btn').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: SVG внешние углы == */
(function(){
const W=380, H=320, cx=W/2, cy=H/2, R=100, Rext=30;
let n=6, folded=false;
const colors=['#d97706','#10b981','#0891b2','#8b5cf6','#ef4444','#f97316','#14b8a6','#a855f7','#e11d48','#6366f1'];
function pts(nv){ const v=[];for(let i=0;i<nv;i++){const a=-Math.PI/2+2*Math.PI*i/nv;v.push({x:cx+R*Math.cos(a),y:cy+R*Math.sin(a)});}return v; }
function drawNormal(){
const vs=pts(n);
const extLen=44, Rarc=24, Rlabel=42;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+vs.map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="2.5" stroke-linejoin="round"/>';
vs.forEach((v,i)=>{
const prev=vs[(i-1+n)%n], next=vs[(i+1)%n];
const ux=v.x-prev.x, uy=v.y-prev.y, ul=Math.hypot(ux,uy);
const wx=next.x-v.x, wy=next.y-v.y, wl=Math.hypot(wx,wy);
const u={x:ux/ul, y:uy/ul}, w={x:wx/wl, y:wy/wl};
const ex=v.x+extLen*u.x, ey=v.y+extLen*u.y;
s+='<line x1="'+v.x+'" y1="'+v.y+'" x2="'+ex+'" y2="'+ey+'" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="4 2"/>';
const sx=v.x+Rarc*u.x, sy=v.y+Rarc*u.y;
const tx=v.x+Rarc*w.x, ty=v.y+Rarc*w.y;
const cross=u.x*w.y - u.y*w.x;
const sweep=cross>0?1:0;
const extAngle=360/n;
s+='<path d="M '+v.x+' '+v.y+' L '+sx+' '+sy+' A '+Rarc+' '+Rarc+' 0 0 '+sweep+' '+tx+' '+ty+' Z" fill="rgba(245,158,11,.40)" stroke="#f59e0b" stroke-width="1.5"/>';
let aU=Math.atan2(u.y,u.x), aW=Math.atan2(w.y,w.x);
if(sweep===1){ if(aW<aU) aW+=2*Math.PI; } else { if(aW>aU) aW-=2*Math.PI; }
const mid=(aU+aW)/2;
const lx=v.x+Rlabel*Math.cos(mid), ly=v.y+Rlabel*Math.sin(mid);
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="11" fill="#92400e" font-weight="800">'+extAngle.toFixed(1).replace(/\.0$/,'')+'°</text>';
});
vs.forEach(v=>{
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#d97706" stroke="#fff" stroke-width="2"/>';
});
s+='</svg>';
document.getElementById('p3-ex-svg-wrap').innerHTML=s;
document.getElementById('p3-ex-info').innerHTML='$n='+n+'$, внешний угол $= \\dfrac{360°}{'+n+'} = '+(360/n).toFixed(2).replace(/\.00$/,'')+'°$, сумма $= '+n+' \\times '+(360/n).toFixed(1)+'° = 360°$';
renderMath(document.getElementById('p3-ex-info'));
}
function drawFolded(){
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
const R2=14;
let cumAngle=0;
for(let i=0;i<n;i++){
const extA=360/n*(Math.PI/180);
const sa=cumAngle,ea=cumAngle+extA;
const x1=cx+80*Math.cos(sa),y1=cy+80*Math.sin(sa);
const x2=cx+80*Math.cos(ea),y2=cy+80*Math.sin(ea);
s+='<path d="M '+cx+' '+cy+' L '+x1+' '+y1+' A 80 80 0 0 1 '+x2+' '+y2+' Z" fill="'+colors[i%colors.length]+'55" stroke="'+colors[i%colors.length]+'" stroke-width="1.5"/>';
const mid=(sa+ea)/2;
s+='<text x="'+(cx+55*Math.cos(mid))+'" y="'+(cy+55*Math.sin(mid))+'" text-anchor="middle" dominant-baseline="middle" font-size="10" fill="'+colors[i%colors.length]+'" font-weight="700">'+(360/n).toFixed(0)+'°</text>';
cumAngle=ea;
}
s+='<circle cx="'+cx+'" cy="'+cy+'" r="4" fill="#d97706"/>';
s+='<text x="'+cx+'" y="'+(cy-95)+'" text-anchor="middle" font-size="14" font-weight="700" fill="#d97706" font-family="Unbounded,sans-serif">360°</text>';
s+='</svg>';
document.getElementById('p3-ex-svg-wrap').innerHTML=s;
document.getElementById('p3-ex-info').innerHTML='Все $'+n+'$ внешних углов по $'+(360/n).toFixed(1)+'°$ образуют <b>полный оборот 360°</b>!';
renderMath(document.getElementById('p3-ex-info'));
addXp(2,'p3-fold'); bumpProgress('p3',10);
}
const nlSl=document.getElementById('p3-ex-n'),nlVal=document.getElementById('p3-ex-nval');
nlSl.addEventListener('input',function(){ n=+this.value; nlVal.textContent=n; folded=false; drawNormal(); });
document.getElementById('p3-ex-fold').addEventListener('click',()=>{ folded=true; drawFolded(); });
document.getElementById('p3-ex-reset').addEventListener('click',()=>{ folded=false; drawNormal(); });
drawNormal();
})();
/* == INIT: Калькулятор == */
(function(){
const sl=document.getElementById('p3-cl-n'),val=document.getElementById('p3-cl-nval'),out=document.getElementById('p3-cl-out');
function update(){
const n=+sl.value; val.textContent=n;
const ext=360/n, int=180-ext;
out.innerHTML='$n='+n+'$<br>Внешний угол: $\\dfrac{360°}{'+n+'} = '+ext.toFixed(2).replace(/\.00$/,'')+'°$<br>Внутренний угол: $180° - '+ext.toFixed(2).replace(/\.00$/,'')+'° = '+int.toFixed(2).replace(/\.00$/,'')+'°$';
renderMath(out);
}
sl.addEventListener('input',update); update();
})();
/* == INIT: Тренажёр == */
(function(){
const tasks=[
{ext:30, n:12, text:'Внешний угол правильного многоугольника равен <b>30°</b>. Найди n.'},
{ext:45, n:8, text:'Внешний угол правильного многоугольника равен <b>45°</b>. Найди n.'},
{ext:60, n:6, text:'Внешний угол правильного многоугольника равен <b>60°</b>. Найди n.'},
{ext:72, n:5, text:'Внешний угол правильного многоугольника равен <b>72°</b>. Найди n.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p3-tr-i').textContent=idx+1;
document.getElementById('p3-tr-task').innerHTML=tasks[idx].text+'<br><small style="color:var(--muted)">Подсказка: n = 360°/внешний угол</small>';
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',()=>{
const ans=+document.getElementById('p3-tr-ans').value;
const fb=document.getElementById('p3-tr-fb');
if(ans===tasks[idx].n){
score++; document.getElementById('p3-tr-score').textContent=score; addXp(3,'p3-train'); bumpProgress('p3',8);
if(idx<tasks.length-1){ feedback(fb,true,'Верно! +3 XP'); idx++; setTimeout(()=>show(),900); }
else{ feedback(fb,true,'Все задачи решены! +5 XP'); addXp(5,'p3-train-all'); bumpProgress('p3',10); }
} else feedback(fb,false,'Неверно. Используй: n=360°/'+tasks[idx].ext+'°='+tasks[idx].n);
});
document.getElementById('p3-tr-ans').addEventListener('keydown',e=>{ if(e.key==='Enter') document.getElementById('p3-tr-go').click(); });
show();
})();
/* == INIT: Мини-квиз == */
(function(){
const qs=[
{q:'Сумма внешних углов выпуклого <b>семиугольника</b> равна...', opts:['360°','1260°','900°','Зависит от n'], ans:0},
{q:'Внешний угол правильного <b>пятиугольника</b> равен...', opts:['60°','72°','108°','120°'], ans:1},
{q:'Если внешний угол правильного многоугольника = 40°, то сторон...', opts:['7','8','9','12'], ans:2},
];
let qi=0,qscore=0,answered=false;
const cont=document.getElementById('p3-quiz-container');
function showQ(){
const q=qs[qi];
cont.innerHTML=`<div style="margin-bottom:10px;font-size:.95rem">${q.q}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px" id="p3-q-opts">
${q.opts.map((o,i)=>'<button class="btn" id="p3-qopt-'+i+'" onclick="p3QuizAns('+i+')">'+o+'</button>').join('')}
</div>
<div class="feedback" id="p3-q-fb" style="display:none"></div>
<div style="margin-top:8px;display:flex;gap:8px;justify-content:flex-end">
<span style="font-size:.78rem;color:var(--muted)">Вопрос ${qi+1} / ${qs.length} · Очки: <b>${qscore}</b></span>
</div>`;
answered=false;
}
window.p3QuizAns=function(i){
if(answered) return;
answered=true;
const q=qs[qi];
const fb=document.getElementById('p3-q-fb');
if(i===q.ans){ qscore++; feedback(fb,true,'Верно! +3 XP'); addXp(3,'p3-quiz'); bumpProgress('p3',7); }
else feedback(fb,false,'Неверно. Правильный ответ: '+q.opts[q.ans]);
document.querySelectorAll('[id^="p3-qopt-"]').forEach((b,bi)=>{ b.disabled=true; if(bi===q.ans) b.style.background='var(--ok-bg)'; else if(bi===i) b.style.background='var(--fail-bg)'; });
setTimeout(()=>{
qi++;
if(qi<qs.length) showQ();
else cont.innerHTML='<div style="padding:14px;text-align:center;font-size:1.05rem"><b>Квиз завершён! Очки: '+qscore+'/'+qs.length+'</b></div>';
},1300);
};
showQ();
})();
/* == INIT: Босс §3 == */
(function(){
const tasks=[
{ q:'Внешний угол правильного многоугольника равен <b>20°</b>. Сколько сторон?', ans:18, hint:'n=360/20=18' },
{ q:'Внутренний угол правильного многоугольника равен <b>150°</b>. Сколько сторон?', ans:12, hint:'внешний=180-150=30°, n=360/30=12' },
{ q:'Сумма внешних углов выпуклого <b>двадцатиугольника</b> в градусах?', ans:360, hint:'Всегда 360°!' },
{ q:'Внешний угол правильного многоугольника равен <b>36°</b>. Чему равен внутренний угол?', ans:144, hint:'внутренний = 180-36 = 144°' },
];
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="p3-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p3-boss-a${i}').value;
const fb=document.getElementById('p3-boss-fb${i}');
if(v===${t.ans}){
feedback(fb,true,'Верно! +5 XP');
if(!p3BossSolved.has(${i})){ p3BossSolved.add(${i}); addXp(5,'p3-boss${i}'); bumpProgress('p3',10); }
} else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p3-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p3BossSolved=new Set();
})();
}
/* ============================================================
§ 4 — ПАРАЛЛЕЛОГРАММ
============================================================ */
function buildP4(){
const box = document.getElementById('p4-body');
let html = '';
html += makeCard('theory','Определение и элементы','4.1',`
<p><b>Параллелограмм</b> — четырёхугольник, у которого <em>противоположные стороны попарно параллельны</em>:</p>
\\[AB \\parallel CD \\quad \\text{и} \\quad BC \\parallel AD\\]
<p>Обозначение: $ABCD$ или просто $ABCD$.</p>
<p><b>Элементы параллелограмма:</b></p>
<ul style="margin-left:18px;line-height:1.8">
<li><b>Стороны:</b> $AB$, $BC$, $CD$, $DA$ (противоположные: $AB = CD$, $BC = DA$)</li>
<li><b>Углы:</b> противоположные углы равны; смежные углы — дополнение до $180°$</li>
<li><b>Диагонали:</b> $AC$ и $BD$ — пересекаются и делятся точкой пересечения пополам</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 190" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,158) B=(104,42) C=(250,42) D=(196,158) -->
<!-- This gives AB∥DC and AD∥BC, slant on left/right sides -->
<polygon points="50,158 104,42 250,42 196,158" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Diagonals AC and BD -->
<line x1="50" y1="158" x2="250" y2="42" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="6 3"/>
<line x1="104" y1="42" x2="196" y2="158" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="6 3"/>
<!-- Midpoint O = ((50+250)/2,(158+42)/2) = (150,100) -->
<circle cx="150" cy="100" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="2"/>
<text x="158" y="96" font-size="12" fill="#6d28d9" font-weight="800" font-family="Unbounded,sans-serif">O</text>
<!-- Equal-length tick marks. AB and CD are equal pair (label "b"); BC and AD are equal pair (label "a"). -->
<!-- AB: A=(50,158) → B=(104,42). midpoint=(77,100). dir=(0.422,-0.907). perp=(0.907,0.422). -->
<!-- Double tick (b pair) at AB midpoint: two parallel short perpendicular lines offset by ±2 along dir -->
<line x1="73.7" y1="93.6" x2="80.3" y2="96.7" stroke="#d97706" stroke-width="2.2"/>
<line x1="74.6" y1="98.7" x2="81.2" y2="101.8" stroke="#d97706" stroke-width="2.2"/>
<!-- CD: C=(250,42) → D=(196,158). midpoint=(223,100). same dir, same perp. -->
<line x1="219.7" y1="93.6" x2="226.3" y2="96.7" stroke="#d97706" stroke-width="2.2"/>
<line x1="220.6" y1="98.7" x2="227.2" y2="101.8" stroke="#d97706" stroke-width="2.2"/>
<!-- BC: B=(104,42) → C=(250,42). midpoint=(177,42). dir=(1,0). perp=(0,1). Single tick (a pair). -->
<line x1="177" y1="36" x2="177" y2="48" stroke="#0891b2" stroke-width="2.2"/>
<!-- AD: A=(50,158) → D=(196,158). midpoint=(123,158). dir=(1,0). perp=(0,1). Single tick. -->
<line x1="123" y1="152" x2="123" y2="164" stroke="#0891b2" stroke-width="2.2"/>
<!-- Side labels: a for the horizontal pair (BC, AD), b for the slanted pair (AB, CD). -->
<text x="63" y="112" text-anchor="middle" font-size="12" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">b</text>
<text x="177" y="32" text-anchor="middle" font-size="12" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<text x="238" y="112" text-anchor="middle" font-size="12" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">b</text>
<text x="123" y="172" text-anchor="middle" font-size="12" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<!-- vertex dots -->
<circle cx="50" cy="158" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="104" cy="42" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="42" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="196" cy="158" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="34" y="164" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="104" y="30" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="30" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="210" y="174" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<!-- caption -->
<text x="150" y="186" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">AB∥DC, AD∥BC, диагонали → O</text>
</svg></div>`);
html += makeCard('rule','Основные свойства','4.2',`
<p>В параллелограмме $ABCD$:</p>
\\[AB = CD, \\quad BC = AD\\]
\\[\\angle A = \\angle C, \\quad \\angle B = \\angle D\\]
\\[\\angle A + \\angle B = 180^{\\circ}\\]
<p>Эти свойства доказываются через равенство треугольников, на которые диагональ делит параллелограмм.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 185" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,158) B=(104,42) C=(250,42) D=(196,158) -->
<polygon points="50,158 104,42 250,42 196,158" fill="rgba(16,185,129,.09)" stroke="#10b981" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Angle at A=(50,158): neighbors D=(196,158) and B=(104,42) -->
<!-- Direction A→D=(146,0)/146=(1,0); Direction A→B=(54,-116)/|..| -->
<!-- |AB|=sqrt(54²+116²)=sqrt(2916+13456)=sqrt(16372)=127.95; unit=(0.422,-0.907) -->
<!-- arc start: A+22*(1,0)=(72,158); arc end: A+22*(0.422,-0.907)=(59,138) -->
<path d="M72,158 A22,22 0 0,0 59,138" stroke="#10b981" stroke-width="2" fill="rgba(16,185,129,.25)"/>
<text x="80" y="152" font-size="13" fill="#047857" font-weight="800" font-family="Unbounded,sans-serif">α</text>
<!-- Angle at C=(250,42): neighbors B=(104,42) and D=(196,158) -->
<!-- Direction C→B=(-146,0)/146=(-1,0); Direction C→D=(-54,116)/127.95=(-0.422,0.907) -->
<!-- arc start: C+22*(-1,0)=(228,42); arc end: C+22*(-0.422,0.907)=(241,62) -->
<path d="M228,42 A22,22 0 0,0 241,62" stroke="#10b981" stroke-width="2" fill="rgba(16,185,129,.25)"/>
<text x="220" y="58" font-size="13" fill="#047857" font-weight="800" font-family="Unbounded,sans-serif">α</text>
<!-- Angle at B=(104,42): neighbors A=(50,158) and C=(250,42). Interior angle ≈115° (obtuse).
Need SHORT arc through the interior (below-right of B). sweep=0 for short arc. -->
<path d="M95,62 A22,22 0 0,0 126,42" stroke="#0891b2" stroke-width="2" fill="rgba(8,145,178,.25)"/>
<text x="116" y="68" font-size="13" fill="#0e7490" font-weight="800" font-family="Unbounded,sans-serif">β</text>
<!-- Angle at D=(196,158): neighbors C=(250,42) and A=(50,158). Interior angle ≈115° (obtuse).
SHORT arc through interior (upper-left of D). sweep=0. -->
<path d="M205,138 A22,22 0 0,0 174,158" stroke="#0891b2" stroke-width="2" fill="rgba(8,145,178,.25)"/>
<text x="184" y="150" font-size="13" fill="#0e7490" font-weight="800" font-family="Unbounded,sans-serif">β</text>
<!-- vertex dots -->
<circle cx="50" cy="158" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="104" cy="42" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="42" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="196" cy="158" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="34" y="164" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="104" y="30" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="30" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">C</text>
<text x="210" y="174" text-anchor="middle" font-size="13" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">D</text>
<!-- caption -->
<text x="150" y="181" text-anchor="middle" font-size="11" fill="#64748b" font-weight="700" font-family="JetBrains Mono,monospace">∠A=∠C=α, ∠B=∠D=β, α+β=180°</text>
</svg></div>`);
html += makeCard('example','Примеры','4.3',`
<p><b>Квадрат, прямоугольник, ромб</b> — это частные случаи параллелограмма.</p>
<p style="margin-top:6px"><b>Трапеция</b> — НЕ параллелограмм (только одна пара параллельных сторон).</p>
<p style="margin-top:6px"><b>Задача:</b> в параллелограмме $AB = 8$, $BC = 5$. Найти периметр. Решение: $P = 2(8+5) = 26$.</p>
<p style="margin-top:6px"><b>Задача:</b> $\\angle A = 65°$. Найти остальные углы. $\\angle C = 65°$, $\\angle B = \\angle D = 115°$.</p>
<div style="display:flex;justify-content:center;gap:16px;margin-top:12px;flex-wrap:wrap">
<!-- Parallelogram AB=8, BC=5; A=(16,74) B=(30,18) C=(90,18) D=(76,74) -->
<div style="text-align:center"><svg viewBox="0 0 108 90" style="width:104px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="16,74 30,18 90,18 76,74" fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="2"/>
<!-- AB left side label -->
<text x="16" y="48" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">8</text>
<!-- BC top label -->
<text x="60" y="12" text-anchor="middle" font-size="10" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">5</text>
<!-- CD right side label -->
<text x="98" y="48" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">8</text>
<!-- DA bottom label -->
<text x="46" y="86" text-anchor="middle" font-size="10" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">5</text>
<!-- vertex dots -->
<circle cx="16" cy="74" r="2.5" fill="#d97706"/><circle cx="30" cy="18" r="2.5" fill="#d97706"/>
<circle cx="90" cy="18" r="2.5" fill="#d97706"/><circle cx="76" cy="74" r="2.5" fill="#d97706"/>
<!-- vertex labels -->
<text x="6" y="79" font-size="9" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="26" y="10" font-size="9" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="88" y="10" font-size="9" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="74" y="88" font-size="9" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
</svg>
<div style="font-size:.78rem;color:var(--muted);margin-top:4px;font-weight:600">P = 2(8+5) = 26</div></div>
<!-- Parallelogram ∠A=65°: A=(16,74) B=(30,18) C=(90,18) D=(76,74) -->
<div style="text-align:center"><svg viewBox="0 0 108 90" style="width:104px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="16,74 30,18 90,18 76,74" fill="rgba(16,185,129,.12)" stroke="#10b981" stroke-width="2"/>
<!-- angle arc at A=(16,74): dir A→D=(60,0)/60=(1,0); dir A→B=(14,-56)/|..| -->
<!-- |AB|=57.7; unit=(0.243,-0.970) -->
<!-- arc from (30,74) to (16+16*0.243,74+16*(-0.970))=(20,58) -->
<path d="M30,74 A14,14 0 0,0 20,60" stroke="#10b981" stroke-width="1.8" fill="rgba(16,185,129,.22)"/>
<text x="34" y="67" font-size="9" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">65°</text>
<!-- angle arc at C=(90,18): opposite to A, so also 65° -->
<!-- dir C→B=(-60,0)/60=(-1,0); dir C→D=(-14,56)/57.7=(-0.243,0.970) -->
<!-- arc from (76,18) to (90+14*(-0.243),18+14*0.970)=(87,32) -->
<path d="M76,18 A14,14 0 0,0 87,32" stroke="#10b981" stroke-width="1.8" fill="rgba(16,185,129,.22)"/>
<text x="65" y="31" font-size="9" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">65°</text>
<circle cx="16" cy="74" r="2.5" fill="#10b981"/><circle cx="30" cy="18" r="2.5" fill="#10b981"/>
<circle cx="90" cy="18" r="2.5" fill="#10b981"/><circle cx="76" cy="74" r="2.5" fill="#10b981"/>
<text x="6" y="79" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="26" y="10" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">B</text>
<text x="88" y="10" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">C</text>
<text x="74" y="88" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">D</text>
</svg>
<div style="font-size:.78rem;color:var(--muted);margin-top:4px;font-weight:600">∠A=∠C=65°,<br>∠B=∠D=115°</div></div>
</div>`);
/* --- INTERACTIVE 1: SVG-конструктор параллелограмма --- */
html += `<div class="wg" id="p4-pgram-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор параллелограмма</div></div>
<div class="wg-help">Тащи вершины <b>B</b> и <b>D</b>. Параллелограмм строится автоматически: AB∥CD и BC∥AD. Следи за значениями сторон и углов.</div>
<div id="p4-pgram-svg-wrap" style="display:flex;justify-content:center"></div>
<div id="p4-pgram-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 2: DnD — какие из фигур параллелограммы --- */
html += `<div class="wg" id="p4-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Параллелограмм или нет?</div></div>
<div class="wg-help">Разложи фигуры: является ли она параллелограммом (или его частным случаем)?</div>
<div id="p4-dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="yes">Параллелограмм</h5><div class="drop-items" data-cat="yes"></div></div>
<div class="drop-box"><h5 data-cat="no">Не параллелограмм</h5><div class="drop-items" data-cat="no"></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>`;
/* --- INTERACTIVE 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">Нажимай «Дальше» — идёт пошаговое доказательство через треугольники.</div>
<div id="p4-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p4-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p4-proof-next">Дальше</button>
<button class="btn" id="p4-proof-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 4: Тренажёр --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр задач на параллелограмм</div></div>
<div class="score-display"><span>Задача <b id="p4-tr-i">1</b> / 4</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.05rem;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:110px">
<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>`;
/* --- INTERACTIVE 5: Босс §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">Итоговые задачи §4</div></div>
<div class="wg-help">+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);document.getElementById('p4-read-btn').textContent='Прочитано!';document.getElementById('p4-read-btn').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: SVG-конструктор параллелограмма == */
(function(){
const W=380, H=300, SVG_ID='p4-pgram-svg';
let A={x:60,y:220}, B={x:240,y:220}, D={x:120,y:100};
function getC(){ return {x:D.x+(B.x-A.x),y:D.y+(B.y-A.y)}; }
function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); }
function angDeg(O,P,Q){
const ax=P.x-O.x,ay=P.y-O.y,bx=Q.x-O.x,by=Q.y-O.y;
return Math.acos(Math.max(-1,Math.min(1,(ax*bx+ay*by)/(Math.hypot(ax,ay)*Math.hypot(bx,by)))))*180/Math.PI;
}
function svgToClient(svgEl,sx,sy){
const r=svgEl.getBoundingClientRect(), vb=svgEl.viewBox.baseVal;
return {x:(sx-vb.x)/vb.width*r.width+r.left, y:(sy-vb.y)/vb.height*r.height+r.top};
}
function clientToSvg(clientX,clientY){
const svgEl=document.getElementById(SVG_ID); if(!svgEl) return {x:0,y:0};
const r=svgEl.getBoundingClientRect(), vb=svgEl.viewBox.baseVal;
return {x:(clientX-r.left)/r.width*vb.width+vb.x, y:(clientY-r.top)/r.height*vb.height+vb.y};
}
function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }
function redraw(){
const C=getC(); const pts=[A,B,C,D]; const labels=['A','B','C','D'];
const pcx=(A.x+B.x+C.x+D.x)/4, pcy=(A.y+B.y+C.y+D.y)/4;
let s='<svg id="'+SVG_ID+'" viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">';
s+='<polygon points="'+pts.map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(220,38,38,.10)" stroke="#dc2626" stroke-width="2.5" stroke-linejoin="round"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="5 3"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="5 3"/>';
const sides=[['AB',A,B],['BC',B,C],['CD',C,D],['DA',D,A]];
sides.forEach(([nm,p1,p2])=>{
const mx=(p1.x+p2.x)/2,my=(p1.y+p2.y)/2;
s+='<text x="'+mx+'" y="'+my+'" text-anchor="middle" dominant-baseline="middle" font-size="10" fill="#64748b" font-family="JetBrains Mono,monospace" dy="-8">'+dist(p1,p2).toFixed(1)+'</text>';
});
pts.forEach((v,i)=>{
const m=(i===1||i===3);
const lx=v.x+(v.x-pcx)*0.28, ly=v.y+(v.y-pcy)*0.28;
if(m){
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="16" fill="#dc2626" opacity=".12" data-v="'+labels[i]+'" style="cursor:grab"/>';
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="8" fill="#dc2626" stroke="#fff" stroke-width="2.5" data-v="'+labels[i]+'" style="cursor:grab"/>';
} else {
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#dc2626" stroke="#fff" stroke-width="2"/>';
}
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#b91c1c" font-family="Unbounded,sans-serif">'+labels[i]+'</text>';
});
s+='</svg>';
document.getElementById('p4-pgram-svg-wrap').innerHTML=s;
updateInfo();
}
function updateInfo(){
const C=getC();
const ab=dist(A,B),bc=dist(B,C),angA=angDeg(A,D,B),angB=angDeg(B,A,C);
document.getElementById('p4-pgram-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">AB = CD</div><b>${ab.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">BC = DA</div><b>${bc.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">∠A = ∠C</div><b>${angA.toFixed(1)}°</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">∠A + ∠B</div><b>${(angA+angB).toFixed(1)}°</b></div>`;
}
// Drag state: lives outside redraw so innerHTML replacement never resets it
let p4Active=false, p4Vname='', p4OffX=0, p4OffY=0;
document.getElementById('p4-pgram-svg-wrap').addEventListener('pointerdown',function(ev){
const el=ev.target.closest('[data-v]'); if(!el) return;
if(ev.button!==undefined&&ev.button!==0) return;
const vname=el.dataset.v; if(vname!=='B'&&vname!=='D') return;
ev.preventDefault();
p4Active=true; p4Vname=vname;
const cur=vname==='B'?B:D;
const sp=clientToSvg(ev.clientX,ev.clientY);
p4OffX=sp.x-cur.x; p4OffY=sp.y-cur.y;
el.style.cursor='grabbing';
window.addEventListener('pointermove',p4Move,{passive:false});
window.addEventListener('pointerup',p4Up);
window.addEventListener('pointercancel',p4Up);
});
function p4Move(e){
if(!p4Active) return; e.preventDefault();
const sp=clientToSvg(e.clientX,e.clientY);
const nx=clamp(sp.x-p4OffX,12,W-12), ny=clamp(sp.y-p4OffY,12,H-12);
if(p4Vname==='B') B={x:nx,y:ny}; else D={x:nx,y:ny};
redraw();
}
function p4Up(){
if(!p4Active) return; p4Active=false;
window.removeEventListener('pointermove',p4Move);
window.removeEventListener('pointerup',p4Up);
window.removeEventListener('pointercancel',p4Up);
}
redraw();
})();
/* == INIT: DnD параллелограмм или нет == */
(function(){
const items=[
{id:'rect', html:'Прямоугольник', ans:'yes'},
{id:'rhombus',html:'Ромб', ans:'yes'},
{id:'square', html:'Квадрат', ans:'yes'},
{id:'pgram', html:'Произвольный параллелограмм', ans:'yes'},
{id:'trap', html:'Трапеция', ans:'no'},
{id:'kite', html:'Дельтоид (воздушный змей)', ans:'no'},
{id:'arb4', html:'Произвольный четырёхугольник', ans:'no'},
{id:'tri', html:'Треугольник', ans:'no'},
];
const sorter=setupSorter({poolId:'p4-dnd-pool',scopeSelector:'#p4-dnd-wrap',items,cats:['yes','no']});
document.getElementById('p4-dnd-reset').addEventListener('click',()=>{ sorter.reset(); document.getElementById('p4-dnd-fb').style.display='none'; });
document.getElementById('p4-dnd-check').addEventListener('click',()=>{
let ok=0;
items.forEach(it=>{ if(sorter.placed[it.id]===it.ans) ok++; });
const fb=document.getElementById('p4-dnd-fb');
if(ok===items.length){ feedback(fb,true,'Все '+items.length+' фигур верно! +5 XP'); addXp(5,'p4-dnd'); bumpProgress('p4',15); }
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Прямоугольник, ромб, квадрат — частные случаи параллелограмма.');
});
})();
/* == INIT: Пошаговое доказательство == */
(function(){
const steps=[
{ text:'<b>Дано:</b> $ABCD$ — параллелограмм, $AB \\parallel CD$, $BC \\parallel AD$.<br>Проведём диагональ $AC$.', highlight:'' },
{ text:'<b>Шаг 1.</b> $AB \\parallel CD$ и $AC$ — секущая.<br>По свойству параллельных прямых: $\\angle BAC = \\angle DCA$ (накрест лежащие углы).', highlight:'diag' },
{ text:'<b>Шаг 2.</b> $BC \\parallel AD$ и $AC$ — секущая.<br>Аналогично: $\\angle BCA = \\angle DAC$ (накрест лежащие углы).', highlight:'diag' },
{ text:'<b>Шаг 3.</b> $AC$ — общая сторона треугольников $\\triangle ABC$ и $\\triangle CDA$.<br>По признаку «угол-сторона-угол»: $\\triangle ABC = \\triangle CDA$.', highlight:'both' },
{ text:'<b>Вывод.</b> Из равенства треугольников следует: $AB = CD$ и $BC = DA$.<br>Противоположные стороны параллелограмма равны. <b>ч.т.д.</b>', highlight:'both' },
];
let step=0;
const W=300,H=200;
function drawProof(highlight){
const A={x:30,y:160},B={x:180,y:160},C={x:260,y:50},D={x:110,y:50};
const cx=(A.x+B.x+C.x+D.x)/4, cy=(A.y+B.y+C.y+D.y)/4;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
const t1fill=highlight==='diag'||highlight==='both'?'rgba(220,38,38,.15)':'rgba(220,38,38,.07)';
const t2fill=highlight==='both'?'rgba(16,185,129,.15)':'rgba(220,38,38,.07)';
s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+' '+D.x+','+D.y+'" fill="rgba(220,38,38,.07)" stroke="#dc2626" stroke-width="2"/>';
if(highlight){
s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="'+t1fill+'" stroke="#dc2626" stroke-width="1.5" stroke-dasharray="4 2"/>';
s+='<polygon points="'+C.x+','+C.y+' '+D.x+','+D.y+' '+A.x+','+A.y+'" fill="'+t2fill+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#8b5cf6" stroke-width="2"/>';
}
['A','B','C','D'].forEach((lbl,i)=>{
const v=[A,B,C,D][i];
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#dc2626" stroke="#fff" stroke-width="2"/>';
const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28;
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#b91c1c" font-family="Unbounded,sans-serif">'+lbl+'</text>';
});
s+='</svg>';
document.getElementById('p4-proof-svg').innerHTML=s;
}
function showStep(){
const st=steps[step];
document.getElementById('p4-proof-step').innerHTML=st.text;
renderMath(document.getElementById('p4-proof-step'));
drawProof(st.highlight);
document.getElementById('p4-proof-next').textContent=step<steps.length-1?'Дальше':'Готово';
}
document.getElementById('p4-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){ step++; showStep(); }
else{ addXp(5,'p4-proof'); bumpProgress('p4',12); document.getElementById('p4-proof-next').textContent='Доказательство изучено! +5 XP'; document.getElementById('p4-proof-next').disabled=true; }
});
document.getElementById('p4-proof-restart').addEventListener('click',()=>{ step=0; showStep(); document.getElementById('p4-proof-next').disabled=false; document.getElementById('p4-proof-next').textContent='Дальше'; });
showStep();
})();
/* == INIT: Тренажёр задач == */
(function(){
const tasks=[
{ q:'В параллелограмме <b>AB = 11</b>, <b>BC = 7</b>. Найди периметр.', ans:36, hint:'P=2(AB+BC)=2(11+7)=36' },
{ q:'В параллелограмме <b>∠A = 72°</b>. Найди <b>∠B</b> (смежный угол).', ans:108, hint:'∠A+∠B=180°, ∠B=108°' },
{ q:'В параллелограмме <b>∠A = 72°</b>. Найди <b>∠C</b> (противоположный).', ans:72, hint:'∠A=∠C в параллелограмме' },
{ q:'Периметр параллелограмма равен <b>56</b>, одна сторона равна <b>18</b>. Найди вторую сторону.', ans:10, hint:'2(18+x)=56, x=10' },
];
let idx=0,score=0;
function show(){
document.getElementById('p4-tr-i').textContent=idx+1;
document.getElementById('p4-tr-task').innerHTML=tasks[idx].q;
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',()=>{
const ans=+document.getElementById('p4-tr-ans').value;
const fb=document.getElementById('p4-tr-fb');
if(ans===tasks[idx].ans){
score++; document.getElementById('p4-tr-score').textContent=score; addXp(3,'p4-train'); bumpProgress('p4',6);
if(idx<tasks.length-1){ feedback(fb,true,'Верно! +3 XP'); idx++; setTimeout(()=>show(),900); }
else{ feedback(fb,true,'Все задачи решены! +5 XP'); addXp(5,'p4-train-all'); bumpProgress('p4',10); }
} 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: Босс §4 == */
(function(){
const tasks=[
{ q:'В параллелограмме $AB = 13$, $BC = 9$. Найди периметр.', ans:44, hint:'P=2(13+9)=44' },
{ q:'В параллелограмме $\\angle A = 55°$. Найди $\\angle D$ (смежный с $\\angle A$).', ans:125, hint:'∠A+∠D=180°, ∠D=125°' },
{ q:'В параллелограмме $\\angle B = 130°$. Найди $\\angle A$.', ans:50, hint:'∠A+∠B=180°, ∠A=50°' },
{ q:'Периметр параллелограмма равен 80, одна сторона = 25. Найди вторую сторону.', ans:15, hint:'2(25+x)=80, x=15' },
];
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="p4-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p4-boss-a${i}').value;
const fb=document.getElementById('p4-boss-fb${i}');
if(v===${t.ans}){
feedback(fb,true,'Верно! +5 XP');
if(!p4BossSolved.has(${i})){ p4BossSolved.add(${i}); addXp(5,'p4-boss${i}'); bumpProgress('p4',10); }
} else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p4-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p4BossSolved=new Set();
})();
renderMath(box);
}
function buildP5(){
const box = document.getElementById('p5-body');
let html = '';
html += makeCard('theory','Свойства параллелограмма','5.1',`
<p><b>Теорема.</b> В параллелограмме:</p>
<ul style="margin-left:18px;line-height:2">
<li><b>Свойство 1:</b> противоположные стороны равны: $AB = CD$, $BC = AD$</li>
<li><b>Свойство 2:</b> противоположные углы равны: $\\angle A = \\angle C$, $\\angle B = \\angle D$</li>
<li><b>Свойство 3:</b> диагонали точкой пересечения $O$ делятся пополам: $AO = OC$, $BO = OD$</li>
<li><b>Свойство 4:</b> сумма соседних углов равна $180°$: $\\angle A + \\angle B = 180°$</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 195" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,162) B=(106,40) C=(250,40) D=(194,162) -->
<polygon points="50,162 106,40 250,40 194,162" fill="rgba(217,119,6,.09)" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Diagonals: AC=(50,162)→(250,40) and BD=(106,40)→(194,162) -->
<line x1="50" y1="162" x2="250" y2="40" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="106" y1="40" x2="194" y2="162" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- O = midpoint of AC = ((50+250)/2,(162+40)/2) = (150,101) -->
<circle cx="150" cy="101" r="4.5" fill="#8b5cf6" stroke="#fff" stroke-width="2"/>
<text x="158" y="97" font-size="12" fill="#6d28d9" font-weight="800" font-family="Unbounded,sans-serif">O</text>
<!-- Equal side ticks: AB=CD (single tick), BC=AD (double tick) -->
<!-- AB mid=(78,101): direction AB=(56,-122)/135=(0.415,-0.904); perp=(0.904,0.415) -->
<line x1="71" y1="97" x2="85" y2="105" stroke="#d97706" stroke-width="2.2"/>
<!-- CD mid=(222,101): same direction; perp tick -->
<line x1="215" y1="97" x2="229" y2="105" stroke="#d97706" stroke-width="2.2"/>
<!-- BC mid=(178,40): direction=(144,0)/144=(1,0); perp=(0,1) -->
<line x1="175" y1="34" x2="175" y2="46" stroke="#0891b2" stroke-width="2.2"/>
<line x1="181" y1="34" x2="181" y2="46" stroke="#0891b2" stroke-width="2.2"/>
<!-- AD mid=(122,162): same direction; double tick -->
<line x1="119" y1="156" x2="119" y2="168" stroke="#0891b2" stroke-width="2.2"/>
<line x1="125" y1="156" x2="125" y2="168" stroke="#0891b2" stroke-width="2.2"/>
<!-- Equal half-diagonal ticks on AC: AO and OC -->
<!-- AO mid=(100,132): direction AC=(200,-122)/234=(0.855,-0.521); perp=(0.521,0.855) -->
<line x1="95" y1="126" x2="106" y2="137" stroke="#8b5cf6" stroke-width="2.2"/>
<!-- OC mid=(200,71): same direction -->
<line x1="195" y1="65" x2="206" y2="76" stroke="#8b5cf6" stroke-width="2.2"/>
<!-- Equal half-diagonal ticks on BD: BO and OD -->
<!-- BD direction=(88,122)/150=(0.587,0.813); perp=(-0.813,0.587) -->
<!-- BO mid=(128,71): perp tick -->
<line x1="134" y1="74" x2="123" y2="68" stroke="#d97706" stroke-width="2.2"/>
<!-- OD mid=(172,132): perp tick -->
<line x1="178" y1="135" x2="167" y2="129" stroke="#d97706" stroke-width="2.2"/>
<!-- vertex dots -->
<circle cx="50" cy="162" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="106" cy="40" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="40" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="194" cy="162" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="34" y="168" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="106" y="28" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="28" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="208" y="178" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<!-- caption -->
<text x="150" y="192" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">AB=CD, BC=AD, AO=OC, BO=OD</text>
</svg></div>`);
html += makeCard('rule','Доказательство свойств 1 и 2','5.2',`
<p>Проводим диагональ $AC$. Образуются $\\triangle ABC$ и $\\triangle CDA$.</p>
<p>$AB \\parallel CD$ $\\Rightarrow$ $\\angle BAC = \\angle DCA$ (накрест лежащие).</p>
<p>$BC \\parallel AD$ $\\Rightarrow$ $\\angle BCA = \\angle DAC$ (накрест лежащие).</p>
<p>$AC$ — общая сторона. По признаку «угол–сторона–угол»: $\\triangle ABC = \\triangle CDA$.</p>
<p>Из равенства треугольников: $AB=CD$, $BC=DA$, $\\angle B=\\angle D$. А $\\angle A=\\angle C$ — аналогично через диагональ $BD$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 180" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,154) B=(106,38) C=(250,38) D=(194,154) -->
<!-- Triangle ABC (blue): A,B,C -->
<polygon points="50,154 106,38 250,38" fill="rgba(8,145,178,.18)" stroke="#0891b2" stroke-width="1.8"/>
<!-- Triangle CDA (green): C,D,A -->
<polygon points="250,38 194,154 50,154" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="1.8"/>
<!-- Outer parallelogram border on top -->
<polygon points="50,154 106,38 250,38 194,154" fill="none" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Diagonal AC as the splitting line -->
<line x1="50" y1="154" x2="250" y2="38" stroke="#8b5cf6" stroke-width="2.2" stroke-dasharray="7 3"/>
<!-- Triangle labels at centroids -->
<!-- △ABC centroid: ((50+106+250)/3,(154+38+38)/3) = (135,77) -->
<text x="126" y="80" text-anchor="middle" font-size="12" fill="#0e7490" font-weight="700" font-family="Unbounded,sans-serif">△ABC</text>
<!-- △CDA centroid: ((250+194+50)/3,(38+154+154)/3) = (165,115) -->
<text x="175" y="118" text-anchor="middle" font-size="12" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">△CDA</text>
<!-- vertex dots -->
<circle cx="50" cy="154" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="106" cy="38" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="38" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="194" cy="154" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="34" y="160" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="106" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="208" y="170" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<!-- caption -->
<text x="150" y="176" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">△ABC = △CDA (угол–сторона–угол)</text>
</svg></div>`);
html += makeCard('rule','Доказательство свойства 3','5.3',`
<p>Рассмотрим треугольники $\\triangle AOB$ и $\\triangle COD$, где $O$ — точка пересечения диагоналей.</p>
<p>$AB = CD$ (свойство 1), $\\angle OAB = \\angle OCD$, $\\angle OBA = \\angle ODC$ (накрест лежащие). По «угол–сторона–угол»: $\\triangle AOB = \\triangle COD$.</p>
<p>Следовательно: $AO = CO$ и $BO = DO$. Диагонали делятся пополам. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 180" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,154) B=(106,38) C=(250,38) D=(194,154) -->
<!-- O = midpoint of AC = (150,96) -->
<!-- Triangle AOB (blue): A=(50,154), O=(150,96), B=(106,38) -->
<polygon points="50,154 150,96 106,38" fill="rgba(8,145,178,.20)" stroke="none"/>
<!-- Triangle COD (green): C=(250,38), O=(150,96), D=(194,154) -->
<polygon points="250,38 150,96 194,154" fill="rgba(16,185,129,.20)" stroke="none"/>
<!-- Parallelogram outline -->
<polygon points="50,154 106,38 250,38 194,154" fill="none" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Diagonals -->
<line x1="50" y1="154" x2="250" y2="38" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="106" y1="38" x2="194" y2="154" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- O marker -->
<circle cx="150" cy="96" r="5" fill="#8b5cf6" stroke="#fff" stroke-width="2"/>
<text x="160" y="92" font-size="12" fill="#6d28d9" font-weight="800" font-family="Unbounded,sans-serif">O</text>
<!-- Equal ticks on AO and OC of diagonal AC -->
<!-- AO midpoint=(100,125): direction AC=(200,-116)/234=(0.855,-0.496); perp=(0.496,0.855) -->
<line x1="94" y1="119" x2="106" y2="131" stroke="#0891b2" stroke-width="2.5"/>
<!-- OC midpoint=(200,67): same direction -->
<line x1="194" y1="61" x2="206" y2="73" stroke="#10b981" stroke-width="2.5"/>
<!-- Equal ticks on BO and OD of diagonal BD -->
<!-- BD direction=(88,116)/145=(0.607,0.800); perp=(-0.800,0.607) -->
<!-- BO midpoint=(128,67): perp tick -->
<line x1="135" y1="71" x2="122" y2="62" stroke="#0891b2" stroke-width="2.5"/>
<!-- OD midpoint=(172,125): perp tick -->
<line x1="179" y1="129" x2="166" y2="120" stroke="#10b981" stroke-width="2.5"/>
<!-- vertex dots -->
<circle cx="50" cy="154" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="106" cy="38" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="38" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="194" cy="154" r="3.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="34" y="160" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="106" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="208" y="170" text-anchor="middle" font-size="13" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<!-- caption -->
<text x="150" y="176" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">△AOB = △COD → AO=OC, BO=OD</text>
</svg></div>`);
/* --- INTERACTIVE 1: SVG-параллелограмм с живыми метриками --- */
html += `<div class="wg" id="p5-pgram-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живой параллелограмм — все свойства</div></div>
<div class="wg-help">Тащи вершины <b>B</b> и <b>D</b>. Все четыре свойства проверяются в реальном времени.</div>
<div id="p5-svg-wrap" style="display:flex;justify-content:center"></div>
<div id="p5-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 2: Пошаговое доказательство свойства 1 --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство: AB = CD (шаг за шагом)</div></div>
<div class="wg-help">Нажимай «Дальше» — смотри, как подсвечиваются треугольники.</div>
<div id="p5-proof1-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p5-proof1-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p5-proof1-next">Дальше</button>
<button class="btn" id="p5-proof1-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 3: Доказательство свойства 3 --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Доказательство: AO = OC, BO = OD (шаг за шагом)</div></div>
<div class="wg-help">Диагонали делятся точкой пересечения пополам — доказательство через равные треугольники.</div>
<div id="p5-proof2-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p5-proof2-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p5-proof2-next">Дальше</button>
<button class="btn" id="p5-proof2-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 4: DnD — свойство → иллюстрация --- */
html += `<div class="wg" id="p5-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Соотнеси свойство с описанием</div></div>
<div class="wg-help">Перетащи каждое свойство в правильную категорию.</div>
${DND_HINT_HTML}
<div id="p5-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="sides">Стороны</h5><div class="drop-items" data-cat="sides"></div></div>
<div class="drop-box"><h5 data-cat="angles">Углы</h5><div class="drop-items" data-cat="angles"></div></div>
<div class="drop-box"><h5 data-cat="diag">Диагонали</h5><div class="drop-items" data-cat="diag"></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>`;
/* --- INTERACTIVE 5: Тренажёр --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Тренажёр задач на свойства параллелограмма</div></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.05rem;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:110px">
<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>`;
/* --- INTERACTIVE 6: Босс §5 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<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">+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;
/* == SVG-параллелограмм == */
(function(){
const W=380, H=280, SVG_ID='p5-pgram-svg';
let A={x:55,y:220}, B={x:235,y:220}, D={x:130,y:80};
function getC(){ return {x:D.x+(B.x-A.x), y:D.y+(B.y-A.y)}; }
function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); }
function angDeg(O,P,Q){ const ax=P.x-O.x,ay=P.y-O.y,bx=Q.x-O.x,by=Q.y-O.y; return Math.acos(Math.max(-1,Math.min(1,(ax*bx+ay*by)/(Math.hypot(ax,ay)*Math.hypot(bx,by)))))*180/Math.PI; }
function intersectDiag(p1,p2,p3,p4){ const r={x:p2.x-p1.x,y:p2.y-p1.y},s={x:p4.x-p3.x,y:p4.y-p3.y}; const den=r.x*s.y-r.y*s.x; if(Math.abs(den)<1e-9) return {x:(p1.x+p2.x)/2,y:(p1.y+p2.y)/2}; const t=((p3.x-p1.x)*s.y-(p3.y-p1.y)*s.x)/den; return {x:p1.x+t*r.x,y:p1.y+t*r.y}; }
function clientToSvg(clientX,clientY){ const svgEl=document.getElementById(SVG_ID); if(!svgEl) return {x:0,y:0}; const r=svgEl.getBoundingClientRect(),vb=svgEl.viewBox.baseVal; return {x:(clientX-r.left)/r.width*vb.width+vb.x,y:(clientY-r.top)/r.height*vb.height+vb.y}; }
function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }
function redraw(){
const C=getC(); const pts=[A,B,C,D]; const labels=['A','B','C','D'];
const pcx=(A.x+B.x+C.x+D.x)/4, pcy=(A.y+B.y+C.y+D.y)/4;
const O=intersectDiag(A,C,B,D);
let s='<svg id="'+SVG_ID+'" viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">';
s+='<polygon points="'+pts.map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(225,29,72,.10)" stroke="#e11d48" stroke-width="2.5" stroke-linejoin="round"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#e11d48" stroke-width="1" stroke-dasharray="5 3" opacity=".6"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#e11d48" stroke-width="1" stroke-dasharray="5 3" opacity=".6"/>';
s+='<circle cx="'+O.x+'" cy="'+O.y+'" r="5" fill="#f59e0b" stroke="#fff" stroke-width="2"/>';
s+='<text x="'+(O.x+7)+'" y="'+(O.y-6)+'" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">O</text>';
pts.forEach((v,i)=>{
const m=(i===1||i===3);
const lx=v.x+(v.x-pcx)*0.28, ly=v.y+(v.y-pcy)*0.28;
if(m){
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="16" fill="#e11d48" opacity=".12" data-v="'+labels[i]+'" style="cursor:grab"/>';
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="8" fill="#e11d48" stroke="#fff" stroke-width="2.5" data-v="'+labels[i]+'" style="cursor:grab"/>';
} else {
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#e11d48" stroke="#fff" stroke-width="2"/>';
}
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#be123c" font-family="Unbounded,sans-serif">'+labels[i]+'</text>';
});
s+='</svg>';
document.getElementById('p5-svg-wrap').innerHTML=s;
updateP5Info();
}
function updateP5Info(){
const C=getC(); const O=intersectDiag(A,C,B,D);
const ab=dist(A,B),bc=dist(B,C),ao=dist(A,O),bo=dist(B,O);
const angA=angDeg(A,D,B),angB=angDeg(B,A,C);
document.getElementById('p5-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">AB = CD</div><b>${ab.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">BC = AD</div><b>${bc.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">AO = OC</div><b>${ao.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">BO = OD</div><b>${bo.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">∠A = ∠C</div><b>${angA.toFixed(1)}°</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">∠A + ∠B</div><b>${(angA+angB).toFixed(1)}°</b></div>`;
}
let p5Active=false,p5Vname='',p5OffX=0,p5OffY=0;
document.getElementById('p5-svg-wrap').addEventListener('pointerdown',function(ev){
const el=ev.target.closest('[data-v]'); if(!el) return;
if(ev.button!==undefined&&ev.button!==0) return;
const vname=el.dataset.v; if(vname!=='B'&&vname!=='D') return;
ev.preventDefault();
p5Active=true; p5Vname=vname;
const cur=vname==='B'?B:D;
const sp=clientToSvg(ev.clientX,ev.clientY);
p5OffX=sp.x-cur.x; p5OffY=sp.y-cur.y;
window.addEventListener('pointermove',p5Move,{passive:false});
window.addEventListener('pointerup',p5Up);
window.addEventListener('pointercancel',p5Up);
});
function p5Move(e){
if(!p5Active) return; e.preventDefault();
const sp=clientToSvg(e.clientX,e.clientY);
const nx=clamp(sp.x-p5OffX,12,W-12),ny=clamp(sp.y-p5OffY,12,H-12);
if(p5Vname==='B') B={x:nx,y:ny}; else D={x:nx,y:ny};
redraw();
}
function p5Up(){
if(!p5Active) return; p5Active=false;
window.removeEventListener('pointermove',p5Move);
window.removeEventListener('pointerup',p5Up);
window.removeEventListener('pointercancel',p5Up);
}
redraw();
})();
/* == Доказательство свойства 1 == */
(function(){
const A={x:30,y:160},B={x:185,y:160},C={x:265,y:50},D={x:110,y:50};
const cx=(A.x+B.x+C.x+D.x)/4,cy=(A.y+B.y+C.y+D.y)/4;
const steps=[
{text:'<b>Дано:</b> $ABCD$ — параллелограмм. $AB\\parallel CD$, $BC\\parallel AD$. Проведём диагональ $AC$.', h:''},
{text:'<b>Шаг 1.</b> $AB\\parallel CD$, $AC$ — секущая. $\\angle BAC = \\angle DCA$ — накрест лежащие углы.', h:'diag'},
{text:'<b>Шаг 2.</b> $BC\\parallel AD$, $AC$ — секущая. $\\angle BCA = \\angle DAC$ — накрест лежащие углы.', h:'diag'},
{text:'<b>Шаг 3.</b> $AC$ — общая сторона. По признаку «уголь–сторона–угол»: $\\triangle ABC = \\triangle CDA$.', h:'both'},
{text:'<b>Вывод.</b> $AB = CD$ и $BC = DA$. Свойство 1 доказано. <b>ч.т.д.</b>', h:'both'},
];
let step=0;
function draw(h){
let s='<svg viewBox="0 0 300 200" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
const t1=(h==='diag'||h==='both')?'rgba(225,29,72,.18)':'rgba(225,29,72,.06)';
const t2=h==='both'?'rgba(16,185,129,.18)':'rgba(225,29,72,.06)';
s+='<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(225,29,72,.06)" stroke="#e11d48" stroke-width="2"/>';
if(h){ s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="'+t1+'" stroke="#e11d48" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<polygon points="'+C.x+','+C.y+' '+D.x+','+D.y+' '+A.x+','+A.y+'" fill="'+t2+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#8b5cf6" stroke-width="2"/>'; }
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#e11d48" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#be123c" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p5-proof1-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p5-proof1-step').innerHTML=st.text; renderMath(document.getElementById('p5-proof1-step')); draw(st.h); document.getElementById('p5-proof1-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p5-proof1-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p5-proof1');bumpProgress('p5',12);document.getElementById('p5-proof1-next').textContent='Изучено! +5 XP';document.getElementById('p5-proof1-next').disabled=true;} });
document.getElementById('p5-proof1-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p5-proof1-next').disabled=false;});
show();
})();
/* == Доказательство свойства 3 == */
(function(){
const A={x:30,y:160},B={x:185,y:160},C={x:265,y:50},D={x:110,y:50};
const O={x:(A.x+C.x)/2,y:(A.y+C.y)/2};
const cx=(A.x+B.x+C.x+D.x)/4,cy=(A.y+B.y+C.y+D.y)/4;
const steps=[
{text:'<b>Дано:</b> $ABCD$ — параллелограмм, $O$ — точка пересечения диагоналей. Нужно доказать: $AO=OC$, $BO=OD$.', h:''},
{text:'<b>Шаг 1.</b> $AB\\parallel CD$ и $AB=CD$ (свойство 1). Рассмотрим $\\triangle AOB$ и $\\triangle COD$.', h:'tri1'},
{text:'<b>Шаг 2.</b> $\\angle OAB = \\angle OCD$ (накрест лежащие при $AB\\parallel CD$); $\\angle OBA=\\angle ODC$ (накрест лежащие).', h:'tri1'},
{text:'<b>Шаг 3.</b> $AB=CD$, два угла равны. По «угол–сторона–угол»: $\\triangle AOB = \\triangle COD$.', h:'both3'},
{text:'<b>Вывод.</b> $AO=CO$, $BO=DO$. Диагонали точкой пересечения делятся пополам. <b>ч.т.д.</b>', h:'both3'},
];
let step=0;
function draw(h){
let s='<svg viewBox="0 0 300 200" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
s+='<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(225,29,72,.06)" stroke="#e11d48" stroke-width="2"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 3"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 3"/>';
if(h==='tri1'||h==='both3'){ s+='<polygon points="'+A.x+','+A.y+' '+O.x+','+O.y+' '+B.x+','+B.y+'" fill="rgba(225,29,72,.2)" stroke="#e11d48" stroke-width="1.5"/>'; }
if(h==='both3'){ s+='<polygon points="'+C.x+','+C.y+' '+O.x+','+O.y+' '+D.x+','+D.y+'" fill="rgba(16,185,129,.2)" stroke="#10b981" stroke-width="1.5"/>'; }
s+='<circle cx="'+O.x+'" cy="'+O.y+'" r="5" fill="#f59e0b" stroke="#fff" stroke-width="2"/>';
s+='<text x="'+(O.x+7)+'" y="'+(O.y-5)+'" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">O</text>';
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#e11d48" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#be123c" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p5-proof2-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p5-proof2-step').innerHTML=st.text; renderMath(document.getElementById('p5-proof2-step')); draw(st.h); document.getElementById('p5-proof2-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p5-proof2-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p5-proof2');bumpProgress('p5',12);document.getElementById('p5-proof2-next').textContent='Изучено! +5 XP';document.getElementById('p5-proof2-next').disabled=true;} });
document.getElementById('p5-proof2-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p5-proof2-next').disabled=false;});
show();
})();
/* == DnD == */
(function(){
const items=[
{id:'s1',html:'$AB = CD$', ans:'sides'},
{id:'s2',html:'$BC = AD$', ans:'sides'},
{id:'a1',html:'$\\angle A = \\angle C$', ans:'angles'},
{id:'a2',html:'$\\angle B = \\angle D$', ans:'angles'},
{id:'a3',html:'$\\angle A + \\angle B = 180°$', ans:'angles'},
{id:'d1',html:'$AO = OC$', ans:'diag'},
{id:'d2',html:'$BO = OD$', ans:'diag'},
];
const sorter=setupSorter({poolId:'p5-dnd-pool',scopeSelector:'#p5-dnd-wrap',items,cats:['sides','angles','diag']});
document.getElementById('p5-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p5-dnd-fb').style.display='none';});
document.getElementById('p5-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p5-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все '+items.length+' верно! +5 XP');addXp(5,'p5-dnd');bumpProgress('p5',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.');
});
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'В параллелограмме $\\angle A = 65°$. Найди $\\angle B$.', ans:115, hint:'∠A+∠B=180°, ∠B=115°'},
{q:'В параллелограмме $\\angle A = 65°$. Найди $\\angle C$.', ans:65, hint:'∠C=∠A=65°'},
{q:'В параллелограмме $\\angle A = 65°$. Найди $\\angle D$.', ans:115, hint:'∠D=∠B=115°'},
{q:'В параллелограмме $AB = 12$, периметр $P = 44$. Найди $BC$.', ans:10, hint:'P=2(12+BC), BC=(44/2-12)=10'},
{q:'В параллелограмме $BD = 18$. Найди $BO$ (половину диагонали).', ans:9, hint:'BO=BD/2=9'},
];
let idx=0,score=0;
function show(){ document.getElementById('p5-tr-i').textContent=idx+1; document.getElementById('p5-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p5-tr-task')); 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',()=>{
const ans=+document.getElementById('p5-tr-ans').value; const fb=document.getElementById('p5-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p5-tr-score').textContent=score;addXp(3,'p5-train');bumpProgress('p5',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p5-train-all');} }
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();
})();
/* == Босс §5 == */
(function(){
const tasks=[
{q:'В параллелограмме $\\angle A = 48°$. Найди $\\angle B$.', ans:132, hint:'∠A+∠B=180°'},
{q:'В параллелограмме $AB=15$, $P=58$. Найди $BC$.', ans:14, hint:'P=2(AB+BC), BC=29-15=14'},
{q:'Диагональ параллелограмма $AC=22$. Найди $AO$ ($O$ — точка пересечения диагоналей).', ans:11, hint:'AO=AC/2=11'},
{q:'В параллелограмме $\\angle D=110°$. Найди $\\angle A$.', ans:70, hint:'∠A+∠D=180°, ∠A=70°'},
];
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="p5-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p5-boss-a${i}').value;const fb=document.getElementById('p5-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p5BossSolved.has(${i})){p5BossSolved.add(${i});addXp(5,'p5-boss${i}');bumpProgress('p5',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p5-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p5BossSolved=new Set();
})();
renderMath(box);
}
function buildP6(){
const box = document.getElementById('p6-body');
let html = '';
html += makeCard('theory','Признаки параллелограмма','6.1',`
<p>Четырёхугольник является параллелограммом, если выполнен хотя бы один из признаков:</p>
<ul style="margin-left:18px;line-height:2">
<li><b>Признак 1.</b> Две пары противоположных сторон равны: $AB=CD$ и $BC=AD$.</li>
<li><b>Признак 2.</b> Одна пара противоположных сторон одновременно параллельна и равна: $AB\\parallel CD$ и $AB=CD$.</li>
<li><b>Признак 3.</b> Диагонали точкой пересечения делятся пополам: $AO=OC$ и $BO=OD$.</li>
</ul>
<div style="display:flex;justify-content:center;gap:12px;margin-top:12px;flex-wrap:wrap">
<!-- Criterion 1: equal opposite sides with tick marks -->
<!-- Parallelogram: A=(12,68) B=(22,20) C=(76,20) D=(66,68) -->
<div style="text-align:center"><svg viewBox="0 0 90 82" style="width:86px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="12,68 22,20 76,20 66,68" fill="rgba(192,38,211,.11)" stroke="#c026d3" stroke-width="2"/>
<!-- AB left side: mid=(17,44); dir=(10,-48)/49=(0.204,-0.979); perp=(0.979,0.204) -->
<!-- single tick at mid of AB -->
<line x1="11" y1="41" x2="23" y2="47" stroke="#c026d3" stroke-width="2.2"/>
<!-- CD right side: mid=(71,44); same direction; single tick -->
<line x1="65" y1="41" x2="77" y2="47" stroke="#c026d3" stroke-width="2.2"/>
<!-- BC top: mid=(49,20); perp=(0,1) double tick -->
<line x1="46" y1="14" x2="46" y2="26" stroke="#10b981" stroke-width="2"/>
<line x1="52" y1="14" x2="52" y2="26" stroke="#10b981" stroke-width="2"/>
<!-- DA bottom: mid=(39,68); double tick -->
<line x1="36" y1="62" x2="36" y2="74" stroke="#10b981" stroke-width="2"/>
<line x1="42" y1="62" x2="42" y2="74" stroke="#10b981" stroke-width="2"/>
<circle cx="12" cy="68" r="2" fill="#c026d3"/><circle cx="22" cy="20" r="2" fill="#c026d3"/>
<circle cx="76" cy="20" r="2" fill="#c026d3"/><circle cx="66" cy="68" r="2" fill="#c026d3"/>
<text x="44" y="80" text-anchor="middle" font-size="8" fill="#a21caf" font-weight="700" font-family="Unbounded,sans-serif">Признак 1</text>
</svg></div>
<!-- Criterion 2: one pair parallel+equal -->
<div style="text-align:center"><svg viewBox="0 0 90 82" style="width:86px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="12,68 22,20 76,20 66,68" fill="rgba(192,38,211,.11)" stroke="#c026d3" stroke-width="2"/>
<!-- BC top single tick at mid=(49,20) -->
<line x1="46" y1="14" x2="46" y2="26" stroke="#c026d3" stroke-width="2.2"/>
<!-- DA bottom single tick at mid=(39,68) -->
<line x1="36" y1="62" x2="36" y2="74" stroke="#c026d3" stroke-width="2.2"/>
<!-- parallel arrow marks > on BC: pointing right at mid -->
<polyline points="53,15 57,20 53,25" fill="none" stroke="#c026d3" stroke-width="1.8"/>
<!-- parallel arrow on DA: pointing right at mid -->
<polyline points="43,63 47,68 43,73" fill="none" stroke="#c026d3" stroke-width="1.8"/>
<circle cx="12" cy="68" r="2" fill="#c026d3"/><circle cx="22" cy="20" r="2" fill="#c026d3"/>
<circle cx="76" cy="20" r="2" fill="#c026d3"/><circle cx="66" cy="68" r="2" fill="#c026d3"/>
<text x="44" y="80" text-anchor="middle" font-size="8" fill="#a21caf" font-weight="700" font-family="Unbounded,sans-serif">Признак 2</text>
</svg></div>
<!-- Criterion 3: diagonals bisect each other -->
<div style="text-align:center"><svg viewBox="0 0 90 82" style="width:86px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="12,68 22,20 76,20 66,68" fill="rgba(192,38,211,.11)" stroke="#c026d3" stroke-width="2"/>
<line x1="12" y1="68" x2="76" y2="20" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="22" y1="20" x2="66" y2="68" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- O midpoint of AC: ((12+76)/2,(68+20)/2)=(44,44) -->
<circle cx="44" cy="44" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>
<!-- equal ticks on AO and OC -->
<!-- AO mid=(28,56): dir AC=(64,-48)/80=(0.8,-0.6); perp=(0.6,0.8) -->
<line x1="24" y1="51" x2="32" y2="61" stroke="#8b5cf6" stroke-width="2"/>
<line x1="56" y1="37" x2="64" y2="27" stroke="#8b5cf6" stroke-width="2"/>
<circle cx="12" cy="68" r="2" fill="#c026d3"/><circle cx="22" cy="20" r="2" fill="#c026d3"/>
<circle cx="76" cy="20" r="2" fill="#c026d3"/><circle cx="66" cy="68" r="2" fill="#c026d3"/>
<text x="44" y="80" text-anchor="middle" font-size="8" fill="#a21caf" font-weight="700" font-family="Unbounded,sans-serif">Признак 3</text>
</svg></div>
</div>`);
html += makeCard('rule','Доказательство признака 1','6.2',`
<p>Дано: $AB=CD$, $BC=AD$. Проведём диагональ $AC$.</p>
<p>В $\\triangle ABC$ и $\\triangle CDA$: $AB=CD$, $BC=DA$, $AC=CA$ (общая). По признаку «три стороны»: $\\triangle ABC=\\triangle CDA$.</p>
<p>Из равенства треугольников: $\\angle BAC=\\angle DCA$ и $\\angle BCA=\\angle DAC$. Это накрест лежащие углы, значит $AB\\parallel CD$ и $BC\\parallel AD$. Следовательно, $ABCD$ — параллелограмм. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 178" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,154) B=(106,38) C=(250,38) D=(194,154) -->
<!-- Triangle ABC (purple/blue) -->
<polygon points="50,154 106,38 250,38" fill="rgba(192,38,211,.18)" stroke="#c026d3" stroke-width="1.8" stroke-dasharray="5 2"/>
<!-- Triangle CDA (green) -->
<polygon points="250,38 194,154 50,154" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="1.8" stroke-dasharray="5 2"/>
<!-- Outline -->
<polygon points="50,154 106,38 250,38 194,154" fill="none" stroke="#c026d3" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Diagonal AC -->
<line x1="50" y1="154" x2="250" y2="38" stroke="#8b5cf6" stroke-width="2.2" stroke-dasharray="7 3"/>
<!-- Equal side ticks: AB=CD (single tick on left/right slant sides) -->
<!-- AB: mid=(78,96), dir=(56,-116)/127.95=(0.438,-0.906); perp=(0.906,0.438) -->
<line x1="71" y1="93" x2="85" y2="99" stroke="#c026d3" stroke-width="2.5"/>
<!-- CD: mid=(222,96), same direction -->
<line x1="215" y1="93" x2="229" y2="99" stroke="#c026d3" stroke-width="2.5"/>
<!-- BC=DA double ticks -->
<!-- BC top: mid=(178,38), perp=(0,1) -->
<line x1="175" y1="31" x2="175" y2="45" stroke="#10b981" stroke-width="2"/>
<line x1="181" y1="31" x2="181" y2="45" stroke="#10b981" stroke-width="2"/>
<!-- DA bottom: mid=(122,154) -->
<line x1="119" y1="147" x2="119" y2="161" stroke="#10b981" stroke-width="2"/>
<line x1="125" y1="147" x2="125" y2="161" stroke="#10b981" stroke-width="2"/>
<!-- vertex dots -->
<circle cx="50" cy="154" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="106" cy="38" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="38" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="194" cy="154" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<text x="34" y="160" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">A</text>
<text x="106" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">C</text>
<text x="208" y="170" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">D</text>
<text x="150" y="174" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">△ABC = △CDA (с-с-с)</text>
</svg></div>`);
html += makeCard('rule','Доказательство признака 3','6.3',`
<p>Дано: $AO=OC$, $BO=OD$. В $\\triangle AOB$ и $\\triangle COD$: $AO=CO$, $BO=DO$, $\\angle AOB=\\angle COD$ (вертикальные). По признаку «сторона–угол–сторона»: $\\triangle AOB=\\triangle COD$.</p>
<p>Отсюда $\\angle OAB=\\angle OCD$ — накрест лежащие, значит $AB\\parallel CD$. Аналогично $BC\\parallel AD$. Четырёхугольник — параллелограмм. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 178" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Parallelogram ABCD: A=(50,154) B=(106,38) C=(250,38) D=(194,154) -->
<!-- O = (150,96) midpoint of AC -->
<!-- △AOB (blue): A,O,B -->
<polygon points="50,154 150,96 106,38" fill="rgba(8,145,178,.20)" stroke="none"/>
<!-- △COD (green): C,O,D -->
<polygon points="250,38 150,96 194,154" fill="rgba(16,185,129,.20)" stroke="none"/>
<!-- Parallelogram outline -->
<polygon points="50,154 106,38 250,38 194,154" fill="none" stroke="#c026d3" stroke-width="2.2" stroke-linejoin="round"/>
<!-- Diagonals -->
<line x1="50" y1="154" x2="250" y2="38" stroke="#8b5cf6" stroke-width="1.8" stroke-dasharray="5 3"/>
<line x1="106" y1="38" x2="194" y2="154" stroke="#8b5cf6" stroke-width="1.8" stroke-dasharray="5 3"/>
<!-- O marker -->
<circle cx="150" cy="96" r="5" fill="#8b5cf6" stroke="#fff" stroke-width="2"/>
<text x="160" y="92" font-size="12" fill="#6d28d9" font-weight="800" font-family="Unbounded,sans-serif">O</text>
<!-- Equal ticks on AO and OC (purple) -->
<line x1="94" y1="120" x2="106" y2="132" stroke="#c026d3" stroke-width="2.5"/>
<line x1="194" y1="64" x2="206" y2="72" stroke="#10b981" stroke-width="2.5"/>
<!-- Equal ticks on BO and OD (orange) -->
<line x1="122" y1="63" x2="135" y2="72" stroke="#c026d3" stroke-width="2.5"/>
<line x1="164" y1="122" x2="177" y2="131" stroke="#10b981" stroke-width="2.5"/>
<!-- Vertical angle arcs at O -->
<!-- AOB angle and COD angle (opposite): mark with small arcs -->
<!-- From O toward A: dir OA=(-100,58)/116=(-0.862,0.500); from O toward B: (-44,-58)/73=(-0.603,-0.794) -->
<path d="M138,102 A13,13 0 0,1 144,83" stroke="#d97706" stroke-width="1.8" fill="rgba(217,119,6,.22)"/>
<path d="M162,90 A13,13 0 0,1 156,109" stroke="#d97706" stroke-width="1.8" fill="rgba(217,119,6,.22)"/>
<!-- vertex dots -->
<circle cx="50" cy="154" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="106" cy="38" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="38" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="194" cy="154" r="3.5" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<text x="34" y="160" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">A</text>
<text x="106" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="26" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">C</text>
<text x="208" y="170" text-anchor="middle" font-size="13" font-weight="800" fill="#a21caf" font-family="Unbounded,sans-serif">D</text>
<text x="150" y="174" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">△AOB = △COD (с-у-с)</text>
</svg></div>`);
/* --- INTERACTIVE 1: Три SVG-демонстрации признаков --- */
html += `<div class="wg" id="p6-signs-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Три признака параллелограмма</div></div>
<div class="wg-help">Нажми кнопку признака — смотри иллюстрацию и вывод.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px">
<button class="btn primary" id="p6-sign-btn1">Признак 1</button>
<button class="btn" id="p6-sign-btn2">Признак 2</button>
<button class="btn" id="p6-sign-btn3">Признак 3</button>
</div>
<div id="p6-sign-svg" style="display:flex;justify-content:center"></div>
<div id="p6-sign-text" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-top:10px;font-size:.95rem;line-height:1.65"></div>
</div>`;
/* --- INTERACTIVE 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">Прочитай условие и выбери, какой признак доказывает, что фигура — параллелограмм.</div>
<div class="score-display"><span>Вопрос <b id="p6-quiz-i">1</b> / 5</span><span>Очки: <b id="p6-quiz-score">0</b></span></div>
<div id="p6-quiz-q" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1rem;margin-bottom:10px"></div>
<div id="p6-quiz-opts" style="display:flex;gap:8px;flex-wrap:wrap"></div>
<div class="feedback" id="p6-quiz-fb" style="display:none;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 3: DnD — условие → признак --- */
html += `<div class="wg" id="p6-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какой признак применить?</div></div>
<div class="wg-help">Перетащи условие к соответствующему признаку параллелограмма.</div>
${DND_HINT_HTML}
<div id="p6-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="r1">Признак 1</h5><div class="drop-items" data-cat="r1"></div></div>
<div class="drop-box"><h5 data-cat="r2">Признак 2</h5><div class="drop-items" data-cat="r2"></div></div>
<div class="drop-box"><h5 data-cat="r3">Признак 3</h5><div class="drop-items" data-cat="r3"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p6-dnd-check">Проверить</button><button class="btn" id="p6-dnd-reset">Сначала</button></div>
<div class="feedback" id="p6-dnd-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 4: Мини-доказательство признака 1 --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Доказательство признака 1 шаг за шагом</div></div>
<div id="p6-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p6-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p6-proof-next">Дальше</button>
<button class="btn" id="p6-proof-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 5: Босс §6 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §6</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p6-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p6-read-btn" onclick="addXp(10,'p6-read');bumpProgress('p6',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>
Я прочитал §6 (+10 XP)
</button>
</div>`;
html += secNav('p5','p7');
box.innerHTML = html;
/* == Три признака — SVG-демонстрации == */
(function(){
const A={x:40,y:155},B={x:175,y:155},C={x:245,y:55},D={x:110,y:55};
const cx=(A.x+B.x+C.x+D.x)/4,cy=(A.y+B.y+C.y+D.y)/4;
function baseShape(){ return '<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(192,38,211,.10)" stroke="#c026d3" stroke-width="2.5" stroke-linejoin="round"/>'; }
function labels(){ let s=''; ['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#c026d3" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#a21caf" font-family="Unbounded,sans-serif">'+lbl+'</text>'; }); return s; }
function midTick(p1,p2,col,n){ const mx=(p1.x+p2.x)/2,my=(p1.y+p2.y)/2,dx=p2.x-p1.x,dy=p2.y-p1.y,len=Math.hypot(dx,dy); const nx=-dy/len*6,ny=dx/len*6; let s=''; for(let k=0;k<n;k++){ const off=(k-(n-1)/2)*4; s+='<line x1="'+(mx+nx+off*dx/len)+'" y1="'+(my+ny+off*dy/len)+'" x2="'+(mx-nx+off*dx/len)+'" y2="'+(my-ny+off*dy/len)+'" stroke="'+col+'" stroke-width="2"/>'; } return s; }
function showSign(n){
let s='<svg viewBox="0 0 290 195" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
s+=baseShape();
if(n===1){ s+=midTick(A,B,'#10b981',1)+midTick(C,D,'#10b981',1)+midTick(B,C,'#f59e0b',2)+midTick(D,A,'#f59e0b',2); document.getElementById('p6-sign-text').innerHTML='<b>Признак 1:</b> $AB=CD$ (один штрих) и $BC=AD$ (два штриха). Следовательно, $ABCD$ — параллелограмм.'; }
else if(n===2){ s+=midTick(A,B,'#10b981',1)+midTick(C,D,'#10b981',1); s+='<text x="'+(A.x+B.x)/2+'" y="'+(A.y+B.y)/2+40+'" text-anchor="middle" font-size="11" fill="#10b981" font-weight="700">AB ∥ CD</text>'; document.getElementById('p6-sign-text').innerHTML='<b>Признак 2:</b> $AB\\parallel CD$ и $AB=CD$. Следовательно, $ABCD$ — параллелограмм.'; }
else{ const O={x:(A.x+C.x)/2,y:(A.y+C.y)/2}; s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 3"/>'; s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 3"/>'; s+='<circle cx="'+O.x+'" cy="'+O.y+'" r="5" fill="#f59e0b" stroke="#fff" stroke-width="2"/>'; s+=midTick(A,O,'#8b5cf6',1)+midTick(O,C,'#8b5cf6',1)+midTick(B,O,'#f97316',2)+midTick(O,D,'#f97316',2); document.getElementById('p6-sign-text').innerHTML='<b>Признак 3:</b> $AO=OC$ и $BO=OD$ — диагонали делятся пополам. Следовательно, $ABCD$ — параллелограмм.'; }
s+=labels()+'</svg>';
document.getElementById('p6-sign-svg').innerHTML=s;
renderMath(document.getElementById('p6-sign-text'));
[1,2,3].forEach(k=>{ const btn=document.getElementById('p6-sign-btn'+k); btn.className='btn'+(k===n?' primary':''); });
}
document.getElementById('p6-sign-btn1').addEventListener('click',()=>showSign(1));
document.getElementById('p6-sign-btn2').addEventListener('click',()=>showSign(2));
document.getElementById('p6-sign-btn3').addEventListener('click',()=>showSign(3));
showSign(1);
})();
/* == Тренажёр выбора признака == */
(function(){
const qs=[
{q:'В четырёхугольнике $ABCD$: $AB=CD=7$ и $BC=AD=5$. Это параллелограмм?',opts:['Признак 1','Признак 2','Признак 3','Нет'],ans:0},
{q:'В четырёхугольнике $AB\\parallel CD$ и $AB=CD$. Это параллелограмм?',opts:['Признак 1','Признак 2','Признак 3','Нет'],ans:1},
{q:'Диагонали пересекаются в точке $O$: $AO=OC=6$, $BO=OD=8$. Это параллелограмм?',opts:['Признак 1','Признак 2','Признак 3','Нет'],ans:2},
{q:'В четырёхугольнике все стороны разной длины и диагонали не делятся пополам.',opts:['Признак 1','Признак 2','Признак 3','Нет'],ans:3},
{q:'В четырёхугольнике $BC\\parallel AD$ и $BC=AD$. Это параллелограмм?',opts:['Признак 1','Признак 2','Признак 3','Нет'],ans:1},
];
let qi=0,qscore=0,answered=false;
function showQ(){
answered=false;
const q=qs[qi];
document.getElementById('p6-quiz-i').textContent=qi+1;
document.getElementById('p6-quiz-q').innerHTML=q.q;
renderMath(document.getElementById('p6-quiz-q'));
document.getElementById('p6-quiz-opts').innerHTML=q.opts.map((o,i)=>`<button class="btn" id="p6-qopt-${i}" onclick="p6QuizAns(${i})">${o}</button>`).join('');
document.getElementById('p6-quiz-fb').style.display='none';
}
window.p6QuizAns=function(i){
if(answered)return; answered=true;
const q=qs[qi]; const fb=document.getElementById('p6-quiz-fb');
if(i===q.ans){qscore++;feedback(fb,true,'Верно! +3 XP');addXp(3,'p6-quiz');bumpProgress('p6',7);}
else feedback(fb,false,'Неверно. Правильно: '+q.opts[q.ans]);
document.querySelectorAll('[id^="p6-qopt-"]').forEach((b,bi)=>{b.disabled=true;if(bi===q.ans)b.style.background='var(--ok-bg)';else if(bi===i)b.style.background='var(--fail-bg)';});
setTimeout(()=>{ qi++; if(qi<qs.length)showQ(); else document.getElementById('p6-quiz-q').innerHTML='<b>Квиз завершён! '+qscore+'/'+qs.length+'</b>'; },1300);
};
showQ();
})();
/* == DnD == */
(function(){
const items=[
{id:'c1',html:'$AB=CD$ и $BC=AD$', ans:'r1'},
{id:'c2',html:'Обе пары противоположных сторон равны', ans:'r1'},
{id:'c3',html:'$AB\\parallel CD$ и $AB=CD$', ans:'r2'},
{id:'c4',html:'Одна пара ∥ и =', ans:'r2'},
{id:'c5',html:'$AO=OC$ и $BO=OD$', ans:'r3'},
{id:'c6',html:'Диагонали делятся пополам', ans:'r3'},
];
const sorter=setupSorter({poolId:'p6-dnd-pool',scopeSelector:'#p6-dnd-wrap',items,cats:['r1','r2','r3']});
document.getElementById('p6-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p6-dnd-fb').style.display='none';});
document.getElementById('p6-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p6-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p6-dnd');bumpProgress('p6',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.');
});
})();
/* == Доказательство признака 1 == */
(function(){
const A={x:40,y:155},B={x:175,y:155},C={x:245,y:55},D={x:110,y:55};
const cx=(A.x+B.x+C.x+D.x)/4,cy=(A.y+B.y+C.y+D.y)/4;
const steps=[
{text:'<b>Дано:</b> $AB=CD$, $BC=AD$. Проведём диагональ $AC$.', h:''},
{text:'<b>Шаг 1.</b> В $\\triangle ABC$ и $\\triangle CDA$: $AB=CD$, $BC=DA$, $AC=CA$ (общая сторона).', h:'diag'},
{text:'<b>Шаг 2.</b> По признаку «три стороны»: $\\triangle ABC = \\triangle CDA$.', h:'both'},
{text:'<b>Шаг 3.</b> Из равенства: $\\angle BAC=\\angle DCA$ и $\\angle BCA=\\angle DAC$ — накрест лежащие углы. Значит $AB\\parallel CD$ и $BC\\parallel AD$.', h:'both'},
{text:'<b>Вывод.</b> Обе пары противоположных сторон параллельны $\\Rightarrow$ $ABCD$ — параллелограмм. <b>ч.т.д.</b>', h:'both'},
];
let step=0;
function draw(h){
let s='<svg viewBox="0 0 290 195" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
const t1=(h==='diag'||h==='both')?'rgba(192,38,211,.18)':'rgba(192,38,211,.06)';
const t2=h==='both'?'rgba(16,185,129,.18)':'rgba(192,38,211,.06)';
s+='<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(192,38,211,.06)" stroke="#c026d3" stroke-width="2"/>';
if(h){ s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="'+t1+'" stroke="#c026d3" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<polygon points="'+C.x+','+C.y+' '+D.x+','+D.y+' '+A.x+','+A.y+'" fill="'+t2+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#8b5cf6" stroke-width="2"/>'; }
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#c026d3" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#a21caf" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p6-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p6-proof-step').innerHTML=st.text; renderMath(document.getElementById('p6-proof-step')); draw(st.h); document.getElementById('p6-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p6-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p6-proof');bumpProgress('p6',12);document.getElementById('p6-proof-next').textContent='Изучено! +5 XP';document.getElementById('p6-proof-next').disabled=true;} });
document.getElementById('p6-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p6-proof-next').disabled=false;});
show();
})();
/* == Босс §6 == */
(function(){
const tasks=[
{q:'В четырёхугольнике $AB=CD=9$, $BC=AD=6$. Это параллелограмм? Напиши номер признака (1, 2 или 3).', ans:1, hint:'Признак 1: две пары равных сторон'},
{q:'В параллелограмме $\\angle A=75°$. Найди $\\angle C$.', ans:75, hint:'∠A=∠C в параллелограмме'},
{q:'В четырёхугольнике $AO=OC=5$ и $BO=OD=7$. Это параллелограмм — какой признак? (1/2/3)', ans:3, hint:'Признак 3: диагонали делятся пополам'},
{q:'В параллелограмме $AB=16$, $P=54$. Найди $BC$.', ans:11, hint:'P=2(16+BC), BC=27-16=11'},
];
const bossBox=document.getElementById('p6-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="p6-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p6-boss-a${i}').value;const fb=document.getElementById('p6-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p6BossSolved.has(${i})){p6BossSolved.add(${i});addXp(5,'p6-boss${i}');bumpProgress('p6',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p6-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p6BossSolved=new Set();
})();
renderMath(box);
}
function buildP7(){
const box = document.getElementById('p7-body');
let html = '';
html += makeCard('theory','Прямоугольник — определение','7.1',`
<p><b>Прямоугольник</b> — параллелограмм, у которого один угол прямой.</p>
<p>Так как в параллелограмме сумма соседних углов равна $180°$, а один угол равен $90°$, то <em>все углы прямоугольника прямые</em>.</p>
<p>Обозначение: $ABCD$, где $\\angle A=\\angle B=\\angle C=\\angle D=90°$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 195" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Rectangle ABCD: A=(42,160) B=(258,160) C=(258,42) D=(42,42) -->
<!-- Width=216, Height=118 — nice 16:9-ish proportion -->
<rect x="42" y="42" width="216" height="118" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2.2"/>
<!-- Right angle L-markers (9px inward) at all 4 corners -->
<!-- Corner A=(42,160): edges go right (+x) and up (-y); L-shape: right-then-up inside -->
<polyline points="51,160 51,151 42,151" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<!-- Corner B=(258,160): edges go left (-x) and up (-y); L-shape: left-then-up inside -->
<polyline points="249,160 249,151 258,151" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<!-- Corner C=(258,42): edges go left (-x) and down (+y); L-shape: left-then-down inside -->
<polyline points="249,42 249,51 258,51" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<!-- Corner D=(42,42): edges go right (+x) and down (+y); L-shape: right-then-down inside -->
<polyline points="51,42 51,51 42,51" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<!-- vertex dots -->
<circle cx="42" cy="160" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="258" cy="160" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="258" cy="42" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="42" cy="42" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="26" y="166" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="274" y="166" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="274" y="36" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="26" y="36" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
<!-- side labels: a (horizontal), b (vertical) -->
<text x="150" y="180" text-anchor="middle" font-size="13" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<text x="280" y="104" text-anchor="middle" font-size="13" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">b</text>
<!-- caption -->
<text x="150" y="192" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">∠A=∠B=∠C=∠D=90°</text>
</svg></div>`);
html += makeCard('rule','Свойство диагоналей прямоугольника','7.2',`
<p><b>Теорема.</b> Диагонали прямоугольника равны: $AC = BD$.</p>
<p><b>Доказательство.</b> Рассмотрим $\\triangle ABC$ и $\\triangle BAD$: $AB=BA$ (общая), $\\angle ABC=\\angle BAD=90°$, $BC=AD$ (как в параллелограмме). По признаку «угол–сторона–угол»: $\\triangle ABC=\\triangle BAD$.</p>
<p>Следовательно: $AC=BD$. <b>ч.т.д.</b></p>
<p>По теореме Пифагора: $d = \\sqrt{a^2 + b^2}$, где $a$, $b$ — стороны прямоугольника.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 300 195" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Rectangle ABCD: A=(42,160) B=(258,160) C=(258,42) D=(42,42) -->
<rect x="42" y="42" width="216" height="118" fill="rgba(124,58,237,.09)" stroke="#7c3aed" stroke-width="2.2"/>
<!-- Right angle L-markers at all 4 corners -->
<polyline points="51,160 51,151 42,151" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<polyline points="249,160 249,151 258,151" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<polyline points="249,42 249,51 258,51" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<polyline points="51,42 51,51 42,51" fill="none" stroke="#7c3aed" stroke-width="1.8"/>
<!-- Diagonal AC: A=(42,160)→C=(258,42) — green -->
<line x1="42" y1="160" x2="258" y2="42" stroke="#10b981" stroke-width="2" stroke-dasharray="7 3"/>
<!-- Diagonal BD: B=(258,160)→D=(42,42) — amber -->
<line x1="258" y1="160" x2="42" y2="42" stroke="#d97706" stroke-width="2" stroke-dasharray="7 3"/>
<!-- Center O = (150,101) -->
<circle cx="150" cy="101" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="2"/>
<!-- Equal ticks on AC: first half AO mid=(96,131), second half OC mid=(204,72) -->
<!-- AC direction: (216,-118)/246.5=(0.876,-0.479); perp=(0.479,0.876) -->
<line x1="90" y1="125" x2="102" y2="137" stroke="#10b981" stroke-width="2.5"/>
<line x1="198" y1="66" x2="210" y2="78" stroke="#10b981" stroke-width="2.5"/>
<!-- Equal ticks on BD: first half BO mid=(204,131), second half OD mid=(96,72) -->
<!-- BD direction: (-216,-118)/246.5=(-0.876,-0.479); perp=(-0.479,0.876) -->
<line x1="198" y1="125" x2="210" y2="137" stroke="#d97706" stroke-width="2.5"/>
<line x1="90" y1="66" x2="102" y2="78" stroke="#d97706" stroke-width="2.5"/>
<!-- Diagonal labels -->
<text x="100" y="60" text-anchor="middle" font-size="12" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">AC</text>
<text x="200" y="60" text-anchor="middle" font-size="12" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">BD</text>
<!-- vertex dots -->
<circle cx="42" cy="160" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="258" cy="160" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="258" cy="42" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="42" cy="42" r="3.5" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<!-- vertex labels -->
<text x="26" y="166" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="274" y="166" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="274" y="36" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="26" y="36" text-anchor="middle" font-size="13" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
<!-- caption -->
<text x="150" y="192" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">AC = BD = √(a²+b²)</text>
</svg></div>`);
html += makeCard('example','Примеры','7.3',`
<p>Найти диагональ прямоугольника по формуле $d=\\sqrt{a^2+b^2}$.</p>
<div style="display:flex;justify-content:center;gap:20px;flex-wrap:wrap;margin-top:12px">
<!-- Example 1: a=6, b=8, d=10 -->
<div style="text-align:center">
<svg viewBox="0 0 148 118" style="max-width:148px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<!-- Rectangle: A=(18,94) B=(126,94) C=(126,24) D=(18,24), width=108, height=70 -->
<rect x="18" y="24" width="108" height="70" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2"/>
<!-- Right angle L-marks (7px inward) at all 4 corners -->
<polyline points="25,94 25,87 18,87" fill="none" stroke="#7c3aed" stroke-width="1.5"/>
<polyline points="119,94 119,87 126,87" fill="none" stroke="#7c3aed" stroke-width="1.5"/>
<polyline points="119,24 119,31 126,31" fill="none" stroke="#7c3aed" stroke-width="1.5"/>
<polyline points="25,24 25,31 18,31" fill="none" stroke="#7c3aed" stroke-width="1.5"/>
<!-- Diagonal AC: A=(18,94)→C=(126,24) — amber -->
<line x1="18" y1="94" x2="126" y2="24" stroke="#d97706" stroke-width="1.8" stroke-dasharray="5 3"/>
<!-- vertex dots -->
<circle cx="18" cy="94" r="3" fill="#7c3aed" stroke="#fff" stroke-width="1.2"/>
<circle cx="126" cy="94" r="3" fill="#7c3aed" stroke="#fff" stroke-width="1.2"/>
<circle cx="126" cy="24" r="3" fill="#7c3aed" stroke="#fff" stroke-width="1.2"/>
<circle cx="18" cy="24" r="3" fill="#7c3aed" stroke="#fff" stroke-width="1.2"/>
<!-- vertex labels -->
<text x="8" y="100" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="138" y="100" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="138" y="20" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="8" y="20" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
<!-- side labels -->
<text x="72" y="108" text-anchor="middle" font-size="12" fill="#7c3aed" font-weight="700" font-family="JetBrains Mono,monospace">a = 6</text>
<text x="136" y="62" text-anchor="start" font-size="12" fill="#7c3aed" font-weight="700" font-family="JetBrains Mono,monospace">b=8</text>
<!-- diagonal label -->
<text x="57" y="72" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">d=10</text>
</svg>
<div style="font-size:.82rem;color:var(--text-2);margin-top:4px">$d=\\sqrt{6^2+8^2}=\\sqrt{100}=10$</div>
</div>
<!-- Example 2: a=5, b=12, d=13 -->
<div style="text-align:center">
<svg viewBox="0 0 148 118" style="max-width:148px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<!-- Rectangle: A=(18,94) B=(126,94) C=(126,24) D=(18,24) -->
<rect x="18" y="24" width="108" height="70" fill="rgba(16,185,129,.10)" stroke="#059669" stroke-width="2"/>
<!-- Right angle L-marks at all 4 corners -->
<polyline points="25,94 25,87 18,87" fill="none" stroke="#059669" stroke-width="1.5"/>
<polyline points="119,94 119,87 126,87" fill="none" stroke="#059669" stroke-width="1.5"/>
<polyline points="119,24 119,31 126,31" fill="none" stroke="#059669" stroke-width="1.5"/>
<polyline points="25,24 25,31 18,31" fill="none" stroke="#059669" stroke-width="1.5"/>
<!-- Diagonal AC — amber -->
<line x1="18" y1="94" x2="126" y2="24" stroke="#d97706" stroke-width="1.8" stroke-dasharray="5 3"/>
<!-- vertex dots -->
<circle cx="18" cy="94" r="3" fill="#059669" stroke="#fff" stroke-width="1.2"/>
<circle cx="126" cy="94" r="3" fill="#059669" stroke="#fff" stroke-width="1.2"/>
<circle cx="126" cy="24" r="3" fill="#059669" stroke="#fff" stroke-width="1.2"/>
<circle cx="18" cy="24" r="3" fill="#059669" stroke="#fff" stroke-width="1.2"/>
<!-- vertex labels -->
<text x="8" y="100" text-anchor="middle" font-size="11" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="138" y="100" text-anchor="middle" font-size="11" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">B</text>
<text x="138" y="20" text-anchor="middle" font-size="11" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">C</text>
<text x="8" y="20" text-anchor="middle" font-size="11" font-weight="800" fill="#047857" font-family="Unbounded,sans-serif">D</text>
<!-- side labels -->
<text x="72" y="108" text-anchor="middle" font-size="12" fill="#059669" font-weight="700" font-family="JetBrains Mono,monospace">a = 5</text>
<text x="136" y="62" text-anchor="start" font-size="12" fill="#059669" font-weight="700" font-family="JetBrains Mono,monospace">b=12</text>
<!-- diagonal label -->
<text x="57" y="72" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">d=13</text>
</svg>
<div style="font-size:.82rem;color:var(--text-2);margin-top:4px">$d=\\sqrt{5^2+12^2}=\\sqrt{169}=13$</div>
</div>
</div>`);
/* --- INTERACTIVE 1: SVG-прямоугольник с draggable B --- */
html += `<div class="wg" id="p7-rect-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живой прямоугольник — равенство диагоналей</div></div>
<div class="wg-help">Тащи вершину <b>B</b>, чтобы изменить размеры прямоугольника. Диагонали всегда остаются равными.</div>
<div id="p7-rect-svg" style="display:flex;justify-content:center"></div>
<div id="p7-rect-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 2: Пошаговое доказательство --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство: диагонали прямоугольника равны</div></div>
<div id="p7-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p7-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p7-proof-next">Дальше</button>
<button class="btn" id="p7-proof-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 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">Введи стороны $a$ и $b$ — получи диагональ по теореме Пифагора.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.92rem">$a$ = <input type="number" id="p7-ca" class="tinp" value="3" style="width:70px" min="1"></label>
<label style="font-size:.92rem">$b$ = <input type="number" id="p7-cb" class="tinp" value="4" style="width:70px" min="1"></label>
<button class="btn primary" id="p7-calc-go">Вычислить</button>
</div>
<div id="p7-calc-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.8"></div>
</div>`;
/* --- INTERACTIVE 4: Тренажёр --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр задач на прямоугольник</div></div>
<div class="score-display"><span>Задача <b id="p7-tr-i">1</b> / 5</span><span>Очки: <b id="p7-tr-score">0</b></span></div>
<div id="p7-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p7-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p7-tr-go">Проверить</button>
<button class="btn" id="p7-tr-start">Начать</button>
</div>
<div class="feedback" id="p7-tr-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 5: DnD прямоугольники --- */
html += `<div class="wg" id="p7-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Это прямоугольник?</div></div>
<div class="wg-help">Разложи фигуры: является ли она прямоугольником?</div>
${DND_HINT_HTML}
<div id="p7-dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="yes">Прямоугольник</h5><div class="drop-items" data-cat="yes"></div></div>
<div class="drop-box"><h5 data-cat="no">Не прямоугольник</h5><div class="drop-items" data-cat="no"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p7-dnd-check">Проверить</button><button class="btn" id="p7-dnd-reset">Сначала</button></div>
<div class="feedback" id="p7-dnd-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 6: Босс §7 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §7</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p7-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p7-read-btn" onclick="addXp(10,'p7-read');bumpProgress('p7',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>
Я прочитал §7 (+10 XP)
</button>
</div>`;
html += secNav('p6','p8');
box.innerHTML = html;
/* == SVG-прямоугольник (redesigned) == */
(function(){
const W=400, H=290, SVG_ID='p7-rect-svg-el';
const Ax=55, Ay=240;
let Cx=310, Cy=55;
function getVerts(){ return { A:{x:Ax,y:Ay}, B:{x:Cx,y:Ay}, C:{x:Cx,y:Cy}, D:{x:Ax,y:Cy} }; }
function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); }
function sqMark(ox,oy,dx1,dy1,dx2,dy2,sz,col){
const ex1=ox+dx1*sz,ey1=oy+dy1*sz,ex2=ox+dx2*sz,ey2=oy+dy2*sz;
const mx=ex1+dx2*sz,my=ey1+dy2*sz;
return '<polyline points="'+ex1+','+ey1+' '+mx+','+my+' '+ex2+','+ey2+'" fill="none" stroke="'+col+'" stroke-width="1.8" opacity=".85"/>';
}
function clientToSvg(clientX,clientY){
const svgEl=document.getElementById(SVG_ID); if(!svgEl) return {x:0,y:0};
const r=svgEl.getBoundingClientRect(),vb=svgEl.viewBox.baseVal;
return {x:(clientX-r.left)/r.width*vb.width+vb.x,y:(clientY-r.top)/r.height*vb.height+vb.y};
}
function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }
function redraw(){
const {A,B,C,D}=getVerts();
const pcx=(A.x+B.x+C.x+D.x)/4, pcy=(A.y+B.y+C.y+D.y)/4;
const ab=dist(A,B),bc=dist(B,C),ac=dist(A,C),bd=dist(B,D);
const perimeter=2*(ab+bc),area=ab*bc;
const sq=10;
let s='<svg id="'+SVG_ID+'" viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:420px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">';
s+='<rect x="'+Math.min(A.x,C.x)+'" y="'+Math.min(A.y,C.y)+'" width="'+Math.abs(C.x-A.x)+'" height="'+Math.abs(C.y-A.y)+'" fill="rgba(124,58,237,.09)" stroke="#7c3aed" stroke-width="2.5"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#10b981" stroke-width="2" stroke-dasharray="7 4"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#f59e0b" stroke-width="2" stroke-dasharray="7 4"/>';
const acMx=(A.x+C.x)/2,acMy=(A.y+C.y)/2,bdMx=(B.x+D.x)/2,bdMy=(B.y+D.y)/2,tl=6;
const acDx=C.x-A.x,acDy=C.y-A.y,acL=Math.hypot(acDx,acDy)||1;
const acPx=-acDy/acL*tl,acPy=acDx/acL*tl;
s+='<line x1="'+(acMx+acPx-acDx/acL*5)+'" y1="'+(acMy+acPy-acDy/acL*5)+'" x2="'+(acMx-acPx-acDx/acL*5)+'" y2="'+(acMy-acPy-acDy/acL*5)+'" stroke="#10b981" stroke-width="2.2"/>';
s+='<line x1="'+(acMx+acPx+acDx/acL*5)+'" y1="'+(acMy+acPy+acDy/acL*5)+'" x2="'+(acMx-acPx+acDx/acL*5)+'" y2="'+(acMy-acPy+acDy/acL*5)+'" stroke="#10b981" stroke-width="2.2"/>';
const bdDx=D.x-B.x,bdDy=D.y-B.y,bdL=Math.hypot(bdDx,bdDy)||1;
const bdPx=-bdDy/bdL*tl,bdPy=bdDx/bdL*tl;
s+='<line x1="'+(bdMx+bdPx-bdDx/bdL*5)+'" y1="'+(bdMy+bdPy-bdDy/bdL*5)+'" x2="'+(bdMx-bdPx-bdDx/bdL*5)+'" y2="'+(bdMy-bdPy-bdDy/bdL*5)+'" stroke="#f59e0b" stroke-width="2.2"/>';
s+='<line x1="'+(bdMx+bdPx+bdDx/bdL*5)+'" y1="'+(bdMy+bdPy+bdDy/bdL*5)+'" x2="'+(bdMx-bdPx+bdDx/bdL*5)+'" y2="'+(bdMy-bdPy+bdDy/bdL*5)+'" stroke="#f59e0b" stroke-width="2.2"/>';
s+='<text x="'+(acMx-24)+'" y="'+(acMy-7)+'" font-size="10" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">AC='+ac.toFixed(1)+'</text>';
s+='<text x="'+(bdMx+6)+'" y="'+(bdMy-7)+'" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">BD='+bd.toFixed(1)+'</text>';
s+=sqMark(A.x,A.y,+1,0,0,-1,sq,'#7c3aed');
s+=sqMark(B.x,B.y,-1,0,0,-1,sq,'#7c3aed');
s+=sqMark(C.x,C.y,-1,0,0,+1,sq,'#7c3aed');
s+=sqMark(D.x,D.y,+1,0,0,+1,sq,'#7c3aed');
s+='<text x="'+((A.x+B.x)/2)+'" y="'+(A.y+16)+'" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">a='+ab.toFixed(1)+'</text>';
s+='<text x="'+(C.x+14)+'" y="'+((B.y+C.y)/2)+'" text-anchor="start" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace" dominant-baseline="middle">b='+bc.toFixed(1)+'</text>';
[{p:A,lbl:'A',drag:false},{p:B,lbl:'B',drag:false},{p:C,lbl:'C',drag:true},{p:D,lbl:'D',drag:false}].forEach(({p,lbl,drag})=>{
const lx=p.x+(p.x-pcx)*0.28,ly=p.y+(p.y-pcy)*0.28;
if(drag){
s+='<circle cx="'+p.x+'" cy="'+p.y+'" r="16" fill="#7c3aed" opacity=".15" data-v="C" style="cursor:grab"/>';
s+='<circle cx="'+p.x+'" cy="'+p.y+'" r="8" fill="#7c3aed" stroke="#fff" stroke-width="2.5" data-v="C" style="cursor:grab"/>';
s+='<text x="'+p.x+'" y="'+(p.y-19)+'" text-anchor="middle" font-size="9" fill="#7c3aed" font-family="Inter,sans-serif" opacity=".7">тащи</text>';
} else {
s+='<circle cx="'+p.x+'" cy="'+p.y+'" r="5" fill="#7c3aed" stroke="#fff" stroke-width="2"/>';
}
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">'+lbl+'</text>';
});
s+='</svg>';
document.getElementById('p7-rect-svg').innerHTML=s;
document.getElementById('p7-rect-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Сторона AB = CD</div><b>${ab.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Сторона BC = DA</div><b>${bc.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Периметр</div><b>${perimeter.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Площадь</div><b>${area.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:#d1fae5;border-radius:8px;border:1.5px solid #10b981;font-size:.88rem;grid-column:span 2;text-align:center"><div style="color:#047857;font-size:.72rem;font-weight:800;text-transform:uppercase;margin-bottom:3px">Диагонали AC = BD</div><b style="color:#047857">AC = ${ac.toFixed(2)} = BD = ${bd.toFixed(2)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem;grid-column:span 2;text-align:center"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Все 4 угла</div><b>90°</b></div>`;
}
let p7Active=false,p7OffX=0,p7OffY=0;
document.getElementById('p7-rect-svg').addEventListener('pointerdown',function(ev){
const el=ev.target.closest('[data-v="C"]'); if(!el) return;
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
p7Active=true;
const sp=clientToSvg(ev.clientX,ev.clientY);
p7OffX=sp.x-Cx; p7OffY=sp.y-Cy;
window.addEventListener('pointermove',p7Move,{passive:false});
window.addEventListener('pointerup',p7Up);
window.addEventListener('pointercancel',p7Up);
});
function p7Move(e){
if(!p7Active) return; e.preventDefault();
const sp=clientToSvg(e.clientX,e.clientY);
Cx=clamp(sp.x-p7OffX,Ax+40,W-10);
Cy=clamp(sp.y-p7OffY,10,Ay-40);
redraw();
}
function p7Up(){
if(!p7Active) return; p7Active=false;
window.removeEventListener('pointermove',p7Move);
window.removeEventListener('pointerup',p7Up);
window.removeEventListener('pointercancel',p7Up);
}
redraw();
})();
/* == Доказательство == */
(function(){
const A={x:40,y:155},B={x:230,y:155},C={x:230,y:55},D={x:40,y:55};
const cx=(A.x+B.x+C.x+D.x)/4,cy=(A.y+B.y+C.y+D.y)/4;
const steps=[
{text:'<b>Дано:</b> $ABCD$ — прямоугольник. $\\angle A=\\angle B=90°$. Нужно доказать: $AC=BD$.', h:''},
{text:'<b>Шаг 1.</b> Рассмотрим $\\triangle ABC$ и $\\triangle BAD$. $AB=BA$ — общая сторона.', h:'t1'},
{text:'<b>Шаг 2.</b> $\\angle ABC=\\angle BAD=90°$ — прямые углы прямоугольника. $BC=AD$ — как в параллелограмме.', h:'t1'},
{text:'<b>Шаг 3.</b> По признаку «угол–сторона–угол» (или двум катетам): $\\triangle ABC=\\triangle BAD$.', h:'both7'},
{text:'<b>Вывод.</b> $AC=BD$ — диагонали прямоугольника равны. <b>ч.т.д.</b>', h:'both7'},
];
let step=0;
function draw(h){
let s='<svg viewBox="0 0 280 195" style="width:100%;max-width:300px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
const t1=(h==='t1'||h==='both7')?'rgba(124,58,237,.18)':'rgba(124,58,237,.06)';
const t2=h==='both7'?'rgba(16,185,129,.18)':'rgba(124,58,237,.06)';
s+='<rect x="'+A.x+'" y="'+D.y+'" width="'+(B.x-A.x)+'" height="'+(A.y-D.y)+'" fill="rgba(124,58,237,.06)" stroke="#7c3aed" stroke-width="2"/>';
if(h){ s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="'+t1+'" stroke="#7c3aed" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+D.x+','+D.y+'" fill="'+t2+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>'; }
if(h==='both7'){ s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#7c3aed" stroke-width="1.5" stroke-dasharray="4 3"/>'; s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 3"/>'; }
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#7c3aed" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p7-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p7-proof-step').innerHTML=st.text; renderMath(document.getElementById('p7-proof-step')); draw(st.h); document.getElementById('p7-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p7-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p7-proof');bumpProgress('p7',12);document.getElementById('p7-proof-next').textContent='Изучено! +5 XP';document.getElementById('p7-proof-next').disabled=true;} });
document.getElementById('p7-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p7-proof-next').disabled=false;});
show();
})();
/* == Калькулятор диагонали == */
(function(){
function calc(){
const a=+document.getElementById('p7-ca').value, b=+document.getElementById('p7-cb').value;
if(a<=0||b<=0||isNaN(a)||isNaN(b)){ document.getElementById('p7-calc-out').innerHTML='<span style="color:var(--bad)">Введи положительные числа.</span>'; return; }
const d=Math.sqrt(a*a+b*b);
const dStr=Number.isInteger(d)?String(d):d.toFixed(4).replace(/0+$/,'');
document.getElementById('p7-calc-out').innerHTML=`$d = \\sqrt{a^2+b^2} = \\sqrt{${a}^2+${b}^2} = \\sqrt{${a*a}+${b*b}} = \\sqrt{${a*a+b*b}}$<br><b style="font-size:1.1rem;color:var(--sec-acc-d,var(--pri2))">$d = ${dStr}$</b>`;
renderMath(document.getElementById('p7-calc-out'));
addXp(2,'p7-calc');
}
document.getElementById('p7-calc-go').addEventListener('click',calc);
document.getElementById('p7-ca').addEventListener('keydown',e=>{if(e.key==='Enter')calc();});
document.getElementById('p7-cb').addEventListener('keydown',e=>{if(e.key==='Enter')calc();});
calc();
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Стороны прямоугольника $a=6$, $b=8$. Найди диагональ.', ans:10, hint:'d=√(36+64)=√100=10'},
{q:'Диагональ прямоугольника $d=13$, сторона $a=5$. Найди $b$.', ans:12, hint:'b=√(169-25)=√144=12'},
{q:'Периметр прямоугольника $P=20$, сторона $a=3$. Найди площадь $S$.', ans:21, hint:'b=P/2-a=10-3=7, S=3·7=21'},
{q:'В прямоугольнике $\\angle A=90°$. Найди $\\angle B$.', ans:90, hint:'Все углы прямоугольника = 90°'},
{q:'Стороны прямоугольника $a=9$, $b=12$. Найди диагональ.', ans:15, hint:'d=√(81+144)=√225=15'},
];
let idx=0,score=0;
function show(){ document.getElementById('p7-tr-i').textContent=idx+1; document.getElementById('p7-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p7-tr-task')); document.getElementById('p7-tr-ans').value=''; document.getElementById('p7-tr-fb').style.display='none'; }
document.getElementById('p7-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p7-tr-score').textContent=0;show();});
document.getElementById('p7-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p7-tr-ans').value; const fb=document.getElementById('p7-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p7-tr-score').textContent=score;addXp(3,'p7-train');bumpProgress('p7',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p7-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p7-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p7-tr-go').click();});
show();
})();
/* == DnD == */
(function(){
const items=[
{id:'sq',html:'Квадрат', ans:'yes'},
{id:'rect',html:'Параллелограмм с углом 90°', ans:'yes'},
{id:'rhm',html:'Ромб с углом 90°', ans:'yes'},
{id:'pgr',html:'Произвольный параллелограмм (угол ≠ 90°)', ans:'no'},
{id:'trap',html:'Трапеция', ans:'no'},
{id:'rhm2',html:'Ромб с углом 60°', ans:'no'},
];
const sorter=setupSorter({poolId:'p7-dnd-pool',scopeSelector:'#p7-dnd-wrap',items,cats:['yes','no']});
document.getElementById('p7-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p7-dnd-fb').style.display='none';});
document.getElementById('p7-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p7-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p7-dnd');bumpProgress('p7',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Квадрат — тоже прямоугольник!');
});
})();
/* == Босс §7 == */
(function(){
const tasks=[
{q:'Стороны прямоугольника $a=5$, $b=12$. Найди диагональ.', ans:13, hint:'d=√(25+144)=√169=13'},
{q:'В прямоугольнике диагональ $d=10$, сторона $a=6$. Найди вторую сторону $b$.', ans:8, hint:'b=√(100-36)=√64=8'},
{q:'Периметр прямоугольника равен $34$, одна сторона $= 7$. Найди площадь.', ans:70, hint:'b=17-7=10, S=7·10=70'},
{q:'Все углы прямоугольника. Чему равна сумма всех углов прямоугольника?', ans:360, hint:'4·90°=360°'},
];
const bossBox=document.getElementById('p7-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="p7-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p7-boss-a${i}').value;const fb=document.getElementById('p7-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p7BossSolved.has(${i})){p7BossSolved.add(${i});addXp(5,'p7-boss${i}');bumpProgress('p7',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p7-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p7BossSolved=new Set();
})();
renderMath(box);
}
function buildP8(){
const box = document.getElementById('p8-body');
let html = '';
html += makeCard('theory','Признак прямоугольника','8.1',`
<p><b>Теорема.</b> Если у параллелограмма диагонали равны, то он является прямоугольником.</p>
<p><b>Доказательство.</b> Пусть в параллелограмме $ABCD$ дано $AC=BD$. Рассмотрим $\\triangle ABD$ и $\\triangle BAC$:</p>
<ul style="margin-left:18px;line-height:1.9">
<li>$AB=BA$ — общая сторона</li>
<li>$AD=BC$ — противоположные стороны параллелограмма</li>
<li>$BD=AC$ — по условию</li>
</ul>
<p>По признаку «три стороны»: $\\triangle ABD=\\triangle BAC$. Следовательно, $\\angle DAB=\\angle CBA$.</p>
<p>Но $\\angle DAB+\\angle CBA=180°$ (смежные в параллелограмме). Значит $\\angle DAB=\\angle CBA=90°$. Параллелограмм — прямоугольник. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;gap:16px;margin-top:12px;flex-wrap:wrap">
<div style="text-align:center"><svg viewBox="0 0 120 100" style="width:115px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="18,78 18,28 102,28 102,78" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2"/>
<line x1="18" y1="78" x2="102" y2="28" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="102" y1="78" x2="18" y2="28" stroke="#d97706" stroke-width="1.5" stroke-dasharray="4 2"/>
<text x="42" y="60" font-size="9" fill="#047857" font-family="JetBrains Mono,monospace">AC</text>
<text x="72" y="60" font-size="9" fill="#b45309" font-family="JetBrains Mono,monospace">BD</text>
<text x="60" y="96" text-anchor="middle" font-size="8" fill="#6d28d9" font-weight="700" font-family="Inter,sans-serif">AC = BD</text>
</svg>
<div style="font-size:.78rem;color:var(--muted);margin-top:2px">Равные диагонали</div></div>
<div style="text-align:center"><svg viewBox="0 0 120 100" style="width:115px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<rect x="18" y="22" width="84" height="56" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2"/>
<path d="M18,78 L18,70 L26,70" fill="none" stroke="#7c3aed" stroke-width="1.5"/>
<path d="M102,78 L94,78 L94,70" fill="none" stroke="#7c3aed" stroke-width="1.5"/>
<text x="60" y="96" text-anchor="middle" font-size="8" fill="#6d28d9" font-weight="700" font-family="Inter,sans-serif">→ Прямоугольник</text>
</svg>
<div style="font-size:.78rem;color:var(--muted);margin-top:2px">∠ = 90°</div></div>
</div>`);
/* --- INTERACTIVE 1: SVG-демонстрация признака --- */
html += `<div class="wg" id="p8-demo-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Признак прямоугольника — живая демонстрация</div></div>
<div class="wg-help">Тащи вершину <b>D</b>. Когда диагонали сравняются — оба индикатора загорятся зелёным.</div>
<div id="p8-demo-svg" style="display:flex;justify-content:center"></div>
<div id="p8-demo-indicators" style="display:flex;gap:12px;flex-wrap:wrap;margin-top:10px;justify-content:center"></div>
</div>`;
/* --- INTERACTIVE 2: Пошаговое доказательство --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство признака прямоугольника</div></div>
<div id="p8-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p8-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p8-proof-next">Дальше</button>
<button class="btn" id="p8-proof-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 3: Mini-quiz --- */
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="p8-quiz-list"></div>
</div>`;
/* --- INTERACTIVE 4: Тренажёр --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: прямоугольник или нет?</div></div>
<div class="score-display"><span>Задача <b id="p8-tr-i">1</b> / 4</span><span>Очки: <b id="p8-tr-score">0</b></span></div>
<div id="p8-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p8-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p8-tr-go">Проверить</button>
<button class="btn" id="p8-tr-start">Начать</button>
</div>
<div class="feedback" id="p8-tr-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 5: Босс §8 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §8</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p8-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p8-read-btn" onclick="addXp(10,'p8-read');bumpProgress('p8',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>
Я прочитал §8 (+10 XP)
</button>
</div>`;
html += secNav('p7','p9');
box.innerHTML = html;
/* == SVG-демонстрация признака == */
(function(){
const W=380, H=280, SVG_ID='p8-demo-svg-el';
let A={x:55,y:215}, B={x:225,y:215}, D={x:110,y:80};
function getC(){ return {x:D.x+(B.x-A.x),y:D.y+(B.y-A.y)}; }
function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); }
function clientToSvg(clientX,clientY){
const svgEl=document.getElementById(SVG_ID); if(!svgEl) return {x:0,y:0};
const r=svgEl.getBoundingClientRect(),vb=svgEl.viewBox.baseVal;
return {x:(clientX-r.left)/r.width*vb.width+vb.x,y:(clientY-r.top)/r.height*vb.height+vb.y};
}
function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }
function redraw(){
const C=getC(); const pts=[A,B,C,D]; const labels=['A','B','C','D'];
const pcx=(A.x+B.x+C.x+D.x)/4,pcy=(A.y+B.y+C.y+D.y)/4;
const ac=dist(A,C),bd=dist(B,D);
const eq=Math.abs(ac-bd)<4;
const col=eq?'#10b981':'#2563eb';
let s='<svg id="'+SVG_ID+'" viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">';
s+='<polygon points="'+pts.map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(37,99,235,.08)" stroke="'+col+'" stroke-width="2.5" stroke-linejoin="round"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#2563eb" stroke-width="1.5" stroke-dasharray="5 3"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="5 3"/>';
const diagMx=(A.x+C.x)/2,diagMy=(A.y+C.y)/2;
s+='<text x="'+(diagMx-22)+'" y="'+(diagMy-7)+'" font-size="10" fill="#1d4ed8" font-weight="700" font-family="JetBrains Mono,monospace">AC='+ac.toFixed(1)+'</text>';
s+='<text x="'+((B.x+D.x)/2+5)+'" y="'+((B.y+D.y)/2-7)+'" font-size="10" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">BD='+bd.toFixed(1)+'</text>';
pts.forEach((v,i)=>{
const m=(i===3);
const lx=v.x+(v.x-pcx)*0.28,ly=v.y+(v.y-pcy)*0.28;
if(m){
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="16" fill="'+col+'" opacity=".15" data-v="D" style="cursor:grab"/>';
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="8" fill="'+col+'" stroke="#fff" stroke-width="2.5" data-v="D" style="cursor:grab"/>';
} else {
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="'+col+'" stroke="#fff" stroke-width="2"/>';
}
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#1d4ed8" font-family="Unbounded,sans-serif">'+labels[i]+'</text>';
});
s+='</svg>';
document.getElementById('p8-demo-svg').innerHTML=s;
const okCol=eq?'#10b981':'#94a3b8';
document.getElementById('p8-demo-indicators').innerHTML=`
<div style="padding:10px 16px;border-radius:10px;border:2px solid ${eq?'#10b981':'#94a3b8'};background:${eq?'rgba(16,185,129,.12)':'var(--card)'};font-size:.88rem;font-weight:700;color:${okCol}">Диагонали равны: ${eq?'ДА':'НЕТ'} (AC=${ac.toFixed(1)}, BD=${bd.toFixed(1)})</div>
<div style="padding:10px 16px;border-radius:10px;border:2px solid ${eq?'#10b981':'#94a3b8'};background:${eq?'rgba(16,185,129,.12)':'var(--card)'};font-size:.88rem;font-weight:700;color:${okCol}">Прямоугольник: ${eq?'ДА':'НЕТ'}</div>`;
}
let p8Active=false,p8OffX=0,p8OffY=0;
document.getElementById('p8-demo-svg').addEventListener('pointerdown',function(ev){
const el=ev.target.closest('[data-v="D"]'); if(!el) return;
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
p8Active=true;
const sp=clientToSvg(ev.clientX,ev.clientY);
p8OffX=sp.x-D.x; p8OffY=sp.y-D.y;
window.addEventListener('pointermove',p8Move,{passive:false});
window.addEventListener('pointerup',p8Up);
window.addEventListener('pointercancel',p8Up);
});
function p8Move(e){
if(!p8Active) return; e.preventDefault();
const sp=clientToSvg(e.clientX,e.clientY);
D={x:clamp(sp.x-p8OffX,12,W-12),y:clamp(sp.y-p8OffY,12,H-12)};
redraw();
}
function p8Up(){
if(!p8Active) return; p8Active=false;
window.removeEventListener('pointermove',p8Move);
window.removeEventListener('pointerup',p8Up);
window.removeEventListener('pointercancel',p8Up);
}
redraw();
})();
/* == Доказательство == */
(function(){
const A={x:40,y:155},B={x:230,y:155},C={x:265,y:55},D={x:75,y:55};
const cx=(A.x+B.x+C.x+D.x)/4,cy=(A.y+B.y+C.y+D.y)/4;
const steps=[
{text:'<b>Дано:</b> $ABCD$ — параллелограмм, $AC=BD$. Доказать: $\\angle DAB=90°$.', h:''},
{text:'<b>Шаг 1.</b> Рассмотрим $\\triangle ABD$ и $\\triangle BAC$: $AB=BA$ (общая), $AD=BC$ (стороны ||грамма), $BD=AC$ (по условию).', h:'t1'},
{text:'<b>Шаг 2.</b> По признаку «три стороны»: $\\triangle ABD=\\triangle BAC$.', h:'t2'},
{text:'<b>Шаг 3.</b> Следовательно $\\angle DAB=\\angle CBA$. Но они смежные в параллелограмме: $\\angle DAB+\\angle CBA=180°$.', h:'t2'},
{text:'<b>Вывод.</b> $2\\angle DAB=180°$, $\\angle DAB=90°$. $ABCD$ — прямоугольник. <b>ч.т.д.</b>', h:'t2'},
];
let step=0;
function draw(h){
let s='<svg viewBox="0 0 310 195" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
s+='<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(37,99,235,.06)" stroke="#2563eb" stroke-width="2"/>';
if(h==='t1'){ s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+D.x+','+D.y+'" fill="rgba(37,99,235,.18)" stroke="#2563eb" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#2563eb" stroke-width="1.5" stroke-dasharray="4 3"/>'; }
if(h==='t2'){ s+='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+D.x+','+D.y+'" fill="rgba(37,99,235,.18)" stroke="#2563eb" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<polygon points="'+B.x+','+B.y+' '+A.x+','+A.y+' '+C.x+','+C.y+'" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>'; s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 3"/>'; s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#2563eb" stroke-width="1.5" stroke-dasharray="4 3"/>'; }
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#2563eb" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#1d4ed8" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p8-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p8-proof-step').innerHTML=st.text; renderMath(document.getElementById('p8-proof-step')); draw(st.h); document.getElementById('p8-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p8-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p8-proof');bumpProgress('p8',12);document.getElementById('p8-proof-next').textContent='Изучено! +5 XP';document.getElementById('p8-proof-next').disabled=true;} });
document.getElementById('p8-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p8-proof-next').disabled=false;});
show();
})();
/* == Mini-quiz == */
(function(){
const items=[
{text:'Параллелограмм с равными диагоналями — прямоугольник.', ans:true},
{text:'У любого параллелограмма диагонали равны.', ans:false},
{text:'Прямоугольник — частный случай параллелограмма.', ans:true},
{text:'Ромб с равными диагоналями является квадратом.', ans:true},
];
const solved=new Set();
document.getElementById('p8-quiz-list').innerHTML=items.map((it,i)=>`
<div style="padding:12px;background:var(--card);border:1px solid var(--border);border-radius:10px;margin-bottom:8px" id="p8-qi-${i}">
<p style="margin-bottom:8px;font-size:.95rem">${it.text}</p>
<div style="display:flex;gap:8px">
<button class="btn primary small" onclick="p8QuizCheck(${i},true)">Верно</button>
<button class="btn small" onclick="p8QuizCheck(${i},false)">Неверно</button>
</div>
<div class="feedback" id="p8-qi-fb-${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p8QuizCheck=function(i,v){
const it=items[i]; const fb=document.getElementById('p8-qi-fb-'+i);
if(v===it.ans){ feedback(fb,true,'Верно! +3 XP'); if(!solved.has(i)){solved.add(i);addXp(3,'p8-quiz'+i);bumpProgress('p8',6);} }
else feedback(fb,false,'Неверно. '+( it.ans?'Утверждение истинно.':'Утверждение ложно.'));
};
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'В параллелограмме $AC=14$, $BD=14$. Прямоугольник? Введи 1 (да) или 0 (нет).', ans:1, hint:'Признак: равные диагонали → прямоугольник'},
{q:'В параллелограмме $AC=10$, $BD=12$. Прямоугольник? Введи 1 (да) или 0 (нет).', ans:0, hint:'Диагонали не равны → не прямоугольник'},
{q:'В прямоугольнике $a=8$, $b=15$. Найди диагональ.', ans:17, hint:'d=√(64+225)=√289=17'},
{q:'В прямоугольнике диагональ $d=25$, сторона $a=7$. Найди $b$.', ans:24, hint:'b=√(625-49)=√576=24'},
];
let idx=0,score=0;
function show(){ document.getElementById('p8-tr-i').textContent=idx+1; document.getElementById('p8-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p8-tr-task')); document.getElementById('p8-tr-ans').value=''; document.getElementById('p8-tr-fb').style.display='none'; }
document.getElementById('p8-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p8-tr-score').textContent=0;show();});
document.getElementById('p8-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p8-tr-ans').value; const fb=document.getElementById('p8-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p8-tr-score').textContent=score;addXp(3,'p8-train');bumpProgress('p8',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p8-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p8-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p8-tr-go').click();});
show();
})();
/* == Босс §8 == */
(function(){
const tasks=[
{q:'У параллелограмма диагонали $AC=BD=20$. Прямоугольник? (1=да, 0=нет)', ans:1, hint:'Признак: равные диагонали'},
{q:'В прямоугольнике $a=3$, $b=4$. Найди $d$.', ans:5, hint:'d=√(9+16)=5'},
{q:'В прямоугольнике $d=26$, $a=10$. Найди $b$.', ans:24, hint:'b=√(676-100)=√576=24'},
{q:'Периметр прямоугольника $P=26$, $a=5$. Найди $d$.', ans:13, hint:'b=13-5=8, d=√(25+64)=√169=13'},
];
const bossBox=document.getElementById('p8-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="p8-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p8-boss-a${i}').value;const fb=document.getElementById('p8-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p8BossSolved.has(${i})){p8BossSolved.add(${i});addXp(5,'p8-boss${i}');bumpProgress('p8',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p8-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p8BossSolved=new Set();
})();
renderMath(box);
}
function buildP9(){
const box = document.getElementById('p9-body');
let html = '';
html += makeCard('theory','Ромб — определение и свойства','9.1',`
<p><b>Ромб</b> — параллелограмм, у которого все стороны равны: $AB=BC=CD=DA$.</p>
<p><b>Свойство диагоналей ромба:</b></p>
<ul style="margin-left:18px;line-height:1.9">
<li>Диагонали ромба взаимно перпендикулярны: $AC \\perp BD$.</li>
<li>Каждая диагональ является биссектрисой соответствующих углов.</li>
<li>Диагонали точкой пересечения делятся пополам (как в любом параллелограмме).</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 165" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Rhombus with perpendicular diagonals highlighted -->
<polygon points="140,18 236,88 140,158 44,88" fill="rgba(234,88,12,.10)" stroke="#ea580c" stroke-width="2.5"/>
<!-- diagonals - perpendicular -->
<line x1="140" y1="18" x2="140" y2="158" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="44" y1="88" x2="236" y2="88" stroke="#10b981" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- right angle mark at center -->
<rect x="140" y="88" width="10" height="10" fill="none" stroke="#64748b" stroke-width="1.5"/>
<!-- equal side ticks -->
<line x1="181" y1="48" x2="189" y2="56" stroke="#ea580c" stroke-width="2"/>
<line x1="191" y1="122" x2="183" y2="130" stroke="#ea580c" stroke-width="2"/>
<line x1="89" y1="48" x2="97" y2="56" stroke="#ea580c" stroke-width="2"/>
<line x1="97" y1="122" x2="89" y2="130" stroke="#ea580c" stroke-width="2"/>
<!-- center O -->
<circle cx="140" cy="88" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<!-- vertices -->
<circle cx="140" cy="18" r="4" fill="#ea580c" stroke="#fff" stroke-width="1.5"/>
<circle cx="236" cy="88" r="4" fill="#ea580c" stroke="#fff" stroke-width="1.5"/>
<circle cx="140" cy="158" r="4" fill="#ea580c" stroke="#fff" stroke-width="1.5"/>
<circle cx="44" cy="88" r="4" fill="#ea580c" stroke="#fff" stroke-width="1.5"/>
<text x="140" y="8" text-anchor="middle" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">A</text>
<text x="250" y="92" text-anchor="middle" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">B</text>
<text x="140" y="170" text-anchor="middle" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">C</text>
<text x="30" y="92" text-anchor="middle" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">D</text>
<text x="156" y="84" font-size="8" fill="#64748b" font-family="JetBrains Mono,monospace">90°</text>
<text x="140" y="158" text-anchor="middle" font-size="9" fill="#c2410c" font-weight="700" font-family="JetBrains Mono,monospace">AC ⊥ BD</text>
</svg></div>`);
html += makeCard('rule','Доказательство: диагонали ромба перпендикулярны','9.2',`
<p>Рассмотрим $\\triangle AOB$ и $\\triangle COB$, где $O$ — точка пересечения диагоналей.</p>
<p>$AO=OC$ (диагональ делится пополам), $OB=OB$ (общая), $AB=CB$ (стороны ромба).</p>
<p>По признаку «три стороны»: $\\triangle AOB=\\triangle COB$. Следовательно, $\\angle AOB=\\angle COB$.</p>
<p>Но $\\angle AOB+\\angle COB=180°$ (смежные). Значит $\\angle AOB=90°$, т.е. $AC\\perp BD$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Rhombus split: triangles AOB and COB colored -->
<polygon points="140,18 236,88 140,158 44,88" fill="none" stroke="#ea580c" stroke-width="2"/>
<line x1="140" y1="18" x2="140" y2="158" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="44" y1="88" x2="236" y2="88" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- highlight AOB (right) and COB (left) -->
<polygon points="140,18 236,88 140,88" fill="rgba(8,145,178,.18)" stroke="none"/>
<polygon points="140,88 236,88 140,158" fill="rgba(16,185,129,.18)" stroke="none"/>
<circle cx="140" cy="88" r="5" fill="#8b5cf6" stroke="#fff" stroke-width="2"/>
<rect x="140" y="88" width="10" height="10" fill="none" stroke="#64748b" stroke-width="1.5"/>
<text x="155" y="84" font-size="8" fill="#64748b" font-family="JetBrains Mono,monospace">O</text>
<!-- triangle labels -->
<text x="200" y="72" font-size="10" fill="#0e7490" font-weight="700" font-family="Inter,sans-serif">△AOB</text>
<text x="200" y="116" font-size="10" fill="#047857" font-weight="700" font-family="Inter,sans-serif">△COB</text>
<circle cx="140" cy="18" r="3.5" fill="#ea580c"/><circle cx="236" cy="88" r="3.5" fill="#ea580c"/>
<circle cx="140" cy="158" r="3.5" fill="#ea580c"/><circle cx="44" cy="88" r="3.5" fill="#ea580c"/>
<text x="140" y="8" text-anchor="middle" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">A</text>
<text x="250" y="92" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">B</text>
<text x="140" y="170" text-anchor="middle" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">C</text>
<text x="28" y="92" font-size="11" font-weight="700" fill="#c2410c" font-family="Unbounded,sans-serif">D</text>
<text x="140" y="144" text-anchor="middle" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">△AOB = △COB (с-с-с)</text>
</svg></div>`);
html += makeCard('rule','Признаки ромба','9.3',`
<p><b>Признак 1.</b> Если у параллелограмма диагонали взаимно перпендикулярны, то он является ромбом.</p>
<p><b>Признак 2.</b> Если у параллелограмма диагональ является биссектрисой угла, то он является ромбом.</p>
<p><b>Площадь ромба:</b> $S = \\dfrac{d_1 \\cdot d_2}{2}$, где $d_1$, $d_2$ — диагонали.</p>
<p>Также: $S = a^2 \\sin\\alpha$, где $a$ — сторона, $\\alpha$ — угол ромба.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Rhombus with d1 and d2 labeled, S formula shown -->
<polygon points="140,18 230,80 140,142 50,80" fill="rgba(234,88,12,.10)" stroke="#ea580c" stroke-width="2.5"/>
<line x1="140" y1="18" x2="140" y2="142" stroke="#8b5cf6" stroke-width="2" stroke-dasharray="5 3"/>
<line x1="50" y1="80" x2="230" y2="80" stroke="#10b981" stroke-width="2" stroke-dasharray="5 3"/>
<!-- right angle at center -->
<rect x="140" y="80" width="9" height="9" fill="none" stroke="#64748b" stroke-width="1.5"/>
<!-- d1 and d2 labels -->
<text x="148" y="55" font-size="11" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">d₁</text>
<text x="148" y="108" font-size="11" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">d₁</text>
<text x="84" y="76" font-size="11" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">d₂</text>
<text x="170" y="76" font-size="11" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">d₂</text>
<!-- center -->
<circle cx="140" cy="80" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="140" cy="18" r="3.5" fill="#ea580c"/><circle cx="230" cy="80" r="3.5" fill="#ea580c"/>
<circle cx="140" cy="142" r="3.5" fill="#ea580c"/><circle cx="50" cy="80" r="3.5" fill="#ea580c"/>
<text x="140" y="139" text-anchor="middle" font-size="9" fill="#c2410c" font-weight="700" font-family="JetBrains Mono,monospace">S = d₁·d₂/2</text>
</svg></div>`);
/* --- INTERACTIVE 1: SVG-ромб с draggable вершиной --- */
html += `<div class="wg" id="p9-rhombus-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живой ромб — все стороны равны, диагонали ⊥</div></div>
<div class="wg-help">Тащи вершину <b>B</b> или <b>D</b>. Все стороны остаются равными, диагонали — всегда перпендикулярны.</div>
<div id="p9-rhombus-svg" style="display:flex;justify-content:center"></div>
<div id="p9-rhombus-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 2: Пошаговое доказательство --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство: диагонали ромба перпендикулярны</div></div>
<div id="p9-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p9-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p9-proof-next">Дальше</button>
<button class="btn" id="p9-proof-restart">Сначала</button>
</div>
</div>`;
/* --- INTERACTIVE 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">Введи диагонали — получи площадь и сторону ромба.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.92rem">$d_1$ = <input type="number" id="p9-cd1" class="tinp" value="10" style="width:70px" min="1"></label>
<label style="font-size:.92rem">$d_2$ = <input type="number" id="p9-cd2" class="tinp" value="24" style="width:70px" min="1"></label>
<button class="btn primary" id="p9-calc-go">Вычислить</button>
</div>
<div id="p9-calc-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.9"></div>
</div>`;
/* --- INTERACTIVE 4: DnD — свойства ромба, прямоугольника, параллелограмма --- */
html += `<div class="wg" id="p9-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Чьё это свойство?</div></div>
<div class="wg-help">Разложи свойства: только ромба, только прямоугольника, или любого параллелограмма.</div>
${DND_HINT_HTML}
<div id="p9-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="any">Любой параллелограмм</h5><div class="drop-items" data-cat="any"></div></div>
<div class="drop-box"><h5 data-cat="rect">Только прямоугольник</h5><div class="drop-items" data-cat="rect"></div></div>
<div class="drop-box"><h5 data-cat="rhm">Только ромб</h5><div class="drop-items" data-cat="rhm"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p9-dnd-check">Проверить</button><button class="btn" id="p9-dnd-reset">Сначала</button></div>
<div class="feedback" id="p9-dnd-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 5: Тренажёр --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Тренажёр задач на ромб</div></div>
<div class="score-display"><span>Задача <b id="p9-tr-i">1</b> / 5</span><span>Очки: <b id="p9-tr-score">0</b></span></div>
<div id="p9-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p9-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p9-tr-go">Проверить</button>
<button class="btn" id="p9-tr-start">Начать</button>
</div>
<div class="feedback" id="p9-tr-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 6: Босс §9 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §9</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p9-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p9-read-btn" onclick="addXp(10,'p9-read');bumpProgress('p9',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>
Я прочитал §9 (+10 XP)
</button>
</div>`;
html += secNav('p8','p10');
box.innerHTML = html;
/* == SVG-ромб == */
(function(){
const W=360, H=300, RCX=180, RCY=150, SVG_ID='p9-rhombus-svg-el';
let half1=110, half2=75;
function getVerts(h1,h2){ return {A:{x:RCX-h1,y:RCY},B:{x:RCX,y:RCY-h2},C:{x:RCX+h1,y:RCY},D:{x:RCX,y:RCY+h2}}; }
function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); }
function clientToSvg(clientX,clientY){
const svgEl=document.getElementById(SVG_ID); if(!svgEl) return {x:0,y:0};
const r=svgEl.getBoundingClientRect(),vb=svgEl.viewBox.baseVal;
return {x:(clientX-r.left)/r.width*vb.width+vb.x,y:(clientY-r.top)/r.height*vb.height+vb.y};
}
function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }
function redraw(){
const {A,B,C,D}=getVerts(half1,half2);
const side=dist(A,B);
const sq=8;
let s='<svg id="'+SVG_ID+'" viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:380px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">';
s+='<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(8,145,178,.10)" stroke="#0891b2" stroke-width="2.5" stroke-linejoin="round"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3" opacity=".7"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3" opacity=".7"/>';
s+='<polyline points="'+(RCX+sq)+','+RCY+' '+(RCX+sq)+','+(RCY-sq)+' '+RCX+','+(RCY-sq)+'" fill="none" stroke="#0891b2" stroke-width="1.5"/>';
s+='<circle cx="'+RCX+'" cy="'+RCY+'" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>';
// side-length tick marks
[[A,B],[B,C],[C,D],[D,A]].forEach(([p1,p2])=>{
const mx=(p1.x+p2.x)/2,my=(p1.y+p2.y)/2;
const dx=p2.x-p1.x,dy=p2.y-p1.y,ln=Math.hypot(dx,dy)||1;
const px=-dy/ln*5,py=dx/ln*5;
s+='<line x1="'+(mx+px)+'" y1="'+(my+py)+'" x2="'+(mx-px)+'" y2="'+(my-py)+'" stroke="#0891b2" stroke-width="2"/>';
});
const labels=['A','B','C','D']; const pts=[A,B,C,D];
pts.forEach((v,i)=>{
const m=(i===0||i===1||i===2||i===3);
const lx=v.x+(v.x-RCX)*0.22,ly=v.y+(v.y-RCY)*0.22;
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="16" fill="#0891b2" opacity=".10" data-v="'+labels[i]+'" style="cursor:grab"/>';
s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="8" fill="#0891b2" stroke="#fff" stroke-width="2.5" data-v="'+labels[i]+'" style="cursor:grab"/>';
s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">'+labels[i]+'</text>';
});
s+='</svg>';
document.getElementById('p9-rhombus-svg').innerHTML=s;
const S=half1*half2*2;
document.getElementById('p9-rhombus-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Сторона AB=BC=CD=DA</div><b>${side.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Диагональ AC</div><b>${(half1*2).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Диагональ BD</div><b>${(half2*2).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Угол между диаг.</div><b>90°</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Площадь S = d1·d2/2</div><b>${S.toFixed(1)}</b></div>`;
}
let p9Active=false,p9Vname='',p9OffX=0,p9OffY=0;
document.getElementById('p9-rhombus-svg').addEventListener('pointerdown',function(ev){
const el=ev.target.closest('[data-v]'); if(!el) return;
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
p9Active=true; p9Vname=el.dataset.v;
const {A,B,C,D}=getVerts(half1,half2);
const cur={A,B,C,D}[p9Vname];
const sp=clientToSvg(ev.clientX,ev.clientY);
p9OffX=sp.x-cur.x; p9OffY=sp.y-cur.y;
window.addEventListener('pointermove',p9Move,{passive:false});
window.addEventListener('pointerup',p9Up);
window.addEventListener('pointercancel',p9Up);
});
function p9Move(e){
if(!p9Active) return; e.preventDefault();
const sp=clientToSvg(e.clientX,e.clientY);
const nx=sp.x-p9OffX,ny=sp.y-p9OffY;
if(p9Vname==='B') half2=clamp(RCY-ny,20,RCY-10);
else if(p9Vname==='D') half2=clamp(ny-RCY,20,RCY-10);
else if(p9Vname==='A') half1=clamp(RCX-nx,20,RCX-10);
else if(p9Vname==='C') half1=clamp(nx-RCX,20,W-RCX-10);
redraw();
}
function p9Up(){
if(!p9Active) return; p9Active=false;
window.removeEventListener('pointermove',p9Move);
window.removeEventListener('pointerup',p9Up);
window.removeEventListener('pointercancel',p9Up);
}
redraw();
})();
/* == Доказательство == */
(function(){
const cx=150,cy=110;
const A={x:cx-100,y:cy},B={x:cx,y:cy-70},C={x:cx+100,y:cy},D={x:cx,y:cy+70};
const O={x:cx,y:cy};
const steps=[
{text:'<b>Дано:</b> $ABCD$ — ромб (все стороны равны). $O$ — точка пересечения диагоналей. Доказать: $AC\\perp BD$.', h:''},
{text:'<b>Шаг 1.</b> Рассмотрим $\\triangle AOB$ и $\\triangle COB$. $AO=OC$ (диагональ делится пополам), $OB=OB$ (общая), $AB=CB$ (стороны ромба).', h:'tri'},
{text:'<b>Шаг 2.</b> По признаку «три стороны»: $\\triangle AOB=\\triangle COB$.', h:'tri'},
{text:'<b>Шаг 3.</b> $\\angle AOB=\\angle COB$. Но $\\angle AOB+\\angle COB=180°$ (смежные). Значит $\\angle AOB=90°$.', h:'right'},
{text:'<b>Вывод.</b> $AC\\perp BD$ — диагонали ромба взаимно перпендикулярны. <b>ч.т.д.</b>', h:'right'},
];
let step=0;
function draw(h){
let s='<svg viewBox="0 0 300 210" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:12px">';
s+='<polygon points="'+[A,B,C,D].map(v=>v.x+','+v.y).join(' ')+'" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2"/>';
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 3"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 3"/>';
if(h==='tri'||h==='right'){ s+='<polygon points="'+A.x+','+A.y+' '+O.x+','+O.y+' '+B.x+','+B.y+'" fill="rgba(8,145,178,.2)" stroke="#0891b2" stroke-width="1.5"/>'; s+='<polygon points="'+C.x+','+C.y+' '+O.x+','+O.y+' '+B.x+','+B.y+'" fill="rgba(16,185,129,.2)" stroke="#10b981" stroke-width="1.5"/>'; }
if(h==='right'){ const sq=8; s+='<polyline points="'+(O.x+sq)+','+O.y+' '+(O.x+sq)+','+(O.y-sq)+' '+O.x+','+(O.y-sq)+'" fill="none" stroke="#ef4444" stroke-width="2"/>'; }
s+='<circle cx="'+O.x+'" cy="'+O.y+'" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>';
s+='<text x="'+(O.x+8)+'" y="'+(O.y-6)+'" font-size="10" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">O</text>';
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.22,ly=v.y+(v.y-cy)*0.22; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p9-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p9-proof-step').innerHTML=st.text; renderMath(document.getElementById('p9-proof-step')); draw(st.h); document.getElementById('p9-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p9-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p9-proof');bumpProgress('p9',12);document.getElementById('p9-proof-next').textContent='Изучено! +5 XP';document.getElementById('p9-proof-next').disabled=true;} });
document.getElementById('p9-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p9-proof-next').disabled=false;});
show();
})();
/* == Калькулятор площади ромба == */
(function(){
function calc(){
const d1=+document.getElementById('p9-cd1').value, d2=+document.getElementById('p9-cd2').value;
if(d1<=0||d2<=0||isNaN(d1)||isNaN(d2)){ document.getElementById('p9-calc-out').innerHTML='<span style="color:var(--bad)">Введи положительные числа.</span>'; return; }
const S=d1*d2/2;
const side=Math.sqrt((d1/2)*(d1/2)+(d2/2)*(d2/2));
const sideStr=Number.isInteger(side)?String(side):side.toFixed(4).replace(/0+$/,'');
document.getElementById('p9-calc-out').innerHTML=`$S = \\dfrac{d_1 \\cdot d_2}{2} = \\dfrac{${d1} \\cdot ${d2}}{2} = ${S}$<br>Сторона: $a = \\sqrt{\\left(\\dfrac{d_1}{2}\\right)^2+\\left(\\dfrac{d_2}{2}\\right)^2} = \\sqrt{${d1/2}^2+${d2/2}^2} = ${sideStr}$`;
renderMath(document.getElementById('p9-calc-out'));
addXp(2,'p9-calc');
}
document.getElementById('p9-calc-go').addEventListener('click',calc);
calc();
})();
/* == DnD == */
(function(){
const items=[
{id:'pr1',html:'Противоположные стороны равны', ans:'any'},
{id:'pr2',html:'Диагонали делятся пополам', ans:'any'},
{id:'pr3',html:'Сумма соседних углов 180°', ans:'any'},
{id:'re1',html:'Все углы 90°', ans:'rect'},
{id:'re2',html:'Диагонали равны', ans:'rect'},
{id:'rh1',html:'Все стороны равны', ans:'rhm'},
{id:'rh2',html:'Диагонали перпендикулярны', ans:'rhm'},
{id:'rh3',html:'Диагонали — биссектрисы углов', ans:'rhm'},
];
const sorter=setupSorter({poolId:'p9-dnd-pool',scopeSelector:'#p9-dnd-wrap',items,cats:['any','rect','rhm']});
document.getElementById('p9-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p9-dnd-fb').style.display='none';});
document.getElementById('p9-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p9-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p9-dnd');bumpProgress('p9',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.');
});
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Диагонали ромба $d_1=10$, $d_2=24$. Найди сторону ромба.', ans:13, hint:'a=√(5²+12²)=√169=13'},
{q:'Диагонали ромба $d_1=10$, $d_2=24$. Найди площадь.', ans:120, hint:'S=10·24/2=120'},
{q:'Периметр ромба $P=40$, диагональ $d_1=12$. Найди вторую диагональ $d_2$.', ans:16, hint:'a=10, d2=2√(100-36)=2·8=16'},
{q:'Угол между диагоналями ромба (в градусах)?', ans:90, hint:'Диагонали ромба всегда ⊥'},
{q:'Площадь ромба $d_1=6$, $d_2=8$. Чему равна площадь?', ans:24, hint:'S=6·8/2=24'},
];
let idx=0,score=0;
function show(){ document.getElementById('p9-tr-i').textContent=idx+1; document.getElementById('p9-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p9-tr-task')); document.getElementById('p9-tr-ans').value=''; document.getElementById('p9-tr-fb').style.display='none'; }
document.getElementById('p9-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p9-tr-score').textContent=0;show();});
document.getElementById('p9-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p9-tr-ans').value; const fb=document.getElementById('p9-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p9-tr-score').textContent=score;addXp(3,'p9-train');bumpProgress('p9',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p9-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p9-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p9-tr-go').click();});
show();
})();
/* == Босс §9 == */
(function(){
const tasks=[
{q:'Диагонали ромба $d_1=16$, $d_2=12$. Найди сторону.', ans:10, hint:'a=√(8²+6²)=√100=10'},
{q:'Диагонали ромба $d_1=16$, $d_2=12$. Найди площадь.', ans:96, hint:'S=16·12/2=96'},
{q:'Сторона ромба $a=10$, одна диагональ $d_1=12$. Найди вторую диагональ $d_2$.', ans:16, hint:'d2=2√(100-36)=2·8=16'},
{q:'Периметр ромба $P=52$. Найди сторону.', ans:13, hint:'a=P/4=52/4=13'},
];
const bossBox=document.getElementById('p9-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="p9-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p9-boss-a${i}').value;const fb=document.getElementById('p9-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p9BossSolved.has(${i})){p9BossSolved.add(${i});addXp(5,'p9-boss${i}');bumpProgress('p9',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p9-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p9BossSolved=new Set();
})();
renderMath(box);
}
function buildP10(){
const box = document.getElementById('p10-body');
let html = '';
html += makeCard('theory','Квадрат — определение','10.1',`
<p><b>Квадрат</b> — прямоугольник, у которого все стороны равны.</p>
<p>Эквивалентно: квадрат — это ромб с прямыми углами.</p>
<p>Таким образом, квадрат одновременно является:</p>
<ul style="margin-left:18px;line-height:1.9">
<li>Параллелограммом</li>
<li>Прямоугольником</li>
<li>Ромбом</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 158" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Square with equal-side ticks, right angle marks, both diagonals -->
<rect x="60" y="26" width="108" height="108" fill="rgba(234,179,8,.12)" stroke="#d97706" stroke-width="2.5"/>
<!-- right angle marks — L-shape pointing INSIDE the square -->
<polyline points="69,134 69,125 60,125" fill="none" stroke="#d97706" stroke-width="1.5"/>
<polyline points="159,134 159,125 168,125" fill="none" stroke="#d97706" stroke-width="1.5"/>
<polyline points="159,26 159,35 168,35" fill="none" stroke="#d97706" stroke-width="1.5"/>
<polyline points="69,26 69,35 60,35" fill="none" stroke="#d97706" stroke-width="1.5"/>
<!-- equal side ticks (all 4 sides) -->
<line x1="107" y1="24" x2="121" y2="24" stroke="#d97706" stroke-width="2"/>
<line x1="107" y1="136" x2="121" y2="136" stroke="#d97706" stroke-width="2"/>
<line x1="58" y1="74" x2="58" y2="88" stroke="#d97706" stroke-width="2"/>
<line x1="170" y1="74" x2="170" y2="88" stroke="#d97706" stroke-width="2"/>
<!-- diagonals -->
<line x1="60" y1="134" x2="168" y2="26" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="168" y1="134" x2="60" y2="26" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- perpendicular mark at diagonal intersection -->
<circle cx="114" cy="80" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<!-- vertices -->
<circle cx="60" cy="134" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="168" cy="134" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="168" cy="26" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="60" cy="26" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="46" y="138" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<text x="176" y="138" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<text x="176" y="22" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">C</text>
<text x="46" y="22" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">D</text>
<!-- side label a -->
<text x="114" y="152" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<!-- hierarchy at right -->
<text x="200" y="60" font-size="9" fill="#64748b" font-family="Inter,sans-serif">▸ Параллелограмм</text>
<text x="200" y="80" font-size="9" fill="#64748b" font-family="Inter,sans-serif">▸ Прямоугольник</text>
<text x="200" y="100" font-size="9" fill="#64748b" font-family="Inter,sans-serif">▸ Ромб</text>
<text x="200" y="120" font-size="9" fill="#b45309" font-weight="700" font-family="Inter,sans-serif">▶ Квадрат</text>
</svg></div>`);
html += makeCard('rule','Свойства квадрата','10.2',`
<p>В квадрате со стороной $a$:</p>
<ul style="margin-left:18px;line-height:1.9">
<li>Все стороны равны: $AB=BC=CD=DA=a$</li>
<li>Все углы прямые: $\\angle A=\\angle B=\\angle C=\\angle D=90°$</li>
<li>Диагонали равны: $AC=BD=a\\sqrt{2}$</li>
<li>Диагонали взаимно перпендикулярны: $AC\\perp BD$</li>
<li>Диагонали делятся пополам и делят углы пополам</li>
<li>Площадь: $S=a^2$</li>
<li>Периметр: $P=4a$</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Square with labeled a, diagonal a√2, S=a² annotation -->
<rect x="68" y="24" width="100" height="100" fill="rgba(234,179,8,.12)" stroke="#d97706" stroke-width="2.5"/>
<!-- diagonal -->
<line x1="68" y1="124" x2="168" y2="24" stroke="#8b5cf6" stroke-width="2" stroke-dasharray="6 3"/>
<!-- side label a -->
<text x="118" y="142" text-anchor="middle" font-size="12" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<text x="52" y="76" text-anchor="middle" font-size="12" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">a</text>
<!-- diagonal label -->
<text x="102" y="64" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">a√2</text>
<!-- right angle marks at all 4 corners — L-shape pointing INSIDE the square -->
<polyline points="77,124 77,115 68,115" fill="none" stroke="#d97706" stroke-width="1.5"/>
<polyline points="159,124 159,115 168,115" fill="none" stroke="#d97706" stroke-width="1.5"/>
<polyline points="77,24 77,33 68,33" fill="none" stroke="#d97706" stroke-width="1.5"/>
<polyline points="159,24 159,33 168,33" fill="none" stroke="#d97706" stroke-width="1.5"/>
<!-- formula box at right -->
<rect x="188" y="38" width="78" height="74" rx="6" fill="rgba(234,179,8,.10)" stroke="#d97706" stroke-width="1"/>
<text x="227" y="58" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">P = 4a</text>
<text x="227" y="78" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">S = a²</text>
<text x="227" y="98" text-anchor="middle" font-size="9" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">d = a√2</text>
<!-- vertices -->
<circle cx="68" cy="124" r="3.5" fill="#d97706"/><circle cx="168" cy="124" r="3.5" fill="#d97706"/>
<circle cx="168" cy="24" r="3.5" fill="#d97706"/><circle cx="68" cy="24" r="3.5" fill="#d97706"/>
</svg></div>`);
html += makeCard('example','Формулы квадрата','10.3',`
<p>$a$ — сторона, $d$ — диагональ, $P$ — периметр, $S$ — площадь:</p>
\\[d = a\\sqrt{2} \\qquad a = \\dfrac{d}{\\sqrt{2}} = \\dfrac{d\\sqrt{2}}{2}\\]
\\[P = 4a \\qquad S = a^2 = \\dfrac{d^2}{2}\\]
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- 3 squares of different sizes labeled with a=3,4,5 and their S, d values -->
<rect x="20" y="80" width="40" height="40" fill="rgba(16,185,129,.14)" stroke="#10b981" stroke-width="2"/>
<text x="40" y="130" text-anchor="middle" font-size="9" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">a=4</text>
<text x="40" y="142" text-anchor="middle" font-size="8" fill="#047857" font-family="JetBrains Mono,monospace">S=16, d≈5.7</text>
<rect x="90" y="58" width="60" height="60" fill="rgba(8,145,178,.14)" stroke="#0891b2" stroke-width="2"/>
<text x="120" y="130" text-anchor="middle" font-size="9" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">a=6</text>
<text x="120" y="142" text-anchor="middle" font-size="8" fill="#0e7490" font-family="JetBrains Mono,monospace">S=36, d≈8.5</text>
<rect x="178" y="30" width="82" height="82" fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="2"/>
<text x="219" y="124" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">a=8.2</text>
<text x="219" y="136" text-anchor="middle" font-size="8" fill="#b45309" font-family="JetBrains Mono,monospace">S=67, d≈11.6</text>
<!-- diagonal lines -->
<line x1="20" y1="120" x2="60" y2="80" stroke="#10b981" stroke-width="1" stroke-dasharray="4 2" opacity=".7"/>
<line x1="90" y1="118" x2="150" y2="58" stroke="#0891b2" stroke-width="1" stroke-dasharray="4 2" opacity=".7"/>
<line x1="178" y1="112" x2="260" y2="30" stroke="#d97706" stroke-width="1" stroke-dasharray="4 2" opacity=".7"/>
<!-- right angle marks — square 1 (green, 40×40 at 20,80) -->
<polyline points="26,120 26,114 20,114" fill="none" stroke="#10b981" stroke-width="1.2"/>
<polyline points="54,120 54,114 60,114" fill="none" stroke="#10b981" stroke-width="1.2"/>
<polyline points="54,80 54,86 60,86" fill="none" stroke="#10b981" stroke-width="1.2"/>
<polyline points="26,80 26,86 20,86" fill="none" stroke="#10b981" stroke-width="1.2"/>
<!-- right angle marks — square 2 (blue, 60×60 at 90,58) -->
<polyline points="96,118 96,112 90,112" fill="none" stroke="#0891b2" stroke-width="1.2"/>
<polyline points="144,118 144,112 150,112" fill="none" stroke="#0891b2" stroke-width="1.2"/>
<polyline points="144,58 144,64 150,64" fill="none" stroke="#0891b2" stroke-width="1.2"/>
<polyline points="96,58 96,64 90,64" fill="none" stroke="#0891b2" stroke-width="1.2"/>
<!-- right angle marks — square 3 (orange, 82×82 at 178,30) -->
<polyline points="185,112 185,105 178,105" fill="none" stroke="#d97706" stroke-width="1.2"/>
<polyline points="253,112 253,105 260,105" fill="none" stroke="#d97706" stroke-width="1.2"/>
<polyline points="253,30 253,37 260,37" fill="none" stroke="#d97706" stroke-width="1.2"/>
<polyline points="185,30 185,37 178,37" fill="none" stroke="#d97706" stroke-width="1.2"/>
</svg></div>`);
/* --- INTERACTIVE 1: SVG-квадрат со слайдером --- */
html += `<div class="wg" id="p10-sq-wrap">
<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>Сторона $a$ = <b id="p10-sq-aval">80</b><input type="range" min="30" max="140" value="80" id="p10-sq-sl" style="accent-color:var(--sec-acc,var(--pri))"></label></div>
<div id="p10-sq-svg" style="display:flex;justify-content:center"></div>
<div id="p10-sq-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* --- INTERACTIVE 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">Нажимай на фигуры — увидишь, какие свойства ей принадлежат.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px">
<button class="btn" id="p10-hier-pg">Параллелограмм</button>
<button class="btn" id="p10-hier-re">Прямоугольник</button>
<button class="btn" id="p10-hier-rh">Ромб</button>
<button class="btn primary" id="p10-hier-sq">Квадрат</button>
</div>
<div id="p10-hier-out" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.75"></div>
</div>`;
/* --- INTERACTIVE 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">Введи сторону или диагональ — получи все остальные значения.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.92rem">Сторона $a$ = <input type="number" id="p10-ca" class="tinp" value="5" style="width:80px" min="0.001" step="any"></label>
<label style="font-size:.92rem">или диагональ $d$ = <input type="number" id="p10-cd" class="tinp" placeholder="авто" style="width:80px" min="0.001" step="any"></label>
<button class="btn primary" id="p10-calc-go">Вычислить</button>
</div>
<div id="p10-calc-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.9"></div>
</div>`;
/* --- INTERACTIVE 4: DnD — категоризация фигур --- */
html += `<div class="wg" id="p10-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Наиболее конкретное название фигуры</div></div>
<div class="wg-help">Для каждой фигуры выбери самое точное название (квадрат → квадрат, не «прямоугольник»).</div>
${DND_HINT_HTML}
<div id="p10-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="pg">Параллелограмм</h5><div class="drop-items" data-cat="pg"></div></div>
<div class="drop-box"><h5 data-cat="re">Прямоугольник</h5><div class="drop-items" data-cat="re"></div></div>
<div class="drop-box"><h5 data-cat="rh">Ромб</h5><div class="drop-items" data-cat="rh"></div></div>
<div class="drop-box"><h5 data-cat="sq">Квадрат</h5><div class="drop-items" data-cat="sq"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p10-dnd-check">Проверить</button><button class="btn" id="p10-dnd-reset">Сначала</button></div>
<div class="feedback" id="p10-dnd-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 5: Тренажёр --- */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Тренажёр задач на квадрат</div></div>
<div class="score-display"><span>Задача <b id="p10-tr-i">1</b> / 5</span><span>Очки: <b id="p10-tr-score">0</b></span></div>
<div id="p10-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p10-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p10-tr-go">Проверить</button>
<button class="btn" id="p10-tr-start">Начать</button>
</div>
<div class="feedback" id="p10-tr-fb" style="display:none"></div>
</div>`;
/* --- INTERACTIVE 6: Босс §10 --- */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §10</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p10-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p10-read-btn" onclick="addXp(10,'p10-read');bumpProgress('p10',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>
Я прочитал §10 (+10 XP)
</button>
</div>`;
html += secNav('p9','p11');
box.innerHTML = html;
/* == SVG-квадрат со слайдером == */
(function(){
const W=300, H=300, cx=150, cy=150;
function redraw(){
const sl=document.getElementById('p10-sq-sl');
const a=+sl.value;
document.getElementById('p10-sq-aval').textContent=a;
const half=a/2;
const A={x:cx-half,y:cy+half},B={x:cx+half,y:cy+half},C={x:cx+half,y:cy-half},D={x:cx-half,y:cy-half};
const d=a*Math.sqrt(2);
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:300px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<rect x="'+(cx-half)+'" y="'+(cy-half)+'" width="'+a+'" height="'+a+'" fill="rgba(5,150,105,.10)" stroke="#059669" stroke-width="2.5"/>';
// diagonals
s+='<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="#059669" stroke-width="1.5" stroke-dasharray="5 3" opacity=".7"/>';
s+='<line x1="'+B.x+'" y1="'+B.y+'" x2="'+D.x+'" y2="'+D.y+'" stroke="#059669" stroke-width="1.5" stroke-dasharray="5 3" opacity=".7"/>';
// right-angle at diagonal intersection O
const sq=7;
s+='<polyline points="'+(cx+sq)+','+cy+' '+(cx+sq)+','+(cy-sq)+' '+cx+','+(cy-sq)+'" fill="none" stroke="#059669" stroke-width="1.5" opacity=".8"/>';
// right-angle marks at all 4 corners — proper L-shape pointing INSIDE the square
const m=8;
// A = bottom-left (cx-half, cy+half)
s+='<polyline points="'+(A.x+m)+','+A.y+' '+(A.x+m)+','+(A.y-m)+' '+A.x+','+(A.y-m)+'" fill="none" stroke="#059669" stroke-width="1.5" opacity=".8"/>';
// B = bottom-right (cx+half, cy+half)
s+='<polyline points="'+(B.x-m)+','+B.y+' '+(B.x-m)+','+(B.y-m)+' '+B.x+','+(B.y-m)+'" fill="none" stroke="#059669" stroke-width="1.5" opacity=".8"/>';
// C = top-right (cx+half, cy-half)
s+='<polyline points="'+(C.x-m)+','+C.y+' '+(C.x-m)+','+(C.y+m)+' '+C.x+','+(C.y+m)+'" fill="none" stroke="#059669" stroke-width="1.5" opacity=".8"/>';
// D = top-left (cx-half, cy-half)
s+='<polyline points="'+(D.x+m)+','+D.y+' '+(D.x+m)+','+(D.y+m)+' '+D.x+','+(D.y+m)+'" fill="none" stroke="#059669" stroke-width="1.5" opacity=".8"/>';
// center dot
s+='<circle cx="'+cx+'" cy="'+cy+'" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>';
// side labels
s+='<text x="'+cx+'" y="'+(A.y+16)+'" text-anchor="middle" font-size="11" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">a='+a+'</text>';
// diagonal label
s+='<text x="'+(cx+8)+'" y="'+(cy-8)+'" font-size="10" fill="#64748b" font-family="JetBrains Mono,monospace">d</text>';
// vertices
['A','B','C','D'].forEach((lbl,i)=>{ const v=[A,B,C,D][i]; s+='<circle cx="'+v.x+'" cy="'+v.y+'" r="5" fill="#059669" stroke="#fff" stroke-width="2"/>'; const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; s+='<text x="'+lx+'" y="'+ly+'" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">'+lbl+'</text>'; });
s+='</svg>';
document.getElementById('p10-sq-svg').innerHTML=s;
const dStr=(d%1===0)?String(d):d.toFixed(4).replace(/0+$/,'');
document.getElementById('p10-sq-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Сторона a</div><b>${a}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Периметр P=4a</div><b>${4*a}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Площадь S=a²</div><b>${a*a}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Диагональ d=a√2</div><b>${dStr}</b></div>`;
}
document.getElementById('p10-sq-sl').addEventListener('input',redraw);
redraw();
})();
/* == Иерархия == */
(function(){
const data={
pg:{label:'Параллелограмм',props:['Противоположные стороны попарно параллельны','Противоположные стороны равны ($AB=CD$, $BC=AD$)','Противоположные углы равны','Сумма соседних углов $= 180°$','Диагонали делятся пополам']},
re:{label:'Прямоугольник',props:['Все свойства параллелограмма','Все углы прямые ($90°$)','Диагонали равны ($AC=BD$)']},
rh:{label:'Ромб',props:['Все свойства параллелограмма','Все стороны равны','Диагонали взаимно перпендикулярны','Диагонали делят углы пополам']},
sq:{label:'Квадрат',props:['Все свойства параллелограмма','Все стороны равны','Все углы прямые','Диагонали равны $d=a\\sqrt{2}$','Диагонали взаимно перпендикулярны','Диагонали делят углы пополам по $45°$','$S=a^2$, $P=4a$']},
};
function show(k){
const d=data[k];
document.getElementById('p10-hier-out').innerHTML='<b>'+d.label+':</b><ul style="margin-left:18px;line-height:1.8">'+d.props.map(p=>'<li>'+p+'</li>').join('')+'</ul>';
renderMath(document.getElementById('p10-hier-out'));
Object.keys(data).forEach(kk=>{ const btn=document.getElementById('p10-hier-'+kk); btn.className='btn'+(kk===k?' primary':''); });
}
['pg','re','rh','sq'].forEach(k=>document.getElementById('p10-hier-'+k).addEventListener('click',()=>show(k)));
show('sq');
})();
/* == Калькулятор квадрата == */
(function(){
function calc(){
let a=+document.getElementById('p10-ca').value;
const dInp=+document.getElementById('p10-cd').value;
if(dInp>0){ a=dInp/Math.sqrt(2); }
if(a<=0||isNaN(a)){ document.getElementById('p10-calc-out').innerHTML='<span style="color:var(--bad)">Введи положительное значение.</span>'; return; }
const d=a*Math.sqrt(2), P=4*a, S=a*a;
const fmt2=x=>Number.isInteger(x)?String(x):x.toFixed(4).replace(/0+$/,'');
document.getElementById('p10-calc-out').innerHTML=`$a = ${fmt2(a)}$<br>$P = 4a = ${fmt2(P)}$<br>$S = a^2 = ${fmt2(S)}$<br>$d = a\\sqrt{2} = ${fmt2(d)}$`;
renderMath(document.getElementById('p10-calc-out'));
addXp(2,'p10-calc');
}
document.getElementById('p10-calc-go').addEventListener('click',calc);
calc();
})();
/* == DnD == */
(function(){
const items=[
{id:'f1',html:'Параллелограмм с углом 60° и сторонами 5 и 8', ans:'pg'},
{id:'f2',html:'Параллелограмм, у которого все углы 90°, стороны 4 и 7', ans:'re'},
{id:'f3',html:'Параллелограмм, все стороны = 6, угол 120°', ans:'rh'},
{id:'f4',html:'Параллелограмм, все стороны = 5, все углы 90°', ans:'sq'},
{id:'f5',html:'Прямоугольник со сторонами 3 и 3', ans:'sq'},
{id:'f6',html:'Ромб с углом 90°', ans:'sq'},
];
const sorter=setupSorter({poolId:'p10-dnd-pool',scopeSelector:'#p10-dnd-wrap',items,cats:['pg','re','rh','sq']});
document.getElementById('p10-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p10-dnd-fb').style.display='none';});
document.getElementById('p10-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p10-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p10-dnd');bumpProgress('p10',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Подсказка: квадрат — ромб с прямыми углами.');
});
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Сторона квадрата $a=7$. Найди периметр.', ans:28, hint:'P=4·7=28'},
{q:'Сторона квадрата $a=9$. Найди площадь.', ans:81, hint:'S=9²=81'},
{q:'Площадь квадрата $S=49$. Найди сторону.', ans:7, hint:'a=√49=7'},
{q:'Сторона квадрата $a=5$. Найди диагональ (округли до целого).', ans:7, hint:'d=5√2≈7.07≈7'},
{q:'Периметр квадрата $P=36$. Найди сторону.', ans:9, hint:'a=P/4=36/4=9'},
];
let idx=0,score=0;
function show(){ document.getElementById('p10-tr-i').textContent=idx+1; document.getElementById('p10-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p10-tr-task')); document.getElementById('p10-tr-ans').value=''; document.getElementById('p10-tr-fb').style.display='none'; }
document.getElementById('p10-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p10-tr-score').textContent=0;show();});
document.getElementById('p10-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p10-tr-ans').value; const fb=document.getElementById('p10-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p10-tr-score').textContent=score;addXp(3,'p10-train');bumpProgress('p10',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p10-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p10-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p10-tr-go').click();});
show();
})();
/* == Босс §10 == */
(function(){
const tasks=[
{q:'Диагональ квадрата $d=10\\sqrt{2}$. Найди сторону.', ans:10, hint:'a=d/√2=10√2/√2=10'},
{q:'Площадь квадрата $S=144$. Найди периметр.', ans:48, hint:'a=12, P=4·12=48'},
{q:'Периметр квадрата $P=32$. Найди площадь.', ans:64, hint:'a=8, S=64'},
{q:'Сторона квадрата $a=10$. Найди площадь.', ans:100, hint:'S=10²=100'},
];
const bossBox=document.getElementById('p10-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="p10-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p10-boss-a${i}').value;const fb=document.getElementById('p10-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p10BossSolved.has(${i})){p10BossSolved.add(${i});addXp(5,'p10-boss${i}');bumpProgress('p10',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p10-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p10BossSolved=new Set();
})();
renderMath(box);
}
function buildP11(){
const box = document.getElementById('p11-body');
let html = '';
html += makeCard('theory','Теорема Фалеса','11.1',`
<p><b>Теорема Фалеса.</b> Если на одной стороне угла отложены равные отрезки и через их концы проведены прямые, параллельные другой стороне угла, то эти прямые отсекают на второй стороне угла равные отрезки.</p>
<p><b>Обобщение:</b> если параллельные прямые пересекают две прямые (секущие) и отсекают на одной из них равные отрезки, то и на другой они отсекают равные отрезки.</p>
<p>Формально: $a_1\\parallel a_2\\parallel a_3$, $OA_1=A_1A_2 \\Rightarrow OB_1=B_1B_2$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 168" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Two diverging rays from O, with 3 parallel lines cutting equal segments -->
<!-- vertex O -->
<circle cx="44" cy="84" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="30" y="88" font-size="11" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">O</text>
<!-- ray 1: upper right -->
<line x1="44" y1="84" x2="268" y2="36" stroke="#64748b" stroke-width="1.5"/>
<!-- ray 2: lower right -->
<line x1="44" y1="84" x2="268" y2="154" stroke="#64748b" stroke-width="1.5"/>
<!-- 3 parallel lines (vertical-ish) -->
<line x1="110" y1="20" x2="110" y2="168" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="176" y1="20" x2="176" y2="168" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="242" y1="20" x2="242" y2="168" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- intersection points on upper ray (A points) -->
<circle cx="110" cy="62" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/><text x="106" y="54" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">A₁</text>
<circle cx="176" cy="48" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/><text x="172" y="40" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">A₂</text>
<!-- intersection points on lower ray (B points) -->
<circle cx="110" cy="107" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/><text x="106" y="122" font-size="9" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">B₁</text>
<circle cx="176" cy="119" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/><text x="172" y="134" font-size="9" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">B₂</text>
<!-- equal segment braces -->
<text x="138" y="55" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">=</text>
<text x="138" y="115" text-anchor="middle" font-size="9" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">=</text>
<!-- caption -->
<text x="140" y="160" text-anchor="middle" font-size="9" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">OA₁=A₁A₂ ⟹ OB₁=B₁B₂</text>
</svg></div>`);
html += makeCard('rule','Деление отрезка на n равных частей','11.2',`
<p><b>Алгоритм</b> (деление $AB$ на $n$ частей):</p>
<ol style="margin-left:18px;line-height:1.9">
<li>Из точки $A$ провести произвольный луч $AC$, не совпадающий с $AB$.</li>
<li>На луче $AC$ отложить $n$ равных отрезков: $AA_1=A_1A_2=\\ldots=A_{n-1}A_n$.</li>
<li>Соединить $A_n$ с $B$.</li>
<li>Через $A_1,A_2,\\ldots,A_{n-1}$ провести прямые, параллельные $A_nB$.</li>
<li>Точки пересечения с $AB$ — искомые точки деления.</li>
</ol>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Segment AB divided into 4 equal parts using construction ray -->
<!-- AB on horizontal line -->
<line x1="20" y1="116" x2="260" y2="116" stroke="#d97706" stroke-width="2.5"/>
<circle cx="20" cy="116" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/><text x="14" y="132" font-size="10" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">A</text>
<circle cx="260" cy="116" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/><text x="256" y="132" font-size="10" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">B</text>
<!-- auxiliary ray AC at angle -->
<line x1="20" y1="116" x2="160" y2="28" stroke="#94a3b8" stroke-width="1.5"/>
<!-- 4 points on ray, equally spaced -->
<circle cx="55" cy="94" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="90" cy="72" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="125" cy="50" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="160" cy="28" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<text x="166" y="24" font-size="9" fill="#6d28d9" font-weight="700" font-family="Unbounded,sans-serif">A₄</text>
<!-- line from A4 to B -->
<line x1="160" y1="28" x2="260" y2="116" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- parallel lines through A1, A2, A3 -->
<line x1="55" y1="94" x2="80" y2="116" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="90" y1="72" x2="140" y2="116" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="125" y1="50" x2="200" y2="116" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- division points on AB -->
<circle cx="80" cy="116" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="140" cy="116" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="200" cy="116" r="3.5" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<text x="140" y="144" text-anchor="middle" font-size="9" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">AB разделён на 4 равные части</text>
</svg></div>`);
html += makeCard('example','Доказательство теоремы Фалеса','11.3',`
<p>Через $A_1$ проведём прямую, параллельную $OB$. Она пересекает $A_2B_2$ в точке $C$.</p>
<p>$\\triangle OA_1B_1 \\cong \\triangle A_1A_2C$ (два угла и сторона): $\\angle A_1OB_1 = \\angle A_1A_2C$ (параллельные), $OA_1=A_1A_2$ (условие). $\\Rightarrow B_1C = OB_1$.</p>
<p>Аналогично $\\triangle A_1B_1C \\cong \\triangle A_2B_2C'$... итог: $OB_1 = B_1B_2$. <b>ч.т.д.</b></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">
<!-- Proof construction: O with 2 rays, 3 parallel lines, auxiliary parallel through A1 -->
<circle cx="36" cy="78" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="20" y="82" font-size="10" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">O</text>
<!-- rays -->
<line x1="36" y1="78" x2="260" y2="36" stroke="#64748b" stroke-width="1.5"/>
<line x1="36" y1="78" x2="260" y2="138" stroke="#64748b" stroke-width="1.5"/>
<!-- parallel lines -->
<line x1="108" y1="18" x2="108" y2="155" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="184" y1="18" x2="184" y2="155" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- intersection points -->
<circle cx="108" cy="57" r="3.5" fill="#d97706"/><text x="104" y="49" font-size="8" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">A₁</text>
<circle cx="184" cy="47" r="3.5" fill="#d97706"/><text x="180" y="39" font-size="8" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">A₂</text>
<circle cx="108" cy="99" r="3.5" fill="#10b981"/><text x="104" y="114" font-size="8" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">B₁</text>
<circle cx="184" cy="109" r="3.5" fill="#10b981"/><text x="180" y="124" font-size="8" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">B₂</text>
<!-- auxiliary line through A1 parallel to OB ray (highlighted) -->
<line x1="108" y1="57" x2="184" y2="99" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- C point -->
<circle cx="184" cy="99" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/><text x="190" y="97" font-size="8" fill="#6d28d9" font-weight="700" font-family="Unbounded,sans-serif">C</text>
<!-- triangles labeled -->
<text x="78" y="78" text-anchor="middle" font-size="8" fill="#0e7490" font-family="Inter,sans-serif">△₁</text>
<text x="148" y="80" text-anchor="middle" font-size="8" fill="#0e7490" font-family="Inter,sans-serif">△₂</text>
<text x="140" y="150" text-anchor="middle" font-size="9" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">△OA₁B₁ ≅ △A₁A₂C</text>
</svg></div>`);
/* INTERACTIVE 1: SVG Теорема Фалеса */
html += `<div class="wg" id="p11-thales-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живая теорема Фалеса — тащи вершину угла</div></div>
<div class="wg-help">Перетащи вершину <b>O</b> или точку <b>A₁</b>. Отрезки на обеих сторонах угла остаются равными.</div>
<div class="sliders"><label>Количество отрезков n = <b id="p11-n-val">3</b><input type="range" min="2" max="6" value="3" id="p11-n-sl" style="accent-color:var(--sec-acc,var(--pri))"></label></div>
<div id="p11-thales-svg" style="display:flex;justify-content:center"></div>
<div id="p11-thales-info" style="margin-top:8px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.9rem"></div>
</div>`;
/* INTERACTIVE 2: Конструктор деления отрезка */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Конструктор: деление отрезка на n частей</div></div>
<div class="wg-help">Выбери число частей и нажми «Построить» — увидишь пошаговое построение.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px">
${[2,3,4,5,6,7,8].map(n=>`<button class="btn p11-ndiv-btn" data-n="${n}">${n}</button>`).join('')}
</div>
<div id="p11-div-svg" style="display:flex;justify-content:center"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p11-div-build">Построить пошагово</button>
<button class="btn" id="p11-div-reset">Сброс</button>
</div>
<div id="p11-div-step" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.9rem;margin-top:8px;min-height:40px"></div>
</div>`;
/* INTERACTIVE 3: Тренажёр */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Тренажёр задач на теорему Фалеса</div></div>
<div class="score-display"><span>Задача <b id="p11-tr-i">1</b> / 5</span><span>Очки: <b id="p11-tr-score">0</b></span></div>
<div id="p11-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p11-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p11-tr-go">Проверить</button>
<button class="btn" id="p11-tr-start">Начать</button>
</div>
<div class="feedback" id="p11-tr-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 4: DnD — равные / не равные отрезки */
html += `<div class="wg" id="p11-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Какие отрезки равны при данной конфигурации?</div></div>
<div class="wg-help">Три параллельных прямых отсекают равные отрезки на первой секущей. Распредели отрезки по группам.</div>
${DND_HINT_HTML}
<div id="p11-dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="eq">Равны другим</h5><div class="drop-items" data-cat="eq"></div></div>
<div class="drop-box"><h5 data-cat="neq">Не обязательно равны</h5><div class="drop-items" data-cat="neq"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p11-dnd-check">Проверить</button><button class="btn" id="p11-dnd-reset">Сначала</button></div>
<div class="feedback" id="p11-dnd-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 5: Босс §11 */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §11</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p11-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p11-read-btn" onclick="addXp(10,'p11-read');bumpProgress('p11',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>
Я прочитал §11 (+10 XP)
</button>
</div>`;
html += secNav('p10','p12');
box.innerHTML = html;
/* == SVG Теорема Фалеса == */
(function(){
const W=380, H=300;
let Ox=60, Oy=240, A1x=140, A1y=100;
let nPts=3;
function drawThales(){
const n = nPts;
const dx = A1x - Ox, dy = A1y - Oy;
const len1 = Math.hypot(dx, dy);
// unit vector for ray OA
const ux = dx/len1, uy = dy/len1;
// second ray: perpendicular-ish offset
const ang2 = Math.atan2(dy,dx) + 0.55;
const ux2 = Math.cos(ang2), uy2 = Math.sin(ang2);
const step1 = len1 / n;
// points on ray 1: O, A1, A2, ... An
const ray1 = [];
for(let i=0;i<=n;i++) ray1.push({x: Ox + ux*step1*i, y: Oy + uy*step1*i});
// points on ray 2 — Thales says spacing = step1 * |ray2_unit_proj| adjusted so we get EQUAL spacing
// We use the same arc-length step for consistent visual: step2 = step1 (Thales guarantees it)
const step2 = step1;
const ray2 = [];
for(let i=0;i<=n;i++) ray2.push({x: Ox + ux2*step2*i, y: Oy + uy2*step2*i});
let s = `<svg id="p11-svg" viewBox="0 0 ${W} ${H}" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">`;
// parallel lines through each pair
const colors=['#0891b2','#10b981','#f59e0b','#8b5cf6','#ef4444','#ec4899'];
for(let i=0;i<=n;i++){
const p1=ray1[i], p2=ray2[i];
const vx=p2.x-p1.x, vy=p2.y-p1.y;
const ext=200;
const nx=vx/Math.hypot(vx,vy), ny=vy/Math.hypot(vx,vy);
if(i===0){
// just the vertex
} else {
s+=`<line x1="${(p1.x-nx*20).toFixed(1)}" y1="${(p1.y-ny*20).toFixed(1)}" x2="${(p1.x+nx*ext).toFixed(1)}" y2="${(p1.y+ny*ext).toFixed(1)}" stroke="${colors[(i-1)%colors.length]}" stroke-width="1.5" stroke-dasharray="5 3" opacity=".5"/>`;
}
}
// ray 1
s+=`<line x1="${Ox}" y1="${Oy}" x2="${(Ox+ux*step1*(n+0.3)).toFixed(1)}" y2="${(Oy+uy*step1*(n+0.3)).toFixed(1)}" stroke="#0891b2" stroke-width="2.5"/>`;
// ray 2
s+=`<line x1="${Ox}" y1="${Oy}" x2="${(Ox+ux2*step2*(n+0.3)).toFixed(1)}" y2="${(Oy+uy2*step2*(n+0.3)).toFixed(1)}" stroke="#10b981" stroke-width="2.5"/>`;
// tick marks and labels on ray1
for(let i=1;i<=n;i++){
const p=ray1[i];
s+=`<circle cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="5" fill="${colors[(i-1)%colors.length]}" stroke="#fff" stroke-width="2"/>`;
const lx=(p.x+ux*12).toFixed(1), ly=(p.y+uy*12).toFixed(1);
s+=`<text x="${lx}" y="${ly}" text-anchor="middle" dominant-baseline="middle" font-size="11" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">A${i}</text>`;
}
// tick marks and labels on ray2
for(let i=1;i<=n;i++){
const p=ray2[i];
s+=`<circle cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="5" fill="${colors[(i-1)%colors.length]}" stroke="#fff" stroke-width="2"/>`;
const lx=(p.x+ux2*12).toFixed(1), ly=(p.y+uy2*12).toFixed(1);
s+=`<text x="${lx}" y="${ly}" text-anchor="middle" dominant-baseline="middle" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">B${i}</text>`;
}
// O vertex — draggable
s+=`<circle cx="${Ox}" cy="${Oy}" r="9" fill="#0891b2" opacity=".2" class="p11-drag" data-v="O" style="cursor:grab"/>`;
s+=`<circle cx="${Ox}" cy="${Oy}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2" class="p11-drag" data-v="O" style="cursor:grab"/>`;
s+=`<text x="${Ox-14}" y="${Oy+4}" font-size="13" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">O</text>`;
// A1 draggable
s+=`<circle cx="${A1x}" cy="${A1y}" r="9" fill="#f59e0b" opacity=".25" class="p11-drag" data-v="A1" style="cursor:grab"/>`;
s+=`<circle cx="${A1x}" cy="${A1y}" r="5" fill="#f59e0b" stroke="#fff" stroke-width="2" class="p11-drag" data-v="A1" style="cursor:grab"/>`;
s+=`</svg>`;
const wrap = document.getElementById('p11-thales-svg');
wrap.innerHTML = s;
const svgEl = wrap.querySelector('svg');
svgEl.querySelectorAll('.p11-drag').forEach(el=>{
el.addEventListener('pointerdown', ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const vname = el.dataset.v;
function onMove(e){
e.preventDefault();
const rect=svgEl.getBoundingClientRect();
const sx=W/rect.width, sy=H/rect.height;
const nx=(e.clientX-rect.left)*sx, ny=(e.clientY-rect.top)*sy;
if(vname==='O'){ Ox=Math.max(10,Math.min(W-10,nx)); Oy=Math.max(10,Math.min(H-10,ny)); }
else if(vname==='A1'){ A1x=Math.max(10,Math.min(W-10,nx)); A1y=Math.max(10,Math.min(H-10,ny)); }
drawThales();
}
function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); }
window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp);
});
});
const segLen = step1.toFixed(1);
document.getElementById('p11-thales-info').innerHTML =
`Все отрезки на первой стороне равны: <b>OA₁ = A₁A₂ = … = ${segLen}</b><br>Теорема Фалеса гарантирует: <b>OB₁ = B₁B₂ = … = ${segLen}</b> на второй стороне.`;
}
document.getElementById('p11-n-sl').addEventListener('input', function(){ nPts=+this.value; document.getElementById('p11-n-val').textContent=nPts; drawThales(); });
drawThales();
})();
/* == Конструктор деления == */
(function(){
let nDiv=3, animStep=0, animTimer=null;
function drawDiv(step){
const W2=400, H2=260;
const Ax=40, Ay=180, Bx=360, By=180;
const rayAng=-0.55;
const rayLen=120;
const ux=Math.cos(rayAng), uy=Math.sin(rayAng);
const segR = rayLen / nDiv;
let s=`<svg viewBox="0 0 ${W2} ${H2}" style="width:100%;max-width:420px;background:var(--card);border:1px solid var(--border);border-radius:14px">`;
// AB
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Bx}" y2="${By}" stroke="#0891b2" stroke-width="3"/>`;
s+=`<circle cx="${Ax}" cy="${Ay}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>`;
s+=`<circle cx="${Bx}" cy="${By}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>`;
s+=`<text x="${Ax-14}" y="${Ay+4}" font-size="13" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">A</text>`;
s+=`<text x="${Bx+8}" y="${By+4}" font-size="13" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">B</text>`;
if(step >= 1){
// auxiliary ray
const Cnx=Ax+ux*rayLen, Cny=Ay+uy*rayLen;
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Cnx.toFixed(1)}" y2="${Cny.toFixed(1)}" stroke="#f59e0b" stroke-width="2" stroke-dasharray="4 3"/>`;
s+=`<text x="${(Cnx+8).toFixed(1)}" y="${(Cny).toFixed(1)}" font-size="12" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">A${nDiv}</text>`;
}
if(step >= 2){
// equal segments on ray
for(let i=1;i<=nDiv;i++){
const px=Ax+ux*segR*i, py=Ay+uy*segR*i;
s+=`<circle cx="${px.toFixed(1)}" cy="${py.toFixed(1)}" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>`;
if(i<nDiv) s+=`<text x="${(px+7).toFixed(1)}" y="${(py-2).toFixed(1)}" font-size="10" fill="#92400e" font-family="Unbounded,sans-serif">A${i}</text>`;
}
}
if(step >= 3){
// line AnB
const Anx=Ax+ux*rayLen, Any=Ay+uy*rayLen;
s+=`<line x1="${Anx.toFixed(1)}" y1="${Any.toFixed(1)}" x2="${Bx}" y2="${By}" stroke="#ef4444" stroke-width="2"/>`;
}
if(step >= 4){
// parallels through Ai to AB
const Anx=Ax+ux*rayLen, Any=Ay+uy*rayLen;
const vx=Bx-Anx, vy=By-Any;
const colors=['#10b981','#8b5cf6','#ec4899','#f59e0b','#0891b2','#ef4444','#64748b'];
for(let i=1;i<nDiv;i++){
const px=Ax+ux*segR*i, py=Ay+uy*segR*i;
const t=(Ay-py)/vy;
const intX=px+t*vx;
s+=`<line x1="${px.toFixed(1)}" y1="${py.toFixed(1)}" x2="${intX.toFixed(1)}" y2="${Ay}" stroke="${colors[(i-1)%colors.length]}" stroke-width="1.5" stroke-dasharray="5 3"/>`;
s+=`<circle cx="${intX.toFixed(1)}" cy="${Ay}" r="4" fill="${colors[(i-1)%colors.length]}" stroke="#fff" stroke-width="1.5"/>`;
// label
const frac=i+'/'+nDiv;
s+=`<text x="${intX.toFixed(1)}" y="${Ay+16}" text-anchor="middle" font-size="9" fill="#64748b" font-family="JetBrains Mono,monospace">${frac}</text>`;
}
}
s+=`</svg>`;
document.getElementById('p11-div-svg').innerHTML=s;
const stepTexts=['','Шаг 1: Из A проведём вспомогательный луч AC.','Шаг 2: На луче откладываем n равных отрезков AA₁=A₁A₂=…','Шаг 3: Соединяем последнюю точку Aₙ с B.','Шаг 4: Через A₁,…,Aₙ₋₁ проводим прямые, параллельные AₙB. Готово — отрезки равны!'];
document.getElementById('p11-div-step').textContent = stepTexts[Math.min(step,4)] || '';
}
document.querySelectorAll('.p11-ndiv-btn').forEach(btn=>{
btn.addEventListener('click',()=>{
nDiv=+btn.dataset.n;
document.querySelectorAll('.p11-ndiv-btn').forEach(b=>b.className='btn p11-ndiv-btn');
btn.className='btn primary p11-ndiv-btn';
animStep=0; if(animTimer){clearInterval(animTimer);animTimer=null;} drawDiv(0);
});
});
document.getElementById('p11-div-build').addEventListener('click',()=>{
if(animTimer) return;
animStep=0; drawDiv(0);
animTimer=setInterval(()=>{ animStep++; drawDiv(animStep); if(animStep>=4){clearInterval(animTimer);animTimer=null;addXp(3,'p11-div');} },900);
});
document.getElementById('p11-div-reset').addEventListener('click',()=>{ if(animTimer){clearInterval(animTimer);animTimer=null;} animStep=0; drawDiv(0); });
// activate first button
document.querySelector('.p11-ndiv-btn[data-n="3"]').className='btn primary p11-ndiv-btn';
drawDiv(0);
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Отрезок $AB=20$. Разделить на $4$ равные части. Длина каждой части?', ans:5, hint:'20÷4=5'},
{q:'На одной стороне угла $OA_1=A_1A_2=3$. На второй — $OB_1=B_1B_2$. Найди $B_1B_2$.', ans:3, hint:'По теореме Фалеса B₁B₂ = A₁A₂ = 3'},
{q:'Три параллельные прямые отсекают на одной секущей отрезки по $7$. Сколько равных отрезков на второй секущей?', ans:3, hint:'По теореме Фалеса — тоже 3 равных отрезка по 7'},
{q:'Отрезок $AB=36$. Разделить на $9$ равных частей. Длина каждой части?', ans:4, hint:'36÷9=4'},
{q:'$OA_1=5$, $A_1A_2=5$, $A_2A_3=5$. Параллели через $A_1,A_2,A_3$ дают на второй стороне $OB_1$. Найди $OB_1$.', ans:5, hint:'OA₁=5, по Фалесу OB₁=5'},
];
let idx=0, score=0;
function show(){ document.getElementById('p11-tr-i').textContent=idx+1; document.getElementById('p11-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p11-tr-task')); document.getElementById('p11-tr-ans').value=''; document.getElementById('p11-tr-fb').style.display='none'; }
document.getElementById('p11-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p11-tr-score').textContent=0;show();});
document.getElementById('p11-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p11-tr-ans').value; const fb=document.getElementById('p11-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p11-tr-score').textContent=score;addXp(3,'p11-train');bumpProgress('p11',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p11-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p11-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p11-tr-go').click();});
show();
})();
/* == DnD == */
(function(){
const items=[
{id:'d1',html:'OA₁ и A₁A₂ (на первой стороне, равны по условию)', ans:'eq'},
{id:'d2',html:'OB₁ и B₁B₂ (на второй стороне, по Фалесу)', ans:'eq'},
{id:'d3',html:'OA₁ и OB₁ (разные стороны угла)', ans:'neq'},
{id:'d4',html:'A₁A₂ и B₁B₂ (параллельные секущие)', ans:'eq'},
{id:'d5',html:'OA₁ и A₂A₃ (на одной стороне, равны)', ans:'eq'},
{id:'d6',html:'A₁B₁ и A₂B₂ (боковые — не секущие)', ans:'neq'},
];
const sorter=setupSorter({poolId:'p11-dnd-pool',scopeSelector:'#p11-dnd-wrap',items,cats:['eq','neq']});
document.getElementById('p11-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p11-dnd-fb').style.display='none';});
document.getElementById('p11-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p11-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p11-dnd');bumpProgress('p11',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.');
});
})();
/* == Босс §11 == */
(function(){
const tasks=[
{q:'Три параллельных прямых отсекают на первой секущей отрезки $4, 4, 4$. Найди сумму отрезков на второй секущей.', ans:12, hint:'По Фалесу тоже 4+4+4=12'},
{q:'Отрезок $AB=35$. Разделить на $7$ равных частей. Длина каждой?', ans:5, hint:'35÷7=5'},
{q:'$OA_1=6$, параллели дают $OB_1$ и $B_1B_2=6$. Найди $B_1B_2$.', ans:6, hint:'По Фалесу B₁B₂ = A₁A₂ = 6'},
{q:'На одной стороне угла отложены $n=5$ равных отрезков по $3$. Сколько равных отрезков будет на второй стороне?', ans:5, hint:'По Фалесу — тоже 5'},
];
const bossBox=document.getElementById('p11-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="p11-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p11-boss-a${i}').value;const fb=document.getElementById('p11-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p11BossSolved.has(${i})){p11BossSolved.add(${i});addXp(5,'p11-boss${i}');bumpProgress('p11',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p11-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p11BossSolved=new Set();
})();
renderMath(box);
}
function buildP12(){
const box = document.getElementById('p12-body');
let html = '';
html += makeCard('theory','Медианы треугольника','12.1',`
<p><b>Медиана</b> треугольника — отрезок, соединяющий вершину с серединой противоположной стороны.</p>
<p>В треугольнике $ABC$: $AM_A$, $BM_B$, $CM_C$ — три медианы, где $M_A$, $M_B$, $M_C$ — середины сторон $BC$, $AC$, $AB$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 165" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle with all 3 medians meeting at centroid G -->
<polygon points="140,18 244,148 36,148" fill="rgba(16,185,129,.08)" stroke="#10b981" stroke-width="2"/>
<!-- midpoints -->
<!-- M_A = mid of BC = (244+36)/2, (148+148)/2 = (140, 148) -->
<!-- M_B = mid of AC = (140+244)/2, (18+148)/2 = (192, 83) -->
<!-- M_C = mid of AB = (140+36)/2, (18+148)/2 = (88, 83) -->
<!-- medians -->
<line x1="140" y1="18" x2="140" y2="148" stroke="#d97706" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="244" y1="148" x2="88" y2="83" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="36" y1="148" x2="192" y2="83" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- centroid G = ((140+244+36)/3, (18+148+148)/3) = (140, 104.7) -->
<circle cx="140" cy="105" r="5" fill="#ef4444" stroke="#fff" stroke-width="2"/>
<text x="150" y="100" font-size="10" fill="#b91c1c" font-weight="700" font-family="Unbounded,sans-serif">G</text>
<!-- midpoints -->
<circle cx="140" cy="148" r="3.5" fill="#d97706"/>
<circle cx="192" cy="83" r="3.5" fill="#0891b2"/>
<circle cx="88" cy="83" r="3.5" fill="#8b5cf6"/>
<text x="140" y="160" text-anchor="middle" font-size="8" fill="#b45309" font-family="Unbounded,sans-serif">M_A</text>
<text x="204" y="82" font-size="8" fill="#0e7490" font-family="Unbounded,sans-serif">M_B</text>
<text x="72" y="82" font-size="8" fill="#6d28d9" font-family="Unbounded,sans-serif">M_C</text>
<!-- vertices -->
<circle cx="140" cy="18" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="244" cy="148" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="36" cy="148" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<text x="140" y="8" text-anchor="middle" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="152" text-anchor="middle" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">B</text>
<text x="24" y="152" text-anchor="middle" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">C</text>
</svg></div>`);
html += makeCard('rule','Свойство медиан','12.2',`
<p><b>Теорема.</b> Три медианы треугольника пересекаются в одной точке $G$ (<i>центроид</i>, центр тяжести), которая делит каждую медиану в отношении $2:1$, считая от вершины:</p>
\\[AG:GM_A = BG:GM_B = CG:GM_C = 2:1\\]
<p>Следствие: $AG = \\dfrac{2}{3}AM_A$, $GM_A = \\dfrac{1}{3}AM_A$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 158" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- One median with 2:1 ratio highlighted -->
<polygon points="140,18 236,144 44,144" fill="rgba(16,185,129,.07)" stroke="#10b981" stroke-width="2"/>
<!-- single median AM_A highlighted -->
<line x1="140" y1="18" x2="140" y2="144" stroke="#d97706" stroke-width="2.5"/>
<!-- centroid G at 2/3 of the way from A -->
<!-- G = 140, 18 + 2/3*(144-18) = 18 + 84 = 102 -->
<circle cx="140" cy="102" r="5" fill="#ef4444" stroke="#fff" stroke-width="2"/>
<text x="150" y="97" font-size="10" fill="#b91c1c" font-weight="700" font-family="Unbounded,sans-serif">G</text>
<!-- 2:1 label brackets -->
<line x1="128" y1="18" x2="128" y2="102" stroke="#d97706" stroke-width="1.5"/>
<line x1="124" y1="18" x2="132" y2="18" stroke="#d97706" stroke-width="1.5"/>
<line x1="124" y1="102" x2="132" y2="102" stroke="#d97706" stroke-width="1.5"/>
<text x="118" y="64" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">2</text>
<line x1="128" y1="102" x2="128" y2="144" stroke="#8b5cf6" stroke-width="1.5"/>
<line x1="124" y1="144" x2="132" y2="144" stroke="#8b5cf6" stroke-width="1.5"/>
<text x="118" y="126" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">1</text>
<!-- M_A dot -->
<circle cx="140" cy="144" r="4" fill="#d97706"/>
<text x="150" y="148" font-size="9" fill="#b45309" font-family="Unbounded,sans-serif">M_A</text>
<!-- vertex A -->
<circle cx="140" cy="18" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<text x="140" y="8" text-anchor="middle" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="140" y="155" text-anchor="middle" font-size="9" fill="#64748b" font-family="JetBrains Mono,monospace">AG:GM_A = 2:1</text>
</svg></div>`);
html += makeCard('example','Формулы для медиан','12.3',`
<p>Длина медианы $m_a$ (от вершины $A$ к середине $BC$):</p>
\\[m_a = \\frac{1}{2}\\sqrt{2b^2+2c^2-a^2}\\]
<p>где $a,b,c$ — стороны треугольника.</p>
<p>Если $G$ делит $AM_A$ в отношении $2:1$: дано $|GM_A|=x \\Rightarrow |AM_A|=3x$, $|AG|=2x$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle with labeled sides a=6, b=8, c=10 and median m_a -->
<polygon points="60,128 220,128 140,28" fill="rgba(16,185,129,.08)" stroke="#10b981" stroke-width="2"/>
<!-- side labels -->
<text x="140" y="144" text-anchor="middle" font-size="11" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">a=8</text>
<text x="88" y="82" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">c=10</text>
<text x="180" y="82" font-size="10" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">b=7</text>
<!-- median from A(top) to midpoint of BC -->
<!-- midpoint of BC = (60+220)/2, 128 = 140, 128 -->
<line x1="140" y1="28" x2="140" y2="128" stroke="#d97706" stroke-width="2" stroke-dasharray="5 3"/>
<!-- tick mark at midpoint -->
<circle cx="140" cy="128" r="3.5" fill="#d97706"/>
<!-- centroid G at 2/3 from A -->
<circle cx="140" cy="95" r="4.5" fill="#ef4444" stroke="#fff" stroke-width="1.5"/>
<text x="150" y="91" font-size="9" fill="#b91c1c" font-weight="700" font-family="Unbounded,sans-serif">G</text>
<!-- formula box -->
<rect x="155" y="36" width="112" height="36" rx="5" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="1"/>
<text x="211" y="52" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">m_a = ½√(2b²+2c²-a²)</text>
<!-- vertices -->
<circle cx="140" cy="28" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="60" cy="128" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="220" cy="128" r="4" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<text x="140" y="18" text-anchor="middle" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">A</text>
<text x="46" y="132" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">B</text>
<text x="228" y="132" font-size="11" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">C</text>
</svg></div>`);
/* INTERACTIVE 1: SVG-треугольник с медианами */
html += `<div class="wg" id="p12-med-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живые медианы — тащи вершины треугольника</div></div>
<div class="wg-help">Перетащи вершины <b>A</b>, <b>B</b>, <b>C</b>. Медианы пересекаются в центроиде G (соотношение 2:1).</div>
<div id="p12-med-svg" style="display:flex;justify-content:center"></div>
<div id="p12-med-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* INTERACTIVE 2: Доказательство */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство свойства медиан</div></div>
<div id="p12-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p12-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p12-proof-next">Дальше</button>
<button class="btn" id="p12-proof-restart">Сначала</button>
</div>
</div>`;
/* INTERACTIVE 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">Введи длину медианы — найди отрезки AG и GM.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.92rem">Длина медианы $AM$ = <input type="number" id="p12-cmed" class="tinp" value="18" style="width:80px" min="1"></label>
<button class="btn primary" id="p12-calc-go">Вычислить</button>
</div>
<div id="p12-calc-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.9"></div>
</div>`;
/* INTERACTIVE 4: Тренажёр */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр задач на медианы</div></div>
<div class="score-display"><span>Задача <b id="p12-tr-i">1</b> / 5</span><span>Очки: <b id="p12-tr-score">0</b></span></div>
<div id="p12-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p12-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p12-tr-go">Проверить</button>
<button class="btn" id="p12-tr-start">Начать</button>
</div>
<div class="feedback" id="p12-tr-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 5: Босс §12 */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §12</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p12-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p12-read-btn" onclick="addXp(10,'p12-read');bumpProgress('p12',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>
Я прочитал §12 (+10 XP)
</button>
</div>`;
html += secNav('p11','p13');
box.innerHTML = html;
/* == SVG медианы == */
(function(){
const W=380, H=300;
let A={x:190,y:30}, B={x:50,y:270}, C={x:330,y:270};
function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; }
function centroid(A,B,C){ return {x:(A.x+B.x+C.x)/3, y:(A.y+B.y+C.y)/3}; }
function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); }
function redraw(){
const Ma=mid(B,C), Mb=mid(A,C), Mc=mid(A,B);
const G=centroid(A,B,C);
const dAG=dist(A,G), dGMa=dist(G,Ma);
const ratio=(dAG/dGMa).toFixed(2);
let s=`<svg id="p12-svg" viewBox="0 0 ${W} ${H}" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">`;
// triangle fill
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y}" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2.5"/>`;
// medians
const mCols=['#10b981','#f59e0b','#8b5cf6'];
[[A,Ma],[B,Mb],[C,Mc]].forEach(([V,M],i)=>{
s+=`<line x1="${V.x}" y1="${V.y}" x2="${M.x.toFixed(1)}" y2="${M.y.toFixed(1)}" stroke="${mCols[i]}" stroke-width="1.8" stroke-dasharray="5 3"/>`;
// midpoint dot
s+=`<circle cx="${M.x.toFixed(1)}" cy="${M.y.toFixed(1)}" r="4" fill="${mCols[i]}" stroke="#fff" stroke-width="1.5"/>`;
// midpoint label
const ox=[0,-14,14][i], oy=[14,-4,-4][i];
s+=`<text x="${(M.x+ox).toFixed(1)}" y="${(M.y+oy).toFixed(1)}" text-anchor="middle" font-size="10" font-weight="700" fill="${mCols[i]}" font-family="Unbounded,sans-serif">${['Mₐ','Mₙ','Mᶜ'][i]}</text>`;
});
// centroid G
s+=`<circle cx="${G.x.toFixed(1)}" cy="${G.y.toFixed(1)}" r="7" fill="#ef4444" stroke="#fff" stroke-width="2"/>`;
s+=`<text x="${(G.x+10).toFixed(1)}" y="${(G.y-6).toFixed(1)}" font-size="12" font-weight="700" fill="#b91c1c" font-family="Unbounded,sans-serif">G</text>`;
// vertices
const vNames=['A','B','C'], vPts=[A,B,C];
vPts.forEach((V,i)=>{
s+=`<circle cx="${V.x}" cy="${V.y}" r="9" fill="#0891b2" opacity=".2" class="p12-vdrag" data-v="${vNames[i]}" style="cursor:grab"/>`;
s+=`<circle cx="${V.x}" cy="${V.y}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2" class="p12-vdrag" data-v="${vNames[i]}" style="cursor:grab"/>`;
const ox2=[0,-14,14][i], oy2=[-14,14,14][i];
s+=`<text x="${V.x+ox2}" y="${V.y+oy2}" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${vNames[i]}</text>`;
});
s+=`</svg>`;
const wrap=document.getElementById('p12-med-svg'); wrap.innerHTML=s;
const svgEl=wrap.querySelector('svg');
svgEl.querySelectorAll('.p12-vdrag').forEach(el=>{
el.addEventListener('pointerdown',ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const vname=el.dataset.v;
function onMove(e){
e.preventDefault();
const rect=svgEl.getBoundingClientRect();
const sx=W/rect.width, sy=H/rect.height;
const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx));
const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy));
if(vname==='A') A={x:nx,y:ny};
else if(vname==='B') B={x:nx,y:ny};
else C={x:nx,y:ny};
redraw();
}
function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); }
window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp);
});
});
document.getElementById('p12-med-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">|AG| : |GM_A|</div><b>${dAG.toFixed(1)} : ${dGMa.toFixed(1)}${ratio}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Медиана AM_A</div><b>${dist(A,Ma).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Медиана BM_B</div><b>${dist(B,Mb).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Отношение 2:1</div><b style="color:var(--ok)">всегда!</b></div>`;
}
redraw();
})();
/* == Доказательство == */
(function(){
const A={x:160,y:25}, B={x:30,y:215}, C={x:290,y:215};
function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; }
const Ma=mid(B,C), Mb=mid(A,C);
const G={x:(A.x+B.x+C.x)/3, y:(A.y+B.y+C.y)/3};
const steps=[
{text:'<b>Дано:</b> $\\triangle ABC$, $M_a$ — середина $BC$, $M_b$ — середина $AC$, $G$ — точка пересечения медиан $AM_a$ и $BM_b$. Доказать: $AG:GM_a=2:1$.', h:'base'},
{text:'<b>Шаг 1.</b> Рассмотрим среднюю линию $M_aM_b$ треугольника $ABC$. По свойству средней линии: $M_aM_b \\parallel AB$ и $M_aM_b = \\dfrac{1}{2}AB$.', h:'midline'},
{text:'<b>Шаг 2.</b> Рассмотрим $\\triangle AM_bG$ и $\\triangle M_aM_bG$. $\\angle GAM_b = \\angle GM_aM_b$ (как накрест лежащие при $AB\\parallel M_aM_b$); $\\angle AGM_b = \\angle M_aGM_b$ (вертикальные).', h:'simtri'},
{text:'<b>Шаг 3.</b> $\\triangle AGM_b \\sim \\triangle M_aGM_b$ по двум углам. Коэффициент подобия: $k = AB/(M_aM_b) = 2$.', h:'simtri'},
{text:'<b>Шаг 4.</b> Значит $AG/GM_a = 2/1$. Аналогично для остальных медиан. <b>Все три медианы делятся точкой $G$ в отношении $2:1$.</b> <b>ч.т.д.</b>', h:'done'},
];
let step=0;
function draw(h){
let s=`<svg viewBox="0 0 320 240" style="width:100%;max-width:340px;background:var(--card);border:1px solid var(--border);border-radius:12px">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y}" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2"/>`;
if(h==='midline'||h==='simtri'||h==='done'){
s+=`<line x1="${Ma.x}" y1="${Ma.y}" x2="${Mb.x}" y2="${Mb.y}" stroke="#f59e0b" stroke-width="2.5"/>`;
}
if(h==='simtri'||h==='done'){
s+=`<polygon points="${A.x},${A.y} ${G.x.toFixed(1)},${G.y.toFixed(1)} ${Mb.x},${Mb.y}" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="1.5"/>`;
s+=`<polygon points="${Ma.x},${Ma.y} ${G.x.toFixed(1)},${G.y.toFixed(1)} ${Mb.x},${Mb.y}" fill="rgba(239,68,68,.15)" stroke="#ef4444" stroke-width="1.5"/>`;
}
// medians
s+=`<line x1="${A.x}" y1="${A.y}" x2="${Ma.x}" y2="${Ma.y}" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 3"/>`;
s+=`<line x1="${B.x}" y1="${B.y}" x2="${Mb.x}" y2="${Mb.y}" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 3"/>`;
s+=`<circle cx="${G.x.toFixed(1)}" cy="${G.y.toFixed(1)}" r="5" fill="#ef4444" stroke="#fff" stroke-width="2"/>`;
s+=`<text x="${(G.x+8).toFixed(1)}" y="${(G.y-5).toFixed(1)}" font-size="11" font-weight="700" fill="#b91c1c" font-family="Unbounded,sans-serif">G</text>`;
[[A,'A'],[B,'B'],[C,'C'],[Ma,'Mₐ'],[Mb,'Mₙ']].forEach(([P,lbl])=>{
s+=`<circle cx="${P.x}" cy="${P.y}" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${P.x+7}" y="${P.y-4}" font-size="10" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p12-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p12-proof-step').innerHTML=st.text; renderMath(document.getElementById('p12-proof-step')); draw(st.h); document.getElementById('p12-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p12-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p12-proof');bumpProgress('p12',12);document.getElementById('p12-proof-next').textContent='Изучено! +5 XP';document.getElementById('p12-proof-next').disabled=true;} });
document.getElementById('p12-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p12-proof-next').disabled=false;});
show();
})();
/* == Калькулятор == */
(function(){
function calc(){
const m=+document.getElementById('p12-cmed').value;
if(m<=0||isNaN(m)){ document.getElementById('p12-calc-out').innerHTML='<span style="color:var(--bad)">Введи положительное число.</span>'; return; }
const AG=m*2/3, GM=m/3;
document.getElementById('p12-calc-out').innerHTML=`$AM = ${m}$ (вся медиана)<br>$AG = \\dfrac{2}{3} \\cdot ${m} = ${fmt(AG)}$ (от вершины до центроида)<br>$GM_A = \\dfrac{1}{3} \\cdot ${m} = ${fmt(GM)}$ (от центроида до середины стороны)<br>Отношение $AG:GM_A = ${fmt(AG)}:${fmt(GM)} = 2:1$ ✓`;
renderMath(document.getElementById('p12-calc-out'));
addXp(2,'p12-calc');
}
document.getElementById('p12-calc-go').addEventListener('click',calc);
calc();
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Медиана $AM=18$. Найди $|AG|$.', ans:12, hint:'AG=(2/3)·18=12'},
{q:'Медиана $BM=15$. Найди $|GM|$.', ans:5, hint:'GM=(1/3)·15=5'},
{q:'$|GM_A|=4$. Найди длину медианы $AM_A$.', ans:12, hint:'AM=3·GM=3·4=12'},
{q:'$|AG|=10$. Найди $|GM_A|$.', ans:5, hint:'GM=AG/2=10/2=5'},
{q:'Медиана $CM=21$. Найди $|CG|$.', ans:14, hint:'CG=(2/3)·21=14'},
];
let idx=0,score=0;
function show(){ document.getElementById('p12-tr-i').textContent=idx+1; document.getElementById('p12-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p12-tr-task')); document.getElementById('p12-tr-ans').value=''; document.getElementById('p12-tr-fb').style.display='none'; }
document.getElementById('p12-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p12-tr-score').textContent=0;show();});
document.getElementById('p12-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p12-tr-ans').value; const fb=document.getElementById('p12-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p12-tr-score').textContent=score;addXp(3,'p12-train');bumpProgress('p12',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p12-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p12-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p12-tr-go').click();});
show();
})();
/* == Босс §12 == */
(function(){
const tasks=[
{q:'$|GM_A|=6$. Найди $|AG|$.', ans:12, hint:'AG=2·GM=2·6=12'},
{q:'Медиана $AM=27$. Найди $|AG|$.', ans:18, hint:'AG=(2/3)·27=18'},
{q:'$|AG|=8$. Найди длину медианы $AM$.', ans:12, hint:'AM=(3/2)·AG=(3/2)·8=12'},
{q:'Три медианы имеют длины $12$, $15$, $18$. Найди $|GM_A|$ для медианы длиной $12$.', ans:4, hint:'GM=(1/3)·12=4'},
];
const bossBox=document.getElementById('p12-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="p12-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p12-boss-a${i}').value;const fb=document.getElementById('p12-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p12BossSolved.has(${i})){p12BossSolved.add(${i});addXp(5,'p12-boss${i}');bumpProgress('p12',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p12-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p12BossSolved=new Set();
})();
renderMath(box);
}
function buildP13(){
const box = document.getElementById('p13-body');
let html = '';
html += makeCard('theory','Средняя линия треугольника','13.1',`
<p><b>Средняя линия треугольника</b> — отрезок, соединяющий середины двух его сторон.</p>
<p>В $\\triangle ABC$: $M_1$ — середина $AB$, $M_2$ — середина $AC$. Тогда $M_1M_2$ — средняя линия, параллельная $BC$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 162" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle with one middle line highlighted -->
<polygon points="140,16 244,146 36,146" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2"/>
<!-- M1 = mid of AB = (140+36)/2, (16+146)/2 = (88, 81) -->
<!-- M2 = mid of AC = (140+244)/2, (16+146)/2 = (192, 81) -->
<!-- middle line M1M2 -->
<line x1="88" y1="81" x2="192" y2="81" stroke="#d97706" stroke-width="3"/>
<!-- small parallel tick marks to show M1M2 ∥ BC -->
<line x1="154" y1="78" x2="162" y2="86" stroke="#d97706" stroke-width="2"/>
<line x1="158" y1="142" x2="166" y2="150" stroke="#d97706" stroke-width="2"/>
<!-- midpoints -->
<circle cx="88" cy="81" r="4.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="192" cy="81" r="4.5" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="74" y="76" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">M₁</text>
<text x="198" y="76" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">M₂</text>
<!-- labels -->
<text x="140" y="156" text-anchor="middle" font-size="11" fill="#0e7490" font-weight="700" font-family="JetBrains Mono,monospace">BC = a</text>
<text x="140" y="73" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">M₁M₂ = a/2</text>
<!-- vertices -->
<circle cx="140" cy="16" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="244" cy="146" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="36" cy="146" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<text x="140" y="6" text-anchor="middle" font-size="11" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="150" font-size="11" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">B</text>
<text x="24" y="150" font-size="11" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">C</text>
</svg></div>`);
html += makeCard('rule','Свойство средней линии','13.2',`
<p><b>Теорема.</b> Средняя линия треугольника параллельна третьей стороне и равна её половине:</p>
\\[M_1M_2 \\parallel BC, \\quad M_1M_2 = \\frac{1}{2}BC\\]
<p>В треугольнике три средние линии — они образуют <i>срединный треугольник</i>, делящий исходный на 4 равных треугольника.</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">
<!-- Triangle with all 3 middle lines forming inner medial triangle, 4 sub-triangles colored -->
<!-- Outer triangle: A(140,16) B(244,142) C(36,142) -->
<!-- midpoints: M_AB=(192,79) M_AC=(88,79) M_BC=(140,142) -->
<!-- 4 inner triangles -->
<polygon points="140,16 192,79 88,79" fill="rgba(16,185,129,.20)" stroke="#10b981" stroke-width="1.5"/>
<polygon points="192,79 244,142 140,142" fill="rgba(8,145,178,.20)" stroke="#0891b2" stroke-width="1.5"/>
<polygon points="88,79 140,142 36,142" fill="rgba(217,119,6,.20)" stroke="#d97706" stroke-width="1.5"/>
<polygon points="88,79 192,79 140,142" fill="rgba(139,92,246,.20)" stroke="#8b5cf6" stroke-width="1.5"/>
<!-- outer triangle -->
<polygon points="140,16 244,142 36,142" fill="none" stroke="#64748b" stroke-width="2.5"/>
<!-- middle lines (connecting midpoints) -->
<line x1="88" y1="79" x2="192" y2="79" stroke="#d97706" stroke-width="2"/>
<line x1="192" y1="79" x2="140" y2="142" stroke="#d97706" stroke-width="2"/>
<line x1="88" y1="79" x2="140" y2="142" stroke="#d97706" stroke-width="2"/>
<!-- vertices -->
<circle cx="140" cy="16" r="3.5" fill="#64748b"/><circle cx="244" cy="142" r="3.5" fill="#64748b"/><circle cx="36" cy="142" r="3.5" fill="#64748b"/>
<text x="140" y="6" text-anchor="middle" font-size="11" font-weight="700" fill="#475569" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="146" font-size="10" font-weight="700" fill="#475569" font-family="Unbounded,sans-serif">B</text>
<text x="22" y="146" font-size="10" font-weight="700" fill="#475569" font-family="Unbounded,sans-serif">C</text>
<text x="140" y="150" text-anchor="middle" font-size="9" fill="#6d28d9" font-weight="700" font-family="Inter,sans-serif">4 равных треугольника</text>
</svg></div>`);
html += makeCard('example','Периметр срединного треугольника','13.3',`
<p>Если стороны $\\triangle ABC$ равны $a$, $b$, $c$, то стороны срединного треугольника:</p>
\\[\\frac{a}{2},\\quad \\frac{b}{2},\\quad \\frac{c}{2}\\]
<p>Периметр срединного треугольника $= \\dfrac{a+b+c}{2} = \\dfrac{P}{2}$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle with a=10, b=8, c=6. Medial triangle with sides 5, 4, 3 labeled -->
<polygon points="140,16 244,138 36,138" fill="none" stroke="#64748b" stroke-width="2.5"/>
<!-- medial triangle -->
<polygon points="88,77 192,77 140,138" fill="rgba(217,119,6,.18)" stroke="#d97706" stroke-width="2"/>
<!-- outer side labels -->
<text x="86" y="86" font-size="10" fill="#475569" font-weight="700" font-family="JetBrains Mono,monospace">c=6</text>
<text x="176" y="86" font-size="10" fill="#475569" font-weight="700" font-family="JetBrains Mono,monospace">b=8</text>
<text x="140" y="152" text-anchor="middle" font-size="10" fill="#475569" font-weight="700" font-family="JetBrains Mono,monospace">a=10</text>
<!-- medial triangle side labels -->
<text x="104" y="112" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">3</text>
<text x="168" y="112" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">4</text>
<text x="140" y="72" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">5</text>
<!-- P medial = (3+4+5)/2 of original? No: medial P = a/2+b/2+c/2 = 12 -->
<text x="140" y="138" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">P_медиан = (6+8+10)/2 = 12</text>
<!-- vertices -->
<circle cx="140" cy="16" r="3.5" fill="#64748b"/><circle cx="244" cy="138" r="3.5" fill="#64748b"/><circle cx="36" cy="138" r="3.5" fill="#64748b"/>
<text x="140" y="6" text-anchor="middle" font-size="10" font-weight="700" fill="#475569" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="142" font-size="10" font-weight="700" fill="#475569" font-family="Unbounded,sans-serif">B</text>
<text x="22" y="142" font-size="10" font-weight="700" fill="#475569" font-family="Unbounded,sans-serif">C</text>
</svg></div>`);
/* INTERACTIVE 1: SVG треугольник со средними линиями */
html += `<div class="wg" id="p13-ml-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живые средние линии — тащи вершины</div></div>
<div class="wg-help">Перетащи <b>A</b>, <b>B</b>, <b>C</b>. Все три средние линии показаны, срединный треугольник подсвечен.</div>
<div id="p13-ml-svg" style="display:flex;justify-content:center"></div>
<div id="p13-ml-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* INTERACTIVE 2: Доказательство */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство свойства средней линии</div></div>
<div id="p13-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p13-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p13-proof-next">Дальше</button>
<button class="btn" id="p13-proof-restart">Сначала</button>
</div>
</div>`;
/* INTERACTIVE 3: Тренажёр */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Тренажёр задач на среднюю линию треугольника</div></div>
<div class="score-display"><span>Задача <b id="p13-tr-i">1</b> / 5</span><span>Очки: <b id="p13-tr-score">0</b></span></div>
<div id="p13-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p13-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p13-tr-go">Проверить</button>
<button class="btn" id="p13-tr-start">Начать</button>
</div>
<div class="feedback" id="p13-tr-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 4: Mini-quiz верно/неверно */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Верно или неверно?</div></div>
<div id="p13-quiz-list"></div>
<div class="actions"><button class="btn primary" id="p13-quiz-check">Проверить</button><button class="btn" id="p13-quiz-reset">Сначала</button></div>
<div class="feedback" id="p13-quiz-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 5: DnD */
html += `<div class="wg" id="p13-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Распознай среднюю линию</div></div>
<div class="wg-help">Перетащи отрезки: какие являются средними линиями треугольника?</div>
${DND_HINT_HTML}
<div id="p13-dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="yes">Средняя линия</h5><div class="drop-items" data-cat="yes"></div></div>
<div class="drop-box"><h5 data-cat="no">Не средняя линия</h5><div class="drop-items" data-cat="no"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p13-dnd-check">Проверить</button><button class="btn" id="p13-dnd-reset">Сначала</button></div>
<div class="feedback" id="p13-dnd-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 6: Босс §13 */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §13</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p13-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p13-read-btn" onclick="addXp(10,'p13-read');bumpProgress('p13',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>
Я прочитал §13 (+10 XP)
</button>
</div>`;
html += secNav('p12','p14');
box.innerHTML = html;
/* == SVG средние линии == */
(function(){
const W=380, H=300;
let A={x:190,y:30}, B={x:40,y:270}, C={x:340,y:270};
function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; }
function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); }
function redraw(){
const M1=mid(A,B), M2=mid(A,C), M3=mid(B,C);
let s=`<svg id="p13-svg" viewBox="0 0 ${W} ${H}" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">`;
// outer triangle
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y}" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2.5"/>`;
// median triangle fill
s+=`<polygon points="${M1.x.toFixed(1)},${M1.y.toFixed(1)} ${M2.x.toFixed(1)},${M2.y.toFixed(1)} ${M3.x.toFixed(1)},${M3.y.toFixed(1)}" fill="rgba(16,185,129,.2)" stroke="#10b981" stroke-width="2"/>`;
// middle lines labels
const mlCols=['#10b981','#f59e0b','#8b5cf6'];
const mlPairs=[[M1,M2],[M2,M3],[M1,M3]];
const mlLabels=['M₁M₂ ∥ BC','M₂M₃ ∥ AB','M₁M₃ ∥ AC'];
mlPairs.forEach(([P,Q],i)=>{
const mx=(P.x+Q.x)/2, my=(P.y+Q.y)/2;
s+=`<text x="${mx.toFixed(1)}" y="${(my-6).toFixed(1)}" text-anchor="middle" font-size="9" fill="${mlCols[i]}" font-weight="700" font-family="JetBrains Mono,monospace">${mlLabels[i]}</text>`;
});
// midpoints
[[M1,'M₁'],[M2,'M₂'],[M3,'M₃']].forEach(([P,lbl],i)=>{
s+=`<circle cx="${P.x.toFixed(1)}" cy="${P.y.toFixed(1)}" r="5" fill="${mlCols[i]}" stroke="#fff" stroke-width="2"/>`;
s+=`<text x="${(P.x+10).toFixed(1)}" y="${(P.y-4).toFixed(1)}" font-size="10" font-weight="700" fill="${mlCols[i]}" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
// vertices
['A','B','C'].forEach((lbl,i)=>{
const V=[A,B,C][i];
s+=`<circle cx="${V.x}" cy="${V.y}" r="9" fill="#0891b2" opacity=".2" class="p13-vd" data-v="${lbl}" style="cursor:grab"/>`;
s+=`<circle cx="${V.x}" cy="${V.y}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2" class="p13-vd" data-v="${lbl}" style="cursor:grab"/>`;
const ox=[0,-16,16][i], oy=[-16,14,14][i];
s+=`<text x="${V.x+ox}" y="${V.y+oy}" text-anchor="middle" dominant-baseline="middle" font-size="13" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
const wrap=document.getElementById('p13-ml-svg'); wrap.innerHTML=s;
const svgEl=wrap.querySelector('svg');
svgEl.querySelectorAll('.p13-vd').forEach(el=>{
el.addEventListener('pointerdown',ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const vname=el.dataset.v;
function onMove(e){
e.preventDefault();
const rect=svgEl.getBoundingClientRect();
const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width));
const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height));
if(vname==='A') A={x:nx,y:ny};
else if(vname==='B') B={x:nx,y:ny};
else C={x:nx,y:ny};
redraw();
}
function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); }
window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp);
});
});
const bc=dist(B,C), ac=dist(A,C), ab=dist(A,B);
const m12=dist(M1,M2), m23=dist(M2,M3), m13=dist(M1,M3);
document.getElementById('p13-ml-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">M₁M₂ = BC/2</div><b>${m12.toFixed(1)} = ${(bc/2).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">M₂M₃ = AB/2</div><b>${m23.toFixed(1)} = ${(ab/2).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">M₁M₃ = AC/2</div><b>${m13.toFixed(1)} = ${(ac/2).toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">P срединного / P исх.</div><b>${(m12+m23+m13).toFixed(1)} / ${(bc+ac+ab).toFixed(1)} = 1:2</b></div>`;
}
redraw();
})();
/* == Доказательство == */
(function(){
const A={x:160,y:25}, B={x:30,y:215}, C={x:290,y:215};
function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; }
const M1=mid(A,B), M2=mid(A,C);
const steps=[
{text:'<b>Дано:</b> $\\triangle ABC$, $M_1$ — середина $AB$, $M_2$ — середина $AC$. Доказать: $M_1M_2 \\parallel BC$, $M_1M_2 = \\dfrac{1}{2}BC$.', h:'base'},
{text:'<b>Шаг 1.</b> Отложим от $M_2$ отрезок $M_2D = M_1M_2$, так что $D$ лежит на луче $M_1M_2$ за $M_2$.', h:'step1'},
{text:'<b>Шаг 2.</b> Рассмотрим $\\triangle AM_1M_2$ и $\\triangle CM_2D$. $AM_1=CM_2=\\dfrac{1}{2}$-сторон; $\\angle AM_1M_2=\\angle CDM_2$ (вертикальные); $M_1M_2=M_2D$. По признаку «два угла и сторона»: $\\triangle AM_1M_2 \\cong \\triangle CDM_2$.', h:'step2'},
{text:'<b>Шаг 3.</b> Из равенства треугольников: $AM_1=DC$ и $\\angle M_1AM_2=\\angle DCM_2$, значит $AM_1 \\parallel DC$, т.е. $AB \\parallel CD$.', h:'step3'},
{text:'<b>Вывод.</b> $M_1BDC$ — параллелограмм ($M_1B \\parallel CD$, $M_1B=DC$). Значит $M_1D \\parallel BC$ и $BD=M_1M_2$. Но $BD=\\dfrac{1}{2}BC$ (так как $M_1$ — середина). Итак, $M_1M_2 \\parallel BC$ и $M_1M_2=\\dfrac{1}{2}BC$. <b>ч.т.д.</b>', h:'done'},
];
let step=0;
function draw(h){
const D={x:M2.x+(M2.x-M1.x), y:M2.y+(M2.y-M1.y)};
let s=`<svg viewBox="0 0 320 240" style="width:100%;max-width:340px;background:var(--card);border:1px solid var(--border);border-radius:12px">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y}" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2"/>`;
s+=`<line x1="${M1.x}" y1="${M1.y}" x2="${M2.x}" y2="${M2.y}" stroke="#10b981" stroke-width="2.5"/>`;
if(h==='step1'||h==='step2'||h==='step3'||h==='done'){
s+=`<line x1="${M2.x}" y1="${M2.y}" x2="${D.x.toFixed(1)}" y2="${D.y.toFixed(1)}" stroke="#f59e0b" stroke-width="2" stroke-dasharray="4 3"/>`;
s+=`<circle cx="${D.x.toFixed(1)}" cy="${D.y.toFixed(1)}" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${(D.x+8).toFixed(1)}" y="${(D.y).toFixed(1)}" font-size="10" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">D</text>`;
}
if(h==='step2'||h==='step3'||h==='done'){
s+=`<polygon points="${A.x},${A.y} ${M1.x},${M1.y} ${M2.x},${M2.y}" fill="rgba(8,145,178,.2)" stroke="#0891b2" stroke-width="1.5"/>`;
s+=`<polygon points="${C.x},${C.y} ${D.x.toFixed(1)},${D.y.toFixed(1)} ${M2.x},${M2.y}" fill="rgba(239,68,68,.15)" stroke="#ef4444" stroke-width="1.5"/>`;
}
if(h==='done'){
s+=`<polygon points="${M1.x},${M1.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x.toFixed(1)},${D.y.toFixed(1)}" fill="rgba(16,185,129,.1)" stroke="#10b981" stroke-width="1.5" stroke-dasharray="5 3"/>`;
}
[[A,'A'],[B,'B'],[C,'C'],[M1,'M₁'],[M2,'M₂']].forEach(([P,lbl])=>{
s+=`<circle cx="${P.x}" cy="${P.y}" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${P.x+7}" y="${P.y-4}" font-size="10" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p13-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p13-proof-step').innerHTML=st.text; renderMath(document.getElementById('p13-proof-step')); draw(st.h); document.getElementById('p13-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p13-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p13-proof');bumpProgress('p13',12);document.getElementById('p13-proof-next').textContent='Изучено! +5 XP';document.getElementById('p13-proof-next').disabled=true;} });
document.getElementById('p13-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p13-proof-next').disabled=false;});
show();
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'$BC=14$. Найди среднюю линию, параллельную $BC$.', ans:7, hint:'M₁M₂=BC/2=14/2=7'},
{q:'Средняя линия $= 9$. Найди сторону, которой она параллельна.', ans:18, hint:'BC=2·M₁M₂=2·9=18'},
{q:'Стороны треугольника $12, 16, 20$. Найди периметр срединного треугольника.', ans:24, hint:'P/2=(12+16+20)/2=48/2=24'},
{q:'Средняя линия треугольника $= 11$. Найди вторую среднюю линию, параллельную стороне $AB=16$.', ans:8, hint:'M₂M₃=AB/2=16/2=8'},
{q:'$BC=30$, средняя линия $M_1M_2$. Найди $M_1M_2$.', ans:15, hint:'M₁M₂=BC/2=30/2=15'},
];
let idx=0,score=0;
function show(){ document.getElementById('p13-tr-i').textContent=idx+1; document.getElementById('p13-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p13-tr-task')); document.getElementById('p13-tr-ans').value=''; document.getElementById('p13-tr-fb').style.display='none'; }
document.getElementById('p13-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p13-tr-score').textContent=0;show();});
document.getElementById('p13-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p13-tr-ans').value; const fb=document.getElementById('p13-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p13-tr-score').textContent=score;addXp(3,'p13-train');bumpProgress('p13',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p13-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p13-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p13-tr-go').click();});
show();
})();
/* == Mini-quiz == */
(function(){
const qs=[
{text:'Средняя линия треугольника параллельна третьей стороне.', ans:true},
{text:'Средняя линия треугольника равна третьей стороне.', ans:false},
{text:'В треугольнике три средние линии.', ans:true},
{text:'Срединный треугольник делит исходный на 4 равных треугольника.', ans:true},
];
const list=document.getElementById('p13-quiz-list');
list.innerHTML=qs.map((q,i)=>`
<div style="padding:10px 14px;background:var(--card);border:1px solid var(--border);border-radius:10px;margin-bottom:8px">
<div style="margin-bottom:6px;font-size:.95rem">${q.text}</div>
<div style="display:flex;gap:8px">
<button class="btn p13-qbtn" data-i="${i}" data-v="true">Верно</button>
<button class="btn p13-qbtn" data-i="${i}" data-v="false">Неверно</button>
</div>
</div>`).join('');
const sel={};
list.querySelectorAll('.p13-qbtn').forEach(btn=>{
btn.addEventListener('click',()=>{
const i=btn.dataset.i;
sel[i]=btn.dataset.v==='true';
list.querySelectorAll(`.p13-qbtn[data-i="${i}"]`).forEach(b=>b.className='btn p13-qbtn');
btn.className='btn primary p13-qbtn';
});
});
document.getElementById('p13-quiz-check').addEventListener('click',()=>{
let ok=0;
qs.forEach((q,i)=>{ if(sel[i]===q.ans) ok++; });
const fb=document.getElementById('p13-quiz-fb');
if(ok===qs.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p13-quiz');bumpProgress('p13',15);}
else feedback(fb,false,'Верно: '+ok+' из '+qs.length+'. Средняя линия = ПОЛОВИНА стороны (не равна ей).');
});
document.getElementById('p13-quiz-reset').addEventListener('click',()=>{ Object.keys(sel).forEach(k=>delete sel[k]); list.querySelectorAll('.p13-qbtn').forEach(b=>b.className='btn p13-qbtn'); document.getElementById('p13-quiz-fb').style.display='none'; });
})();
/* == DnD == */
(function(){
const items=[
{id:'s1',html:'Отрезок, соединяющий середины AB и AC', ans:'yes'},
{id:'s2',html:'Медиана из A к середине BC', ans:'no'},
{id:'s3',html:'Отрезок, соединяющий середины BC и AC', ans:'yes'},
{id:'s4',html:'Высота из A на BC', ans:'no'},
{id:'s5',html:'Отрезок, соединяющий середины AB и BC', ans:'yes'},
{id:'s6',html:'Биссектриса угла A', ans:'no'},
];
const sorter=setupSorter({poolId:'p13-dnd-pool',scopeSelector:'#p13-dnd-wrap',items,cats:['yes','no']});
document.getElementById('p13-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p13-dnd-fb').style.display='none';});
document.getElementById('p13-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p13-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p13-dnd');bumpProgress('p13',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Средняя линия соединяет середины двух сторон.');
});
})();
/* == Босс §13 == */
(function(){
const tasks=[
{q:'$BC=26$. Найди среднюю линию, параллельную $BC$.', ans:13, hint:'M₁M₂=BC/2=26/2=13'},
{q:'Средняя линия $= 8.5$. Найди параллельную ей сторону.', ans:17, hint:'BC=2·8.5=17'},
{q:'Стороны треугольника $10, 24, 26$. Найди периметр срединного треугольника.', ans:30, hint:'P_mid=(10+24+26)/2=60/2=30'},
{q:'$M_1M_2 = 7$, $M_2M_3 = 9$, $M_1M_3 = 5$. Найди наибольшую сторону треугольника.', ans:18, hint:'AB=2·M₂M₃=2·9=18'},
];
const bossBox=document.getElementById('p13-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="p13-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p13-boss-a${i}').value;const fb=document.getElementById('p13-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p13BossSolved.has(${i})){p13BossSolved.add(${i});addXp(5,'p13-boss${i}');bumpProgress('p13',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p13-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p13BossSolved=new Set();
})();
renderMath(box);
}
function buildP14(){
const box = document.getElementById('p14-body');
let html = '';
html += makeCard('theory','Трапеция — определение','14.1',`
<p><b>Трапеция</b> — четырёхугольник, у которого только одна пара противоположных сторон параллельна.</p>
<p>Параллельные стороны называются <b>основаниями</b> (обычно $a$ — большее, $b$ — меньшее), а две другие — <b>боковыми сторонами</b>.</p>
<p><b>Виды трапеций:</b></p>
<ul style="margin-left:18px;line-height:1.9">
<li><b>Произвольная</b> — боковые стороны не равны.</li>
<li><b>Равнобедренная</b> — боковые стороны равны: $AD=BC$.</li>
<li><b>Прямоугольная</b> — один из углов при боковой стороне прямой.</li>
</ul>
<div style="display:flex;justify-content:center;gap:12px;margin-top:12px;flex-wrap:wrap">
<div style="text-align:center"><svg viewBox="0 0 92 76" style="width:88px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="18,58 74,58 62,20 30,20" fill="rgba(20,184,166,.12)" stroke="#14b8a6" stroke-width="2"/>
<text x="46" y="14" text-anchor="middle" font-size="8" fill="#0f766e" font-family="JetBrains Mono,monospace">b</text>
<text x="46" y="70" text-anchor="middle" font-size="8" fill="#0f766e" font-family="JetBrains Mono,monospace">a</text>
<text x="46" y="80" text-anchor="middle" font-size="7" fill="#0f766e" font-family="Inter,sans-serif">Произвольная</text>
</svg></div>
<div style="text-align:center"><svg viewBox="0 0 92 76" style="width:88px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="14,58 78,58 64,20 28,20" fill="rgba(139,92,246,.12)" stroke="#8b5cf6" stroke-width="2"/>
<line x1="28" y1="22" x2="34" y2="28" stroke="#8b5cf6" stroke-width="1.5"/>
<line x1="64" y1="22" x2="70" y2="28" stroke="#8b5cf6" stroke-width="1.5"/>
<text x="46" y="80" text-anchor="middle" font-size="7" fill="#6d28d9" font-family="Inter,sans-serif">Равнобедренная</text>
</svg></div>
<div style="text-align:center"><svg viewBox="0 0 92 76" style="width:88px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="18,58 74,58 74,20 34,20" fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="2"/>
<path d="M74,58 L66,58 L66,50" fill="none" stroke="#d97706" stroke-width="1.5"/>
<text x="46" y="80" text-anchor="middle" font-size="7" fill="#b45309" font-family="Inter,sans-serif">Прямоугольная</text>
</svg></div>
</div>`);
html += makeCard('rule','Средняя линия трапеции','14.2',`
<p><b>Средняя линия трапеции</b> — отрезок $MN$, соединяющий середины боковых сторон.</p>
<p><b>Свойство:</b> средняя линия параллельна основаниям и равна их полусумме:</p>
\\[MN \\parallel AD \\parallel BC, \\quad MN = \\frac{a+b}{2}\\]
<p>Площадь трапеции: $S = \\dfrac{(a+b)}{2} \\cdot h = MN \\cdot h$, где $h$ — высота.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 158" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Trapezoid with labeled bases a, b, middle line m, height h -->
<!-- ABCD: A(28,130) B(252,130) C(210,42) D(70,42) -->
<polygon points="28,130 252,130 210,42 70,42" fill="rgba(20,184,166,.10)" stroke="#14b8a6" stroke-width="2"/>
<!-- middle line MN (midpoints of AD and BC) -->
<!-- M = mid of AD = (28+70)/2, (130+42)/2 = (49, 86) -->
<!-- N = mid of BC = (252+210)/2, (130+42)/2 = (231, 86) -->
<line x1="49" y1="86" x2="231" y2="86" stroke="#d97706" stroke-width="3"/>
<circle cx="49" cy="86" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<circle cx="231" cy="86" r="4" fill="#d97706" stroke="#fff" stroke-width="1.5"/>
<text x="38" y="82" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">M</text>
<text x="236" y="82" font-size="9" fill="#b45309" font-weight="700" font-family="Unbounded,sans-serif">N</text>
<!-- height h -->
<line x1="140" y1="42" x2="140" y2="130" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2"/>
<text x="146" y="90" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">h</text>
<!-- base labels -->
<text x="140" y="148" text-anchor="middle" font-size="11" fill="#0f766e" font-weight="700" font-family="JetBrains Mono,monospace">a (большее)</text>
<text x="140" y="36" text-anchor="middle" font-size="11" fill="#0f766e" font-weight="700" font-family="JetBrains Mono,monospace">b (меньшее)</text>
<text x="140" y="80" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">m = (a+b)/2</text>
<!-- vertices -->
<circle cx="28" cy="130" r="3.5" fill="#14b8a6"/><circle cx="252" cy="130" r="3.5" fill="#14b8a6"/>
<circle cx="210" cy="42" r="3.5" fill="#14b8a6"/><circle cx="70" cy="42" r="3.5" fill="#14b8a6"/>
</svg></div>`);
html += makeCard('example','Формулы для трапеции','14.3',`
<p>Дано: основания $a$ и $b$, высота $h$, средняя линия $m$.</p>
\\[m = \\frac{a+b}{2} \\quad \\Rightarrow \\quad a+b = 2m\\]
\\[S = m \\cdot h = \\frac{(a+b)}{2} \\cdot h\\]
<p>Из $m$ найти неизвестное основание: $b = 2m - a$.</p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Trapezoid with a=10, b=6, h=5, m=8 labeled -->
<polygon points="22,118 218,118 182,40 58,40" fill="rgba(20,184,166,.10)" stroke="#14b8a6" stroke-width="2"/>
<!-- middle line at y=79 -->
<line x1="40" y1="79" x2="200" y2="79" stroke="#d97706" stroke-width="3"/>
<!-- height -->
<line x1="120" y1="40" x2="120" y2="118" stroke="#8b5cf6" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- right angle mark at bottom -->
<path d="M120,118 L120,110 L128,110" fill="none" stroke="#8b5cf6" stroke-width="1.5"/>
<!-- labels -->
<text x="120" y="36" text-anchor="middle" font-size="11" fill="#0f766e" font-weight="700" font-family="JetBrains Mono,monospace">b = 6</text>
<text x="120" y="134" text-anchor="middle" font-size="11" fill="#0f766e" font-weight="700" font-family="JetBrains Mono,monospace">a = 10</text>
<text x="120" y="73" text-anchor="middle" font-size="10" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">m = 8</text>
<text x="132" y="84" font-size="10" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">h=5</text>
<!-- formula results box -->
<rect x="210" y="36" width="60" height="76" rx="5" fill="rgba(20,184,166,.10)" stroke="#14b8a6" stroke-width="1"/>
<text x="240" y="58" text-anchor="middle" font-size="9" fill="#0f766e" font-weight="700" font-family="JetBrains Mono,monospace">m=8</text>
<text x="240" y="76" text-anchor="middle" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">S=40</text>
<text x="240" y="94" text-anchor="middle" font-size="8" fill="#64748b" font-family="JetBrains Mono,monospace">8·5=40</text>
</svg></div>`);
/* INTERACTIVE 1: SVG-трапеция draggable */
html += `<div class="wg" id="p14-trap-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живая трапеция — тащи вершины</div></div>
<div class="wg-help">Перетащи вершины <b>A</b>, <b>B</b>, <b>C</b>, <b>D</b>. Средняя линия и все формулы пересчитываются живо.</div>
<div id="p14-trap-svg" style="display:flex;justify-content:center"></div>
<div id="p14-trap-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* INTERACTIVE 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">Переключай тип — трапеция меняет форму, свойства обновляются.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px">
<button class="btn primary" id="p14-type-arb">Произвольная</button>
<button class="btn" id="p14-type-iso">Равнобедренная</button>
<button class="btn" id="p14-type-right">Прямоугольная</button>
</div>
<div id="p14-type-svg" style="display:flex;justify-content:center"></div>
<div id="p14-type-info" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.9rem;margin-top:8px"></div>
</div>`;
/* INTERACTIVE 3: Доказательство */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Доказательство свойства средней линии трапеции</div></div>
<div id="p14-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p14-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p14-proof-next">Дальше</button>
<button class="btn" id="p14-proof-restart">Сначала</button>
</div>
</div>`;
/* INTERACTIVE 4: Калькулятор */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Калькулятор трапеции</div></div>
<div class="wg-help">Введи два из трёх значений (a, b, m) — найди третье. Добавь высоту для площади.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.92rem">$a$ = <input type="number" id="p14-ca" class="tinp" value="12" style="width:70px" min="0"></label>
<label style="font-size:.92rem">$b$ = <input type="number" id="p14-cb" class="tinp" value="8" style="width:70px" min="0"></label>
<label style="font-size:.92rem">$h$ = <input type="number" id="p14-ch" class="tinp" value="5" style="width:70px" min="0"></label>
<button class="btn primary" id="p14-calc-go">Вычислить</button>
</div>
<div id="p14-calc-out" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.9"></div>
</div>`;
/* INTERACTIVE 5: Тренажёр */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Тренажёр задач на трапецию</div></div>
<div class="score-display"><span>Задача <b id="p14-tr-i">1</b> / 5</span><span>Очки: <b id="p14-tr-score">0</b></span></div>
<div id="p14-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p14-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p14-tr-go">Проверить</button>
<button class="btn" id="p14-tr-start">Начать</button>
</div>
<div class="feedback" id="p14-tr-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 6: Босс §14 */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §14</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p14-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p14-read-btn" onclick="addXp(10,'p14-read');bumpProgress('p14',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>
Я прочитал §14 (+10 XP)
</button>
</div>`;
html += secNav('p13','p15');
box.innerHTML = html;
/* == SVG трапеция draggable == */
(function(){
const W=400, H=280;
let A={x:60,y:220}, B={x:340,y:220}, C={x:280,y:70}, D={x:120,y:70};
// Keep BC parallel to AD: when dragging C or D, we sync y-coordinate
function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); }
function redraw(){
const Ma={x:(A.x+D.x)/2, y:(A.y+D.y)/2};
const Mb={x:(B.x+C.x)/2, y:(B.y+C.y)/2};
const a=dist(A,B), b=dist(D,C);
const m=(a+b)/2;
const h=Math.abs(A.y-D.y);
let s=`<svg id="p14-svg" viewBox="0 0 ${W} ${H}" style="width:100%;max-width:420px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x},${D.y}" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2.5"/>`;
// height dashes
s+=`<line x1="${D.x}" y1="${D.y}" x2="${D.x}" y2="${A.y}" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4 3"/>`;
// median line
s+=`<line x1="${Ma.x.toFixed(1)}" y1="${Ma.y.toFixed(1)}" x2="${Mb.x.toFixed(1)}" y2="${Mb.y.toFixed(1)}" stroke="#10b981" stroke-width="2.5"/>`;
s+=`<circle cx="${Ma.x.toFixed(1)}" cy="${Ma.y.toFixed(1)}" r="4" fill="#10b981" stroke="#fff" stroke-width="2"/>`;
s+=`<circle cx="${Mb.x.toFixed(1)}" cy="${Mb.y.toFixed(1)}" r="4" fill="#10b981" stroke="#fff" stroke-width="2"/>`;
const mLx=((Ma.x+Mb.x)/2).toFixed(1), mLy=(((Ma.y+Mb.y)/2)-10).toFixed(1);
s+=`<text x="${mLx}" y="${mLy}" text-anchor="middle" font-size="10" font-weight="700" fill="#047857" font-family="JetBrains Mono,monospace">m=${m.toFixed(1)}</text>`;
// base labels
s+=`<text x="${((A.x+B.x)/2).toFixed(1)}" y="${(A.y+16).toFixed(1)}" text-anchor="middle" font-size="10" fill="#0e7490" font-family="JetBrains Mono,monospace">a=${a.toFixed(1)}</text>`;
s+=`<text x="${((D.x+C.x)/2).toFixed(1)}" y="${(D.y-6).toFixed(1)}" text-anchor="middle" font-size="10" fill="#0e7490" font-family="JetBrains Mono,monospace">b=${b.toFixed(1)}</text>`;
// height label
s+=`<text x="${(D.x-22).toFixed(1)}" y="${((D.y+A.y)/2).toFixed(1)}" text-anchor="middle" font-size="10" fill="#64748b" font-family="JetBrains Mono,monospace">h=${h.toFixed(0)}</text>`;
// vertices draggable
const vNames=['A','B','C','D'];
[A,B,C,D].forEach((V,i)=>{
s+=`<circle cx="${V.x}" cy="${V.y}" r="9" fill="#0891b2" opacity=".2" class="p14-vd" data-v="${vNames[i]}" style="cursor:grab"/>`;
s+=`<circle cx="${V.x}" cy="${V.y}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2" class="p14-vd" data-v="${vNames[i]}" style="cursor:grab"/>`;
const offx=[-14,12,12,-14][i], offy=[12,12,-12,-12][i];
s+=`<text x="${V.x+offx}" y="${V.y+offy}" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${vNames[i]}</text>`;
});
s+=`</svg>`;
const wrap=document.getElementById('p14-trap-svg'); wrap.innerHTML=s;
const svgEl=wrap.querySelector('svg');
svgEl.querySelectorAll('.p14-vd').forEach(el=>{
el.addEventListener('pointerdown',ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const vname=el.dataset.v;
function onMove(e){
e.preventDefault();
const rect=svgEl.getBoundingClientRect();
const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width));
const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height));
if(vname==='A'){ A={x:nx,y:ny}; B.y=ny; }
else if(vname==='B'){ B={x:nx,y:ny}; A.y=ny; }
else if(vname==='C'){ C={x:nx,y:ny}; D.y=ny; }
else if(vname==='D'){ D={x:nx,y:ny}; C.y=ny; }
redraw();
}
function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); }
window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp);
});
});
const S=(a+b)/2*h;
document.getElementById('p14-trap-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Основание a</div><b>${a.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Основание b</div><b>${b.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Средняя линия m=(a+b)/2</div><b>${m.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Площадь S=m·h</div><b>${S.toFixed(1)}</b></div>`;
}
redraw();
})();
/* == Конструктор типов == */
(function(){
function drawType(type){
const W2=340, H2=200;
let pts, info;
if(type==='arb'){
pts=[[50,170],[290,170],[230,50],[110,50]];
info='Произвольная трапеция: $AD \\parallel BC$, боковые стороны не равны.';
} else if(type==='iso'){
pts=[[50,170],[290,170],[220,50],[120,50]];
info='Равнобедренная трапеция: $AD \\parallel BC$, $AB = CD$ (боковые стороны равны). Углы при каждом основании равны.';
} else {
pts=[[50,170],[290,170],[290,50],[50,50]];
info='Прямоугольная трапеция: $AD \\parallel BC$, один угол $90°$ при боковой стороне $AB$.';
}
const [A2,B2,C2,D2]=pts.map(p=>({x:p[0],y:p[1]}));
const Ma2={x:(A2.x+D2.x)/2, y:(A2.y+D2.y)/2};
const Mb2={x:(B2.x+C2.x)/2, y:(B2.y+C2.y)/2};
let s=`<svg viewBox="0 0 ${W2} ${H2}" style="width:100%;max-width:360px;background:var(--card);border:1px solid var(--border);border-radius:14px">`;
s+=`<polygon points="${pts.map(p=>p.join(',')).join(' ')}" fill="rgba(8,145,178,.10)" stroke="#0891b2" stroke-width="2.5"/>`;
s+=`<line x1="${Ma2.x}" y1="${Ma2.y}" x2="${Mb2.x}" y2="${Mb2.y}" stroke="#10b981" stroke-width="2.5"/>`;
if(type==='right'){
const sq=8;
s+=`<polyline points="${A2.x+sq},${A2.y} ${A2.x+sq},${A2.y-sq} ${A2.x},${A2.y-sq}" fill="none" stroke="#0891b2" stroke-width="1.5"/>`;
}
if(type==='iso'){
// equal side marks
function tickMark(P,Q,col){
const mx=(P.x+Q.x)/2, my=(P.y+Q.y)/2;
const dx=Q.x-P.x, dy=Q.y-P.y, l=Math.hypot(dx,dy);
const nx=-dy/l*6, ny=dx/l*6;
return `<line x1="${(mx-nx).toFixed(1)}" y1="${(my-ny).toFixed(1)}" x2="${(mx+nx).toFixed(1)}" y2="${(my+ny).toFixed(1)}" stroke="${col}" stroke-width="2"/>`;
}
s+=tickMark(A2,D2,'#10b981'); s+=tickMark(B2,C2,'#10b981');
}
['A','B','C','D'].forEach((lbl,i)=>{
const V=pts[i];
const offx=[-16,12,12,-16][i], offy=[12,12,-12,-12][i];
s+=`<text x="${V[0]+offx}" y="${V[1]+offy}" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p14-type-svg').innerHTML=s;
document.getElementById('p14-type-info').innerHTML=info;
renderMath(document.getElementById('p14-type-info'));
}
document.getElementById('p14-type-arb').addEventListener('click',()=>{ ['arb','iso','right'].forEach(t=>document.getElementById('p14-type-'+t).className='btn'); document.getElementById('p14-type-arb').className='btn primary'; drawType('arb'); });
document.getElementById('p14-type-iso').addEventListener('click',()=>{ ['arb','iso','right'].forEach(t=>document.getElementById('p14-type-'+t).className='btn'); document.getElementById('p14-type-iso').className='btn primary'; drawType('iso'); });
document.getElementById('p14-type-right').addEventListener('click',()=>{ ['arb','iso','right'].forEach(t=>document.getElementById('p14-type-'+t).className='btn'); document.getElementById('p14-type-right').className='btn primary'; drawType('right'); });
drawType('arb');
})();
/* == Доказательство == */
(function(){
const A3={x:50,y:190}, B3={x:290,y:190}, C3={x:230,y:60}, D3={x:110,y:60};
const M={x:(A3.x+D3.x)/2, y:(A3.y+D3.y)/2};
const N={x:(B3.x+C3.x)/2, y:(B3.y+C3.y)/2};
const steps=[
{text:'<b>Дано:</b> трапеция $ABCD$, $AD \\parallel BC$. $M$ — середина $AB$, $N$ — середина $CD$. Доказать: $MN \\parallel AD$, $MN = \\dfrac{AD+BC}{2}$.', h:'base'},
{text:'<b>Шаг 1.</b> Проведём диагональ $AC$. Рассмотрим $\\triangle ABC$: $M$ — середина $AB$, пересечение $MN$ с $AC$ — середина $AC$ (назовём $P$).', h:'diag'},
{text:'<b>Шаг 2.</b> В $\\triangle ABC$: $MP$ — средняя линия, $MP \\parallel BC$, $MP = \\dfrac{BC}{2}$.', h:'midABC'},
{text:'<b>Шаг 3.</b> В $\\triangle ACD$: $P$ — середина $AC$, $N$ — середина $CD$. $PN$ — средняя линия, $PN \\parallel AD$, $PN = \\dfrac{AD}{2}$.', h:'midACD'},
{text:'<b>Вывод.</b> $MP \\parallel BC \\parallel AD$, $PN \\parallel AD \\Rightarrow M$, $P$, $N$ коллинеарны и $MN \\parallel AD$.<br>$MN = MP + PN = \\dfrac{BC}{2} + \\dfrac{AD}{2} = \\dfrac{AD+BC}{2}$. <b>ч.т.д.</b>', h:'done'},
];
let step=0;
function draw(h){
const P={x:(A3.x+C3.x)/2, y:(A3.y+C3.y)/2};
let s=`<svg viewBox="0 0 340 250" style="width:100%;max-width:360px;background:var(--card);border:1px solid var(--border);border-radius:12px">`;
s+=`<polygon points="${A3.x},${A3.y} ${B3.x},${B3.y} ${C3.x},${C3.y} ${D3.x},${D3.y}" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2"/>`;
s+=`<line x1="${M.x}" y1="${M.y}" x2="${N.x}" y2="${N.y}" stroke="#10b981" stroke-width="2.5"/>`;
if(h==='diag'||h==='midABC'||h==='midACD'||h==='done'){
s+=`<line x1="${A3.x}" y1="${A3.y}" x2="${C3.x}" y2="${C3.y}" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 3"/>`;
s+=`<circle cx="${P.x}" cy="${P.y}" r="4" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${P.x+7}" y="${P.y-4}" font-size="10" font-weight="700" fill="#b45309" font-family="Unbounded,sans-serif">P</text>`;
}
if(h==='midABC'||h==='done'){
s+=`<polygon points="${A3.x},${A3.y} ${B3.x},${B3.y} ${C3.x},${C3.y}" fill="rgba(8,145,178,.12)" stroke="#0891b2" stroke-width="1.5"/>`;
}
if(h==='midACD'||h==='done'){
s+=`<polygon points="${A3.x},${A3.y} ${C3.x},${C3.y} ${D3.x},${D3.y}" fill="rgba(16,185,129,.15)" stroke="#10b981" stroke-width="1.5"/>`;
}
[[A3,'A'],[B3,'B'],[C3,'C'],[D3,'D'],[M,'M'],[N,'N']].forEach(([P2,lbl])=>{
s+=`<circle cx="${P2.x}" cy="${P2.y}" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${P2.x+7}" y="${P2.y-4}" font-size="10" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p14-proof-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p14-proof-step').innerHTML=st.text; renderMath(document.getElementById('p14-proof-step')); draw(st.h); document.getElementById('p14-proof-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p14-proof-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p14-proof');bumpProgress('p14',12);document.getElementById('p14-proof-next').textContent='Изучено! +5 XP';document.getElementById('p14-proof-next').disabled=true;} });
document.getElementById('p14-proof-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p14-proof-next').disabled=false;});
show();
})();
/* == Калькулятор == */
(function(){
function calc(){
const a=+document.getElementById('p14-ca').value;
const b=+document.getElementById('p14-cb').value;
const h=+document.getElementById('p14-ch').value;
if(a<=0||b<=0||isNaN(a)||isNaN(b)){ document.getElementById('p14-calc-out').innerHTML='<span style="color:var(--bad)">Введи положительные основания.</span>'; return; }
const m=(a+b)/2;
const S=h>0?m*h:null;
let html2=`$m = \\dfrac{a+b}{2} = \\dfrac{${a}+${b}}{2} = ${m}$`;
if(S!==null) html2+=`<br>$S = m \\cdot h = ${m} \\cdot ${h} = ${S}$`;
html2+=`<br>Если известно $m=${m}$ и $a=${a}$, то $b = 2m - a = ${2*m} - ${a} = ${b}$`;
document.getElementById('p14-calc-out').innerHTML=html2;
renderMath(document.getElementById('p14-calc-out'));
addXp(2,'p14-calc');
}
document.getElementById('p14-calc-go').addEventListener('click',calc);
calc();
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Основания трапеции $a=10$, $b=6$. Найди среднюю линию.', ans:8, hint:'m=(10+6)/2=8'},
{q:'Средняя линия $m=9$, основание $a=14$. Найди второе основание $b$.', ans:4, hint:'b=2m-a=18-14=4'},
{q:'Основания $a=15$, $b=7$, высота $h=4$. Найди площадь.', ans:44, hint:'S=(15+7)/2·4=11·4=44'},
{q:'Средняя линия $m=11$, высота $h=6$. Найди площадь.', ans:66, hint:'S=m·h=11·6=66'},
{q:'Основания $a=20$, $b=12$. Найди среднюю линию.', ans:16, hint:'m=(20+12)/2=16'},
];
let idx=0,score=0;
function show(){ document.getElementById('p14-tr-i').textContent=idx+1; document.getElementById('p14-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p14-tr-task')); document.getElementById('p14-tr-ans').value=''; document.getElementById('p14-tr-fb').style.display='none'; }
document.getElementById('p14-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p14-tr-score').textContent=0;show();});
document.getElementById('p14-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p14-tr-ans').value; const fb=document.getElementById('p14-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p14-tr-score').textContent=score;addXp(3,'p14-train');bumpProgress('p14',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p14-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p14-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p14-tr-go').click();});
show();
})();
/* == Босс §14 == */
(function(){
const tasks=[
{q:'Основания трапеции $a=18$, $b=10$. Найди среднюю линию.', ans:14, hint:'m=(18+10)/2=14'},
{q:'Средняя линия $m=13$, основание $b=9$. Найди $a$.', ans:17, hint:'a=2m-b=26-9=17'},
{q:'Средняя линия $m=7$, высота $h=8$. Найди площадь.', ans:56, hint:'S=7·8=56'},
{q:'Основания $a=24$, $b=16$, высота $h=5$. Найди площадь.', ans:100, hint:'S=(24+16)/2·5=20·5=100'},
];
const bossBox=document.getElementById('p14-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="p14-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p14-boss-a${i}').value;const fb=document.getElementById('p14-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p14BossSolved.has(${i})){p14BossSolved.add(${i});addXp(5,'p14-boss${i}');bumpProgress('p14',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p14-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p14BossSolved=new Set();
})();
renderMath(box);
}
function buildP15(){
const box = document.getElementById('p15-body');
let html = '';
html += makeCard('theory','Равнобедренная трапеция','15.1',`
<p><b>Равнобедренная трапеция</b> — трапеция, у которой боковые стороны равны: $AB = CD$.</p>
<p><b>Свойство 1.</b> Углы при каждом основании равны: $\\angle A = \\angle B$, $\\angle C = \\angle D$.</p>
<p><b>Свойство 2.</b> Диагонали равны: $AC = BD$.</p>
<p><b>Свойство 3.</b> Сумма углов при одной боковой стороне равна $180°$: $\\angle A + \\angle D = 180°$, $\\angle B + \\angle C = 180°$.</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">
<!-- Isosceles trapezoid ABCD: A(28,128), B(252,128), C(196,40), D(84,40) -->
<polygon points="28,128 252,128 196,40 84,40" fill="rgba(139,92,246,.10)" stroke="#8b5cf6" stroke-width="2"/>
<!-- equal diagonals -->
<line x1="28" y1="128" x2="196" y2="40" stroke="#10b981" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="252" y1="128" x2="84" y2="40" stroke="#d97706" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- equal side ticks AD = BC -->
<line x1="50" y1="84" x2="58" y2="76" stroke="#8b5cf6" stroke-width="2"/>
<line x1="222" y1="84" x2="230" y2="76" stroke="#8b5cf6" stroke-width="2"/>
<!-- equal angle arcs at A and B -->
<path d="M42,118 A18,18 0 0,1 56,104" stroke="#8b5cf6" stroke-width="2" fill="rgba(139,92,246,.2)"/>
<text x="62" y="118" font-size="9" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">α</text>
<path d="M224,104 A18,18 0 0,1 238,118" stroke="#8b5cf6" stroke-width="2" fill="rgba(139,92,246,.2)"/>
<text x="206" y="118" font-size="9" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">α</text>
<!-- diag labels -->
<text x="98" y="84" font-size="9" fill="#047857" font-weight="700" font-family="JetBrains Mono,monospace">AC</text>
<text x="172" y="84" font-size="9" fill="#b45309" font-weight="700" font-family="JetBrains Mono,monospace">BD</text>
<!-- vertices -->
<circle cx="28" cy="128" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="252" cy="128" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="196" cy="40" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="84" cy="40" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<text x="14" y="132" font-size="11" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="132" font-size="11" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="202" y="34" font-size="11" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="70" y="34" font-size="11" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
<text x="140" y="150" text-anchor="middle" font-size="9" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">∠A=∠B, AC=BD</text>
</svg></div>`);
html += makeCard('rule','Доказательство свойства 1: углы при основании','15.2',`
<p>Из $A$ и $B$ опустим высоты $AH_1$ и $BH_2$ на $CD$ (верхнее основание).</p>
<p>$\\triangle AH_1D$ и $\\triangle BH_2C$: $AH_1=BH_2$ (высоты в трапеции с равными боковыми), $AD=BC$ (условие), $\\angle H_1=\\angle H_2=90°$.</p>
<p>По «гипотенуза-катет»: $\\triangle AH_1D \\cong \\triangle BH_2C \\Rightarrow \\angle D = \\angle C$.</p>
<p>Аналогично $\\angle A = \\angle B$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Isosceles trapezoid with heights H1, H2 dropped -->
<polygon points="28,120 252,120 196,38 84,38" fill="rgba(139,92,246,.08)" stroke="#8b5cf6" stroke-width="2"/>
<!-- heights from D(84,38) and C(196,38) to bottom -->
<line x1="84" y1="38" x2="84" y2="120" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="196" y1="38" x2="196" y2="120" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- right angle marks -->
<path d="M84,120 L84,112 L92,112" fill="none" stroke="#0891b2" stroke-width="1.5"/>
<path d="M196,120 L188,120 L188,112" fill="none" stroke="#0891b2" stroke-width="1.5"/>
<!-- H1, H2 labels -->
<text x="88" y="132" font-size="9" fill="#0e7490" font-weight="700" font-family="Unbounded,sans-serif">H₁</text>
<text x="192" y="132" font-size="9" fill="#0e7490" font-weight="700" font-family="Unbounded,sans-serif">H₂</text>
<!-- triangles colored -->
<polygon points="28,120 84,120 84,38" fill="rgba(8,145,178,.18)" stroke="none"/>
<polygon points="196,38 196,120 252,120" fill="rgba(16,185,129,.18)" stroke="none"/>
<!-- equal angle arcs -->
<path d="M40,110 A16,16 0 0,1 50,98" stroke="#8b5cf6" stroke-width="1.5" fill="rgba(139,92,246,.2)"/>
<path d="M230,98 A16,16 0 0,1 240,110" stroke="#8b5cf6" stroke-width="1.5" fill="rgba(139,92,246,.2)"/>
<text x="56" y="108" font-size="8" fill="#6d28d9" font-family="JetBrains Mono,monospace">∠A</text>
<text x="210" y="108" font-size="8" fill="#6d28d9" font-family="JetBrains Mono,monospace">∠B</text>
<circle cx="28" cy="120" r="3.5" fill="#8b5cf6"/><circle cx="252" cy="120" r="3.5" fill="#8b5cf6"/>
<circle cx="196" cy="38" r="3.5" fill="#8b5cf6"/><circle cx="84" cy="38" r="3.5" fill="#8b5cf6"/>
<text x="140" y="144" text-anchor="middle" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">△AH₁D ≅ △BH₂C ⟹ ∠A=∠B</text>
</svg></div>`);
html += makeCard('rule','Доказательство свойства 2: диагонали равны','15.3',`
<p>Рассмотрим $\\triangle ABD$ и $\\triangle BAC$ (общее основание $AB$).</p>
<p>$AD = BC$ (равнобедренная), $\\angle A = \\angle B$ (свойство 1), $AB = AB$.</p>
<p>По признаку «два угла и сторона»: $\\triangle ABD \\cong \\triangle BAC \\Rightarrow BD = AC$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Isosceles trapezoid with both diagonals and 2 triangles highlighted -->
<polygon points="28,120 252,120 196,38 84,38" fill="rgba(139,92,246,.07)" stroke="#8b5cf6" stroke-width="2"/>
<!-- triangles ABD and BAC -->
<polygon points="28,120 252,120 84,38" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>
<polygon points="28,120 252,120 196,38" fill="rgba(217,119,6,.18)" stroke="#d97706" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- diagonals -->
<line x1="28" y1="120" x2="196" y2="38" stroke="#10b981" stroke-width="2"/>
<line x1="252" y1="120" x2="84" y2="38" stroke="#d97706" stroke-width="2"/>
<!-- equal diagonal tick marks -->
<line x1="100" y1="88" x2="108" y2="80" stroke="#10b981" stroke-width="2.5"/>
<line x1="172" y1="88" x2="180" y2="80" stroke="#d97706" stroke-width="2.5"/>
<!-- triangle labels -->
<text x="110" y="100" font-size="9" fill="#047857" font-weight="700" font-family="Inter,sans-serif">△ABD</text>
<text x="158" y="100" font-size="9" fill="#b45309" font-weight="700" font-family="Inter,sans-serif">△BAC</text>
<circle cx="28" cy="120" r="3.5" fill="#8b5cf6"/><circle cx="252" cy="120" r="3.5" fill="#8b5cf6"/>
<circle cx="196" cy="38" r="3.5" fill="#8b5cf6"/><circle cx="84" cy="38" r="3.5" fill="#8b5cf6"/>
<text x="14" y="124" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="124" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="202" y="32" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="68" y="32" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
<text x="140" y="142" text-anchor="middle" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">△ABD ≅ △BAC ⟹ AC = BD</text>
</svg></div>`);
/* INTERACTIVE 1: SVG равнобедренная трапеция */
html += `<div class="wg" id="p15-trap-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Живая равнобедренная трапеция — тащи вершины</div></div>
<div class="wg-help">Трапеция остаётся равнобедренной. Наблюдай: боковые стороны равны, диагонали равны, углы при основании равны.</div>
<div id="p15-trap-svg" style="display:flex;justify-content:center"></div>
<div id="p15-trap-info" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-top:10px"></div>
</div>`;
/* INTERACTIVE 2: Доказательство 1 */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство: углы при основании равны</div></div>
<div id="p15-proof1-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p15-proof1-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p15-proof1-next">Дальше</button>
<button class="btn" id="p15-proof1-restart">Сначала</button>
</div>
</div>`;
/* INTERACTIVE 3: Доказательство 2 */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Доказательство: диагонали равны</div></div>
<div id="p15-proof2-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p15-proof2-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p15-proof2-next">Дальше</button>
<button class="btn" id="p15-proof2-restart">Сначала</button>
</div>
</div>`;
/* INTERACTIVE 4: Тренажёр */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр задач на равнобедренную трапецию</div></div>
<div class="score-display"><span>Задача <b id="p15-tr-i">1</b> / 5</span><span>Очки: <b id="p15-tr-score">0</b></span></div>
<div id="p15-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p15-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p15-tr-go">Проверить</button>
<button class="btn" id="p15-tr-start">Начать</button>
</div>
<div class="feedback" id="p15-tr-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 5: DnD */
html += `<div class="wg" id="p15-dnd-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Чьё это свойство?</div></div>
<div class="wg-help">Распредели свойства по фигурам.</div>
${DND_HINT_HTML}
<div id="p15-dnd-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="any">Любая трапеция</h5><div class="drop-items" data-cat="any"></div></div>
<div class="drop-box"><h5 data-cat="iso">Только равнобедренная</h5><div class="drop-items" data-cat="iso"></div></div>
<div class="drop-box"><h5 data-cat="rect">Прямоугольник тоже</h5><div class="drop-items" data-cat="rect"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p15-dnd-check">Проверить</button><button class="btn" id="p15-dnd-reset">Сначала</button></div>
<div class="feedback" id="p15-dnd-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 6: Босс §15 */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §15</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p15-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p15-read-btn" onclick="addXp(10,'p15-read');bumpProgress('p15',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>
Я прочитал §15 (+10 XP)
</button>
</div>`;
html += secNav('p14','p16');
box.innerHTML = html;
/* == SVG равнобедренная трапеция == */
(function(){
const W=400, H=280;
// Keep it isosceles: D and C are symmetric around centerX, top base fixed y
let cx=200, topY=60, botY=220, halfTop=80, halfBot=140;
function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); }
function angDeg(P,O,Q){ const ax=P.x-O.x,ay=P.y-O.y,bx=Q.x-O.x,by=Q.y-O.y; return Math.acos(Math.max(-1,Math.min(1,(ax*bx+ay*by)/(Math.hypot(ax,ay)*Math.hypot(bx,by)))))*180/Math.PI; }
function redraw(){
const A={x:cx-halfBot, y:botY}, B={x:cx+halfBot, y:botY};
const C={x:cx+halfTop, y:topY}, D={x:cx-halfTop, y:topY};
const side=dist(A,D);
const diag=dist(A,C);
const angA=angDeg(D,A,B);
const angD=angDeg(A,D,C);
let s=`<svg id="p15-svg" viewBox="0 0 ${W} ${H}" style="width:100%;max-width:420px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x},${D.y}" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2.5"/>`;
// diagonals
s+=`<line x1="${A.x}" y1="${A.y}" x2="${C.x}" y2="${C.y}" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="5 3"/>`;
s+=`<line x1="${B.x}" y1="${B.y}" x2="${D.x}" y2="${D.y}" stroke="#8b5cf6" stroke-width="1.8" stroke-dasharray="5 3"/>`;
// diagonal labels
s+=`<text x="${((A.x+C.x)/2-16).toFixed(1)}" y="${((A.y+C.y)/2).toFixed(1)}" font-size="10" fill="#b45309" font-family="JetBrains Mono,monospace">AC=${diag.toFixed(1)}</text>`;
s+=`<text x="${((B.x+D.x)/2+4).toFixed(1)}" y="${((B.y+D.y)/2).toFixed(1)}" font-size="10" fill="#6d28d9" font-family="JetBrains Mono,monospace">BD=${dist(B,D).toFixed(1)}</text>`;
// equal side marks
function tickMark2(P,Q,col){
const mx=(P.x+Q.x)/2, my=(P.y+Q.y)/2;
const dx=Q.x-P.x, dy=Q.y-P.y, l=Math.hypot(dx,dy);
const nx=-dy/l*7, ny=dx/l*7;
return `<line x1="${(mx-nx).toFixed(1)}" y1="${(my-ny).toFixed(1)}" x2="${(mx+nx).toFixed(1)}" y2="${(my+ny).toFixed(1)}" stroke="${col}" stroke-width="2.5"/>`;
}
s+=tickMark2(A,D,'#10b981'); s+=tickMark2(B,C,'#10b981');
// angle arcs
function arcMark(O,P,Q,col,r){
const a1=Math.atan2(P.y-O.y,P.x-O.x), a2=Math.atan2(Q.y-O.y,Q.x-O.x);
const x1=O.x+r*Math.cos(a1), y1=O.y+r*Math.sin(a1);
const x2=O.x+r*Math.cos(a2), y2=O.y+r*Math.sin(a2);
return `<path d="M ${x1.toFixed(1)},${y1.toFixed(1)} A ${r},${r} 0 0,0 ${x2.toFixed(1)},${y2.toFixed(1)}" fill="none" stroke="${col}" stroke-width="2"/>`;
}
s+=arcMark(A,D,B,'#ef4444',22); s+=arcMark(B,A,C,'#ef4444',22);
// vertices draggable
const vNames=['A','B','C','D'], vPts=[A,B,C,D];
vPts.forEach((V,i)=>{
s+=`<circle cx="${V.x}" cy="${V.y}" r="9" fill="#0891b2" opacity=".2" class="p15-vd" data-v="${vNames[i]}" style="cursor:grab"/>`;
s+=`<circle cx="${V.x}" cy="${V.y}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2" class="p15-vd" data-v="${vNames[i]}" style="cursor:grab"/>`;
const offx=[-14,12,12,-14][i], offy=[14,14,-12,-12][i];
s+=`<text x="${V.x+offx}" y="${V.y+offy}" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${vNames[i]}</text>`;
});
s+=`</svg>`;
const wrap=document.getElementById('p15-trap-svg'); wrap.innerHTML=s;
const svgEl=wrap.querySelector('svg');
svgEl.querySelectorAll('.p15-vd').forEach(el=>{
el.addEventListener('pointerdown',ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const vname=el.dataset.v;
function onMove(e){
e.preventDefault();
const rect=svgEl.getBoundingClientRect();
const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width));
const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height));
if(vname==='A'){ halfBot=Math.max(30, cx-nx); botY=ny; }
else if(vname==='B'){ halfBot=Math.max(30, nx-cx); botY=ny; }
else if(vname==='C'){ halfTop=Math.max(10, nx-cx); topY=ny; }
else if(vname==='D'){ halfTop=Math.max(10, cx-nx); topY=ny; }
redraw();
}
function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); }
window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp);
});
});
const eq=(Math.abs(diag-dist(B,D))<0.5)?'<span style="color:var(--ok)">Равны!</span>':'не равны';
document.getElementById('p15-trap-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Боковые AD = BC</div><b>${side.toFixed(1)}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Диагонали AC = BD</div><b>${eq}</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">Угол A = Угол B</div><b>${angA.toFixed(1)}° = ${angA.toFixed(1)}°</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.85rem"><div style="color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;margin-bottom:3px">A+D = 180°</div><b>${(angA+angD).toFixed(1)}°</b></div>`;
}
redraw();
})();
/* == Доказательство 1: углы == */
(function(){
const A={x:50,y:210}, B={x:290,y:210}, C={x:230,y:70}, D={x:110,y:70};
const H1={x:A.x,y:D.y}, H2={x:B.x,y:C.y};
const steps=[
{text:'<b>Дано:</b> равнобедренная трапеция $ABCD$ ($AD \\parallel BC$, $AB = CD$). Доказать: $\\angle A = \\angle B$, $\\angle C = \\angle D$.', h:'base'},
{text:'<b>Шаг 1.</b> Проведём высоты $AH_1$ и $BH_2$ из вершин $A$ и $B$ на $DC$ (или его продолжение).', h:'heights'},
{text:'<b>Шаг 2.</b> В прямоугольных треугольниках $\\triangle AH_1D$ и $\\triangle BH_2C$: $AH_1 = BH_2$ (высоты параллельной трапеции), $AD = BC$ (боковые стороны).', h:'tri'},
{text:'<b>Шаг 3.</b> По признаку «гипотенуза-катет»: $\\triangle AH_1D \\cong \\triangle BH_2C \\Rightarrow \\angle D = \\angle C$.', h:'tri'},
{text:'<b>Вывод.</b> $\\angle A = 180° - \\angle D = 180° - \\angle C = \\angle B$. Углы при нижнем и верхнем основаниях равны попарно. <b>ч.т.д.</b>', h:'done'},
];
let step=0;
function draw(h){
let s=`<svg viewBox="0 0 340 250" style="width:100%;max-width:360px;background:var(--card);border:1px solid var(--border);border-radius:12px">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x},${D.y}" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2"/>`;
if(h==='heights'||h==='tri'||h==='done'){
s+=`<line x1="${A.x}" y1="${A.y}" x2="${H1.x}" y2="${H1.y}" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="4 3"/>`;
s+=`<line x1="${B.x}" y1="${B.y}" x2="${H2.x}" y2="${H2.y}" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="4 3"/>`;
const sq=7;
s+=`<polyline points="${H1.x+sq},${H1.y} ${H1.x+sq},${H1.y-sq} ${H1.x},${H1.y-sq}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
s+=`<polyline points="${H2.x-sq},${H2.y} ${H2.x-sq},${H2.y-sq} ${H2.x},${H2.y-sq}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
}
if(h==='tri'||h==='done'){
s+=`<polygon points="${A.x},${A.y} ${H1.x},${H1.y} ${D.x},${D.y}" fill="rgba(8,145,178,.18)" stroke="#0891b2" stroke-width="1.5"/>`;
s+=`<polygon points="${B.x},${B.y} ${H2.x},${H2.y} ${C.x},${C.y}" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="1.5"/>`;
}
[[A,'A'],[B,'B'],[C,'C'],[D,'D']].forEach(([P,lbl])=>{
s+=`<circle cx="${P.x}" cy="${P.y}" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${P.x+7}" y="${P.y-4}" font-size="10" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p15-proof1-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p15-proof1-step').innerHTML=st.text; renderMath(document.getElementById('p15-proof1-step')); draw(st.h); document.getElementById('p15-proof1-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p15-proof1-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p15-proof1');bumpProgress('p15',10);document.getElementById('p15-proof1-next').textContent='Изучено! +5 XP';document.getElementById('p15-proof1-next').disabled=true;} });
document.getElementById('p15-proof1-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p15-proof1-next').disabled=false;});
show();
})();
/* == Доказательство 2: диагонали == */
(function(){
const A={x:50,y:210}, B={x:290,y:210}, C={x:230,y:70}, D={x:110,y:70};
const steps=[
{text:'<b>Дано:</b> равнобедренная трапеция $ABCD$, $\\angle A = \\angle B$ (доказано). Доказать: $AC = BD$.', h:'base'},
{text:'<b>Шаг 1.</b> Рассмотрим $\\triangle ABD$ и $\\triangle BAC$.', h:'tri1'},
{text:'<b>Шаг 2.</b> $AD = BC$ (боковые стороны равны), $\\angle DAB = \\angle CBA$ (свойство 1), $AB = AB$ (общее).', h:'tri2'},
{text:'<b>Шаг 3.</b> По признаку «два угла и сторона»: $\\triangle ABD \\cong \\triangle BAC$.', h:'tri2'},
{text:'<b>Вывод.</b> $BD = AC$ — диагонали равны. <b>ч.т.д.</b>', h:'done'},
];
let step=0;
function draw(h){
let s=`<svg viewBox="0 0 340 250" style="width:100%;max-width:360px;background:var(--card);border:1px solid var(--border);border-radius:12px">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x},${D.y}" fill="rgba(8,145,178,.06)" stroke="#0891b2" stroke-width="2"/>`;
if(h==='tri1'||h==='tri2'||h==='done'){
s+=`<line x1="${A.x}" y1="${A.y}" x2="${C.x}" y2="${C.y}" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5 3"/>`;
s+=`<line x1="${B.x}" y1="${B.y}" x2="${D.x}" y2="${D.y}" stroke="#8b5cf6" stroke-width="2" stroke-dasharray="5 3"/>`;
}
if(h==='tri2'||h==='done'){
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${D.x},${D.y}" fill="rgba(8,145,178,.15)" stroke="#0891b2" stroke-width="1.5"/>`;
s+=`<polygon points="${B.x},${B.y} ${A.x},${A.y} ${C.x},${C.y}" fill="rgba(16,185,129,.15)" stroke="#10b981" stroke-width="1.5"/>`;
}
[[A,'A'],[B,'B'],[C,'C'],[D,'D']].forEach(([P,lbl])=>{
s+=`<circle cx="${P.x}" cy="${P.y}" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>`;
s+=`<text x="${P.x+7}" y="${P.y-4}" font-size="10" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p15-proof2-svg').innerHTML=s;
}
function show(){ const st=steps[step]; document.getElementById('p15-proof2-step').innerHTML=st.text; renderMath(document.getElementById('p15-proof2-step')); draw(st.h); document.getElementById('p15-proof2-next').textContent=step<steps.length-1?'Дальше':'Готово'; }
document.getElementById('p15-proof2-next').addEventListener('click',()=>{ if(step<steps.length-1){step++;show();}else{addXp(5,'p15-proof2');bumpProgress('p15',10);document.getElementById('p15-proof2-next').textContent='Изучено! +5 XP';document.getElementById('p15-proof2-next').disabled=true;} });
document.getElementById('p15-proof2-restart').addEventListener('click',()=>{step=0;show();document.getElementById('p15-proof2-next').disabled=false;});
show();
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'Равнобедренная трапеция, $\\angle A = 70°$. Найди $\\angle B$.', ans:70, hint:'∠B = ∠A = 70°'},
{q:'Равнобедренная трапеция, $\\angle A = 70°$. Найди $\\angle D$.', ans:110, hint:'∠A + ∠D = 180°, ∠D = 18070 = 110°'},
{q:'Диагональ равнобедренной трапеции $AC = 15$. Найди $BD$.', ans:15, hint:'AC = BD = 15 (диагонали равны)'},
{q:'Равнобедренная трапеция, $\\angle C = 65°$. Найди $\\angle D$.', ans:65, hint:'∠C = ∠D = 65°'},
{q:'Равнобедренная трапеция, $\\angle B = 120°$. Найди $\\angle C$.', ans:60, hint:'∠B + ∠C = 180°, ∠C = 60°'},
];
let idx=0,score=0;
function show(){ document.getElementById('p15-tr-i').textContent=idx+1; document.getElementById('p15-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p15-tr-task')); document.getElementById('p15-tr-ans').value=''; document.getElementById('p15-tr-fb').style.display='none'; }
document.getElementById('p15-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p15-tr-score').textContent=0;show();});
document.getElementById('p15-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p15-tr-ans').value; const fb=document.getElementById('p15-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p15-tr-score').textContent=score;addXp(3,'p15-train');bumpProgress('p15',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p15-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p15-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p15-tr-go').click();});
show();
})();
/* == DnD == */
(function(){
const items=[
{id:'p1',html:'Только одна пара параллельных сторон', ans:'any'},
{id:'p2',html:'Сумма углов при боковой стороне = 180°', ans:'any'},
{id:'p3',html:'Боковые стороны равны', ans:'iso'},
{id:'p4',html:'Диагонали равны', ans:'iso'},
{id:'p5',html:'Углы при каждом основании равны', ans:'iso'},
{id:'p6',html:'Все углы 90°', ans:'rect'},
];
const sorter=setupSorter({poolId:'p15-dnd-pool',scopeSelector:'#p15-dnd-wrap',items,cats:['any','iso','rect']});
document.getElementById('p15-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p15-dnd-fb').style.display='none';});
document.getElementById('p15-dnd-check').addEventListener('click',()=>{
let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;});
const fb=document.getElementById('p15-dnd-fb');
if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p15-dnd');bumpProgress('p15',15);}
else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.');
});
})();
/* == Босс §15 == */
(function(){
const tasks=[
{q:'Равнобедренная трапеция, $\\angle A = 55°$. Найди $\\angle D$.', ans:125, hint:'∠A+∠D=180°, ∠D=125°'},
{q:'$BD = 17$ в равнобедренной трапеции. Найди $AC$.', ans:17, hint:'AC = BD = 17'},
{q:'Равнобедренная трапеция, $\\angle C = 40°$. Найди $\\angle A$.', ans:140, hint:'∠C+∠B=180°, ∠A=∠B=18040=140°'},
{q:'Равнобедренная трапеция, $\\angle A = 80°$. Найди $\\angle C$.', ans:100, hint:'∠A+∠D=180°→∠D=100°; ∠C=∠D=100°'},
];
const bossBox=document.getElementById('p15-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="p15-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p15-boss-a${i}').value;const fb=document.getElementById('p15-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p15BossSolved.has(${i})){p15BossSolved.add(${i});addXp(5,'p15-boss${i}');bumpProgress('p15',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p15-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p15BossSolved=new Set();
})();
renderMath(box);
}
function buildP16(){
const box = document.getElementById('p16-body');
let html = '';
html += makeCard('theory','Признаки равнобедренной трапеции','16.1',`
<p><b>Признак 1.</b> Если в трапеции углы при одном из оснований равны, то она является равнобедренной.</p>
<p><b>Признак 2.</b> Если в трапеции диагонали равны, то она является равнобедренной.</p>
<div style="display:flex;justify-content:center;gap:14px;margin-top:12px;flex-wrap:wrap">
<div style="text-align:center"><svg viewBox="0 0 120 96" style="width:115px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="14,76 106,76 84,26 36,26" fill="rgba(139,92,246,.10)" stroke="#8b5cf6" stroke-width="2"/>
<!-- equal angle arcs at A and B -->
<path d="M24,66 A16,16 0 0,1 34,54" stroke="#8b5cf6" stroke-width="2" fill="rgba(139,92,246,.2)"/>
<path d="M86,54 A16,16 0 0,1 96,66" stroke="#8b5cf6" stroke-width="2" fill="rgba(139,92,246,.2)"/>
<text x="38" y="66" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">α</text>
<text x="72" y="66" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">α</text>
<text x="60" y="92" text-anchor="middle" font-size="8" fill="#6d28d9" font-weight="700" font-family="Inter,sans-serif">Признак 1</text>
</svg></div>
<div style="text-align:center"><svg viewBox="0 0 120 96" style="width:115px;background:#fafafa;border:1px solid var(--border);border-radius:8px">
<polygon points="14,76 106,76 84,26 36,26" fill="rgba(139,92,246,.10)" stroke="#8b5cf6" stroke-width="2"/>
<!-- equal diagonals -->
<line x1="14" y1="76" x2="84" y2="26" stroke="#10b981" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="106" y1="76" x2="36" y2="26" stroke="#d97706" stroke-width="1.5" stroke-dasharray="4 2"/>
<!-- equal tick marks -->
<line x1="42" y1="56" x2="50" y2="48" stroke="#10b981" stroke-width="2"/>
<line x1="70" y1="56" x2="78" y2="48" stroke="#d97706" stroke-width="2"/>
<text x="60" y="92" text-anchor="middle" font-size="8" fill="#6d28d9" font-weight="700" font-family="Inter,sans-serif">Признак 2</text>
</svg></div>
</div>`);
html += makeCard('rule','Доказательство признака 1','16.2',`
<p>Дано: трапеция $ABCD$, $AD \\parallel BC$, $\\angle A = \\angle B$. Доказать: $AD = BC$ (т.е. трапеция равнобедренная).</p>
<p>Через $C$ проведём прямую, параллельную $BD$, до пересечения с $AD$ в точке $E$. $BDCE$ — параллелограмм, $BE = CD$, $CE = BD$.</p>
<p>В $\\triangle AEC$: $\\angle A = \\angle AEC$ (как внутренние односторонние при $BC \\parallel AE$, но $\\angle AEC = \\angle B = \\angle A$) $\\Rightarrow \\triangle AEC$ — равнобедренный, $AE = AC$... Итог: $AD = BC$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 144" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Trapezoid ABCD with extension to E, parallelogram BDCE highlighted -->
<polygon points="28,118 252,118 196,38 84,38" fill="rgba(139,92,246,.07)" stroke="#8b5cf6" stroke-width="2"/>
<!-- CE parallel to BD: C(196,38) → E. BD: B(252,118) to D(84,38). Direction: (84-252, 38-118) = (-168,-80). E = C + that: (196-168, 38-80) = (28, -42) ... too far. Use shorter: E=(196+84-252, 38+38-118)=(28,-42) clip to (28,38) ~ use E at x=28,y=38 roughly -->
<!-- Instead, simple construction: draw auxiliary line CE -->
<line x1="196" y1="38" x2="28" y2="118" stroke="#10b981" stroke-width="1.5" stroke-dasharray="5 3"/>
<!-- point E on bottom: extend from C parallel to BD, E ≈ (32,118) -->
<circle cx="32" cy="118" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<text x="20" y="132" font-size="9" fill="#047857" font-weight="700" font-family="Unbounded,sans-serif">E</text>
<!-- parallelogram BDCE highlighted -->
<polygon points="252,118 84,38 196,38 32,118" fill="rgba(16,185,129,.14)" stroke="#10b981" stroke-width="1" stroke-dasharray="4 2"/>
<!-- equal angle arcs -->
<path d="M40,108 A16,16 0 0,1 50,96" stroke="#8b5cf6" stroke-width="1.5" fill="rgba(139,92,246,.2)"/>
<path d="M222,96 A16,16 0 0,1 232,108" stroke="#8b5cf6" stroke-width="1.5" fill="rgba(139,92,246,.2)"/>
<text x="56" y="107" font-size="8" fill="#6d28d9" font-family="JetBrains Mono,monospace">α</text>
<text x="208" y="107" font-size="8" fill="#6d28d9" font-family="JetBrains Mono,monospace">α</text>
<circle cx="28" cy="118" r="3.5" fill="#8b5cf6"/><circle cx="252" cy="118" r="3.5" fill="#8b5cf6"/>
<circle cx="196" cy="38" r="3.5" fill="#8b5cf6"/><circle cx="84" cy="38" r="3.5" fill="#8b5cf6"/>
<text x="14" y="122" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="122" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="202" y="32" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="68" y="32" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
</svg></div>`);
html += makeCard('rule','Доказательство признака 2','16.3',`
<p>Дано: трапеция $ABCD$, $AD \\parallel BC$, $AC = BD$. Доказать: $AD = BC$.</p>
<p>Рассмотрим $\\triangle ADB$ и $\\triangle BCA$: $AD = BC$ нужно доказать... применим метод от противного или через высоты.</p>
<p>Из $A$ и $B$ опустим высоты $AH_1$, $BH_2$. В $\\triangle ACH_1$ и $\\triangle BDH_2$: $AC = BD$ (дано), $AH_1 = BH_2$ (высоты), значит $CH_1 = DH_2$. Откуда $AD = BC$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px"><svg viewBox="0 0 280 148" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Trapezoid with equal diagonals AC=BD and heights dropped -->
<polygon points="28,120 252,120 196,38 84,38" fill="rgba(139,92,246,.07)" stroke="#8b5cf6" stroke-width="2"/>
<!-- diagonals AC and BD -->
<line x1="28" y1="120" x2="196" y2="38" stroke="#10b981" stroke-width="2"/>
<line x1="252" y1="120" x2="84" y2="38" stroke="#d97706" stroke-width="2"/>
<!-- equal tick marks -->
<line x1="98" y1="86" x2="106" y2="78" stroke="#10b981" stroke-width="2.5"/>
<line x1="174" y1="86" x2="182" y2="78" stroke="#d97706" stroke-width="2.5"/>
<!-- heights from D and C (top) to base -->
<line x1="84" y1="38" x2="84" y2="120" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<line x1="196" y1="38" x2="196" y2="120" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<path d="M84,120 L84,112 L92,112" fill="none" stroke="#0891b2" stroke-width="1.5"/>
<path d="M196,120 L188,120 L188,112" fill="none" stroke="#0891b2" stroke-width="1.5"/>
<text x="88" y="134" font-size="8" fill="#0e7490" font-family="Unbounded,sans-serif">H₁</text>
<text x="190" y="134" font-size="8" fill="#0e7490" font-family="Unbounded,sans-serif">H₂</text>
<circle cx="28" cy="120" r="3.5" fill="#8b5cf6"/><circle cx="252" cy="120" r="3.5" fill="#8b5cf6"/>
<circle cx="196" cy="38" r="3.5" fill="#8b5cf6"/><circle cx="84" cy="38" r="3.5" fill="#8b5cf6"/>
<text x="14" y="124" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">A</text>
<text x="256" y="124" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">B</text>
<text x="202" y="32" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">C</text>
<text x="68" y="32" font-size="10" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">D</text>
<text x="140" y="144" text-anchor="middle" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">AC=BD ⟹ AD=BC (равнобедр.)</text>
</svg></div>`);
/* INTERACTIVE 1: SVG признак 1 — равные углы */
html += `<div class="wg" id="p16-sign1-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Признак 1: равные углы при основании → равнобедренная</div></div>
<div class="wg-help">Меняй угол слайдером. При равенстве углов A и B индикатор загорается.</div>
<div class="sliders">
<label>Угол A = <b id="p16-angA-val">70</b>°<input type="range" min="30" max="150" value="70" id="p16-angA-sl" style="accent-color:var(--sec-acc,var(--pri))"></label>
<label>Угол B = <b id="p16-angB-val">70</b>°<input type="range" min="30" max="150" value="70" id="p16-angB-sl" style="accent-color:var(--sec-acc,var(--pri))"></label>
</div>
<div id="p16-sign1-svg" style="display:flex;justify-content:center"></div>
<div id="p16-sign1-ind" style="padding:10px 16px;border-radius:10px;font-size:.95rem;font-weight:700;margin-top:10px;text-align:center;transition:background .3s"></div>
</div>`;
/* INTERACTIVE 2: SVG признак 2 — равные диагонали */
html += `<div class="wg" id="p16-sign2-wrap">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Признак 2: равные диагонали → равнобедренная</div></div>
<div class="wg-help">Тащи вершины трапеции. Когда диагонали становятся равны — трапеция равнобедренная (индикатор).</div>
<div id="p16-sign2-svg" style="display:flex;justify-content:center"></div>
<div id="p16-sign2-ind" style="padding:10px 16px;border-radius:10px;font-size:.95rem;font-weight:700;margin-top:10px;text-align:center;transition:background .3s"></div>
</div>`;
/* INTERACTIVE 3: Доказательство признака 1 */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Доказательство признака 1 пошагово</div></div>
<div id="p16-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p16-proof-step" style="padding:12px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;min-height:60px;line-height:1.65"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn primary" id="p16-proof-next">Дальше</button>
<button class="btn" id="p16-proof-restart">Сначала</button>
</div>
</div>`;
/* INTERACTIVE 4: Mini-quiz */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Верно или неверно? — Признаки трапеции</div></div>
<div id="p16-quiz-list"></div>
<div class="actions"><button class="btn primary" id="p16-quiz-check">Проверить</button><button class="btn" id="p16-quiz-reset">Сначала</button></div>
<div class="feedback" id="p16-quiz-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 5: Тренажёр */
html += `<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Тренажёр: равнобедренная ли трапеция?</div></div>
<div class="score-display"><span>Задача <b id="p16-tr-i">1</b> / 5</span><span>Очки: <b id="p16-tr-score">0</b></span></div>
<div id="p16-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.05rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p16-tr-ans" class="tinp" placeholder="Ответ" style="width:110px">
<button class="btn primary" id="p16-tr-go">Проверить</button>
<button class="btn" id="p16-tr-start">Начать</button>
</div>
<div class="feedback" id="p16-tr-fb" style="display:none"></div>
</div>`;
/* INTERACTIVE 6: Босс §16 */
html += `<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §16</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">+5 XP за каждую верную задачу.</div>
<div id="p16-boss-tasks"></div>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p16-read-btn" onclick="addXp(10,'p16-read');bumpProgress('p16',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>
Я прочитал §16 (+10 XP)
</button>
</div>`;
html += secNav('p15','final1');
box.innerHTML = html;
/* == Признак 1: слайдеры углов == */
(function(){
function drawSign1(){
const angA=+document.getElementById('p16-angA-sl').value;
const angB=+document.getElementById('p16-angB-sl').value;
document.getElementById('p16-angA-val').textContent=angA;
document.getElementById('p16-angB-val').textContent=angB;
const W=380, H=200;
const botY=170, Ax=50, Bx=330;
// Compute top vertices from angles
const radA=(180-angA)*Math.PI/180, radB=(180-angB)*Math.PI/180;
const height=100;
const Dx=Ax+height/Math.tan(radA), Dy=botY-height;
const Cx=Bx-height/Math.tan(radB), Cy=botY-height;
let s=`<svg viewBox="0 0 ${W} ${H}" style="width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:14px">`;
const A2={x:Ax,y:botY}, B2={x:Bx,y:botY}, C2={x:Math.max(20,Math.min(W-20,Cx)),y:Math.max(20,Math.min(H-20,Cy))}, D2={x:Math.max(20,Math.min(W-20,Dx)),y:Math.max(20,Math.min(H-20,Dy))};
s+=`<polygon points="${A2.x},${A2.y} ${B2.x},${B2.y} ${C2.x},${C2.y} ${D2.x},${D2.y}" fill="rgba(8,145,178,.10)" stroke="#0891b2" stroke-width="2.5"/>`;
// angle arc A
const arcR=20;
function arcSVG(O,p1,p2,col){ const a1=Math.atan2(p1.y-O.y,p1.x-O.x),a2=Math.atan2(p2.y-O.y,p2.x-O.x); return `<path d="M ${(O.x+arcR*Math.cos(a1)).toFixed(1)},${(O.y+arcR*Math.sin(a1)).toFixed(1)} A ${arcR},${arcR} 0 0,0 ${(O.x+arcR*Math.cos(a2)).toFixed(1)},${(O.y+arcR*Math.sin(a2)).toFixed(1)}" fill="none" stroke="${col}" stroke-width="2"/>`; }
s+=arcSVG(A2,D2,B2,'#ef4444'); s+=arcSVG(B2,A2,C2,'#10b981');
// labels
s+=`<text x="${A2.x-14}" y="${A2.y+4}" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">A</text>`;
s+=`<text x="${A2.x+6}" y="${A2.y-14}" font-size="10" fill="#ef4444" font-family="JetBrains Mono,monospace">${angA}°</text>`;
s+=`<text x="${B2.x+4}" y="${B2.y+4}" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">B</text>`;
s+=`<text x="${B2.x-26}" y="${B2.y-14}" font-size="10" fill="#10b981" font-family="JetBrains Mono,monospace">${angB}°</text>`;
s+=`<text x="${D2.x-14}" y="${D2.y-4}" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">D</text>`;
s+=`<text x="${C2.x+4}" y="${C2.y-4}" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">C</text>`;
s+=`</svg>`;
document.getElementById('p16-sign1-svg').innerHTML=s;
const eq=Math.abs(angA-angB)<=1;
const ind=document.getElementById('p16-sign1-ind');
if(eq){ ind.style.background='var(--ok-bg,#d1fae5)'; ind.style.color='var(--ok,#059669)'; ind.textContent='Углы равны (∠A = ∠B) — трапеция равнобедренная!'; addXp(2,'p16-sign1'); }
else { ind.style.background='var(--pri-soft)'; ind.style.color='var(--muted)'; ind.textContent=`∠A = ${angA}°, ∠B = ${angB}° — не равны, трапеция произвольная.`; }
}
document.getElementById('p16-angA-sl').addEventListener('input',drawSign1);
document.getElementById('p16-angB-sl').addEventListener('input',drawSign1);
drawSign1();
})();
/* == Признак 2: draggable трапеция с диагоналями == */
(function(){
const W=400, H=260;
let A={x:50,y:220}, B={x:350,y:220}, C={x:270,y:60}, D={x:130,y:60};
function dist(P,Q){ return Math.hypot(Q.x-P.x,Q.y-P.y); }
function redraw2(){
const ac=dist(A,C), bd=dist(B,D);
const eq=Math.abs(ac-bd)<6;
let s=`<svg id="p16-s2-svg" viewBox="0 0 ${W} ${H}" style="width:100%;max-width:420px;background:var(--card);border:1px solid var(--border);border-radius:14px;touch-action:none">`;
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x},${D.y}" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2.5"/>`;
s+=`<line x1="${A.x}" y1="${A.y}" x2="${C.x}" y2="${C.y}" stroke="${eq?'#10b981':'#f59e0b'}" stroke-width="2" stroke-dasharray="5 3"/>`;
s+=`<line x1="${B.x}" y1="${B.y}" x2="${D.x}" y2="${D.y}" stroke="${eq?'#10b981':'#8b5cf6'}" stroke-width="2" stroke-dasharray="5 3"/>`;
s+=`<text x="${((A.x+C.x)/2-20).toFixed(1)}" y="${((A.y+C.y)/2).toFixed(1)}" font-size="10" fill="#b45309" font-family="JetBrains Mono,monospace">AC=${ac.toFixed(1)}</text>`;
s+=`<text x="${((B.x+D.x)/2+4).toFixed(1)}" y="${((B.y+D.y)/2).toFixed(1)}" font-size="10" fill="#6d28d9" font-family="JetBrains Mono,monospace">BD=${bd.toFixed(1)}</text>`;
const vNames=['A','B','C','D'], vPts=[A,B,C,D];
vPts.forEach((V,i)=>{
s+=`<circle cx="${V.x}" cy="${V.y}" r="9" fill="#0891b2" opacity=".2" class="p16-vd2" data-v="${vNames[i]}" style="cursor:grab"/>`;
s+=`<circle cx="${V.x}" cy="${V.y}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2" class="p16-vd2" data-v="${vNames[i]}" style="cursor:grab"/>`;
const offx=[-14,12,12,-14][i], offy=[14,14,-12,-12][i];
s+=`<text x="${V.x+offx}" y="${V.y+offy}" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${vNames[i]}</text>`;
});
s+=`</svg>`;
const wrap=document.getElementById('p16-sign2-svg'); wrap.innerHTML=s;
const svgEl=wrap.querySelector('svg');
svgEl.querySelectorAll('.p16-vd2').forEach(el=>{
el.addEventListener('pointerdown',ev=>{
if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault();
const vname=el.dataset.v;
function onMove(e){
e.preventDefault();
const rect=svgEl.getBoundingClientRect();
const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width));
const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height));
if(vname==='A'){ A={x:nx,y:ny}; B.y=ny; }
else if(vname==='B'){ B={x:nx,y:ny}; A.y=ny; }
else if(vname==='C'){ C={x:nx,y:ny}; D.y=ny; }
else if(vname==='D'){ D={x:nx,y:ny}; C.y=ny; }
redraw2();
}
function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); }
window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp);
});
});
const ind=document.getElementById('p16-sign2-ind');
if(eq){ ind.style.background='var(--ok-bg,#d1fae5)'; ind.style.color='var(--ok,#059669)'; ind.textContent='AC ≈ BD — диагонали равны → трапеция равнобедренная!'; addXp(2,'p16-sign2'); }
else { ind.style.background='var(--pri-soft)'; ind.style.color='var(--muted)'; ind.textContent=`AC = ${ac.toFixed(1)}, BD = ${bd.toFixed(1)} — не равны, трапеция произвольная.`; }
}
redraw2();
})();
/* == Доказательство признака 1 (redesigned) == */
(function(){
// Isosceles trapezoid ABCD: A,B = bottom base; C,D = top base
// A bottom-left, B bottom-right, C top-right, D top-left
// AD || BC (bottom base AD is longer)
const A={x:45,y:215}, B={x:295,y:215}, C={x:235,y:75}, D={x:105,y:75};
// E on segment AB extended: draw CE || BD until it hits AB
// CE is parallel to AB (the lateral side), so E is on AD (bottom base) with BE = DC direction
// CE || AB means: from C go in direction of AB. AB direction = B-A = (250,0), unit = (1,0)
// CE length = AB distance along CE until bottom line y=215
// Since C=(235,75) and we go parallel to A->B... wait, CE must be parallel to BD (the lateral side B to C already goes up-left)
// The proof: through C draw CE || BD. Let's compute E properly.
// BD direction: D-B = (105-295, 75-215) = (-190,-140)
// From C=(235,75) in direction (-190,-140) scaled to reach y=215:
// 75 + t*(-140) = 215 => t = -140/(-140) = 1 => E = (235+(-190)*1, 75+(-140)*1) = (45, -65) — off screen
// Instead use: CE || AB (lateral side of the parallelogram ABCE approach)
// Actually the standard proof draws CE || AB (a lateral side) to form parallelogram ABCE
// where E is on AD. Let's verify: AB is lateral side A->B going right. No wait —
// In the standard proof, we drop CE parallel to AB (lateral AB = right side).
// Actually the proof uses: through D draw DE || AB where E is on BC. Let's use that:
// Draw DE || AB, E on BC. DE || AB and DE = AB (parallelogram ABDE since AB || DE and AB=DE).
// Then triangle DCE has DC=AB (proven), CE=AD-AE (proven equal)...
// Simplest clean proof: extend through D draw DF || BC until it hits AB extended, forming parallelogram.
// Let's use the standard textbook approach with clean coordinates:
// Trapezoid: A(45,215) B(295,215) C(235,75) D(105,75). Angles A and B are at bottom.
// Draw CE || AB where E on AD. E = A + (some offset).
// AB direction is horizontal. CE || AB means E has same y as A. CE is vertical? No — CE || AB means horizontal.
// From C(235,75) go in direction of AB vector (250,0)... wait that goes to the right, not toward AD.
// The correct construction: through C draw CE PARALLEL to BD (not AB).
// BD vector = D - B = (105-295, 75-215) = (-190,-140).
// From C=(235,75) in direction BD-normalized until y=215:
// y: 75 + t*(-140) = 215 -> t = 1. x: 235 + 1*(-190) = 45. E = (45, 215) = A!
// So E coincides with A when the trapezoid is isosceles. Use a slightly different construction.
// Use the cleaner proof: through B draw BF || DC, F on AD extended.
// DC vector = C-D = (130,0). BF || DC -> F = B + t*(130,0)/130 ... F at y=215, F=(B.x + s, 215).
// That means F is just to the right of B which isn't helpful.
// BEST approach for the proof: use the auxiliary point E on AD such that CE || AB (lateral).
// This means: vector CE = k * vector AB-lateral.
// Lateral AB: from A(45,215) to B... no, lateral sides are AD and BC.
// AD: from A(45,215) to D(105,75). BC: from B(295,215) to C(235,75).
// CE || AD: from C(235,75) in direction of AD = (60,-140). Scale to reach y=215: 75+t*(-140)=215 -> t=-1 (wrong direction).
// CE || BC: from C(235,75) in reverse direction of BC = (60,-140) -> same issue.
//
// SIMPLEST CLEAN APPROACH for the diagram: just show the key steps visually.
// Step 1: Trapezoid with equal angles marked.
// Step 2: Drop perpendiculars from D and C to base AB -> H1, H2. Show right triangles.
// Step 3: The right triangles ADH1 and BCH2 are congruent (hypotenuse-leg).
// Step 4: Conclusion AD = BC.
// This is cleaner to draw and easier to understand.
const H1={x:D.x, y:A.y}; // foot of perpendicular from D to AB
const H2={x:C.x, y:B.y}; // foot of perpendicular from C to AB
const cx=(A.x+B.x+C.x+D.x)/4, cy=(A.y+B.y+C.y+D.y)/4;
const steps=[
{
h:'base',
badge:'Дано',
text:'<b>Дано:</b> трапеция $ABCD$, $AD \\parallel BC$ (основания), $\\angle DAB = \\angle CBA$ (углы при основании $AB$ равны).<br>Доказать: $AD = BC$ (трапеция равнобедренная).',
},
{
h:'heights',
badge:'Шаг 1',
text:'<b>Шаг 1.</b> Проведём высоты $DH_1 \\perp AB$ и $CH_2 \\perp AB$ из вершин $D$ и $C$ на основание $AB$. Получаем два прямоугольных треугольника: $\\triangle DH_1A$ и $\\triangle CH_2B$.',
},
{
h:'triangles',
badge:'Шаг 2',
text:'<b>Шаг 2.</b> В прямоугольных треугольниках $\\triangle DH_1A$ и $\\triangle CH_2B$:<br>' +
'— $DH_1 = CH_2$ (высоты трапеции с параллельными основаниями равны)<br>' +
'— $\\angle H_1AD = \\angle H_2BC = \\angle DAB = \\angle CBA$ (дано)',
},
{
h:'congruent',
badge:'Шаг 3',
text:'<b>Шаг 3.</b> По признаку «угол и катет» прямоугольного треугольника:<br>$\\triangle DH_1A \\cong \\triangle CH_2B$.<br>Из равенства следует: $AD = BC$ (гипотенузы равны).',
},
{
h:'done',
badge:'Вывод',
text:'<b>Вывод.</b> Поскольку $AD = BC$ (боковые стороны равны), трапеция $ABCD$ является равнобедренной. <b>ч.т.д.</b>',
},
];
let step=0;
function draw(h){
let s=`<svg viewBox="0 0 340 260" style="width:100%;max-width:370px;background:var(--card);border:1px solid var(--border);border-radius:12px">`;
// base trapezoid
const trapFill = (h==='base') ? 'rgba(8,145,178,.12)' : 'rgba(8,145,178,.06)';
s+=`<polygon points="${A.x},${A.y} ${B.x},${B.y} ${C.x},${C.y} ${D.x},${D.y}" fill="${trapFill}" stroke="#0891b2" stroke-width="2.5"/>`;
// equal-angle arcs at A and B
if(h==='base'||h==='heights'||h==='triangles'||h==='congruent'||h==='done'){
const r=24;
// angle at A (between AB rightward and AD upward)
const a1A=Math.atan2(D.y-A.y,D.x-A.x), a2A=Math.atan2(B.y-A.y,B.x-A.x);
s+=`<path d="M ${(A.x+r*Math.cos(a1A)).toFixed(1)},${(A.y+r*Math.sin(a1A)).toFixed(1)} A ${r},${r} 0 0,1 ${(A.x+r*Math.cos(a2A)).toFixed(1)},${(A.y+r*Math.sin(a2A)).toFixed(1)}" fill="rgba(239,68,68,.15)" stroke="#ef4444" stroke-width="1.8"/>`;
// angle at B (between BC leftward and BA leftward)
const a1B=Math.atan2(C.y-B.y,C.x-B.x), a2B=Math.atan2(A.y-B.y,A.x-B.x);
s+=`<path d="M ${(B.x+r*Math.cos(a1B)).toFixed(1)},${(B.y+r*Math.sin(a1B)).toFixed(1)} A ${r},${r} 0 0,0 ${(B.x+r*Math.cos(a2B)).toFixed(1)},${(B.y+r*Math.sin(a2B)).toFixed(1)}" fill="rgba(239,68,68,.15)" stroke="#ef4444" stroke-width="1.8"/>`;
s+=`<text x="${A.x+28}" y="${A.y-8}" font-size="11" fill="#dc2626" font-weight="700" font-family="JetBrains Mono,monospace">α</text>`;
s+=`<text x="${B.x-38}" y="${B.y-8}" font-size="11" fill="#dc2626" font-weight="700" font-family="JetBrains Mono,monospace">α</text>`;
}
// heights DH1 and CH2
if(h==='heights'||h==='triangles'||h==='congruent'||h==='done'){
s+=`<line x1="${D.x}" y1="${D.y}" x2="${H1.x}" y2="${H1.y}" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5 3"/>`;
s+=`<line x1="${C.x}" y1="${C.y}" x2="${H2.x}" y2="${H2.y}" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5 3"/>`;
const sq=7;
// right angle at H1
s+=`<polyline points="${H1.x+sq},${H1.y} ${H1.x+sq},${H1.y-sq} ${H1.x},${H1.y-sq}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
// right angle at H2
s+=`<polyline points="${H2.x-sq},${H2.y} ${H2.x-sq},${H2.y-sq} ${H2.x},${H2.y-sq}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
// H1, H2 labels
s+=`<text x="${H1.x-4}" y="${H1.y+14}" text-anchor="middle" font-size="9" fill="#92400e" font-weight="700" font-family="Unbounded,sans-serif">H₁</text>`;
s+=`<text x="${H2.x+4}" y="${H2.y+14}" text-anchor="middle" font-size="9" fill="#92400e" font-weight="700" font-family="Unbounded,sans-serif">H₂</text>`;
}
// triangles highlighted
if(h==='triangles'||h==='congruent'||h==='done'){
s+=`<polygon points="${A.x},${A.y} ${H1.x},${H1.y} ${D.x},${D.y}" fill="rgba(8,145,178,.20)" stroke="#0891b2" stroke-width="1.8"/>`;
s+=`<polygon points="${B.x},${B.y} ${H2.x},${H2.y} ${C.x},${C.y}" fill="rgba(16,185,129,.20)" stroke="#10b981" stroke-width="1.8"/>`;
}
// congruence marks on equal sides (height ticks + lateral side ticks)
if(h==='congruent'||h==='done'){
// height DH1 = CH2: tick marks
const h1mx=(D.x+H1.x)/2, h1my=(D.y+H1.y)/2;
const h2mx=(C.x+H2.x)/2, h2my=(C.y+H2.y)/2;
s+=`<line x1="${h1mx-5}" y1="${h1my}" x2="${h1mx+5}" y2="${h1my}" stroke="#0891b2" stroke-width="2.2"/>`;
s+=`<line x1="${h2mx-5}" y1="${h2my}" x2="${h2mx+5}" y2="${h2my}" stroke="#0891b2" stroke-width="2.2"/>`;
// lateral AD and BC equal marks
const adMx=(A.x+D.x)/2, adMy=(A.y+D.y)/2;
const bcMx=(B.x+C.x)/2, bcMy=(B.y+C.y)/2;
const adDx=D.x-A.x, adDy=D.y-A.y, adL=Math.hypot(adDx,adDy)||1;
const adPx=-adDy/adL*6, adPy=adDx/adL*6;
s+=`<line x1="${(adMx+adPx).toFixed(1)}" y1="${(adMy+adPy).toFixed(1)}" x2="${(adMx-adPx).toFixed(1)}" y2="${(adMy-adPy).toFixed(1)}" stroke="#ef4444" stroke-width="2.5"/>`;
s+=`<line x1="${(adMx+adPx+adDx/adL*4).toFixed(1)}" y1="${(adMy+adPy+adDy/adL*4).toFixed(1)}" x2="${(adMx-adPx+adDx/adL*4).toFixed(1)}" y2="${(adMy-adPy+adDy/adL*4).toFixed(1)}" stroke="#ef4444" stroke-width="2.5"/>`;
const bcDx=C.x-B.x, bcDy=C.y-B.y, bcL=Math.hypot(bcDx,bcDy)||1;
const bcPx=-bcDy/bcL*6, bcPy=bcDx/bcL*6;
s+=`<line x1="${(bcMx+bcPx).toFixed(1)}" y1="${(bcMy+bcPy).toFixed(1)}" x2="${(bcMx-bcPx).toFixed(1)}" y2="${(bcMy-bcPy).toFixed(1)}" stroke="#ef4444" stroke-width="2.5"/>`;
s+=`<line x1="${(bcMx+bcPx+bcDx/bcL*4).toFixed(1)}" y1="${(bcMy+bcPy+bcDy/bcL*4).toFixed(1)}" x2="${(bcMx-bcPx+bcDx/bcL*4).toFixed(1)}" y2="${(bcMy-bcPy+bcDy/bcL*4).toFixed(1)}" stroke="#ef4444" stroke-width="2.5"/>`;
}
// vertices
[[A,'A',[-14,8]],[B,'B',[8,8]],[C,'C',[8,-6]],[D,'D',[-14,-6]]].forEach(([P,lbl,[ox,oy]])=>{
s+=`<circle cx="${P.x}" cy="${P.y}" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>`;
s+=`<text x="${P.x+ox}" y="${P.y+oy}" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">${lbl}</text>`;
});
s+=`</svg>`;
document.getElementById('p16-proof-svg').innerHTML=s;
}
function show(){
const st=steps[step];
const total=steps.length;
// step counter badge
document.getElementById('p16-proof-step').innerHTML=
`<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">` +
`<span style="background:var(--sec-acc-d,var(--pri2));color:#fff;font-size:.72rem;font-weight:800;padding:3px 10px;border-radius:99px;font-family:Unbounded,sans-serif">${st.badge}</span>` +
`<span style="font-size:.78rem;color:var(--muted);font-weight:600">${step+1} / ${total}</span>` +
`</div>${st.text}`;
renderMath(document.getElementById('p16-proof-step'));
draw(st.h);
const btn=document.getElementById('p16-proof-next');
btn.textContent=step<total-1?'Далее':'Готово';
btn.disabled=false;
}
document.getElementById('p16-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){ step++; show(); }
else {
addXp(5,'p16-proof'); bumpProgress('p16',12);
document.getElementById('p16-proof-next').textContent='Изучено! +5 XP';
document.getElementById('p16-proof-next').disabled=true;
}
});
document.getElementById('p16-proof-restart').addEventListener('click',()=>{
step=0; show();
});
show();
})();
/* == Mini-quiz == */
(function(){
const qs=[
{text:'Если в трапеции ∠A = ∠B, то она равнобедренная.', ans:true},
{text:'Если в трапеции ∠A = ∠D, то она равнобедренная.', ans:false},
{text:'Если в трапеции AC = BD, то она равнобедренная.', ans:true},
{text:'Любая трапеция с равными диагоналями — прямоугольная.', ans:false},
{text:'Признак 1 и признак 2 — два разных способа доказать одно и то же.', ans:true},
];
const list=document.getElementById('p16-quiz-list');
list.innerHTML=qs.map((q,i)=>`
<div style="padding:10px 14px;background:var(--card);border:1px solid var(--border);border-radius:10px;margin-bottom:8px">
<div style="margin-bottom:6px;font-size:.95rem">${q.text}</div>
<div style="display:flex;gap:8px">
<button class="btn p16-qbtn" data-i="${i}" data-v="true">Верно</button>
<button class="btn p16-qbtn" data-i="${i}" data-v="false">Неверно</button>
</div>
</div>`).join('');
const sel={};
list.querySelectorAll('.p16-qbtn').forEach(btn=>{
btn.addEventListener('click',()=>{
const i=btn.dataset.i;
sel[i]=btn.dataset.v==='true';
list.querySelectorAll(`.p16-qbtn[data-i="${i}"]`).forEach(b=>b.className='btn p16-qbtn');
btn.className='btn primary p16-qbtn';
});
});
document.getElementById('p16-quiz-check').addEventListener('click',()=>{
let ok=0;
qs.forEach((q,i)=>{ if(sel[i]===q.ans) ok++; });
const fb=document.getElementById('p16-quiz-fb');
if(ok===qs.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p16-quiz');bumpProgress('p16',15);}
else feedback(fb,false,'Верно: '+ok+' из '+qs.length+'. Признак: углы при ОДНОМ ОСНОВАНИИ (A и B), а не при одной боковой стороне.');
});
document.getElementById('p16-quiz-reset').addEventListener('click',()=>{ Object.keys(sel).forEach(k=>delete sel[k]); list.querySelectorAll('.p16-qbtn').forEach(b=>b.className='btn p16-qbtn'); document.getElementById('p16-quiz-fb').style.display='none'; });
})();
/* == Тренажёр == */
(function(){
const tasks=[
{q:'В трапеции $\\angle A = \\angle B = 75°$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:1, hint:'Да, по признаку 1 (углы при основании AB равны)'},
{q:'В трапеции $\\angle A = 80°$, $\\angle D = 80°$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:0, hint:'Нет: ∠A и ∠D — при разных основаниях, это не признак'},
{q:'В трапеции $AC = BD = 13$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:1, hint:'Да, по признаку 2 (диагонали равны)'},
{q:'В трапеции $\\angle A = 65°$, $\\angle B = 65°$. Найди $\\angle D$.', ans:115, hint:'∠A+∠D=180°, ∠D=115°'},
{q:'Признак равнобедренной трапеции — равные (1) диагонали или (2) медианы? Введи 1 или 2.', ans:1, hint:'Признак — равные диагонали'},
];
let idx=0,score=0;
function show(){ document.getElementById('p16-tr-i').textContent=idx+1; document.getElementById('p16-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p16-tr-task')); document.getElementById('p16-tr-ans').value=''; document.getElementById('p16-tr-fb').style.display='none'; }
document.getElementById('p16-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p16-tr-score').textContent=0;show();});
document.getElementById('p16-tr-go').addEventListener('click',()=>{
const ans=+document.getElementById('p16-tr-ans').value; const fb=document.getElementById('p16-tr-fb');
if(ans===tasks[idx].ans){ score++;document.getElementById('p16-tr-score').textContent=score;addXp(3,'p16-train');bumpProgress('p16',5); if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p16-train-all');} }
else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint);
});
document.getElementById('p16-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p16-tr-go').click();});
show();
})();
/* == Босс §16 == */
(function(){
const tasks=[
{q:'В трапеции $\\angle A = \\angle B = 62°$. Найди $\\angle C$.', ans:118, hint:'∠B+∠C=180°, ∠C=118°'},
{q:'В трапеции $AC = 20$, $BD = 20$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:1, hint:'Да, диагонали равны — признак 2'},
{q:'В трапеции $\\angle D = 55°$. Если она равнобедренная, найди $\\angle C$.', ans:55, hint:'∠C = ∠D = 55° в равнобедренной трапеции'},
{q:'$\\angle A = 72°$, $\\angle B = 72°$. Найди $\\angle A + \\angle D$.', ans:180, hint:'∠A+∠D = 180° (свойство трапеции)'},
];
const bossBox=document.getElementById('p16-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="p16-boss-a${i}" placeholder="Ответ" style="width:100px">
<button class="btn primary small" onclick="(function(){const v=+document.getElementById('p16-boss-a${i}').value;const fb=document.getElementById('p16-boss-fb${i}');if(v===${t.ans}){feedback(fb,true,'Верно! +5 XP');if(!p16BossSolved.has(${i})){p16BossSolved.add(${i});addXp(5,'p16-boss${i}');bumpProgress('p16',10);}}else feedback(fb,false,'Неверно. Подсказка: ${t.hint}');})()">Проверить</button>
</div>
<div class="feedback" id="p16-boss-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p16BossSolved=new Set();
})();
renderMath(box);
}
/* ============================================================
ФИНАЛ ГЛАВЫ 1 — Итоги · 7 боссов · Достижение
============================================================ */
function buildFinal1(){
const box = document.getElementById('final1-body');
let html = '';
/* === ЧАСТЬ 1: Итоговая шпаргалка === */
html += `<div class="card" style="border-color:var(--sec-acc,var(--pri));background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)))">
<div class="card-header">
<div class="card-icon 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>
</div>
<div class="card-title">Итоговая шпаргалка · Вся Глава 1</div>
</div>
<div class="card-body">
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px">
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§13 Многоугольники</div>
<p style="font-size:.88rem;margin-bottom:5px">Диагонали: $D = \\dfrac{n(n-3)}{2}$</p>
<p style="font-size:.88rem;margin-bottom:5px">Сумма углов: $(n-2)\\cdot180°$</p>
<p style="font-size:.88rem;margin-bottom:0">Сумма внешних: $360°$ (всегда)</p>
<p style="font-size:.78rem;color:var(--muted);margin-top:5px">Пример: шестиугольник — 9 диагоналей, $720°$ внутренних</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§46 Параллелограмм</div>
<p style="font-size:.86rem;margin-bottom:4px"><b>Свойства:</b> противоположные стороны равны; противоположные углы равны; смежные углы в сумме $180°$; диагонали делятся пополам</p>
<p style="font-size:.86rem;margin-bottom:0"><b>Признаки:</b> 2 пары равных сторон; 1 пара параллельных и равных; диагонали делятся пополам</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§78 Прямоугольник</div>
<p style="font-size:.88rem;margin-bottom:5px">Все углы $= 90°$</p>
<p style="font-size:.88rem;margin-bottom:5px">Диагонали равны: $AC = BD$</p>
<p style="font-size:.88rem;margin-bottom:0">$d = \\sqrt{a^2 + b^2}$</p>
<p style="font-size:.78rem;color:var(--muted);margin-top:5px">Признак: параллелограмм с равными диагоналями</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§9 Ромб</div>
<p style="font-size:.88rem;margin-bottom:5px">Все стороны равны</p>
<p style="font-size:.88rem;margin-bottom:5px">Диагонали $\\perp$ и делят углы пополам</p>
<p style="font-size:.88rem;margin-bottom:0">$S = \\dfrac{d_1 \\cdot d_2}{2}$</p>
<p style="font-size:.78rem;color:var(--muted);margin-top:5px">Пример: ромб со стороной 5 и диагоналями 6, 8 — $S=24$</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§10 Квадрат</div>
<p style="font-size:.88rem;margin-bottom:5px">Прямоугольник + ромб одновременно</p>
<p style="font-size:.88rem;margin-bottom:5px">$d = a\\sqrt{2}$</p>
<p style="font-size:.88rem;margin-bottom:0">$S = a^2$, $P = 4a$</p>
<p style="font-size:.78rem;color:var(--muted);margin-top:5px">Все свойства ромба и прямоугольника вместе</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§11 Теорема Фалеса</div>
<p style="font-size:.88rem;margin-bottom:5px">Параллельные прямые отсекают пропорциональные отрезки</p>
<p style="font-size:.88rem;margin-bottom:0">Если $n$ параллельных делят одну прямую на равные части — они делят на равные части и другую прямую</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§12 Медианы</div>
<p style="font-size:.88rem;margin-bottom:5px">3 медианы пересекаются в центроиде $G$</p>
<p style="font-size:.88rem;margin-bottom:0">$G$ делит каждую медиану в отношении $2:1$ от вершины</p>
<p style="font-size:.78rem;color:var(--muted);margin-top:5px">$BG = \\tfrac{2}{3}m_b$, $GM_b = \\tfrac{1}{3}m_b$</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§13 Средняя линия &Delta;</div>
<p style="font-size:.88rem;margin-bottom:5px">Соединяет середины двух сторон</p>
<p style="font-size:.88rem;margin-bottom:5px">Параллельна третьей стороне</p>
<p style="font-size:.88rem;margin-bottom:0">$MN = \\dfrac{1}{2} \\cdot BC$</p>
</div>
<div style="background:var(--card);border:1.5px solid var(--border);border-radius:11px;padding:12px">
<div style="font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§1416 Трапеция</div>
<p style="font-size:.88rem;margin-bottom:5px">Одна пара параллельных сторон ($a \\parallel b$)</p>
<p style="font-size:.88rem;margin-bottom:5px">Средняя линия: $m = \\dfrac{a+b}{2}$</p>
<p style="font-size:.88rem;margin-bottom:0">Равнобедренная: углы при основании равны, диагонали равны</p>
<p style="font-size:.78rem;color:var(--muted);margin-top:5px">Признаки: равные углы при осн. или равные диагонали</p>
</div>
</div>
</div>
</div>`;
/* === ЧАСТЬ 2: Карта связей — иерархия четырёхугольников === */
html += `<div class="wg" id="final1-hier-wrap">
<div class="wg-header"><span class="wg-badge">КАРТА СВЯЗЕЙ</span><div class="wg-title">Иерархия четырёхугольников</div></div>
<div class="wg-help">Нажми на фигуру, чтобы увидеть её ключевые свойства. Стрелки означают «является частным случаем».</div>
<div id="final1-hier-svg" style="display:flex;justify-content:center;overflow-x:auto"></div>
<div id="final1-hier-info" style="min-height:56px;padding:12px 16px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-top:10px;font-size:.9rem;line-height:1.65;color:var(--text)">Нажми на фигуру в схеме выше</div>
</div>`;
/* === ЧАСТЬ 3: 7 боссов === */
html += `<div class="wg" style="border-color:#d97706;background:linear-gradient(135deg,var(--card),#fef9c3)">
<div class="wg-header">
<span class="wg-badge" style="background:#d97706">7 БОССОВ ГЛАВЫ 1</span>
<div class="wg-title">Интегрированные задачи</div>
</div>
<div class="wg-help">Каждая задача объединяет 2–3 темы главы. +10 XP за каждого побеждённого босса. Победи всех семерых — получишь +50 XP и достижение!</div>
<div id="final1-bosses"></div>
</div>`;
/* === ЧАСТЬ 4: Финальная плашка === */
html += `<div id="final1-finish" style="display:none;margin-top:20px;padding:24px;background:linear-gradient(135deg,#fef3c7,#d1fae5);border:2px solid var(--ok,#10b981);border-radius:16px;text-align:center">
<div style="font-family:'Unbounded',sans-serif;font-size:1.2rem;font-weight:900;color:#065f46;margin-bottom:10px">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:28px;height:28px;vertical-align:middle;margin-right:6px"><polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26"/></svg>
Мастер многоугольников Главы 1!
</div>
<p style="color:#065f46;font-size:.95rem;margin-bottom:16px">Ты победил всех 7 боссов и освоил всю Главу 1. Это серьёзно!</p>
<a href="/textbook/geometry-8-ch2" class="btn primary" style="font-size:.98rem;padding:12px 28px">
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
Перейти к Главе 2
</a>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="final1-read-btn" onclick="addXp(10,'final1-read');bumpProgress('final1',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('p16', null);
box.innerHTML = html;
/* === JS: Карта связей SVG === */
(function(){
const W = 680, H = 320;
const nodes = [
{ id:'quad', x:340, y:30, label:'Четырёхугольник', r:52,
props:'4 стороны, 4 угла, сумма углов = 360°. Диагонали — отрезки, соединяющие несмежные вершины.' },
{ id:'trap', x:120, y:130, label:'Трапеция', r:42,
props:'Одна пара параллельных сторон. Средняя линия = (a+b)/2. Признак равнобедренной: равные углы при основании или равные диагонали.' },
{ id:'istrap', x:90, y:250, label:'Равнобедренная трапеция', r:42,
props:'Боковые стороны равны. Углы при каждом основании равны. Диагонали равны.' },
{ id:'para', x:400, y:130, label:'Параллелограмм', r:48,
props:'Две пары параллельных сторон. Противоположные стороны и углы равны. Диагонали делятся пополам.' },
{ id:'rect', x:290, y:250, label:'Прямоугольник', r:44,
props:'Все углы = 90°. Диагонали равны: AC = BD. d = √(a²+b²). Признак: параллелограмм с равными диагоналями.' },
{ id:'rhomb', x:490, y:250, label:'Ромб', r:40,
props:'Все стороны равны. Диагонали ⊥ и делят углы пополам. S = d₁·d₂/2.' },
{ id:'sq', x:390, y:300, label:'Квадрат', r:38,
props:'Прямоугольник и ромб одновременно. d = a√2. S = a². Все свойства ромба и прямоугольника.' },
];
const edges = [
['quad','trap'], ['quad','para'],
['trap','istrap'], ['para','rect'], ['para','rhomb'],
['rect','sq'], ['rhomb','sq'],
];
let sel = null;
function draw(selId){
const colors = { quad:'#d97706', trap:'#f97316', istrap:'#ef4444', para:'#8b5cf6', rect:'#2563eb', rhomb:'#0891b2', sq:'#059669' };
let s = `<svg viewBox="0 0 ${W} ${H}" style="width:100%;max-width:700px;background:var(--card);border:1.5px solid var(--border);border-radius:14px;cursor:pointer">`;
s += `<defs><marker id="fh-arr" markerWidth="7" markerHeight="7" refX="6" refY="3.5" orient="auto"><polygon points="0 0,7 3.5,0 7" fill="#94a3b8"/></marker></defs>`;
edges.forEach(([a,b])=>{
const na=nodes.find(n=>n.id===a), nb=nodes.find(n=>n.id===b);
const dx=nb.x-na.x, dy=nb.y-na.y, len=Math.sqrt(dx*dx+dy*dy);
const sx=na.x+dx/len*na.r, sy=na.y+dy/len*na.r;
const ex=nb.x-dx/len*(nb.r+7), ey=nb.y-dy/len*(nb.r+7);
const isActive = selId===a||selId===b;
s += `<line x1="${sx}" y1="${sy}" x2="${ex}" y2="${ey}" stroke="${isActive?'#d97706':'#94a3b8'}" stroke-width="${isActive?2.5:1.5}" marker-end="url(#fh-arr)"/>`;
});
nodes.forEach(n=>{
const isS = selId===n.id;
const col = colors[n.id] || '#d97706';
s += `<ellipse cx="${n.x}" cy="${n.y}" rx="${n.r}" ry="${n.r*0.52}" fill="${isS?col:'var(--card)'}" stroke="${col}" stroke-width="${isS?3:2}" data-nid="${n.id}" style="cursor:pointer;transition:all .18s"/>`;
const words = n.label.split(' ');
const line1 = words.slice(0,2).join(' '), line2 = words.slice(2).join(' ');
const textCol = isS ? '#fff' : col;
if(line2){
s += `<text x="${n.x}" y="${n.y-5}" text-anchor="middle" font-size="9" font-weight="800" font-family="Unbounded,sans-serif" fill="${textCol}" style="pointer-events:none">${line1}</text>`;
s += `<text x="${n.x}" y="${n.y+8}" text-anchor="middle" font-size="9" font-weight="800" font-family="Unbounded,sans-serif" fill="${textCol}" style="pointer-events:none">${line2}</text>`;
} else {
s += `<text x="${n.x}" y="${n.y+4}" text-anchor="middle" font-size="9" font-weight="800" font-family="Unbounded,sans-serif" fill="${textCol}" style="pointer-events:none">${line1}</text>`;
}
});
s += '</svg>';
document.getElementById('final1-hier-svg').innerHTML = s;
document.getElementById('final1-hier-svg').querySelector('svg').addEventListener('click', function(e){
const el = e.target.closest('[data-nid]');
if(!el) return;
const nid = el.dataset.nid;
sel = (sel===nid) ? null : nid;
const nd = nodes.find(n=>n.id===nid);
if(sel && nd) document.getElementById('final1-hier-info').innerHTML = '<b>' + nd.label + '</b>: ' + nd.props;
else document.getElementById('final1-hier-info').textContent = 'Нажми на фигуру в схеме выше';
draw(sel);
});
}
draw(null);
})();
/* === JS: 7 боссов === */
(function(){
const bosses = [
{
n: 1,
title: 'Многоугольники и углы',
color: '#d97706',
cond: 'В выпуклом многоугольнике сумма внутренних углов равна $1620°$.',
parts: [
{ label: 'Сколько у него сторон?', ans: 11, hint: '$(n-2)\\cdot180°=1620°$ → $n-2=9$ → $n=11$' },
{ label: 'Сколько диагоналей?', ans: 44, hint: '$\\dfrac{11\\cdot(11-3)}{2}=\\dfrac{88}{2}=44$' },
{ label: 'Если он правильный, найди один внутренний угол (в градусах).', ans: 147, hint: '$\\dfrac{1620°}{11}\\approx147.27°$ — ответ: 147 (до целых)', tol: 1 },
{ label: 'Один внешний угол правильного 11-угольника (в градусах, до целых).', ans: 33, hint: '$\\dfrac{360°}{11}\\approx32.7°$ — ответ: 33', tol: 1 },
],
},
{
n: 2,
title: 'Параллелограмм через диагональ',
color: '#8b5cf6',
cond: 'В параллелограмме $ABCD$ диагональ $BD$ делит его на два треугольника. $\\angle ABD=40°$, $\\angle ADB=65°$.',
parts: [
{ label: 'Найди угол A параллелограмма (в градусах).', ans: 75, hint: 'В треугольнике ABD: $\\angle A = 180°-40°-65°=75°$' },
{ label: 'Найди угол B параллелограмма (в градусах).', ans: 105, hint: 'Смежные углы параллелограмма в сумме $180°$: $\\angle B=180°-75°=105°$' },
{ label: 'Найди угол D параллелограмма (в градусах).', ans: 105, hint: 'Противоположные углы параллелограмма равны: $\\angle D=\\angle B=105°$' },
],
},
{
n: 3,
title: 'Прямоугольник и ромб',
color: '#2563eb',
cond: 'В прямоугольнике $ABCD$: $AB=6$, $AD=8$. Точки $M, N, K, L$ — середины сторон $AB, BC, CD, DA$ соответственно.',
parts: [
{ label: 'Найди длину MN (стороны MNKL).', ans: 5, hint: 'По теореме средней линии треугольника: $MN = \\tfrac{1}{2}AC$, $AC=\\sqrt{36+64}=10$, $MN=5$' },
{ label: 'Докажи, что MNKL — ромб. Все стороны MNKL равны? (1 — да, 0 — нет)', ans: 1, hint: 'Все стороны = 5 (по теореме средней линии во всех четырёх треугольниках). MNKL — ромб.' },
{ label: 'Найди площадь MNKL.', ans: 24, hint: '$S_{MNKL} = \\tfrac{1}{2}S_{ABCD}=\\tfrac{1}{2}\\cdot48=24$' },
],
},
{
n: 4,
title: 'Ромб и теорема Пифагора',
color: '#0891b2',
cond: 'В ромбе $ABCD$ сторона $a=10$, угол $\\angle A=60°$.',
parts: [
{ label: 'Найди длину меньшей диагонали AC (проходит через угол A=60°, единицы).', ans: 10, hint: 'Треугольник ABD при угле 60°: $\\triangle ABD$ равносторонний ($AB=BD=AD=10$). $AC=10$. Диагональ через вершины с углом 60° равна стороне.' },
{ label: 'Найди длину бо́льшей диагонали BD (единицы), используя теорему Пифагора.', ans: 17, hint: '$AO=5$ (полудиагональ AC), $BO=\\sqrt{10^2-5^2}=\\sqrt{75}\\approx8.66$. $BD=2\\cdot8.66\\approx17.32$. Округли до 17.', tol: 1 },
{ label: 'Найди площадь ромба (округли до целых).', ans: 87, hint: '$S=\\dfrac{d_1\\cdot d_2}{2}=\\dfrac{10\\cdot17.32}{2}\\approx86.6\\approx87$', tol: 1 },
],
},
{
n: 5,
title: 'Теорема Фалеса',
color: '#16a34a',
cond: 'По теореме Фалеса параллельные прямые делят отрезки пропорционально.',
parts: [
{ label: 'Отрезок AB разделён на 5 равных частей параллельными прямыми, пересекающими отрезок CD (CD ∥ AB, CD=20). Расстояние между соседними точками деления на CD (число).', ans: 4, hint: '5 равных частей делят CD тоже на 5 равных частей по теореме Фалеса: $20/5=4$' },
{ label: 'Отрезок AB=15 делится параллельными прямыми на n равных частей, расстояние между точками деления = 3. Найди n.', ans: 5, hint: 'Шаг = $AB/n = 15/n = 3$ → $n=5$' },
{ label: 'Три параллельные прямые делят отрезок PQ на части 4, 4, 4 (три равные части). Отрезок RS, параллельный PQ, имеет длину 30. Какова длина каждой части RS?', ans: 10, hint: 'По теореме Фалеса — тоже 3 равные части: $30/3=10$' },
],
},
{
n: 6,
title: 'Медиана и средняя линия',
color: '#e11d48',
cond: 'В треугольнике $ABC$: $AB=12$, $BC=16$, $AC=20$. Это прямоугольный треугольник ($\\angle B=90°$).',
parts: [
{ label: 'M — середина AB, N — середина AC. Найди MN (средняя линия треугольника).', ans: 8, hint: '$MN = \\tfrac{1}{2}BC = \\tfrac{1}{2}\\cdot16 = 8$' },
{ label: 'G — центроид. Длина медианы из B к середине AC: $m_b=\\tfrac{1}{2}AC=10$. Найди BG.', ans: 7, hint: '$BG = \\tfrac{2}{3}m_b = \\tfrac{2}{3}\\cdot10 = 6.67 \\approx 7$', tol: 1 },
{ label: 'В прямоугольном треугольнике медиана из прямого угла равна половине гипотенузы. Гипотенуза AC=20. Найди медиану из B (m_b).', ans: 10, hint: '$m_b = \\tfrac{1}{2}\\cdot AC = \\tfrac{1}{2}\\cdot20 = 10$' },
],
},
{
n: 7,
title: 'Трапеция (комплексная)',
color: '#f97316',
cond: 'В равнобедренной трапеции $ABCD$: $AD \\parallel BC$, $AD=20$, $BC=8$, боковая сторона $AB=10$.',
parts: [
{ label: 'Найди высоту трапеции h.', ans: 8, hint: 'Проекция основания: $(20-8)/2=6$. $h=\\sqrt{10^2-6^2}=\\sqrt{64}=8$' },
{ label: 'Найди среднюю линию трапеции m.', ans: 14, hint: '$m=\\dfrac{20+8}{2}=14$' },
{ label: 'Найди длину диагонали AC (округли до целых).', ans: 16, hint: 'Сдвиг горизонтальный = $(20-8)/2=6$. $AC=\\sqrt{(8+6)^2+8^2}=\\sqrt{196+64}=\\sqrt{260}\\approx16$', tol: 1 },
],
},
];
window.final1BossSolved = new Set();
const bossBox = document.getElementById('final1-bosses');
bossBox.innerHTML = bosses.map(b => {
const partsHtml = b.parts.map((p,pi) => `
<div style="padding:10px 0;border-top:1px dashed var(--border)">
<div style="font-size:.9rem;margin-bottom:7px">${p.label}</div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" class="tinp final1-boss-inp" data-b="${b.n-1}" data-p="${pi}" placeholder="Ответ" style="width:110px">
<button class="btn primary final1-boss-check" data-b="${b.n-1}" data-p="${pi}" style="background:${b.color};border-color:${b.color}">Проверить</button>
<span class="final1-boss-ok" data-b="${b.n-1}" data-p="${pi}" style="display:none;color:var(--ok);font-weight:700">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="width:16px;height:16px;vertical-align:middle"><polyline points="20 6 9 17 4 12"/></svg>
</span>
</div>
<div class="feedback final1-boss-fb" data-b="${b.n-1}" data-p="${pi}" style="display:none;margin-top:6px"></div>
</div>`).join('');
return `<div style="padding:16px;background:var(--card);border-radius:12px;border:2px solid ${b.color};margin-bottom:14px" id="final1-boss-card-${b.n-1}">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap">
<span style="background:${b.color};color:#fff;padding:4px 10px;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.7rem;font-weight:800">БОСС ${b.n}</span>
<span style="font-family:'Unbounded',sans-serif;font-size:.88rem;font-weight:800;color:${b.color}">${b.title}</span>
<span id="final1-boss-xp-${b.n-1}" style="margin-left:auto;display:none;background:var(--ok-bg);color:#065f46;padding:3px 10px;border-radius:99px;font-size:.78rem;font-weight:800">+10 XP</span>
</div>
<div style="padding:10px 14px;background:linear-gradient(135deg,#fef3c7,#fff);border-left:4px solid ${b.color};border-radius:9px;font-size:.92rem;line-height:1.6;margin-bottom:4px">${b.cond}</div>
${partsHtml}
</div>`;
}).join('');
/* attach logic */
function checkPart(bi, pi){
const boss = bosses[bi];
const part = boss.parts[pi];
const inp = bossBox.querySelector(`.final1-boss-inp[data-b="${bi}"][data-p="${pi}"]`);
const fb = bossBox.querySelector(`.final1-boss-fb[data-b="${bi}"][data-p="${pi}"]`);
const ok = bossBox.querySelector(`.final1-boss-ok[data-b="${bi}"][data-p="${pi}"]`);
if(!inp) return;
const val = parseFloat(inp.value);
const tol = part.tol !== undefined ? part.tol : 0;
if(Math.abs(val - part.ans) <= tol){
feedback(fb, true, 'Верно! ' + (part.hint ? '<br><span style="font-size:.82rem;opacity:.85">' + part.hint + '</span>' : ''));
inp.disabled = true;
const btn = bossBox.querySelector(`.final1-boss-check[data-b="${bi}"][data-p="${pi}"]`);
if(btn) btn.disabled = true;
if(ok) ok.style.display = 'inline';
/* check if all parts of boss solved */
const allDone = boss.parts.every((_, pj) => {
const el = bossBox.querySelector(`.final1-boss-inp[data-b="${bi}"][data-p="${pj}"]`);
return el && el.disabled;
});
if(allDone && !window.final1BossSolved.has(bi)){
window.final1BossSolved.add(bi);
addXp(10, 'final1-boss-' + (bi+1));
const xpBadge = document.getElementById('final1-boss-xp-' + bi);
if(xpBadge) xpBadge.style.display = 'inline';
bumpProgress('final1', 8);
/* check all bosses done */
if(window.final1BossSolved.size === bosses.length){
setTimeout(()=>{
confetti();
if(!STATE.achievements.has('final1-master')){
STATE.achievements.set('final1-master', 'Мастер многоугольников Главы 1');
saveProgress();
const pop = document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent = 'Мастер многоугольников Главы 1!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'), 4000); }
}
addXp(50, 'final1-all-bosses');
bumpProgress('final1', 20);
const fin = document.getElementById('final1-finish');
if(fin) fin.style.display = 'block';
}, 500);
}
}
} else {
feedback(fb, false, 'Неверно. Подсказка: ' + part.hint);
}
}
bossBox.querySelectorAll('.final1-boss-check').forEach(btn=>{
btn.addEventListener('click', ()=>{ checkPart(+btn.dataset.b, +btn.dataset.p); });
});
bossBox.querySelectorAll('.final1-boss-inp').forEach(inp=>{
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ const btn=bossBox.querySelector(`.final1-boss-check[data-b="${inp.dataset.b}"][data-p="${inp.dataset.p}"]`); if(btn)btn.click(); } });
});
})();
renderMath(box);
}
</script>
</body>
</html>